service的綁定原理

看完service的啓動流程,繼續擼一下service的綁定流程

1.service的綁定流程

首先我們看下如下圖所示的service的綁定原理,應用向AMS發起bindService,然後AMS檢查自己是否有對應service的binder句柄,如果有,則直接返回,如果沒有則會向service請求對應的binder句柄。
然後若service所在的進程沒有啓動,則先啓動進程,進程啓動完了之後,service會將句柄發佈到AMS,然後AMS再返回給應用,這樣應用就可以直接向service發起binder調用了。

在這裏插入圖片描述

我們再來看下bindService的具體的代碼流程,
在這裏插入圖片描述
這裏我們需要重點關注的有以下幾個方法
首先我們看下應用端的流程
應用端綁定流程
如下所示爲bindServiceCommon方法,我們可以看到傳入的參數爲ServiceConnection,而最終ServiceConnection被轉化成爲IServiceConnection傳遞給了AMS,這是因爲ServiceConnection不是binder對象,不能夠在進程之間傳遞,需要將其轉化成爲binder對象之後纔可以。

//ContextImpl
private boolean bindServiceCommon(Intent service, ServiceConnection conn, int flags, Handler
        handler, UserHandle user) {
   ......
    IServiceConnection sd;
    if (mPackageInfo != null) {
        sd = mPackageInfo.getServiceDispatcher(conn, getOuterContext(), handler, flags);
    } else {
        throw new RuntimeException("Not supported in system context");
    }
    ActivityManager.getService().bindService(
            mMainThread.getApplicationThread(), getActivityToken(), service,
            service.resolveTypeIfNeeded(getContentResolver()),
            sd, flags, getOpPackageName(), user.getIdentifier());
     .......
}

接下來我們看下getServiceDispatcher是如何實現的,
如下所示,在註釋1處,從mServices當中取出map,key爲context,再從取出的map當中取出
ServiceDispatcher,如註釋2所示。若取出的ServiceDispatcher爲空,則new一個新的對象put進去,最後再返回ServiceDispatcher裏面的IServiceConnection對象。

//LoadApk
public final IServiceConnection getServiceDispatcher(ServiceConnection c,
        Context context, Handler handler, int flags) {
    synchronized (mServices) {
        LoadedApk.ServiceDispatcher sd = null;
        //1.mServices是一個map,key爲context
        ArrayMap<ServiceConnection, LoadedApk.ServiceDispatcher> map = mServices.get(context);
        if (map != null) {
            if (DEBUG) Slog.d(TAG, "Returning existing dispatcher " + sd + " for conn " + c);
            //2.從map裏面取出ServiceDispatcher
            sd = map.get(c);
        }
        if (sd == null) {
            sd = new ServiceDispatcher(c, context, handler, flags);
            if (DEBUG) Slog.d(TAG, "Creating new dispatcher " + sd + " for conn " + c);
            if (map == null) {
                map = new ArrayMap<>();
                mServices.put(context, map);
            }
            map.put(c, sd);
        } else {
            sd.validate(context, handler);
        }
        return sd.getIServiceConnection();
    }
}

接着我們再來看下ServiceDispatcher的構造函數,
註釋1處,new了一個InnerConnection對象,這個就是要跨進程傳遞給AMS的binder對象,註釋2處就是我們傳入的ServiceConnection對象
當服務連接上之後,InnerConnection對象的connected方法會被調用,然後我們傳入的ServiceConnection也會被調用

 ServiceDispatcher(ServiceConnection conn,
         Context context, Handler activityThread, int flags) {
     //1.binder對象    
     mIServiceConnection = new InnerConnection(this);
     //2.我們傳入的ServiceConnection對象
     mConnection = conn;
     mContext = context;
     mActivityThread = activityThread;
     mLocation = new ServiceConnectionLeaked(null);
     mLocation.fillInStackTrace();
     mFlags = flags;
 }

private static class InnerConnection extends IServiceConnection.Stub {
    final WeakReference<LoadedApk.ServiceDispatcher> mDispatcher;

    InnerConnection(LoadedApk.ServiceDispatcher sd) {
        mDispatcher = new WeakReference<LoadedApk.ServiceDispatcher>(sd);
    }

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

需要注意的點
如下圖所示爲應用端的調用圖,應用端和AMS最終是通過IServiceConnection進行交互,然後再調用ServiceConnection。
在這裏插入圖片描述
從以下代碼中可以看出,context和ServiceConnection可以唯一確定一個ServiceDispatcher,進而確定IServiceConnection,因此同一個activity使用不同的ServiceConnection,和不同的activity使用同一個ServiceConnection,對於AMS來說都是不一樣的綁定。

ArrayMap<ServiceConnection, LoadedApk.ServiceDispatcher> map = mServices.get(context);

服務端的綁定流程
當AMS收到bindService請求之後會執行ActiveServices的bindServiceLocked的方法,然後執行bringUpServiceLocked–>realStartServiceLocked–>requestServiceBindingsLocked。
首先我們來看下realStartServiceLocked
在註釋1處,通知應用端創建service對象,並執行onCreate周期函數。
在註釋2處,當service創建成功之後,請求service發佈其binder句柄

