Service通過(guò)onBind啟動(dòng)流程源碼探究

根據(jù)《Activity啟動(dòng)流程源碼探究》我們可以清楚以下幾點(diǎn):
1)Context的通用實(shí)現(xiàn)是在ContextIml這個(gè)類中
2)Activity的啟動(dòng)過(guò)程需要借助ActivityManagerService(AMS)這個(gè)服務(wù)端來(lái)完成,其本質(zhì)是通過(guò)Binder通信
3)Binder通信使用了2次贺嫂,第一次Context作為客戶端向AMS發(fā)起start請(qǐng)求悍缠,第二次AMS作為客戶端向IApplicationThread發(fā)起最終的啟動(dòng)請(qǐng)求,我們暫且稱為“雙Binder切換機(jī)制”姑荷。
4)第二次Binder通信后盒延,通過(guò)H這個(gè)Handler進(jìn)行線程切換,并且切回了主線程鼠冕。Application添寺、Activity、ContextImpl等實(shí)例創(chuàng)建都在主線程中懈费,那些耗時(shí)操作其實(shí)是在Binder線程完成的计露。
鑒于此,我們分析Service的綁定啟動(dòng)過(guò)程從ContextImpl的bindService方法開(kāi)始分析。

1.第一次Binder機(jī)制

在ContextImpl中提供了bindService和bindServiceAsUser兩種方法啟動(dòng)Service薄坏,后者提供了兩個(gè)重載方法趋厉,他們最終都會(huì)調(diào)用bindServiceCommon方法。通過(guò)對(duì)比bindService和bindServiceAsUser方法的時(shí)候不難發(fā)現(xiàn)胶坠,他們都同時(shí)傳了mMainThread.getHandler()這個(gè)Handler君账,追蹤代碼會(huì)發(fā)現(xiàn)它就是我們ActivityThread中的H,最終傳給 LoadedApk.ServiceDispatcher的構(gòu)造方法沈善,用于切換到主線程乡数。
先來(lái)看一下bindServiceCommon這個(gè)方法,它完成了兩個(gè)任務(wù):
1)將H這個(gè)handler對(duì)象傳遞給LoadedApk.ServiceDispatcher的構(gòu)造方法闻牡,創(chuàng)建IServiceConnection實(shí)例净赴,其實(shí)是其內(nèi)部的ServiceDispatcher實(shí)例。IServiceConnection是Binder代理接口罩润,IServiceConnection.Stub存根的派生類的具體實(shí)現(xiàn)是LoadedApk.ServiceDispatcher的靜態(tài)內(nèi)部類InnerConnection玖翅。這一調(diào)用流程的核心代碼如下:

private boolean bindServiceCommon(Intent service, ServiceConnection conn, int flags, Handler
            handler, UserHandle user) {
        // Keep this in sync with DevicePolicyManager.bindDeviceAdminServiceAsUser.
        IServiceConnection sd;
        if (conn == null) {
            throw new IllegalArgumentException("connection is null");
        }
        if (mPackageInfo != null) {
            sd = mPackageInfo.getServiceDispatcher(conn, getOuterContext(), handler, flags);
        } else {
            throw new RuntimeException("Not supported in system context");
        }

這段代碼的意圖其實(shí)很明顯,就是為后期調(diào)用ServiceConnection#onServiceConnected方法后切回主線程做準(zhǔn)備割以。點(diǎn)擊進(jìn)入getServiceDispatcher的實(shí)現(xiàn)會(huì)看到金度,傳入的參數(shù)被用來(lái)構(gòu)建ServiceDispatcher,核心代碼如下:

public final IServiceConnection getServiceDispatcher(ServiceConnection c,
            Context context, Handler handler, int flags) {
        synchronized (mServices) {
            LoadedApk.ServiceDispatcher sd = null;
            ...
            if (sd == null) {
                sd = new ServiceDispatcher(c, context, handler, flags);
                if (DEBUG) Slog.d(TAG, "Creating new dispatcher " + sd + " for conn " + c)
            } 
            ...
            return sd.getIServiceConnection();
        }
    }

來(lái)看一下ServiceDispatcher的構(gòu)造方法如下:

        ServiceDispatcher(ServiceConnection conn,
                Context context, Handler activityThread, int flags) {
            mIServiceConnection = new InnerConnection(this);
            mConnection = conn;
            mContext = context;
            mActivityThread = activityThread;
            mLocation = new ServiceConnectionLeaked(null);
            mLocation.fillInStackTrace();
            mFlags = flags;
        }

mIServiceConnection = new InnerConnection(this)說(shuō)明IServiceConnection的最終實(shí)現(xiàn)是ServiceDispatcher.InnerConnection严沥,然后在getServiceDispatcher返回了sd.getIServiceConnection()這個(gè)方法猜极,它當(dāng)然就是mIServiceConnection這個(gè)InnerConnection的實(shí)例。注意消玄,Handler被命名為mActivityThread跟伏,并通過(guò)activityThread這個(gè)Handler變量賦值,就是為了便于提醒翩瓜,要切回到主線程受扳。
雖然這里先開(kāi)了一個(gè)Binder線程,其實(shí)它什么都沒(méi)干奥溺,就是在為Service綁定成功后的調(diào)用ServiceConnection#onServiceConnected方法做準(zhǔn)備工作辞色。當(dāng)然,ServiceConnection#onServiceConnected方法在子線程執(zhí)行浮定,需要通過(guò)mActivityThread這個(gè)主線程Handler切回到主線程相满。
2)調(diào)用ActivityManager.getService().bindService方法,開(kāi)啟第二個(gè)Binder線程桦卒。當(dāng)然立美,這里就順暢多了,跟我們之前的分析一致方灾,ContextImpl這個(gè)代理或者叫客戶端開(kāi)始通知AMS這個(gè)服務(wù)端發(fā)起執(zhí)行綁定任務(wù)了建蹄。

