1. 程式人生 > >Android筆記-service啟動過程分析:bindService原始碼分析、startService和bindService區別

Android筆記-service啟動過程分析:bindService原始碼分析、startService和bindService區別

前言:

Service的啟動流程將會分為一個系列來講述。
本系列開始將分析Service的啟動過程。
看這個系列文章之前你所需要知道的知識點:
1. 熟悉service的基本用法。
2. 瞭解bind機制,知道android的客戶端和AMS間通訊流程。
3. 最好學習過activity的啟動流程。

本系列將涉及到以下一些分支:
startService原始碼分析
bindService原始碼分析、startService和bindService區別
第二次startService為什麼沒有呼叫onCreate
為什麼bindService和startService同時呼叫後需要同時呼叫unBind和stop才能使服務停止。
前臺Service原理

今天這一篇將講述bindService原始碼分析:

service啟動過程分析

bindService原始碼分析

bindService、startService區別

分析bindService,不打算像startService一樣,分析整個AMS流程。
因為兩者的AMS流程很相似,所以打算結合startService流程,分析兩者的不同之處。
首先我們要思考一下startService和bindService用法上有什麼不同:
1. 服務端:

區別 startService bindService
生命週期 會呼叫onStartCommend 會呼叫onBind
返回值 onStartCommend沒有返回值 onBind需要返回一個Bind型別的值

2. 客戶端

區別 startService bindService
啟動 用startService來啟動startService 用bindService來啟動bindService
引數 startService引數只需要一個Intent bindService引數多需要ServiceConnection
回撥 沒有回撥 需要重寫ServiceConnection的onServiceConnected方法

3、其他區別

區別 startService bindService
直接UI更新(非廣播或eventbus等其他機制) 不可以 可以
跨程序 不可以 可以

區別大概就是以上幾點。可以看出bindService和startService相比,多了一個bind機制,可以讓Service和Activity之間相互通訊。當然,主要目的是為了可以進行跨程序通訊。

ServiceDispatch、InnerConnection

我們先從客戶端的bindService方法開始看,
當然,客戶端經過一系列呼叫最終會執行到ContextImpl的bindServiceCommon方法:

 private boolean bindServiceCommon(Intent service, ServiceConnection conn, int flags, Handler
            handler, UserHandle user) {
        // Keep this in sync with DevicePolicyManager.bindDeviceAdminServiceAsUser.
        IServiceConnection sd;//sd具體實現類是ServiceDispatch
        if (conn == null) {
            throw new IllegalArgumentException("connection is null");
        }
        if (mPackageInfo != null) {
        //新建一個ServiceDispatch.InnerConnection,第一個引數是:ServiceConnection
            sd = mPackageInfo.getServiceDispatcher(conn, getOuterContext(), handler, flags);
        } else {
            throw new RuntimeException("Not supported in system context");
        }
        //檢查正確性
        validateServiceIntent(service);
        try {
            IBinder token = getActivityToken();
            if (token == null && (flags&BIND_AUTO_CREATE) == 0 && mPackageInfo != null
                    && mPackageInfo.getApplicationInfo().targetSdkVersion
                    < android.os.Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
                flags |= BIND_WAIVE_PRIORITY;
            }
            //Prepare this {@link Intent} to leave an app process. 跨程序通訊準備
            service.prepareToLeaveProcess(this);
            //開始AMS通訊,注意引數多了一個sd,該引數包含了ServiceConnection資訊
            int res = ActivityManager.getService().bindService(
                mMainThread.getApplicationThread(), getActivityToken(), service,
                service.resolveTypeIfNeeded(getContentResolver()),
                sd, flags, getOpPackageName(), user.getIdentifier());
            if (res < 0) {
                throw new SecurityException(
                        "Not allowed to bind to service " + service);
            }
            return res != 0;
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }

bindServiceCommon相比startServiceCommend多了一步sd引數的生成。sd引數是什麼?怎麼生成的呢?
先來看下獲取sd的方法mPackageInfo.getServiceDispatcher(conn, getOuterContext(), handler, flags);

 public final IServiceConnection getServiceDispatcher(ServiceConnection c,
            Context context, Handler handler, int flags) {
        synchronized (mServices) {
            LoadedApk.ServiceDispatcher sd = null;
            ArrayMap<ServiceConnection, LoadedApk.ServiceDispatcher> map = mServices.get(context);
            if (map != null) {
                if (DEBUG) Slog.d(TAG, "Returning existing dispatcher " + sd + " for conn " + c);
                sd = map.get(c);
            }
            if (sd == null) {
            //用ServiceConnection生成一個LoadedApk.ServiceDispatcher
                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();//獲取InnerConnection
        }
    }

ServiceDispatcher的getIServiceConnection方法

        private final ServiceDispatcher.InnerConnection mIServiceConnection;
        ..
        IServiceConnection getIServiceConnection() {
            return mIServiceConnection;
        }

從上面兩段程式碼我們看到sd原來是一個IServiceConnection。看上去是一個跨程序通訊的玩意兒。

這裡涉及到三個類(LoadedApk,ServiceDispatch,InnerConnection)、一個介面IServiceConnection:
LoadedApk,ServiceDispatch,InnerConnection關係如下:
LoadedApk,ServiceDispatch,InnerConnection關係
LoadedApk: Local state maintained about a currently loaded .apk.:保持當前載入的.apk的本地狀態。
ServiceDispatch:它是LoadedApk內部類,其建構函式:

 ServiceDispatcher(ServiceConnection conn,
                Context context, Handler activityThread, int flags) {
            mIServiceConnection = new InnerConnection(this);//用自己新建一個InnerConnection
            mConnection = conn;//儲存了ServiceConnection
            mContext = context;
            mActivityThread = activityThread;
            mLocation = new ServiceConnectionLeaked(null);
            mLocation.fillInStackTrace();
            mFlags = flags;
        }

InnerConnection:它是ServiceDispatch內部類,繼承了IServiceConnection.Stub,可以跨程序通訊,其原始碼:

  private static class InnerConnection extends IServiceConnection.Stub {//繼承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) {
                    sd.connected(name, service, dead);
                }
            }
        }

