天天看點

源碼分析Android bindService與startService差別

android啟動service,不管是bindService還是startService都不像activity那樣複雜,需要建立activity的視窗,pause、resume activity等一系列生命周期操作,簡單粗暴直奔ActivityManagerNative.getDefault().startService和bindService, 跟activity一樣運用的是著名的binder機制,下面直接在server端,也就是ActivityManagerService直接分析他們的差別。

startservice和bindservice的差別,其中一個就是生命周期的不同

1.通過startservice開啟的服務.一旦服務開啟, 這個服務和開啟他的調用者之間就沒有任何的關系了. 

調用者不可以通路 service裡面的方法. 調用者如果被系統回收了或者調用了ondestroy方法, service還會繼續存在  

2.通過bindService開啟的服務,服務開啟之後,調用者和服務之間 還存在着聯系 ,  一旦調用者挂掉了.service也會跟着挂掉 .

我們的問題是如何做到的,我們繼續從源碼分析。 bindService源碼

public int bindService(IApplicationThread caller, IBinder token,
            Intent service, String resolvedType,
            IServiceConnection connection, int flags, int userId) {
        enforceNotIsolatedCaller("bindService");
        // Refuse possible leaked file descriptors
        if (service != null && service.hasFileDescriptors() == true) {
            throw new IllegalArgumentException("File descriptors passed in Intent");
        }
        synchronized(this) {
            return mServices.bindServiceLocked(caller, token, service, resolvedType,
                    connection, flags, userId);
        }
    }
int bindServiceLocked(IApplicationThread caller, IBinder token,
             Intent service, String resolvedType,
            IServiceConnection connection, int flags,int userId) {

          ...
ConnectionRecord c = new ConnectionRecord(b, activity,
                     connection, flags, clientLabel, clientIntent);
             IBinder binder = connection.asBinder();
             ArrayList<ConnectionRecord> clist = s.connections.get(binder);
             if (clist == null) {
                 clist = new ArrayList<ConnectionRecord>();
                 s.connections.put(binder, clist);
             }
             clist.add(c);
             b.connections.add(c);
             if (activity != null) {
                 if (activity.connections == null) {
                     activity.connections = new HashSet<ConnectionRecord>();
                 }
                 activity.connections.add(c);
             }
             b.client.connections.add(c);
             if ((c.flags&Context.BIND_ABOVE_CLIENT) != 0) {
                 b.client.hasAboveClient = true;
             }
             clist = mServiceConnections.get(binder);
             if (clist == null) {
                 clist = new ArrayList<ConnectionRecord>();
                 mServiceConnections.put(binder, clist);
             }
             clist.add(c);
             if ((flags&Context.BIND_AUTO_CREATE) != 0) {
                 s.lastActivity = SystemClock.uptimeMillis();
                 if (bringUpServiceLocked(s, service.getFlags(), callerFg, false) != null) {
                     return 0;
                 }
             }
          ...
}
           

startService源碼

ComponentName startServiceLocked(IApplicationThread caller,
             Intent service, String resolvedType,
             int callingPid, int callingUid, int userId) {
          ...

    return startServiceInnerLocked(smap, service, r, callerFg, addToStarting);
}

ComponentName startServiceInnerLocked(ServiceMap smap, Intent service,
             ServiceRecord r, boolean callerFg, boolean addToStarting) {
     ...
         String error = bringUpServiceLocked(r, service.getFlags(), callerFg, false);
         if (error != null) {
             return new ComponentName("!!", error);
         }
     ...
}
           

從源碼上看,不管是bindService,還是startService,最後都調了bringUpServiceLocked,不同的是bindService之前會建立一個ConnnectionRecord對象,并儲存到ConnectionRecord ArrayList中,還有

if (activity != null) {
                 if (activity.connections == null) {
                     activity.connections = new HashSet<ConnectionRecord>();
                 }
                 activity.connections.add(c);
             }
           

添加到ActivityRecord當中。下面我們來看unbindService究竟幹了什麼。

boolean unbindServiceLocked(IServiceConnection connection) {
         IBinder binder = connection.asBinder();
         if (DEBUG_SERVICE) Slog.v(TAG, "unbindService: conn=" + binder);
         ArrayList<ConnectionRecord> clist = mServiceConnections.get(binder);
         if (clist == null) {
             Slog.w(TAG, "Unbind failed: could not find connection for "
                   + connection.asBinder());
             return false;
         }
         final long origId = Binder.clearCallingIdentity();
         try {
             while (clist.size() > 0) {
                 ConnectionRecord r = clist.get(0);
                 removeConnectionLocked(r, null, null);
                 if (r.binding.service.app != null) {
                     // This could have made the service less important.
                     mAm.updateOomAdjLocked(r.binding.service.app);
                 }
             }
         } finally {
             Binder.restoreCallingIdentity(origId);
         }
         return true;
     }
           

從取出對應的ConnectionRecord,然後調用removeConnectionLock,從代碼上看,隻是remove ConnectionRecord,跟activity生命周期沒有關系,我們要找的不在這裡。我們再從Activity結束來分析,Activity的生命周期主要是由ActivityStack來控制,我們發現Activity有個私有對象是mPausingActivity,activity onDestroy 時肯定會調到destroyActivityLocked。

final boolean destroyActivityLocked(ActivityRecord r,
            boolean removeFromApp, boolean oomAdj, String reason) {
          ...
}

final void cleanUpActivityLocked(ActivityRecord r, boolean cleanServices,
           boolean setState) {
    …
}

finalvoid cleanUpActivityServicesLocked(ActivityRecord r) {
        // Throw away any services that have been bound by this activity.
        if (r.connections != null) {
            Iterator<ConnectionRecord> it = r.connections.iterator();
            while (it.hasNext()) {
                ConnectionRecord c = it.next();
                mService.mServices.removeConnectionLocked(c, null, r);
            }
            r.connections = null;
        }
    }
           

最終,我們找到了,在ActivityStack cleanUpActivityServicesLocked方法中找到了答案,在activity結束之前會周遊,看是否還是ConnectionRecord,如果有,就結束它,也就解釋了為什麼bindService生命周期與調用者同步。

繼續閱讀