2.第二次Binder機(jī)制

直接來(lái)看bindServiceCommon方法中的核心代碼部分碌更,如下:

private boolean bindServiceCommon(Intent service, ServiceConnection conn, int flags, Handler
            handler, UserHandle user) {
        // Keep this in sync with DevicePolicyManager.bindDeviceAdminServiceAsUser.
        IServiceConnection sd;
        ...
        if (mPackageInfo != null) {
            sd = mPackageInfo.getServiceDispatcher(conn, getOuterContext(), handler, flags);
        } 
        ...
        try {
            ...
            int res = ActivityManager.getService().bindService( mMainThread.getApplicationThread(), getActivityToken(), service, service.resolveTypeIfNeeded(getContentResolver()), sd, flags, getOpPackageName(), user.getIdentifier());
                ...
            return res != 0;
        } 
    }

其實(shí)只有一行,調(diào)用了ActivityManager.getService().bindService方法洞慎,并且把sd這個(gè)IServiceConnection也傳了進(jìn)去痛单,這里ContextImpl這個(gè)客戶端通過(guò)bindService這個(gè)方法向AMS這個(gè)服務(wù)端發(fā)起了啟動(dòng)請(qǐng)求。第二次Binder正式啟動(dòng)劲腿,接下來(lái)就是AMS的表演時(shí)間了~
AMS#bindService并無(wú)特殊之處旭绒,將任務(wù)交給了ActiveServices的bindServiceLocked方法,代碼如下:

return mServices.bindServiceLocked(caller, token, service,resolvedType, connection, flags, callingPackage, userId);

注意焦人,這里的connection參數(shù)就是IServiceConnection挥吵。在bindServiceLocked內(nèi)部會(huì)調(diào)用bringUpServiceLocked方法喚起服務(wù),調(diào)用核心代碼如下:

ConnectionRecord c = new ConnectionRecord(b, activity, connection, flags, clientLabel, clientIntent);
                   ...
            if ((flags&Context.BIND_AUTO_CREATE) != 0) {
                s.lastActivity = SystemClock.uptimeMillis();
                if (bringUpServiceLocked(s, service.getFlags(), callerFg, false, permissionsReviewRequired) != null) {
                    return 0;
                }
            }

因?yàn)槭墙壎▌?chuàng)建花椭,所以系統(tǒng)會(huì)自動(dòng)添加BIND_AUTO_CREATE標(biāo)記忽匈,注意
ConnectionRecord c = new ConnectionRecord(b, activity, connection, flags, clientLabel, clientIntent);
這行代碼已經(jīng)將IServiceConnection存入ConnectionRecord中,變量c又被存入ServiceRecord s這個(gè)變量里矿辽。在bringUpServiceLocked方法的參數(shù)s既是丹允。在bringUpServiceLocked方法里會(huì)調(diào)用realStartServiceLocked(r, app, execInFg)方法(注意,這個(gè)r就是剛剛的s參數(shù))嗦锐。

3.第三次Binder機(jī)制