InnerConnection持有了一個ServiceDispatcher,且該connected方法執行的是ServiceDispatcher的connected方法。再看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);
            }
        }

可以看到connected方法會會執行RunConnection的run()方法或者doConnected方法,再看RunConnection類:

private final class RunConnection implements Runnable {
            RunConnection(ComponentName name, IBinder service, int command, boolean dead) {
                mName = name;
                mService = service;
                mCommand = command;
                mDead = dead;
            }

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

            final ComponentName mName;
            final IBinder mService;
            final int mCommand;
            final boolean mDead;
        }

可以看到最終會執行doConnected方法,doConnected方法原始碼:

public void doConnected(ComponentName name, IBinder service, boolean dead) {
            ServiceDispatcher.ConnectionInfo old;
            ServiceDispatcher.ConnectionInfo info;

            ...省略一些程式碼

            // If there was an old service, it is now disconnected.
            if (old != null) {

                mConnection.onServiceDisconnected(name);
            }
            if (dead) {
                mConnection.onBindingDied(name);
            }
            // If there is a new service, it is now connected.
            if (service != null) {
                 //執行ServiceConnection的onServiceConnected
                 mConnection.onServiceConnected(name, service);
            }
        }

最終會執行ServiceConnection的onServiceConnected方法。
至於為什麼這麼麻煩的使用這麼多類來生成一個sd,主要是因為涉及到跨程序通訊,而ServiceConnection並不能進行跨程序通訊,只能用ServiceDispatcher.InnerConnection來儲存其資訊,進行跨程序通訊。

以上分析了ServiceConnection是如何被封裝成ServiceDispatcher.InnerConnection的。
我們只要知道2點:
1. 由於ServiceConnection不能跨程序,所以使用了ServiceDispatcher.InnerConnection來儲存ServiceConnection的資訊,
2. ServiceDispatcher.InnerConnection執行connected方法會執行ServiceConnection的onServiceConnected方法。

記住以上兩點,在後面的onServiceConnected我們流程中會用到,繼續回頭看,下面會進行跨程序通訊。

AMS中的bindService

