Android中Service啟動過程及綁定過程

先了解以下2篇,可以對Service的啟動過程有更好的理解
Android應用程序進程啟動過程
Binder深入理解及與AIDL的使用和理解

Service啟動過程

1. 先看一下startService()的使用示例

  • 首先創建一個Service:

    public class ClientService extends Service {
          //startService()方式啟動Service不會回調此方法
        @Override
        public IBinder onBind(Intent intent) {
            return  null;
        }
          //當Service被創建時調用
        @Override
        public void onCreate() {
            super.onCreate();
        }
          //當Service運行時調用
        @Override
        public int onStartCommand(Intent intent, int flags, int startId) {
            return super.onStartCommand(intent, flags, startId);
        }
          //當Service被銷毀時調用
        @Override
        public void onDestroy() {
            super.onDestroy();
        }
    }
    
  • 啟動Service:

            Intent intent = new Intent(this, ClientService.class);
            startService(intent);
    

    以上就是startService()方式啟動Service的示例

生命周期:onCreate()->onStartCommand()->onDestory()。多次調用startService()時onCreate()只會執行一次,但是onStartCommand()會多次執行。多次調用stopService(),onDestory()只會執行一次。

通過startService()方式啟動Service,只要不調用stopService(),就會一直運行,不會與調用者產生關聯。它的onBind()沒有返回Binder對象,所以調用者無法與Service產生交互。

2. Service的啟動過程:

startService()其實是由ContextImpl類型的mBase成員變量調用的,這個變量在ActivityThread的performLaunchActivity()中被創建,并通過attach()與Activity產生關聯。

startService()的啟動分為3個部分

ContextImpl到AMS到過程

AMS到ActivityThread過程

