Service - 01

前言

來到公司實習一個半月了,一直在做測試,每天都是重復機械性的動作,腦子都快生銹了。前幾天佳哥讓我們幾個實習生選擇一個想要深入的模塊,聽到消息我興奮了半天。冷靜下來之后我去詢問華仔師兄哪個模塊能學到更多的東西,師兄告訴我他是做iPod的,他說這個模塊應(yīng)該是最難的,會接觸到JNI、IPC通信、多線程和很多設(shè)計模式的知識,我又興奮了,第二天就找佳哥確定了下來。師兄給了我一份代碼,然后跟我講解了開發(fā)流程、MFi認證、代碼執(zhí)行流程并且?guī)艺J識了DQA做iPod測試的曾姐,這里再次謝謝師兄啦~自己開始看代碼的時候簡直一頭霧水,好多類,每個類都有好多方法和變量,跳轉(zhuǎn)過來跳轉(zhuǎn)過去,跳轉(zhuǎn)之間我看到了Service、ServiceConnetion,可惜之前學的這部分內(nèi)容差不多已經(jīng)忘光光了,所以我在網(wǎng)上百度了半天,終于有點眉目了。為了防止以后出現(xiàn)這種忘記之前學的內(nèi)容的情況,所以我決定開始寫博客,主要記錄自己學到的東西和一些筆記,順帶當成日志咯,廢話說完了,開始正題吧。


什么是Service?

在Android的四大組件里面,Activity用來展示一個界面,與用戶交互,Service則是處理一些“后臺”的事物。我們在Activity里面執(zhí)行代碼都是在主線程里面,如果直接在主線程里面執(zhí)行費時操作的話就會產(chǎn)生ANR(Application Not Responding)錯誤,所以按理說如果在Service中去執(zhí)行這些費時的操作比較符合情理吧,但是通過分別在Activity的onCreate和在Service的onCreate中打印線程id:

Log.d(TAG, "pid : " + Process.myPid());

可以發(fā)現(xiàn)兩者運行在同一個線程當中。什么鬼?這樣的話,在Service的線程中執(zhí)行費時操作不就會引起ANR錯誤嗎(第一次面試的時候被問到這個問題,我也是這個萌萌噠反應(yīng))?直接在Service中執(zhí)行費時操作確實會引起ANR,所以需要在Service中開子線程去執(zhí)行費時操作。新司機們可能會問了,那既然是同一個線程,為什么不直接在Activity中開子線程執(zhí)行費時操作咯,干嘛這么麻煩?這個時候就引申出如下的問題。


在Activity和Service中開線程有什么區(qū)別?

Android系統(tǒng)是穿上Dalvik和JVM外衣的Linux,一個app執(zhí)行的時候,Linux創(chuàng)建一個進程來跑一個Dalvik虛擬機,在虛擬機中執(zhí)行我們的應(yīng)用代碼,順帶給這個進程分配一些內(nèi)存。當Android系統(tǒng)內(nèi)存不足的時候,它老人家就會銷毀一些進程來釋放內(nèi)存,那應(yīng)該銷毀哪些進程呢?正在和用戶交互、用戶看得見的肯定不能銷毀,不然會讓用戶不開心,正在執(zhí)行一些重要的任務(wù)的進程也不能銷毀,比如播放音樂、下載文件這類的進程,銷毀了也會讓影響用戶感受。那應(yīng)該銷毀哪些進程才合適呢?或者,我們換個方向思考:上述的進程為什么會被Andoird系統(tǒng)判斷不能銷毀?因為它們使用了Service中開線程的方式去執(zhí)行費時操作的。反言之,如果我們的app在執(zhí)行費時操作的時候只是在Acticvity中開子線程去執(zhí)行,進程的優(yōu)先級會比較低,在系統(tǒng)內(nèi)存不足的時候就會被優(yōu)先回收掉。以后再來說這個進程的分類吧,接下來看一下Service的生命周期咯


Service的生命周期

  • void onCreate():在Service被創(chuàng)建的時候回調(diào),這里面可以執(zhí)行一些初始化的工作。
  • int onStartCommand(Intent intent, int flags, int startId):這個方法是在開啟Service過程的onCreate之后回調(diào),里面的參數(shù)intent可以用來傳一下數(shù)據(jù)給Service。來看看API怎么說:

Note that the system calls this on your service's main thread. A service's main thread is the same thread where UI operations take place for Activities running in the same process. You should always avoid stalling the main thread's event loop. When doing long-running operations, network calls, or heavy disk I/O, you should kick off a new thread, or use {@link android.os.AsyncTask}.


這個方法在Service的主線程里面調(diào)用,Service的主線程跟Activity里面進行UI操作是在同一個線程,不應(yīng)該在里面做事件循環(huán)、費時操作和大量I/O操作,應(yīng)該開一個新的線程或者AsyncTask去完成這些事。

  • ** IBinder onBind(Intent intent)** : 在綁定Service的時候回調(diào),這個方法能夠返回一個IBinder的對象,跟onStartCommand最大的區(qū)別就在這里。下一篇我們詳細來說,先來看看這個方法API怎么說:

Return the communication channel to the service. May return null if clients can not bind to the service. The returned {@link android.os.IBinder} is usually for a complex interface that has been <a href="{@docRoot}guide/components/aidl.html">described using aidl.
Note that unlike other application components, calls on to the IBinder interface returned here may not happen on the main thread of the process</em>. More information about the main thread can be found in <a href="{@docRoot}guide/topics/fundamentals/processes-and-threads.html">Processes and Threads.


返回一個與Service通信的通道,客戶端不能bind到Service的時候可能會返回null。返回的IBinder通常是一個用AIDL描述過的復雜的接口。不像其他的組件,這個方法返回值得過程可能不在跟主線程所在的進程里面完成。

第二段解釋有點不明白啊,“可能不在跟主線程所在的進程里面完成”,我的理解是Service在注冊的時候被指定了process,跟主進程不再是同一個進程。既然不在同一個進程了,那么那些堆啊棧啊的內(nèi)存區(qū)也就不同了,這個時候很多東西在使用的時候就不能像以前我們還在同一片內(nèi)存里面那么幸福愉快了,比如大家的靜態(tài)成員變量不能一起用了,同步鎖也失敗了,單利模式也不行了等等,有機會再詳細說吧。當Service跟要使用Service的Client不在同一個進程的時候,這里就需要使用跨進程通信了,API第一段中說的"用AIDL描述過的復雜的接口"就是其中一種方法,下下一篇將詳細說這個東西。

  • boolean onUnbind(Intent intent) : 在Unbind我們已經(jīng)開啟的Service的時候回調(diào),有一個參數(shù)可以傳必要的信息過來,來看看API怎么說:

Called when all clients have disconnected from a particular interface published by the service. The default implementation does nothing and returns false.


當Client與Service傳給它的接口斷開連接的時候在Service端調(diào)用,默認的實現(xiàn)什么都沒干,返回一個false。

沒啥好說的,下一個。

  • void onRebind(Intent intent) : 在Client重新綁定Service的時候回調(diào),這個方法回調(diào)的前提就是之前你得綁定過,并且已經(jīng)Unbind了,我們來看看API:

Called when new clients have connected to the service, after it had
previously been notified that all had disconnected in its
{@link #onUnbind}. This will only be called if the implementation
of {@link #onUnbind} was overridden to return true.


當已經(jīng)連接到Service的Client在onUnbind里面斷開了與Service的連接,然后新的Client已經(jīng)連接到Service的時候回調(diào)這個方法,并且實現(xiàn)重寫這個onUnbind這個方法的時候得返回true才行。

就是說要想重新綁定的話,前一個已經(jīng)綁定的Client得先斷開連接,并且onUnbind方法的返回值要重寫成true,但是我在測試的時候發(fā)現(xiàn)光按照這樣的說不行,我得執(zhí)行流程是直接bind,然后unbind(已經(jīng)重寫了返回true),然后我再次bind的時候什么都沒回調(diào)啊,然后又查了一下,找到原因是我在bind之前沒有先startService,為什么會這樣呢?我們來研究以下執(zhí)行流程:

  • *第一種回調(diào)流程:onCreate -> onBind -> onUnbind -> onDestroy *
  • *第二種回調(diào)流程:onCreate -> onStartCommand -> onBind -> onUnbind -> onRebind *

我們看,第一種流程沒有執(zhí)行start,在unbind之后,直接就destroy了,也就是說Service已經(jīng)銷毀了,當我們再次bind的時候,當然就不是rebind了,而是重新回調(diào)bind,第二種流程在bind之前執(zhí)行了start,當我們onUnbind的時候,不會回調(diào)onDestroy,Service還存在,我們再來bind的時候就會去回調(diào)onRebind啦。

  • void onDestroy() : 這個悲傷的方法無以言表,直接看API吧:

Called by the system to notify a Service that it is no longer used and is being removed.The service should clean up any resources it holds (threads, registered receivers, etc) at this point. Upon return, there will be no more calls in to this Service object and it is effectively dead. Do not call this method directly.


當Service被系統(tǒng)告知"你沒用了"的時候回調(diào),在這個方法里面應(yīng)該做一些回收資源的工作,線程、注冊的廣播接收器、etc等等,回調(diào)完這個方法之后,Service里面的方法不會再有任何回調(diào),這個Service絕對已經(jīng)掛掉了。

正如API所說,Service沒用了就會回調(diào)這個方法,做一些回收工作。那如果不回收會怎么樣呢?隨帶讓我想起了OOM,OOM的原因主要有兩種,一個是圖片回收沒處理好產(chǎn)生的OOM,另外一種就是Activity中很多引用、資源等沒有及時回收引起的OOM,以后有機會再說這個吧?;仡^看看寫了好多啊,比寫過的所有作文都用心。我的一血先到這里吧,如果文中出現(xiàn)不正確的地方非常歡迎各位大神幫小弟指出來,在這里感激不盡~下一篇將會寫我理解到的startService和bindService的區(qū)別。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容