Android 進階 - Activity服務啟動分析

來源:互聯網
上載者:User

前面已經介紹了如何建立一個應用服務,如何建立一個系統服務,這裡我把Android服務分為:應用服務(ActivityService),系統服務(SystemService),分類是否正確也不清楚,網上並沒有資料明確定義,之所以這樣分類,因為應用服務放在ActiveServices中管理,而系統服務放在ServiceManager中管理,兩者存在明顯的不同。由於Android設計時已經把中介層標準化了,我們實現一個服務時,只需要簡單實現服務端(Native)和調用端(Proxy)即可。本文將詳細描述ActiveService的啟動全過程,有關Binder的部分沒有詳細介紹,後續文章再介紹。

1、Activity服務啟動的幾個階段Activity服務啟動大致可以分為以下幾個階段:準備階段:做進程啟動前的準備工作。進程啟動階段:通過Zygote啟動進程。(當服務已經啟動時,此步驟略)Activity啟動階段:在新的進程裡,啟動Activity。本文重點說明第1階段和第3階段,進程啟動階段參見前一篇文章《Android 進階-進程啟動分析》。所以,Activity服務與應用的啟動過程大致相同。

2. 準備階段2.1 流程圖
2.2 關鍵流程說明上面的流程圖,和Activity應用啟動相似,最後都分為服務進程已經啟動和服務進程未啟動兩種情況,服務進程已經啟動的情況下,不需要進程啟動,直接到服務啟動步驟。從先前服務執行個體中,我們知道,當要調用一個服務時,需要先建立一個ServiceConnection,並在OnServiceConnected函數中,儲存服務的Binder介面,以便調用服務的各種介面。然後,再綁定服務,再調用服務。2.2.1 ContextImpl.bindServiceClient是調用Activity.bindService來綁定服務的,怎麼會到ContextImpl.bindService中?中略去了一些步驟,這裡結合代碼說明:Activity 繼承自 ContextThemeWrapper ,而ContextThemeWrapper繼承自 ContextWrapper,bindService就定義在ContextWraper中。frameworks/base/core/java/android/content/ContextWrapper.java
public class ContextWrapper extends Context {    Context mBase;    public ContextWrapper(Context base) {        mBase = base;    }    ...    public boolean bindService(Intent service, ServiceConnection conn,            int flags) {        return mBase.bindService(service, conn, flags);    }    ...}
再看ContextThemeWrapperframeworks/base/core/java/android/view/ContextThemeWrapper.java
public class ContextThemeWrapper extends ContextWrapper {    ...    public ContextThemeWrapper() {        super(null);    }    ...}
而Activity沒有建構函式,這說明我們new一個Activity時,mBase是null,那怎麼bindService呢?一定有一個地方設定了mBase。在《Android進階-Activity應用啟動分析》一文中寫到ActivityThread.performLaunchActivity函數有介紹。現在繼續這個函數:frameworks/base/core/java/android/app/ActivityThread.java
public ActivityThread{    private Context createBaseContextForActivity(ActivityClientRecord r,            final Activity activity) {        ContextImpl appContext = new ContextImpl();        appContext.init(r.packageInfo, r.token, this);        appContext.setOuterContext(activity);        ...        Context baseContext = appContext;        ...        return baseContext;    }    ...    private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {        // System.out.println("##### [" + System.currentTimeMillis() + "] ActivityThread.performLaunchActivity(" + r + ")");        ...        try {            Application app = r.packageInfo.makeApplication(false, mInstrumentation);            ...            if (activity != null) {            //建立上下文                Context appContext = createBaseContextForActivity(r, activity);                CharSequence title = r.activityInfo.loadLabel(appContext.getPackageManager());                Configuration config = new Configuration(mCompatConfiguration);                if (DEBUG_CONFIGURATION) Slog.v(TAG, "Launching activity "                        + r.activityInfo.name + " with config " + config);                //串連上下文                activity.attach(appContext, this, getInstrumentation(), r.token,                        r.ident, app, r.intent, r.activityInfo, title, r.parent,                        r.embeddedID, r.lastNonConfigurationInstances, config);               ...            }            r.paused = true;            mActivities.put(r.token, r);        } catch (SuperNotCalledException e) {            throw e;        } catch (Exception e) {            if (!mInstrumentation.onException(activity, e)) {                throw new RuntimeException(                    "Unable to start activity " + component                    + ": " + e.toString(), e);            }        }        return activity;    }}
frameworks/base/core/java/android/app/Activity.java
public Activity{...    final void attach(Context context, ActivityThread aThread,            Instrumentation instr, IBinder token, int ident,            Application application, Intent intent, ActivityInfo info,            CharSequence title, Activity parent, String id,            NonConfigurationInstances lastNonConfigurationInstances,            Configuration config) {        // 串連mBase        attachBaseContext(context);        mFragments.attachActivity(this, mContainer, null);        ...    }}
frameworks/base/core/java/android/view/ContextThemeWrapper.java
public class ContextThemeWrapper{protected void attachBaseContext(Context newBase) {//串連mBase        super.attachBaseContext(newBase);        mBase = newBase;    }}
從上面的原始碼可以看出,當載入Activity類後,便會調用createBaseContextForActivity來建立appContext,再用activity.attach來串連context。而appContext是用newContextImpl()來建立的,所以,Activity.mBase就是一個ContextImpl的類執行個體。因此,從ContextImpl.bindService開始。
2.2.2 ContextImpl.bindServiceCommonframeworks/base/core/java/android/app/ContextImpl.java
    private boolean bindServiceCommon(Intent service, ServiceConnection conn, int flags,            UserHandle user) {        IServiceConnection sd;        if (conn == null) {            throw new IllegalArgumentException("connection is null");        }        if (mPackageInfo != null) {        // 建立一個IServiceConnection對象,服務綁定後,需要調用此對象的connected函數,觸發ServiceConnection.onServiceConnected事件            // 此對象是一個LoadedApk.ServiceDispatcher.InnerConnection對象,見後面的LoadedApk的代碼解釋            sd = mPackageInfo.getServiceDispatcher(conn, getOuterContext(),                    mMainThread.getHandler(), flags);        } else {            throw new RuntimeException("Not supported in system context");        }        validateServiceIntent(service);        try {            ...            // 通過ActivieyManagerProxy.bindService,經由Binder調用ActivityManagerService.bindService            int res = ActivityManagerNative.getDefault().bindService(                mMainThread.getApplicationThread(), getActivityToken(),                service, service.resolveTypeIfNeeded(getContentResolver()),                sd, flags, user.getIdentifier());            if (res < 0) {                throw new SecurityException(                        "Not allowed to bind to service " + service);            }            return res != 0;        } catch (RemoteException e) {            return false;        }    }
frameworks/base/core/java/android/app/LoadedApk.java
public class LoadedApk{...static final class ServiceDispatcher {private final ServiceDispatcher.InnerConnection mIServiceConnection;...private static class InnerConnection extends IServiceConnection.Stub {            ...        }        ...        IServiceConnection getIServiceConnection() {        // 返回一個InnerConnection串連            return mIServiceConnection;        }}...public final IServiceConnection getServiceDispatcher(ServiceConnection c,            Context context, Handler handler, int flags) {        synchronized (mServices) {            LoadedApk.ServiceDispatcher sd = null;            ArrayMap map = mServices.get(context);            if (map != null) {                sd = map.get(c);            }            if (sd == null) {            // 建立ServiceDispatcher                sd = new ServiceDispatcher(c, context, handler, flags);                if (map == null) {                    map = new ArrayMap();                    mServices.put(context, map);                }                map.put(c, sd);            } else {                sd.validate(context, handler);            }            // 返回ServiceDispatcher.getIServiceConnection()            return sd.getIServiceConnection();        }    }...}
本函數做兩個事情:調用mPackageInfo.getServiceDispatcher建立IServiceConnection串連,返回的是ServiceDispatcher.InnerConnection對象,此對象作為bindService的參數,傳給後續實現者。此對象可以通過connected函數,來觸發ServiceConection.onServiceConnected,來通知調用方,服務已經綁定了,並傳入IBinder對象,可以調用方通過此對象來調用服務的各種操作;通過ActivityManagerProxy.bindService,來調用ActivityManagerService中的bindService方法。說明:ActivityManagerNavite.getDefault()返回的是一個ActivityManagerProxy對象,這在《Android進階- Activity應用啟動分析》一文中已經有介紹。具體的過程見,Binder通訊過程本文忽略。2.2.3 ActivityManagerService.bindServiceContextImpl通過ActivityManagerProxy,經由Binder驅動程式,將bindService的操作傳入到了ActivityManagerService.bindService中,圖中有較為清楚的表述,文字上不再細說。流程走到ActivityManagerService.bindService中後,又有兩個關鍵動作,即ActiveServices.realStartServiceLocked和ActiveServices.reuqestServiceBindLocket。2.2.4 ActiveServices.bringUpServiceLockedframeworks/base/services/java/com/android/server/am/ActiveServices.java
public class ActiveServices{...private final String bringUpServiceLocked(ServiceRecord r,            int intentFlags, boolean execInFg, boolean whileRestarting) {        ...        ProcessRecord app;        if (!isolated) {            app = mAm.getProcessRecordLocked(procName, r.appInfo.uid, false);            if (DEBUG_MU) Slog.v(TAG_MU, "bringUpServiceLocked: appInfo.uid=" + r.appInfo.uid                        + " app=" + app);            if (app != null && app.thread != null) {            // 如果服務進程已經啟動                try {                    app.addPackage(r.appInfo.packageName, mAm.mProcessStats);                    realStartServiceLocked(r, app, execInFg);                    return null;                } catch (RemoteException e) {                    Slog.w(TAG, "Exception when starting service " + r.shortName, e);                }                // If a dead object exception was thrown -- fall through to                // restart the application.            }        } else {            ...        }        // Not running -- get it started, and enqueue this service record        // to be executed when the app comes up.        if (app == null) {        // 如果服務進程未啟動            if ((app=mAm.startProcessLocked(procName, r.appInfo, true, intentFlags,                    "service", r.name, false, isolated, false)) == null) {                ...                return msg;            }            if (isolated) {                r.isolatedProc = app;            }        }...        return null;    }}
上面的函數流程很簡單,就是判斷如果服務進程已經啟動,則直接調用realStartServiceLocked啟動服務,否則調用ActivityManagerService.startProcessLocked啟動進程,進入進程啟動階段。3. 進程啟動階段略。詳見《Android 進階 - 進程啟動分析》一文。4. 服務啟動階段4.1 流程圖4.2 關鍵流程分析上面的圖看似比Activity啟動要複雜,實際上大的步驟差不過,只不過,服務啟動時,先要createService,再bindService,要發兩次訊息,而Activity啟動只需要發一次訊息。如果服務進程已經啟動的情況下,可直接從3.1:realStartServiceLocked一步往下看。請先參看《Android 進階 - Looper進程內通訊》和《Android 進階 -Activity應用啟動分析》,從bindServiceLocked到sendMessage,再到訊息進入到Looper.mainLooper.queue隊列中,如果看了前面兩篇文章,相信這一部分很容易看懂,這裡不再討論。流程主要發了兩個訊息H.CREATE_SERVICE和H.BIND_SERVICE,一個是建立服務的訊息,一個是綁定服務的訊息。這些訊息會在Looper.loop函數依次處理。4.2.1 handleCreateServiceH.CREATE_SERVICE的訊息,經由H.dispatchMessage,會進入ActivityThread.handleCreateService函數。frameworks/base/core/java/android/app/ActivityThread.java
public clas ActivityThread{...private void handleCreateService(CreateServiceData data) {        // If we are getting ready to gc after going to the background, well        // we are back active so skip it.        unscheduleGcIdler();        LoadedApk packageInfo = getPackageInfoNoCheck(                data.info.applicationInfo, data.compatInfo);        Service service = null;        try {            java.lang.ClassLoader cl = packageInfo.getClassLoader();            service = (Service) cl.loadClass(data.info.name).newInstance();        } catch (Exception e) {            if (!mInstrumentation.onException(service, e)) {                throw new RuntimeException(                    "Unable to instantiate service " + data.info.name                    + ": " + e.toString(), e);            }        }        try {            if (localLOGV) Slog.v(TAG, "Creating service " + data.info.name);            ContextImpl context = new ContextImpl();            context.init(packageInfo, null, this);            Application app = packageInfo.makeApplication(false, mInstrumentation);            context.setOuterContext(service);            // 串連服務上下文            service.attach(context, this, data.info.name, data.token, app,                    ActivityManagerNative.getDefault());            // 觸發服務的onCreate事件            service.onCreate();            mServices.put(data.token, service);            try {                ActivityManagerNative.getDefault().serviceDoneExecuting(                        data.token, 0, 0, 0);            } catch (RemoteException e) {                // nothing to do.            }        } catch (Exception e) {            if (!mInstrumentation.onException(service, e)) {                throw new RuntimeException(                    "Unable to create service " + data.info.name                    + ": " + e.toString(), e);            }        }    }}
4.2.2 handleBindServiceH.BIND_SERVICE的訊息,經由H.dispatchMessage,會進入ActivityThread.handleBindService函數。frameworks/base/core/java/android/app/ActivityThread.java
public class ActivityThread{...    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());                try {                    if (!data.rebind) {                    // 觸發服務的onBind事件                        IBinder binder = s.onBind(data.intent);                        // 通過Binder,調用ActivityManagerService.publishService發布服務                        ActivityManagerNative.getDefault().publishService(                                data.token, data.intent, binder);                    } else {                        ...                    }                    ensureJitEnabled();                } catch (RemoteException ex) {                }            } catch (Exception e) {               ...            }        }    }}
4.2.3 publishService上節的源碼中說明,服務綁定完成之後,會通過ActivityManagerProxy代理,經由Binder,調用ActivityManagerService的publishService函數。frameworks/base/services/java/com/android/server/am/ActivityManagerService.java
pulbic class ActivityManagerService{...public void publishService(IBinder token, Intent intent, IBinder service) {        // Refuse possible leaked file descriptors        if (intent != null && intent.hasFileDescriptors() == true) {            throw new IllegalArgumentException("File descriptors passed in Intent");        }        synchronized(this) {            if (!(token instanceof ServiceRecord)) {                throw new IllegalArgumentException("Invalid service token");            }            mServices.publishServiceLocked((ServiceRecord)token, intent, service);        }    }    ...}    
frameworks/base/services/java/com/android/server/am/ActiveServices.java
pulbic class ActiveService{...void publishServiceLocked(ServiceRecord r, Intent intent, IBinder service) {        final long origId = Binder.clearCallingIdentity();        try {            if (DEBUG_SERVICE) Slog.v(TAG, "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 clist = r.connections.valueAt(conni);                        for (int i=0; iframeworks/base/core/java/android/app/LoadedApk.java
public class LoadApk{...static final class ServiceDispatcher {private static class InnerConnection extends IServiceConnection.Stub {            final WeakReference mDispatcher;            InnerConnection(LoadedApk.ServiceDispatcher sd) {                mDispatcher = new WeakReference(sd);            }// 通知已經串連            public void connected(ComponentName name, IBinder service) throws RemoteException {                LoadedApk.ServiceDispatcher sd = mDispatcher.get();                if (sd != null) {                // 進入下面的connected函數                    sd.connected(name, service);                }            }        }        ...        public void connected(ComponentName name, IBinder service) {            if (mActivityThread != null) {                mActivityThread.post(new RunConnection(name, service, 0));            } else {            // 進入下面的doConnected函數                doConnected(name, service);            }        }        ...        public void doConnected(ComponentName name, IBinder service) {            ServiceDispatcher.ConnectionInfo old;            ServiceDispatcher.ConnectionInfo info;            ...            // If there was an old service, it is not disconnected.            if (old != null) {            // 假如是服務,觸發onServiceDisconnected事件                mConnection.onServiceDisconnected(name);            }            // If there is a new service, it is now connected.            if (service != null) {            // 假如是新服務,則觸發onServiceConnected。mConnection為在Activity.bindService是傳入的參數,也即是綁定服務前使用者建立的ServiceConnection類執行個體。                mConnection.onServiceConnected(name, service);            }        }}}
流程走到這裡,就算是完成了,進入了調用者建立的ServiceConnection.onServiceConnected函數中,此函數會傳回服務的IBinder介面,調用者可以儲存此介面調用服務的各類操作。

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.