【Android話題-5.1應用相關】說說service的啓動原理

考察內容:

  • service啓動有哪幾種方式?
  • service啓動過程中主要流程有哪些?
  • service啓動過程涉及哪些參與者,通信過程是怎樣的?

Service啓動原理

用startService啓動Service:

@Override
public ComponentName startService(Intent service) {
  return startServiceCommon(service, mUser);
}

ComponentName startServiceCommon(Intent service, ...){
  ComponentName cn = ActivityManagerNative.getDefault()
    .startService(mMainThread.getApplicationThread(), service, ...);
  return cn;
}

AMS中的處理:

ComponentName startService(IApplicationThread caller, Intent service, ...){
  ComponentName res = mServices.startServiceLocked(caller, service, ...);
  return res;
}

根據intent 查詢service Record對象:
每個應用端的Service在AMS中都對應一個ServiceRecore對象

ComponentName startServiceLocked(Intent service, ...){
  ServiceLookupResult res = retrieveServiceLocked(service, ...);
  ServiceRecord r = res.record;
  ...
  //查到ServiceRecord之後new了一個StartItem並加到pendingStart裏面,
  //爲後面調用onStartCommand準備
  r.pendingStart.add(new ServiceRecord.StartItem(r, ...));
  ...
  return startServiceInnerLocked(smap, service, r, ...);
}

ComponentName startServiceInnerLocked(ServiceMap smap, Intent service, ...){
  bringUpServiceLocked(r, service.getFlags(), callerFg, false);
}

final String bringUpServiceLocked(ServiceRecord r, ...){
  if(r.app != null && r.app.thread != null) {
    //如果Service已經啓動了,就調用下面這個函數,
    //它將觸發應用端的onStartCommand
    sendServiceArgsLocked(r, ...);
    return null;
  }
  //Service還沒啓動的情況:
  ProcessRecord app = mAm.getProcessRecordLocked(procName, ...);
  if(app != null && app.thread != null){
    //如果Service所在的進程已經啓動了,真正啓動Service
    //r.app就是在下面這個函數中設置的
    realStartServiceLocked(r, app, execInFg);
    return null;
  }
  //Service所在進程沒有啓動或者雖然已經啓動但還沒就緒的情況:
  if(app == null){
    //app進程還沒啓動,啓動進程
    app = mAm.startProcessLocked(procName, ...);
  }
  if(!mPendingServices.contains(r)){
    //把ServiceRecord加到pending列表裏面,等待進程啓動後再處理
    mPendingServices.add(r);
  }
  ...
}

進程啓動的流程圖:
在這裏插入圖片描述

  • AMS通過socket向Zygote發起啓動進程的請求
  • Zygote收到請求之後就會啓動進程
  • 應用進程向AMS發起attachApplication的binder調用,把自己的binder對象註冊到AMS(註冊完之後對於AMS來說這個應用就算是就緒了)
  • 通過應用進程註冊過來的applicationThread嚮應用端發起bindApplication調用,主應用初始化application(這步完事後AMS就可以處理Pending的組件了)

AMS中處理attachApplication的函數:

boolean attachApplicationLocked(IApplicationThread thread, ...) {
  ...
  mServices.attachApplicationLocked(app, processName);
  ...
  return true;
}
//處理pending的Service
boolean attachApplicationLocked(ProcessRecord proc, ...){
  for(int i = 0; i < mPendingServices.size(); i++){
    sr = mPendingServices.get(i);
    ...
    mPendingServices.remove(i--);
    //對每一個pending的service調用realStartServiceLocked函數真正啓動service
    realStartServiceLocked(sr, proc, ...);
  }
  ...
}

void realStartServiceLocked(ServiceRecord r, ProcessRecord app, ...){
  r.app = app;
  ...
  //嚮應用端發起IPC調用,應用收到後就會創建Service,執行應用裏的onCreate回調
  //參數r其實是一個binder對象: final class ServiceRecord extends Binder{}
  //這是要存在應用端的,應用端有一個用來保存service對象的map,這裏的r就是key
  app.thread.scheduleCreateService(r, r.serviceInfo, ...);
  ...
  //觸發應用端Service的onStartCommand
  sendServiceArgsLocked(r, ...);
}