realStartServiceLocked(r, app, execInFg)方法首先會(huì)通過(guò)調(diào)用app.thread.scheduleCreateService方法創(chuàng)建Service嫌松,這個(gè)邏輯跟《Service啟動(dòng)流程源碼探究
的創(chuàng)建邏輯一致沪曙。ActiveServices是客戶端向ApplicationThread這個(gè)服務(wù)端發(fā)起創(chuàng)建Service請(qǐng)求奕污。這是第三次Binder機(jī)制。

4.第四次Binder機(jī)制

接著液走,bringUpServiceLocked內(nèi)部會(huì)執(zhí)行requestServiceBindingsLocked(r, execInFg)方法碳默,該方法又會(huì)調(diào)用requestServiceBindingLocked(r, ibr, execInFg, false)方法,內(nèi)部有一句核心代碼如下:

 //i.requested這個(gè)條件是為了標(biāo)記缘眶,如果已經(jīng)bind過(guò)就不會(huì)再次綁定嘱根,也就是Service.onBind方法只執(zhí)行一次
 if ((!i.requested || rebind) && i.apps.size() > 0) {
   r.app.thread.scheduleBindService(r, i.intent.getIntent(), rebind, r.app.repProcState);

這就是第四次Binder機(jī)制,這樣一級(jí)一級(jí)的將r往下傳遞巷懈。r.app.thread仍然是IApplicationThread.stub的具體實(shí)現(xiàn)该抒,也就是ActivityThread.ApplicationThread,ActiveServices作為客戶端向服務(wù)端ApplicationThread發(fā)起bind請(qǐng)求顶燕,ApplicationThread的處理代碼如下:

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);
        }

跟ActivityThread.ApplicationThread#scheduleCreateService一致凑保,通過(guò)一個(gè)靜態(tài)內(nèi)部類BindServiceData初始化綁定數(shù)據(jù),通過(guò)H.BIND_SERVICE切換到主線程完成具體的綁定工作涌攻。

5.BIND_SERVICE消息處理

在主線程中處理H.BIND_SERVICE的方法是handleBindService處理也很簡(jiǎn)單欧引,它首先執(zhí)行s.onBind(data.intent)獲得了IBinder實(shí)例,這里的s就是app.thread.scheduleCreateService方法創(chuàng)建的Service恳谎,注意:Service的onBind方法是在主線程調(diào)用的芝此,所以不可以執(zhí)行耗時(shí)任務(wù)憋肖。
然后將綁定任務(wù)又交給了AMS的publishService,核心代碼如下:

if (!data.rebind) {
  IBinder binder = s.onBind(data.intent); 
  ActivityManager.getService().publishService( data.token, data.intent, binder);
} else {
  s.onRebind(data.intent);
  ActivityManager.getService().serviceDoneExecuting(data.token, SERVICE_DONE_EXECUTING_ANON, 0, 0);
}

剛切到主線程婚苹,立馬有開(kāi)啟了Binder線程岸更,這次ActivityThread.ApplicationThread又擔(dān)當(dāng)起了客戶端的角色,向服務(wù)端AMS發(fā)起publishService請(qǐng)求膊升,既然選擇在Binder線程里面做坐慰,必然是相當(dāng)耗時(shí)啊用僧!第五次Binder機(jī)制的開(kāi)始了结胀。

6.第五次Binder機(jī)制

看一下最后的狂歡吧,代碼如下:

mServices.publishServiceLocked((ServiceRecord)token, intent, service)

AMS通過(guò)橋接方式责循,將任務(wù)交給了ActiveServices的publishServiceLocked方法糟港,注意token就是s。該方法仍然只有一行關(guān)鍵代碼如下:

try {
      c.conn.connected(r.name, service, false);
    }

這里的c就是上面存儲(chǔ)的ConnectionRecord c變量院仿,它內(nèi)部的c.conn就是我們傳入IServiceConnection實(shí)例秸抚,也就是InnerConnection的實(shí)例。這樣我們進(jìn)入LoadedApk.ServiceDispatcher.InnerConnection#connected(r.name, service, false)方法歹垫,看到如下代碼:

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

點(diǎn)擊進(jìn)入ServiceDispatcher的connected方法剥汤,會(huì)發(fā)現(xiàn)如下代碼:

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);
            }
        }

這里的mActivityThread只有一個(gè)賦值在ServiceDispatcher的構(gòu)造方法中,它是一個(gè)Handler排惨,而且是我們?cè)诘?節(jié)談到構(gòu)造ServiceDispatcher實(shí)例時(shí)傳入的H吭敢,mActivityThread.post清晰地告訴我們它將RunConnection切換到了主線程。