呼叫AMS的bindService,經過一系列呼叫最終同樣會走到realStartServiceLocked。具體呼叫了哪些方法可以看時序圖。總結如下:
bindService(ActivityManagerService)—bindServiceLocked(ActiveServices)—-bringUpServiceLocked(ActiveServices)—-realStartServiceLocked(ActiveServices)。
最終執行的realStartServiceLocked方法,其實在startService中最終也是realStartServiceLocked方法。很神奇,怎麼都走了這個方法?為什麼後面客戶端的流程會不一樣呢(startService執行了onStartCommend,bindService執行了onBind)?那麼我們再來回顧一下realStartServiceLocked方法吧:

 private final void realStartServiceLocked(ServiceRecord r,
            ProcessRecord app, boolean execInFg) throws RemoteException {
        if (app.thread == null) {
            throw new RemoteException();
        }
        if (DEBUG_MU)
            Slog.v(TAG_MU, "realStartServiceLocked, ServiceRecord.uid = " + r.appInfo.uid
                    + ", ProcessRecord.uid = " + app.uid);
        r.app = app;
        r.restartTime = r.lastActivity = SystemClock.uptimeMillis();

        final boolean newService = app.services.add(r);
        bumpServiceExecutingLocked(r, execInFg, "create");
        mAm.updateLruProcessLocked(app, false, null);
        mAm.updateOomAdjLocked();

        boolean created = false;
        try {
            if (LOG_SERVICE_START_STOP) {
                String nameTerm;
                int lastPeriod = r.shortName.lastIndexOf('.');
                nameTerm = lastPeriod >= 0 ? r.shortName.substring(lastPeriod) : r.shortName;
                EventLogTags.writeAmCreateService(
                        r.userId, System.identityHashCode(r), nameTerm, r.app.uid, r.app.pid);
            }
            synchronized (r.stats.getBatteryStats()) {
                r.stats.startLaunchedLocked();
            }
            mAm.notifyPackageUse(r.serviceInfo.packageName,
                                 PackageManager.NOTIFY_PACKAGE_USE_SERVICE);
            app.forceProcessStateUpTo(ActivityManager.PROCESS_STATE_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;
        }
//後續onBind方法會呼叫
        requestServiceBindingsLocked(r, execInFg);

        updateServiceClientActivitiesLocked(app, null, true);

        // If the service is in the started state, and there are no
        // pending arguments, then fake up one so its onStartCommand() will
        // be called.
        if (r.startRequested && r.callStart && r.pendingStarts.size() == 0) {
            r.pendingStarts.add(new ServiceRecord.StartItem(r, false, r.makeNextStartId(),
                    null, null));
        }
//後續onStartCommend方法會呼叫
        sendServiceArgsLocked(r, execInFg, true);

        if (r.delayed) {
            if (DEBUG_DELAYED_STARTS) Slog.v(TAG_SERVICE, "REM FR DELAY LIST (new proc): " + r);
            getServiceMap(r.userId).mDelayedStartList.remove(r);
            r.delayed = false;
        }

        if (r.delayedStop) {
            // Oh and hey we've already been asked to stop!
            r.delayedStop = false;
            if (r.startRequested) {
                if (DEBUG_DELAYED_STARTS) Slog.v(TAG_SERVICE,
                        "Applying delayed stop (from start): " + r);
                stopServiceLocked(r);
            }
        }
    }

注意requestServiceBindingsLocked方法,它後續會呼叫bindService。


    private final void requestServiceBindingsLocked(ServiceRecord r, boolean execInFg)
            throws TransactionTooLargeException {
        for (int i=r.bindings.size()-1; i>=0; i--) {
            IntentBindRecord ibr = r.bindings.valueAt(i);
            if (!requestServiceBindingLocked(r, ibr, execInFg, false)) {
                break;
            }
        }
    }

該方法用到了r.bindings。它是一個ArrayMap,儲存了客戶端的bind訊息:

final ArrayMap<Intent.FilterComparison, IntentBindRecord> bindings
            = new ArrayMap<Intent.FilterComparison, IntentBindRecord>();

哈哈,前文中的賣得關子現在看到了,原來是用r.bindings來判斷是否執行onBind方法的啊~。
具體儲存方法在AMS一開始的方法bindServiceLocked中:

int bindServiceLocked(IApplicationThread caller, IBinder token, Intent service,
            String resolvedType, final IServiceConnection connection, int flags,
            String callingPackage, final int userId) throws TransactionTooLargeException {

            ...
//該方法給bindings賦值
            AppBindRecord b = s.retrieveAppBindingLocked(service, callerApp);

            ....



        return 1;
    }
  public AppBindRecord retrieveAppBindingLocked(Intent intent,
            ProcessRecord app) {
        Intent.FilterComparison filter = new Intent.FilterComparison(intent);
        IntentBindRecord i = bindings.get(filter);
        if (i == null) {
            i = new IntentBindRecord(this, filter);
            bindings.put(filter, i);
        }
        AppBindRecord a = i.apps.get(app);
        if (a != null) {
            return a;
        }
        a = new AppBindRecord(this, i, app);
        i.apps.put(app, a);
        return a;
    }

這裡可以看到bindings裡面儲存了IntentBindRecord記錄。IntentBindRecord儲存的是intent。
requestServiceBindingsLocked通過迴圈取出該記錄,執行requestServiceBindingLocked:

 private final boolean requestServiceBindingLocked(ServiceRecord r, IntentBindRecord i,
            boolean execInFg, boolean rebind) throws TransactionTooLargeException {
        if (r.app == null || r.app.thread == null) {
            // If service is not currently running, can't yet bind.
            return false;
        }
        if ((!i.requested || rebind) && i.apps.size() > 0) {
            try {
                bumpServiceExecutingLocked(r, execInFg, "bind");
                r.app.forceProcessStateUpTo(ActivityManager.PROCESS_STATE_SERVICE);
               //呼叫客戶端的scheduleBindService方法
                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) {
                // Keep the executeNesting count accurate.
                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) {
                if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "Crashed while binding " + r);
                // Keep the executeNesting count accurate.
                final boolean inDestroying = mDestroyingServices.contains(r);
                serviceDoneExecutingLocked(r, inDestroying, inDestroying);
                return false;
            }
        }
        return true;
    }