    private final void realStartServiceLocked(ServiceRecord r,
            ProcessRecord app, boolean execInFg) throws RemoteException {
        ......
        try {
            ......
            mAm.notifyPackageUse(r.serviceInfo.packageName,
                                 PackageManager.NOTIFY_PACKAGE_USE_SERVICE);
            app.forceProcessStateUpTo(ActivityManager.PROCESS_STATE_SERVICE);
            //1.通知應用端,創建service對象,執行onCreate
            app.thread.scheduleCreateService(r, r.serviceInfo,
                    mAm.compatibilityInfoForPackageLocked(r.serviceInfo.applicationInfo),
                    app.repProcState);
            r.postNotification();
            created = true;
        } catch (DeadObjectException e) {
            Slog.w(TAG, "Application dead when creating service " + r);
            mAm.appDiedLocked(app);
            throw e;
        } finally {
            if (!created) {
                // Keep the executeNesting count accurate.
                final boolean inDestroying = mDestroyingServices.contains(r);
                serviceDoneExecutingLocked(r, inDestroying, inDestroying);

                // Cleanup.
                if (newService) {
                    app.services.remove(r);
                    r.app = null;
                }

                // Retry.
                if (!inDestroying) {
                    scheduleServiceRestartLocked(r, false);
                }
            }
        }

        if (r.whitelistManager) {
            app.whitelistManager = true;
        }
       //2.請求service發佈其binder句柄
        requestServiceBindingsLocked(r, execInFg);
        ......
    }

我們再來看下requestServiceBindingLocked,是如何請求發佈句柄的。
在註釋1處,如果service沒有啓動,則直接返回
在註釋2處,向service請求binder對象,會執行應用端的scheduleBindService方法。
在註釋4處,應用端遠程調用AMS,向AMS發佈binder對象。

//ActiveServices
private final boolean requestServiceBindingLocked(ServiceRecord r, IntentBindRecord i,
        boolean execInFg, boolean rebind) throws TransactionTooLargeException {
    //1.若service沒有啓動,則直接返回false    
    if (r.app == null || r.app.thread == null) {
        return false;
    }
    if ((!i.requested || rebind) && i.apps.size() > 0) {
        try {
            bumpServiceExecutingLocked(r, execInFg, "bind");
            r.app.forceProcessStateUpTo(ActivityManager.PROCESS_STATE_SERVICE);
            //2.向service請求對象
            r.app.thread.scheduleBindService(r, i.intent.getIntent(), rebind,
                    r.app.repProcState);
            if (!rebind) {
                i.requested = true;
            }
            i.hasBound = true;
            i.doRebind = false;
        } catch (TransactionTooLargeException e) {
            if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "Crashed while binding " + r, e);
            final boolean inDestroying = mDestroyingServices.contains(r);
            serviceDoneExecutingLocked(r, inDestroying, inDestroying);
            throw e;
        } catch (RemoteException e) {
            final boolean inDestroying = mDestroyingServices.contains(r);
            serviceDoneExecutingLocked(r, inDestroying, inDestroying);
            return false;
        }
    }
    return true;
}

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

//ActivityThread
private void handleBindService(BindServiceData data) {
    Service s = mServices.get(data.token);
    if (s != null) {
        try {
            data.intent.setExtrasClassLoader(s.getClassLoader());
            data.intent.prepareToEnterProcess();
            try {
                if (!data.rebind) {
                    //3.執行onbind
                    IBinder binder = s.onBind(data.intent);
                    //4.向AMS發佈binder對象
                    ActivityManager.getService().publishService(
                            data.token, data.intent, binder);
                } else {
                    s.onRebind(data.intent);
                    ActivityManager.getService().serviceDoneExecuting(
                            data.token, SERVICE_DONE_EXECUTING_ANON, 0, 0);
                }
                ensureJitEnabled();
            } catch (RemoteException ex) {
                throw ex.rethrowFromSystemServer();
            }
        } catch (Exception e) {
            if (!mInstrumentation.onException(s, e)) {
                throw new RuntimeException(
                        "Unable to bind to service " + s
                        + " with " + data.intent + ": " + e.toString(), e);
            }
        }
    }
}
發佈了131 篇原創文章 · 獲贊 21 · 訪問量 12萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章