ServiceDispatcher(ServiceConnection conn,
                Context context, Handler activityThread, int flags) {
            mIServiceConnection = new InnerConnection(this);
            mConnection = conn;
            mContext = context;
            mActivityThread = activityThread;
            mLocation = new ServiceConnectionLeaked(null);
            mLocation.fillInStackTrace();
            mFlags = flags;
        }

當(dāng)然暮芭,我們還是要看一下RunConnection內(nèi)部是如何執(zhí)行的鹿驼。代碼如下:

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

它的run方法調(diào)用了doConnected方法,看看下面這行代碼吧:

            old = mActiveConnections.get(name);
            // 這里表示重復(fù)綁定沒(méi)用辕宏,不會(huì)向下執(zhí)行調(diào)用
            if (old != null && old.binder == service) {
               // Huh, already have this one.  Oh well!
               return;
            }
            // If there is a new service, it is now connected.
            if (service != null) {
                mConnection.onServiceConnected(name, service);
            }

是的畜晰,服務(wù)被綁定成功了,并且通知了客戶端瑞筐。注意:這里的意思是ServiceConnection.onServiceConnected方法在Binder工作線程執(zhí)行連接成功后凄鼻,被H切換到了主線程。
終于綁定成功了~

7.總結(jié)

很顯然聚假,相對(duì)于Activity的啟動(dòng)和Service的啟動(dòng)块蚌,Service的綁定任務(wù)相對(duì)復(fù)雜,回顧《Service啟動(dòng)流程源碼探究》 可以發(fā)現(xiàn)Service綁定任務(wù)多了2次Binder機(jī)制魔策。分別是ContextImpl作為客戶端LoadedApk作為服務(wù)端時(shí)的Binder機(jī)制匈子,和AMS作為客戶端ApplicationThread作為服務(wù)端的Binder機(jī)制。那么闯袒,整個(gè)流程就很明朗了虎敦,就是5次Binder機(jī)制游岳。
第1次Binder機(jī)制,ContextImpl作為客戶端LoadedApk.ServiceDispather.InnerConnection作為服務(wù)端創(chuàng)建IServiceConnection實(shí)例其徙,其本質(zhì)就是將H這個(gè)handler和ServiceConnection封裝在ServiceDispatcher中胚迫。
第2次Binder機(jī)制,ContextImpl作為客戶端向AMS這個(gè)服務(wù)端發(fā)起綁定請(qǐng)求唾那,并將IServiceConnection實(shí)例傳給AMS访锻。這樣AMS就具備了切換回主線程的H了。
第3次Binder機(jī)制闹获,AMS作為客戶端向ActivityThread.ApplicationThread這個(gè)服務(wù)端發(fā)起CreateService的請(qǐng)求期犬,ActivityThread.ApplicationThread接到請(qǐng)求后,通過(guò)H這個(gè)Handler切回主線程處理H.CREATE_SERVICE這個(gè)消息避诽。處理過(guò)程包括:創(chuàng)建ContextImpl龟虎、Application、Service等任務(wù)沙庐。
第4次Binder機(jī)制鲤妥,創(chuàng)建完任務(wù)之后,AMS作為客戶端向ActivityThread.ApplicationThread這個(gè)服務(wù)端發(fā)起B(yǎng)indService的請(qǐng)求拱雏,ApplicationThread接到請(qǐng)求后棉安,通過(guò)H這個(gè)Handler切回主線程處理H.BIND_SERVICE這個(gè)消息。處理過(guò)程相當(dāng)簡(jiǎn)單铸抑,調(diào)用Service的onBind方法返回IBinder實(shí)例贡耽,然后將該參數(shù)傳給AMS#publishService方法。注意:這個(gè)IBinder其實(shí)是ServiceRecord的一個(gè)實(shí)例羡滑,記錄了我們要綁定的服務(wù)的所有信息菇爪。這樣就進(jìn)入第5次Binder機(jī)制了。
第5次Binder機(jī)制柒昏,一旦接到綁定任務(wù),AMS#ActiveServices這個(gè)服務(wù)端就會(huì)提取ServiceRecord的服務(wù)信息熙揍,ConnectionRecord和IServiceConnection實(shí)例职祷,執(zhí)行綁定任務(wù),最后通過(guò)第2次Binder機(jī)制傳入的H這個(gè)Handler將連接結(jié)果回傳給主線程届囚。如果綁定成功有梆,系統(tǒng)會(huì)調(diào)用客戶端的mConnection.onServiceConnected接口通知客戶端。

詩(shī)云:

早歲那知世事艱意系,中原北望氣如山泥耀。樓船夜雪瓜洲渡,鐵馬秋風(fēng)大散關(guān)蛔添。
塞上長(zhǎng)城空自許痰催,鏡中雙鬢已先斑兜辞。出師一表真名世,千載誰(shuí)堪伯仲間夸溶!

筆者在寫這篇文章的時(shí)候逸吵,參考了任玉剛老師《Android開(kāi)發(fā)藝術(shù)探索》第9章中四大組件的啟動(dòng)相關(guān)內(nèi)容,請(qǐng)知悉~

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末缝裁,一起剝皮案震驚了整個(gè)濱河市扫皱,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌捷绑,老刑警劉巖韩脑,帶你破解...
    沈念sama閱讀 218,036評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異粹污,居然都是意外死亡扰才,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,046評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門厕怜,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)衩匣,“玉大人,你說(shuō)我怎么就攤上這事粥航±拍螅” “怎么了?”我有些...
    開(kāi)封第一講書人閱讀 164,411評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵递雀,是天一觀的道長(zhǎng)柄延。 經(jīng)常有香客問(wèn)我,道長(zhǎng)缀程,這世上最難降的妖魔是什么搜吧? 我笑而不...
    開(kāi)封第一講書人閱讀 58,622評(píng)論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮杨凑,結(jié)果婚禮上滤奈,老公的妹妹穿的比我還像新娘。我一直安慰自己撩满,他們只是感情好蜒程,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,661評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著伺帘,像睡著了一般昭躺。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上伪嫁,一...
    開(kāi)封第一講書人閱讀 51,521評(píng)論 1 304
  • 那天领炫,我揣著相機(jī)與錄音,去河邊找鬼张咳。 笑死帝洪,一個(gè)胖子當(dāng)著我的面吹牛似舵,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播碟狞,決...
    沈念sama閱讀 40,288評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼啄枕,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了族沃?” 一聲冷哼從身側(cè)響起频祝,我...
    開(kāi)封第一講書人閱讀 39,200評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎脆淹,沒(méi)想到半個(gè)月后常空,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,644評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡盖溺,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,837評(píng)論 3 336
  • 正文 我和宋清朗相戀三年漓糙,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片烘嘱。...
    茶點(diǎn)故事閱讀 39,953評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡昆禽,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出蝇庭,到底是詐尸還是另有隱情醉鳖,我是刑警寧澤,帶...
    沈念sama閱讀 35,673評(píng)論 5 346
  • 正文 年R本政府宣布哮内,位于F島的核電站盗棵,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏北发。R本人自食惡果不足惜纹因,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,281評(píng)論 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望琳拨。 院中可真熱鬧瞭恰,春花似錦、人聲如沸从绘。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 31,889評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至驳棱,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間社搅,已是汗流浹背乳规。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 33,011評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工合呐, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人淌实。 一個(gè)月前我還...
    沈念sama閱讀 48,119評(píng)論 3 370
  • 正文 我出身青樓拆祈,卻偏偏與公主長(zhǎng)得像恨闪,于是被迫代替她去往敵國(guó)和親放坏。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,901評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容

  • 2.1 Activity 2.1.1 Activity的生命周期全面分析 典型情況下的生命周期:在用戶參與的情況下...
    AndroidMaster閱讀 3,040評(píng)論 0 8
  • bindService的過(guò)程要比startService的過(guò)程復(fù)雜一些,因?yàn)閎ingService之后麸粮,發(fā)起者可以...
    小爨閱讀 6,098評(píng)論 1 21
  • Zygote是什么?有什么作用炊昆? Android系統(tǒng)底層基于Linux Kernel, 當(dāng)Kernel啟動(dòng)過(guò)程會(huì)創(chuàng)...
    Mr槑閱讀 2,808評(píng)論 4 18
  • 好久沒(méi)回家了威根,晚上乘著T95火車回赤壁凤巨,此車的終點(diǎn)站是深圳洛搀,很熟悉的名稱啊,有個(gè)人已經(jīng)不在了留美,不過(guò)記憶還殘余著,停...
    這回吃飽了閱讀 154評(píng)論 0 0
  • 一次偶然的機(jī)會(huì)逢倍,在豆瓣上認(rèn)識(shí)了遠(yuǎn)在美利堅(jiān)的Daniel.他是一名在讀碩士景图,對(duì)中國(guó)文化很感興趣较雕。而且有計(jì)劃來(lái)中國(guó)實(shí)地...
    維枷閱讀 848評(píng)論 0 1