這裡值得注意的是:
1. startService流程也會執行到requestServiceBindingsLocked方法,可是由於沒有給bindings賦值,所以不會執行迴圈裡的requestServiceBindingLocked方法。即不會執行onBind方法
2. bindService流程執行requestServiceBindingsLocked後,會繼續執行sendServiceArgsLocked方法,通過startService流程解析,我們知道該方法後續會執行onStartCommend。執行onStartCommend的標識是pendingStarts這個記錄。在前文中有提到記錄如何賦值的。bindService流程並沒有給其賦值。所以bindService並不會執行onStartCommend方法。

我們再看requestServiceBindingLocked方法又開始執行客戶端ActivityThread中的方法:scheduleBindService:

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;

            if (DEBUG_SERVICE)
                Slog.v(TAG, "scheduleBindService token=" + token + " intent=" + intent + " uid="
                        + Binder.getCallingUid() + " pid=" + Binder.getCallingPid());
            sendMessage(H.BIND_SERVICE, s);
        }

ServiceConnection的onServiceConnected流程

後面的流程和startService在客戶端流程一致,會呼叫到ActivityThread中的方法handleBindService:

private void handleBindService(BindServiceData data) {
        Service s = mServices.get(data.token);
        if (DEBUG_SERVICE)
            Slog.v(TAG, "handleBindService s=" + s + " rebind=" + data.rebind);
        if (s != null) {
            try {
                data.intent.setExtrasClassLoader(s.getClassLoader());
                data.intent.prepareToEnterProcess();
                try {
                    if (!data.rebind) {
                    //執行客戶端-server的onBind方法,並返回IBinder
                        IBinder binder = s.onBind(data.intent);
                        //注意:又一個AMS流程,用返回的IBinder呼叫客戶端-client的onServiceConnected
                        ActivityManagerNative.getDefault().publishService(
                                data.token, data.intent, binder);
                    } else {
                        s.onRebind(data.intent);
                        ActivityManagerNative.getDefault().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);
                }
            }
        }
    }