應用端是如何處理AMS發過來的CreateServicd請求的:

private void handleCreateService(CreateServiceData data){
  //先拿到loadedApk
  LoadedApk packageInfo = getPackageInfoNoCheck(...);
  //加載Service類,並通過newInstance調用其構造函數,從而獲得Service對象
  Service service = (Service)cl.loadClass(data.info.name).newInstance();
  //給這個Service創建一個Context對象
  ContextImpl context = ContextImpl.createAppContext(this, ...);
  //獲取Application,這個application是在應用啓動的時候創建的
  Application app = packageInfo.makeApplication(flase, ...);
  //給service賦予上下文
  service.attach(context, this, ...);
  //執行Service的生命週期
  service.onCreate();
  mServices.put(data.token, service);
  ...
}

mServices是一個map:
ArrayMap<IBinder, Service>
private final void sendServiceArgsLocked(ServiceRecord r, ){
  while(r.pendingStarts.size() > 0){
    //取出每一個pending的startItem
    StartItem si = r.pendingStarts.remove(0);
    ...
    //嚮應用端發起IPC調用
    r.app.thread.scheduleServiceArgs(r, ...);
  }
}

AMS是如何觸發應用端Service的onStartCommand的

AMS調用r.app.thread.scheduleServiceArgs(r, …)後,應用端的處理:

public final void scheduleServiceArgs(IBinder token, ...) {
  //封裝了一個ServiceArgsData對象
  ServiceArgsData s = new ServiceArgsData();
  ...
  //丟到應用的主線程去處理
  sendMessage(H.SERVICE_ARGS, s);
}

//應用端主線程的處理:
private void handleServiceArgs(ServiceArgsData data) {
  //首先從mServices中把Service對象取出來。mService是一個map,其中:
  //key就是AMS中的ServiceRecord的對象
  //value就是應用端的Service對象
  //data.token就是AMS中的ServiceRecord對象
  Service s = mServices.get(data.token);
  if(s != null){
    ...
    //調用Service的onStartCommand
    s.onStartCommand(data.args, data.flags, data.startId);
    ...
  }
}

總結Service啓動的流程:

在這裏插入圖片描述
AMS端:

  • 先看Service啓動了沒有:如果啓動了就直接發指令,讓應用端執行onStartCommand()
  • 如果Service沒有啓動,就看它所在進程啓動了沒有:如果已經啓動,就去啓動Service,等Service啓動了之後再發送指令讓其執行onStartCommand
  • 如果進程沒有啓動就去啓動進程,等進程啓動後再啓動Service

應用端:

  • 先創建Service對象
  • 再賦予上下文
  • 最後調用生命週期onCreate()

用bindService啓動Service

啓動Service還有另一種情況:bindService的時候帶上BIND_AUTO_CREATE標記

int bindServiceLocked(IApplicationThread caller, ...){
  ...
  if((flags & Context.BIND_AUTO_CREATE) != 0){
    //如果帶上BIND_AUTO_CREATE標記
    bringUpServiceLocked(s, ...);
  }
  ...
}

binderService和startService的區別:

  • binderService不會觸發應用端的onStartCommand函數
  • 因爲binderService沒有把ServiceRecord加到mPendingStart隊列中

迴歸:說說service的啓動原理

  • service啓動有幾種方式?
    a) startService
    b) bindService帶BIND_AUTO_CREATE
  • service啓動過程主要流程有哪些?
    a)AMS端:
    1)先看Service啓動了沒有:如果啓動了就直接發指令,讓應用端執行onStartCommand()
    2)如果Service沒有啓動,就看它所在進程啓動了沒有:如果已經啓動,就去啓動Service,等Service啓動了之後再發送指令讓其執行onStartCommand
    3)如果進程沒有啓動就去啓動進程,等進程啓動後再啓動Service

b)應用端:
1)先創建Service對象
2)再賦予上下文
3)最後調用生命週期onCreate()

  • service啓動過程中有哪些參與者,通信過程是怎樣的?
    a)參考下圖:在這裏插入圖片描述
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章