ActivityThread啟動Service過程

  • ContextImpl到AMS到過程:

    那么看一下ContextImpl的startService():

           @Override
           public boolean stopService(Intent service) {
               return stopServiceCommon(service, mUser);
           }
    

    直接調用了stopServiceCommon():

           private ComponentName startServiceCommon(Intent service, boolean requireForeground,
                   UserHandle user) {\
                          //調用AMS的startService
                   ComponentName cn = ActivityManager.getService().startService(
                       mMainThread.getApplicationThread(), service, service.resolveTypeIfNeeded(
                                   getContentResolver()), requireForeground,
                                   getOpPackageName(), user.getIdentifier());
           }
    

    這里通過ActivityManager.getService()獲取了AMS,然后調用了它的startService(),而AMS是運行在SystemServer進程的,所以這里進程了進程間通訊。

    總結:可以看到,ContextImpl到AMS到過程就是簡單的通知了AMS開始工作

  • AMS到ActivityThread過程:

    這里繼續看AMS的startService():

           public ComponentName startService(IApplicationThread caller, Intent service,
                   String resolvedType, boolean requireForeground, String callingPackage, int userId){
                              //mServices是ActiveServices類型
                       res = mServices.startServiceLocked(caller, service,
                               resolvedType, callingPid, callingUid,
                               requireForeground, callingPackage, userId);
                   return res;
           }
    

    這里調用了ActiveServices的startServiceLocked(),看代碼:

           ArrayMap<ComponentName, ServiceRecord> getServicesLocked(int callingUser) {
               return getServiceMapLocked(callingUser).mServicesByName;
           }
                       //查找是否有與參數service對應的ServiceRecord,如果沒找到就會調用PackageManagerService去獲取參數service對應的Service信息,并封裝到ServiceRecord中。最后將ServiceRecord封裝為ServiceLookupResult返回,ServiceRecord用于描述一個Service
               ServiceLookupResult res =
                   retrieveServiceLocked(service, resolvedType, callingPackage,
                           callingPid, callingUid, userId, true, callerFg, false);
                          //獲取ServiceRecord
               ServiceRecord r = res.record;
                          //傳入ServiceRecord
               ComponentName cmp = startServiceInnerLocked(smap, service, r, callerFg, addToStarting);
               return cmp;
           }
    

    可以看到,這一步其實是獲取了ServiceRecord,它用來描述一個service,然后調用了startServiceInnerLocked(),看代碼:

            ComponentName startServiceInnerLocked()  {
    
               String error = bringUpServiceLocked(r, service.getFlags(), callerFg, false, false);
               return r.name;
           }
    

    這里調用了bringUpServiceLocked(),繼續看代碼:

           private String bringUpServiceLocked() {
               //獲取Service想要在哪個進程中運行,默認為當前進程
               final String procName = r.processName;
               //ProcessRecord用來描述一個應用程序進程
               ProcessRecord app;
               if (!isolated) {
                   //mAm為AMS,通過AMS查詢是否存在一個與Service對應的ProcessRecord(進程)類型對象的app
                   app = mAm.getProcessRecordLocked(procName, r.appInfo.uid, false);
                               //判斷用來運行Service的應用程序進程是否存在
                   if (app != null && app.thread != null) {
                               //應用程序進程如果存在就啟動service
                           realStartServiceLocked(r, app, execInFg);
                           return null;
    
                   }
               } 
    
               if (app == null && !permissionsReviewRequired) {
                   //用來運行Service的應用程序進程如果不存在就去啟動應用程序進程
                   if ((app=mAm.startProcessLocked(procName, r.appInfo, true, intentFlags,
                           hostingType, r.name, false, isolated, false)) == null) {
                       return msg;
                   }
    
               }
               return null;
           }
    

    可以看到這一步主要是判斷應用程序是否存在,如果不存在就去啟動應用程序進程。如果存在則繼續調用realStartServiceLocked(),并把進程信息ProcessRecord傳入,代碼:

           private final void realStartServiceLocked(ServiceRecord r,
                   ProcessRecord app, boolean execInFg) throws RemoteException {
                               //這里這個app.thread指的是ActivityThread的內部類ApplicationThread。
                   app.thread.scheduleCreateService(r, r.serviceInfo,
                           mAm.compatibilityInfoForPackageLocked(r.serviceInfo.applicationInfo),
                           app.repProcState);
    
           }
    

    可以看到這里調用了app.thread.scheduleCreateService(),這里這個app.thread指的是ActivityThread的內部類ApplicationThread。這里進行了進程間通訊,切換到了應用程序進程。參考:Android中根Activity的啟動過程。

    總結:可以看到,AMS到ActivityThread過程其實就是獲取要啟動的Service的信息,并判斷要啟動service的應用程序進程是否存在,如果不存在,就去啟動應用程序進程。如果存在,就進行進程間通訊,通知應用程序進程開始工作。

  • ActivityThread啟動Service過程:

            public final void scheduleCreateService(IBinder token,
                    ServiceInfo info, CompatibilityInfo compatInfo, int processState) {
                updateProcessState(processState, false);
                CreateServiceData s = new CreateServiceData();
                s.token = token;
                s.info = info;
                s.compatInfo = compatInfo;
    
                sendMessage(H.CREATE_SERVICE, s);
            }
    

    再看這個sendMessage():

        private void sendMessage(int what, Object obj, int arg1, int arg2, boolean async) {
            Message msg = Message.obtain();
            msg.what = what;
            msg.obj = obj;
            msg.arg1 = arg1;
            msg.arg2 = arg2;
            if (async) {
                msg.setAsynchronous(true);
            }
              //mH是H,繼承自Handler
            mH.sendMessage(msg);
        }
    

    這里的mH是H,繼承自Handler。因為ApplicationThread負責進程間通訊,它運行在Binder線程池中,這里需要利用Handler切換到主線程。是Handler,那么就看它的handlerMessage():

            public void handleMessage(Message msg) {
                switch (msg.what) {
                    case CREATE_SERVICE:
                        handleCreateService((CreateServiceData)msg.obj);
                        break;
                        }
    

    調用了handleCreateService(),繼續看代碼:

           private void handleCreateService(CreateServiceData data) {
               //獲取要啟動的Service的應用程序的LoadedApk,LoadedApk為APK文件的描述類
               LoadedApk packageInfo = getPackageInfoNoCheck(
                       data.info.applicationInfo, data.compatInfo);
                   Service service = null;
                       //獲取類加載器
                   java.lang.ClassLoader cl = packageInfo.getClassLoader();
                   //使用類加載器創建service
                   service = (Service) cl.loadClass(data.info.name).newInstance();
                               //創建service的上下文環境ContextImpl對象
                   ContextImpl context = ContextImpl.createAppContext(this, packageInfo);
                   context.setOuterContext(service);
    
                   Application app = packageInfo.makeApplication(false, mInstrumentation);
                       //初始化Service
                   service.attach(context, this, data.info.name, data.token, app,
                           ActivityManager.getService());
                       //調用onCreate()
                   service.onCreate();
                       //將service添加到ActivityThread的ArrayMap類型的成員變量mServices中
                   mServices.put(data.token, service);
           }
    

    這里可以看到,通過類加載器完成了service的創建,創建了context。然后通過attach()完成了service內部成員變量的初始化。最后調用了onCreate()。完成了startService()的啟動流程。

Service的綁定過程

1. 先看一下bindService()的使用示例:

  • 首先創建一個Service

    public class ClientService extends Service {
        private ServiceBinder serviceBinder;
          ////當Service綁定時調用,并返回binder對象
        @Override
        public IBinder onBind(Intent intent) {
            return  serviceBinder;
        }
          //當Service被創建時調用
        @Override
        public void onCreate() {
            super.onCreate();
            serviceBinder = new ServiceBinder();
        }
       //當Service被解綁調用
        @Override
        public boolean onUnbind(Intent intent) {
            return super.onUnbind(intent);
        }
          //當Service執行unbindService()解綁后,再次調用bindService()后調用
        @Override
        public void onRebind(Intent intent) {
            super.onRebind(intent);
        }
       //當Service銷毀時調用
        @Override
        public void onDestroy() {
            super.onDestroy();
        }
          //Binder類
        class ServiceBinder extends Binder {
    
        }
    
    }
    
  • 創建一個Connection:

    public class ClientConnection implements ServiceConnection {
          //當Service連接時調用,這里的ibinder就是Service里的onBinder()返回的binder
        @Override
        public void onServiceConnected(ComponentName name, IBinder ibinder) {
        }
    }
    
  • 綁定service:

            Intent intent = new Intent(this, ClientService.class);
            clientConnection = new ClientConnection();
            bindService(intent, clientConnection, Context.BIND_AUTO_CREATE);
    

    以上就是bindService()的示例。

    生命周期:onCreate()->onBind()->onUnbind()->onDestory()

    通過bindService()方式啟動Service會調用onCreate->onBinde(),多次調用bindService(),onCreate->onBinde()都只會執行一次,但是Connection的onServiceConnected()會執行多次。當執行unbindService()后再次bindService()則會執行onRebind()。

    調用onBind()之后會在ClientConnection里返回onBinde()里的Binder對象,這個Binder對象是Service的成員變量,所以外界就可以訪問Service里。

    通過bindService()方式啟動Service會與Activity產生關聯,所以必須在activity銷毀時執行onUnbind(),所以通過bindService()方式Service不能獨立運行。

2. Service的綁定過程

bindService()其實是由ContextImpl類型的mBase成員變量調用的,這個變量在ActivityThread的performLaunchActivity()中被創建,并通過attach()與Activity產生關聯。

bindService()的綁定過程分為3個部分:

ContextImpl到AMS到過程

Service的綁定過程

  • ContextImpl到AMS到過程:

    那么看一下ContextImpl的bindService():

           @Override
           public boolean bindService(Intent service, ServiceConnection conn,
                   int flags) {
                  //調用bindServiceCommon(),并傳入了ServiceConnection
               return bindServiceCommon(service, conn, flags, mMainThread.getHandler(),
                       Process.myUserHandle());
           }
    

    可以看到,這里調用了bindServiceCommon(),繼續看:

           private boolean bindServiceCommon() {
            IServiceConnection sd;
            //這里的mPackageInfo是LoadAPK類型
            sd = mPackageInfo.getServiceDispatcher(conn, getOuterContext(), handler, flags);
            //獲取AMS,調用AMS的bindService(),并傳入了IServiceConnection,這個IServiceConnection是一個本地代理對象,具備進程間通訊能力
            int res = ActivityManager.getService().bindService(
              mMainThread.getApplicationThread(), getActivityToken(), service,
              service.resolveTypeIfNeeded(getContentResolver()),
              sd, flags, getOpPackageName(), user.getIdentifier());
           }
    

    這里先看一下LoadAPK的getServiceDispatcher():

           public final IServiceConnection getServiceDispatcher() {
                   LoadedApk.ServiceDispatcher sd = null;
                       //sd是一個ServiceDispatcher對象
                   return sd.getIServiceConnection();
               }
           }
    

    繼續看getIServiceConnection():

             ServiceDispatcher.InnerConnection mIServiceConnection;
                      IServiceConnection getIServiceConnection() {
                   return mIServiceConnection;
               }
                  private static class InnerConnection extends IServiceConnection.Stub {
    
               }
    

    可以看到,最終返回的是一個InnerConnection對象,它繼承自IServiceConnection.Stub,意味著實現Binder機制,讓Service具備了進程間通訊的能力。

  • Service的綁定過程:

    這里繼續看AMS的bindService():

           public int bindService() {
               synchronized(this) {
                      //mServices是ActiveServices類型
                   return mServices.bindServiceLocked(caller, token, service,
                           resolvedType, connection, flags, callingPackage, userId);
               }
           }
    

    這里調用了ActiveServices.bindServiceLocked(),繼續看代碼:

           int bindServiceLocked() {
                       //獲取AppBindRecord
                   AppBindRecord b = s.retrieveAppBindingLocked(service, callerApp);
                       //獲取ConnectionRecord
                   ConnectionRecord c = new ConnectionRecord(b, activity,
                           connection, flags, clientLabel, clientIntent);
                                   //啟動Service,這里啟動service和startService()里的啟動部分是一樣的,會調用到同一個方法
                       if (bringUpServiceLocked(s, service.getFlags(), callerFg, false,
                               permissionsReviewRequired) != null) {
                           return 0;
                       }
    
                   if (s.app != null && b.intent.received) {
                   //s.app != null表示Service已經運行,b.intent.received表示當前應用程序進程已經收到綁定Service時返回到Binder,如果是第一次調用那么onBind()還沒調用,所以這里暫時不會執行。在重新綁定時才會執行。
                                               //這里的c.conn是InnerConnection,這里最終會執行onServiceConnected(),所以多次綁定會多次執行onServiceConnected();
                           c.conn.connected(s.name, b.intent.binder, false);
                       if (b.intent.apps.size() == 1 && b.intent.doRebind) {
                          //解綁后,再次綁定時。也就是執行了unbindService()后再次執行bindService()
                           requestServiceBindingLocked(s, b.intent, callerFg, true);
                       }
                   } else if (!b.intent.requested) {
                      //第一次綁定時,也就是執行bindService()時
                       requestServiceBindingLocked(s, b.intent, callerFg, false);
                   }
    
              return 1;
           }
    

    AppBindRecord:應用程序進程通過Intent綁定Service時,會通過AppBindRecord來維護Service與應用程序進程之間的關聯。其內部存儲了誰綁定的Service(ProcessRecord),被綁定的Service(AppBindRecord),綁定Service的Intent(IntentBindRecord)和所有綁定通信記錄的信息(ArraySet<ConnectionRecord>)。

    ServiceRecord:用于描述一個Service

    ConnectionRecord:用于描述應用程序進程和Service建立的一次通信

    IntentBindRecord:用于描述綁定Service的Intent。

    這里可以看到,先是獲取了Service的相關信息,然后啟動了Service。最后調用了requestServiceBindingLocked(s, b.intent, callerFg, false):

           private final boolean requestServiceBindingLocked() {
                  //r.app.thread是ActivityThread的內部類ApplicationThread類型
                r.app.thread.scheduleBindService(r, i.intent.getIntent(), rebind,
                               r.app.repProcState);
               return true;
           }
    

    這里執行了BInder代理對象ActivityThread的scheduleBindService(),那么會切換到應用程序進程,執行ActivityThread的scheduleBindService(),看代碼:

               public final void scheduleBindService(IBinder token, Intent intent,
                       boolean rebind, int processState) {
                   BindServiceData s = new BindServiceData();
                   s.token = token;
                   s.intent = intent;
                   s.rebind = rebind;
                   sendMessage(H.BIND_SERVICE, s);
               }
    

    這里會繼續執行到H類的HandleMessage():

               public void handleMessage(Message msg) {
                       case BIND_SERVICE:
                           handleBindService((BindServiceData)msg.obj);
                     }
    

    繼續看handleBindService():

           private void handleBindService(BindServiceData data) {
               //取出Service
               Service s = mServices.get(data.token);
                       try {
                           if (!data.rebind) {
                               //調用onBind()
                               IBinder binder = s.onBind(data.intent);
                               //調用AMS的publishService()
                               ActivityManager.getService().publishService(
                                       data.token, data.intent, binder);
                           } else {
                              //調用Rebind()
                               s.onRebind(data.intent);
                               ActivityManager.getService().serviceDoneExecuting(
                                       data.token, SERVICE_DONE_EXECUTING_ANON, 0, 0);
                           }
                       } 
           }
    

    前面分析過,Service建立后會存在Service中,這里再取出來。然后調用onBind(),這時onBind()生命周期就被執行了。

    這里不關注reBind()的情況。

    然后這里又再次調用了AMS的publishService(),又進行了進程間通訊,又來到了AMS所在的SystemServer進程,看publishService()代碼:

           public void publishService(IBinder token, Intent intent, IBinder service) {
                               //mServices是ActiveServices類型
                   mServices.publishServiceLocked((ServiceRecord)token, intent, service);
           }
    

    再看ActiveServices.publishServiceLocked():

           void publishServiceLocked(ServiceRecord r, Intent intent, IBinder service) {
                   //這里的c.conn是InnerConnection,InnerConnection
                           c.conn.connected(r.name, service, false);
           }
    

    可以看到,這里調用了InnerConnection類型的c.conn的connected(),在前面分析過InnerConnection是LoadApk內部類ServiceDispatcher的內部類,是一個Binder,這里是它的代理Binder對象。

    那么這里就是通過代理Binder對象進行進程間通信,會執行應用程序端的InnerConnection的connected()。看代碼:

                   public void connected(ComponentName name, IBinder service, boolean dead)
                           throws RemoteException {
                       LoadedApk.ServiceDispatcher sd = mDispatcher.get();
                       if (sd != null) {
                           sd.connected(name, service, dead);
                       }
                   }
    

    可以看到執行了ServiceDispatcher的connected():

               public void connected(ComponentName name, IBinder service, boolean dead) {
                   if (mActivityThread != null) {
                       //將消息發送到主線程
                       mActivityThread.post(new RunConnection(name, service, 0, dead));
                   } else {
                       doConnected(name, service, dead);
                   }
               }
    

    現在還處于binder線程池中,所以要切換到主線程,RunConnection是一個Runnable,同時也是ServiceDispatcher的內部類,那么看它的run():

                   public void run() {
                       if (mCommand == 0) {
                           doConnected(mName, mService, mDead);
                       } else if (mCommand == 1) {
                           doDeath(mName, mService);
                       }
                   }
    

    這里執行了ServiceDispatcher的doConnected():

               public void doConnected(ComponentName name, IBinder service, boolean dead) {
                               //執行自定義的Connection的onServiceConnected()
                       mConnection.onServiceConnected(name, service);
               }
    

    到這時,就會執行自定義的Connection的onServiceConnected(),完成整個bindService()流程。

附:

  • startService()方式能長時間運行在主線程,但是Activity無法操作Service。bindService()方式可以讓調用端獲取binder對象,但是無法長期運行。所以可以結合使用。先startService()再bindService(),這樣使用時如果要銷毀Service的話必須stopService()和uBindService()一起使用(順序無所謂)。
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,238評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,430評論 3 415
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事?!?“怎么了?”我有些...
    開封第一講書人閱讀 176,134評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,893評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,653評論 6 408
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,136評論 1 323
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,212評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,372評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,888評論 1 334
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,738評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,939評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,482評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,179評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,588評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,829評論 1 283
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,610評論 3 391
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,916評論 2 372

推薦閱讀更多精彩內容