這裡相比startService不同的是:ActivityManagerNative.getDefault().publishService(data.token, data.intent, binder);方法。該方法其實又是AMS流程,最終呼叫客戶端-client的onServiceConnected方法。
那麼我們看下具體是怎麼實現的吧。經過一系列呼叫,最終會執行ActiveServices的publishServiceLocked方法:

void publishServiceLocked(ServiceRecord r, Intent intent, IBinder service) {
        final long origId = Binder.clearCallingIdentity();
        try {
            if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "PUBLISHING " + r
                    + " " + intent + ": " + service);
            if (r != null) {
                Intent.FilterComparison filter
                        = new Intent.FilterComparison(intent);
                IntentBindRecord b = r.bindings.get(filter);
                if (b != null && !b.received) {
                    b.binder = service;
                    b.requested = true;
                    b.received = true;
                    for (int conni=r.connections.size()-1; conni>=0; conni--) {
                        ArrayList<ConnectionRecord> clist = r.connections.valueAt(conni);
                        for (int i=0; i<clist.size(); i++) {
                            ConnectionRecord c = clist.get(i);
                            if (!filter.equals(c.binding.intent.intent)) {
                                if (DEBUG_SERVICE) Slog.v(
                                        TAG_SERVICE, "Not publishing to: " + c);
                                if (DEBUG_SERVICE) Slog.v(
                                        TAG_SERVICE, "Bound intent: " + c.binding.intent.intent);
                                if (DEBUG_SERVICE) Slog.v(
                                        TAG_SERVICE, "Published intent: " + intent);
                                continue;
                            }
                            if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "Publishing to: " + c);
                            try {
                            //關鍵程式碼
                                c.conn.connected(r.name, service);
                            } catch (Exception e) {
                                Slog.w(TAG, "Failure sending service " + r.name +
                                      " to connection " + c.conn.asBinder() +
                                      " (in " + c.binding.client.processName + ")", e);
                            }
                        }
                    }
                }

                serviceDoneExecutingLocked(r, mDestroyingServices.contains(r), false);
            }
        } finally {
            Binder.restoreCallingIdentity(origId);
        }
    }

關鍵程式碼是c.conn.connected(r.name, service);這裡的c是一個ConnectionRecord。是從ServiceRecord中的connections獲取的。connections是在哪裡賦值的呢?其實還是在一開始的bindServiceLocked方法裡:

int bindServiceLocked(IApplicationThread caller, IBinder token, Intent service,
            String resolvedType, final IServiceConnection connection, int flags,
            String callingPackage, final int userId) throws TransactionTooLargeException {



            ...
            //生成ConnectionRecord作為value
            ConnectionRecord c = new ConnectionRecord(b, activity,
                    connection, flags, clientLabel, clientIntent);
            IBinder binder = connection.asBinder();//獲取 InnerConnection,作為key
            ArrayList<ConnectionRecord> clist = s.connections.get(binder);
            if (clist == null) {
                clist = new ArrayList<ConnectionRecord>();
                s.connections.put(binder, clist);//給connections賦值
            }
            clist.add(c);
            ....



        return 1;
    }

注意這裡的connection引數,其實它就是文章一開始說的InnerConnection。
ConnectionRecord類儲存了該connection。最終在publishServiceLocked裡面迴圈取出並呼叫connection的connected方法。文章開頭已經知道該方法最終會執行ServiceConnection的onServiceConnected方法。

至此bindService整個流程結束。

總結:

  1. startService和bindService在AMS中最後都會執行到realStartServiceLocked方法。
  2. realStartServiceLocked中會依次執行requestServiceBindingsLocked、sendServiceArgsLocked方法
  3. requestServiceBindingsLocked:通過迴圈IntentBindRecord記錄來執行onBind方法
  4. sendServiceArgsLocked:通過迴圈pendingStarts記錄來執行onStartCommend方法
  5. IntentBindRecord記錄在bindServiceLocked中賦值
  6. pendingStarts(StartItem)記錄在startServiceLocked中賦值
  7. bindService流程執行onBind後,會比startService流程多一步,呼叫onServiceConnected方法,該步驟也是一個AMS呼叫過程。用到了ServiceDispatch類。