引言
為朔州等地區(qū)用戶提供了全套網頁設計制作服務,及朔州網站建設行業(yè)解決方案。主營業(yè)務為成都網站制作、成都網站設計、朔州網站設計,以傳統(tǒng)方式定制建設網站,并提供域名空間備案等一條龍服務,秉承以專業(yè)、用心的態(tài)度為用戶提供真誠的服務。我們深信只要達到每一位用戶的要求,就會得到認可,從而選擇與我們長期合作。這樣,我們也可以走得更遠!
Service 服務是 Android 系統(tǒng)最常用的四大部件之一,Android 支持 Service 服務的原因主要目的有兩個,一是簡化后臺任務的實現,二是實現在同一臺設備當中跨進程的遠程信息通信。
Service 服務主要分為 Local Service 本地服務與 Remote Service 遠程服務兩種,本地服務只支持同一進程內的應用程序進行訪問,遠程服務可通過AIDL(Android Interface Definition Language)技術支持跨進程訪問。服務可以通過Context.startService()和Context.bindService()進行啟動,一般Local Service本地服務可使用其中一種方法啟動,但Remote Service遠程服務只能使用Context.bindService()啟動,而兩種調用方式在使用場景與活動流程中都存在差異。還有通過多線程技術處理 Service 服務的延時操作等技術,下文將針對Android 系統(tǒng)的 Service 服務的一系列操作進行深入探討。
目錄
一、Service 服務的概念與說明
二、Service 服務的生命周期
三、Local Service 的應用原理與開發(fā)實例
四、通過多線程方式處理 Service 服務的延時性操作
五、淺談 Remote Service 遠程服務原理
一、Android Service的概念與說明
1.1 Service 服務的定義
Android Service 是 Android 平臺最常用的部件之一,其概念與 Windows Service 類似,熟悉Windows開發(fā)的朋友應該對此概念會有所了解。當 Android 系統(tǒng)需要對現有的程序數據進行監(jiān)聽,或者對現有 Actitvity 提供數據服務支撐時,就會使用到 Android Service 。例如:對用戶地理位置的檢測,對SD卡定時掃描,對當地氣候的定期檢測都會使用到 Service 服務,Service 一般都是運行于后臺,不需要用戶界面支撐。Service 服務不會自動創(chuàng)建線程,如果開發(fā)人員沒有為Service服務添加異步操作,那Service服務將運行于主線程當中。
1.2 Service 服務的類型
1.2.1 按照 Service 的生命周期模型一共分為兩種類型
第一類是直接通過Context.startService()啟動,通過Context.stopService() 結束Service,其特點在于調用簡單,方便控制。缺點在于一旦啟動了 Service 服務,除了再次調用或結束服務外就再無法對服務內部狀態(tài)進行操控,缺乏靈活性。
第二類是通過Context.bindService()啟動,通過Context.unbindService() 結束,相對其特點在運用靈活,可以通過 IBinder 接口中獲取 Service 的句柄,對 Service 狀態(tài)進行檢測。
從 Android 系統(tǒng)設計的架構上看,startService() 是用于啟動本地服務,bindService() 更多是用于對遠程服務進行綁定。當然,也可以結合兩者進行混合式應用,先通過startService()啟動服務,然后通過 bindService() 、unbindService()方法進行多次綁定,以獲取 Service 服務在不同狀態(tài)下的信息,最后通過stopService()方法結束Service運行,在下面文章里將舉例一一說明。
1.2.2 按照 Service 的寄存方式分為兩種類型
本地服務 (Local Service) 寄存于當前的進程當中,當前進程結束后 Service 也會隨之結束,Service 可以隨時與 Activity 等多個部件進行信息交換。Service服務不會自動啟動線程,如果沒有人工調用多線程方式進行啟動,Service將寄存于主線程當中。
遠程服務 (Remote Service ) 獨立寄存于另一進程中, 通過 AIDL (Android Interface Definition Language)接口定義語言,實現Android設備上的兩個進程間通信(IPC)。AIDL 的 IPC 機制是基于 RPC (Remote Proceduce Call) 遠程過程調用協議建立的,用于約束兩個進程間的通訊規(guī)則,供編譯器生成代碼。進程之間的通信信息,首先會被轉換成AIDL協議消息,然后發(fā)送給對方,對方收到AIDL協議消息后再轉換成相應的對象,其使用方法在下文將會詳細說明。
回到目錄
二、Android Service 的生命周期
2.1 Service 服務的常用方法
方法 | 說明 |
void onCreate() | 當Service被啟動時被觸發(fā),無論使用Context.startServcie還是Context.bindService啟動服務,在Service整個生命周期內只會被觸發(fā)一次 |
int onStartCommand(Intent intent, int flags, int startId) | 當通過Context.startService啟動服務時將觸發(fā)此方法,但當使用 Context.bindService 方法時不會觸發(fā)此方法,其中參數 intent 是 startCommand 的輸入對象,參數 flags 代表 service 的啟動方式,參數 startId 當前啟動 service 的唯一標式符。返回值決定服務結束后的處理方式,下文將再作詳細說明。 |
void onStart(Intent intent,int startId) | 2.0舊版本的方法,已被Android拋棄,不推薦使用,默認在onStartCommand 執(zhí)行中會調用此方法 |
IBinder onBind(Intent intent) | 使用 Context.bindService 觸發(fā)服務時將調用此方法,返回一個IBinder 對象,在遠程服務時可用于對 Service 對象進行遠程操控 |
void onRebind(Intent intent) | 當使用startService啟動Service,調用bindService啟動Service,且 onUnbind 返回值為 true 時,下次再次調用 Context.bindService 將觸發(fā)方法 |
boolean onUnbind(Intent intent) | 調用 Context.unbindService 觸發(fā)此方法,默認返回 false, 當返回值 true 后,再次調用 Context.bindService 時將觸發(fā) onRebind 方法 |
void onDestory() | 分三種情況:1.以Context.startService啟動service,調用Context.stopService結束時觸發(fā)此方法;2.以Context.bindService啟動service,以Context.unbindService結束時觸發(fā)此方法;3.先以Context.startService 啟動服務,再用Context.bindService綁定服務,結束時必須先調用Context.unbindService解綁再使用Context.stopService結束service才會觸發(fā)此方法。 |
表2.1
細說onStartCommand 方法
由于手機的RAM、內部資源有限,所以很多Service都會因為資源不足而被Kill掉,這時候返回值就決定了Service被Kill后的處理方式,一般 int onStartCommand(intent,flags,startId)的返回值分為以下幾種:
START_STICKY
如果service進程被kill掉,系統(tǒng)會嘗試重新創(chuàng)建Service,如果在此期間沒有任何啟動命令被傳遞到Service,那么參數intent將為null。
START_NOT_STICKY
使用這個返回值時,如果在執(zhí)行完onStartCommand()后,服務被異常kill掉,系統(tǒng)不會自動重啟該服務。
START_REDELIVER_INTENT
使用這個返回值時,如果在執(zhí)行完onStartCommand()后,服務被異常kill掉,系統(tǒng)會自動重啟該服務,并將intent的值傳入。
START_STICKY_COMPATIBILITY
START_STICKY的兼容版本,但不保證服務被kill后一定能重啟。
而輸入參數flags正是代表此次onStartCommand()方法的啟動方式,正常啟動時,flags默認為0,被kill后重新啟動,參數分為以下兩種:
START_FLAG_RETRY
代表service被kill后重新啟動,由于上次返回值為START_STICKY,所以參數 intent 為null
START_FLAG_REDELIVERY
代表service被kill后重新啟動,由于上次返回值為START_REDELIVER_INTENT,所以帶輸入參數intent
2.2 Service 的運作流程
上文曾經提到 Service 的啟動方法有Context.startService(intent),Context.bindService(intent,serviceConnection,int) 兩種,下面詳細介紹一下它們工作流程。
當系統(tǒng)調用Context.startService()方法時,先會觸發(fā)Service的onCreate()方法,這一般用于對Service的運行條件作初始化處理,且在Service的生命周期內只會被觸發(fā)一次。然后系統(tǒng)將觸發(fā)Service的onStartCommand()方法,用戶每次調用startService()方法,都會觸發(fā)onStartCommand()方法。之后,Service 除非在資源不足的情況下被系統(tǒng) kill 掉,否則Service不會自動結束,直至系統(tǒng)調用Context.stopService()方法時,Service 才會結束。在Service結束時將自動啟動onDestory()方法對運轉中的Service作最后處理。
注意:即使系統(tǒng)多次調用 startService()或 bindService()方法, onCreate() 方法只會在第一次調用時被觸發(fā)。同理 onDestory () 方法也只會在服務完結時被觸發(fā),其原理可看第2.1節(jié)該方法的詳細說明。
當系統(tǒng)調用Context.bindService()方法時,也會觸發(fā)Service的onCreate()方法對Service對象的運行條件作初始化處理,然后觸發(fā)Service 的 onBind ()方法對服務進行綁定,成功獲取Service的句柄后,系統(tǒng)就會通過用戶自定義的serviceConnection對象onServiceConnected(ComponentName name, IBinder service)方法,對 Service 對象作出處理。最后當系統(tǒng)調用Context.unbindService()結束服務時,就會激發(fā)Service的onDestory()方法對運轉中的 Service 作最后的處理。
注意:系統(tǒng)調用 Context.bindService()方法,完成 Service.onBind() 綁定后就會觸發(fā) serviceConnection對象的 onServiceConnected()方法,但只要系統(tǒng)未使用 Context.unbindService()方法對 service 服務進行解綁,即使多次調用bindService(),系統(tǒng)也只會在第一次綁定時調用onBind() 和 onServiceConnected方()法一次。這正是 startService()與 bindService()方法其中的區(qū)別,單從字面上理解 startService () 啟動服務是可以多次執(zhí)行,所以多次調用 startService()方法都會觸發(fā) onStartCommand()事件,而bindService() 是綁定服務,所以只要服務已經被綁定,在未解綁時也不會多次執(zhí)行onServiceConnected()綁定后的操作,這也是兩者在使用場景上的區(qū)別所在。
Service 生命周期 圖2.2
Service 的運轉流程就先介紹到這里,具體的使用方法將在下面的章節(jié)中詳細介紹。
回到目錄
三、Local Service 應用原理與開發(fā)實例
3.1 通過 Context.startService 啟動 Service 服務
首先建立MyService繼承Service,實現onCreate()、onDestory()、onStartCommand()、onStart()等幾個方法,使用日志記錄其運作信息。在Activity中通過Intent綁定Service服務,通過Context.startService()啟動服務,通過Context.stopService()結束服務。
public class MainActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); } public void btnStart_onclick(View view){ //通過Intent綁定MyService,加入輸入參數 Intent intent=new Intent(MainActivity.this,MyService.class); intent.putExtra("Name", "Leslie"); Log.i(Context.ACTIVITY_SERVICE, "----------onClick startService-----------"); //啟動MyService startService(intent); } public void btnStop_onclick(View view){ Intent intent=new Intent(MainActivity.this,MyService.class); Log.i(Context.ACTIVITY_SERVICE, "----------onClick stopService------------"); //停止MyService stopService(intent); } } public class MyService extends Service{ @Override public void onCreate(){ Log.i(Context.ACTIVITY_SERVICE,"Service onCreate"); super.onCreate(); } @Override public void onDestroy() { Log.i(Context.ACTIVITY_SERVICE, "Service onDestroy"); super.onDestroy(); } @Override public void onStart(Intent intent, int startId){ Log.i(Context.ACTIVITY_SERVICE,"Service onStart"); super.onStart(intent, startId); } @Override public int onStartCommand(Intent intent, int flags, int startId) { Log.i(Context.ACTIVITY_SERVICE, "Service onStartCommand"); String name=intent.getStringExtra("Name"); Log.i(Context.ACTIVITY_SERVICE,"His name is "+name); return super.onStartCommand(intent, flags, startId); } }
AndroidManifest.xml 文件綁定
Service 配置說明:
android:name 服務類名,注意如果Service與Activity不在同一個包中,在android:name上必須寫上Service的全路徑
android:label 服務的名字,如果為空,默認顯示的服務名為類名
android:icon 服務的圖標
android:permission 申明此服務的權限,這意味著只有提供了該權限的應用才能控制或連接此服務
android:process 表示該服務是否運行在另外一個進程,如果設置了此項,那么將會在包名后面加上這段字符串表示另一進程的名字
android:enabled 如果此項設置為 true,那么 Service 將會默認被系統(tǒng)啟動,默認值為 false
android:exported 表示該服務是否能夠被其他應用程序所控制或連接,默認值為 false
查看處理結果可清楚看到,多次調用startService()后,使用stopService()結束Service服務,onCreate()、onDestory()只會在Service啟動和結束時被調用一次。只有Service中的onStartCommand()方法會被多次調用。而Android 2.0以下舊版的方法onStart()會在onStartCommand()調用過程中被激發(fā)。
3.2 通過Context.bindService啟動Service服務
在介紹Context.bindService()前,先講解一下與此相關的常用類 Binder、ServiceConnection,首先 IBinder 是 Binder 遠程對象的基本接口,是為高性能而設計的輕量級遠程調用機制的核心部分。這個接口定義了與遠程對象交互的協議,但它不僅用于遠程調用,也用于進程內調用。系統(tǒng)可以通過它以獲取Service的句柄,在此先簡單介紹它的基本用法,在下面關于Remote Service遠程服務對象時再詳細講述IBinder的主體功能。ServiceConnection主要用于通過Binder綁定Service句柄后,對Service對象進行處理,它主要有兩個方法void onServiceConnected(ComponentName name, IBinder service)和void onServiceDisconnected(ComponentName name)。在Context.bindService()完成綁定后,系統(tǒng)就會調用 onServiceConnected() 方法,用戶可以通過 IBinder 參數獲取Service句柄,對Service進行處理。而 onServiceDisconnected() 方法一般不會被調用,只有Service被綁定后,由于內存不足等問題被意外 kill 時才會被調用。下面舉個例子說明一下bindService()的用法。
public class MainActivity extends Activity { private MyServiceConnection serviceConnection; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); serviceConnection=new MyServiceConnection(); } public void btnBind_onclick(View view){ //綁定 MyService Intent intent=new Intent(this,MyService.class); Log.i(Context.ACTIVITY_SERVICE, "----------onClick bindService-----------"); //通過bindService(intent,serviceConnection,int)方式啟動Service bindService(intent,this.serviceConnection,Context.BIND_AUTO_CREATE); } public void btnUnbind_onclick(View view){ Log.i(Context.ACTIVITY_SERVICE, "----------onClick unbindService----------"); unbindService(serviceConnection); } } public class MyService extends Service{ private MyBinder myBinder; @Override public IBinder onBind(Intent intent) { Log.i(Context.ACTIVITY_SERVICE,"Service onBind"); return this.myBinder; } @Override public boolean onUnbind(Intent intent){ Log.i(Context.ACTIVITY_SERVICE,"Service onUnbind"); return super.onUnbind(intent); } @Override public void onCreate(){ super.onCreate(); Log.i(Context.ACTIVITY_SERVICE,"Service onCreate"); myBinder=new MyBinder(); } @Override public void onDestroy() { Log.i(Context.ACTIVITY_SERVICE, "Service onDestroy"); super.onDestroy(); } public String getDate(){ Calendar calendar = Calendar.getInstance(); return calendar.getTime().toString(); } public class MyBinder extends Binder { public MyService getService(){ return MyService.this; } } } public class MyServiceConnection implements ServiceConnection{ @Override public void onServiceConnected(ComponentName name, IBinder service){ Log.i(Context.ACTIVITY_SERVICE, "Service Connected"); String data=null; //通過IBinder獲取Service句柄 MyService.MyBinder myBinder=(MyService.MyBinder)service; MyService myService=myBinder.getService(); data=myService.getDate(); Log.i(Context.ACTIVITY_SERVICE,data); } @Override public void onServiceDisconnected(ComponentName name) { Log.i(Context.ACTIVITY_SERVICE, "Service Disconnected"); } }
在運行時多次點擊按鈕激發(fā)btnBind_ view)方法后再使用btnUnbind_ view)結束服務,請留意處理結果。當系統(tǒng)調用Context .bindService()后,Service將跟隨onCreate()、onBind()、onUnbind()、onDestory()的流程走下去。在成功完成onBind()綁定后,就會激發(fā)ServiceConnection對象的onServiceConnected()方法,在此用戶可對Service進行處理。記得第2.2節(jié)所提過的問題,即使多次調用Context.bindService()方法,只要沒有調用unbindService()結束綁定,系統(tǒng)只會在第一次調用時激發(fā)Service.onBind()和onServiceConnected()方法,這點從運行結果中可得到證實。
注意:調用 Context .bindService() 啟動 Service 后 ,只能調用 unbindService() 一次,如重復多次調用此方法系統(tǒng)將會拋出錯誤異常。所以最簡單的處理方式是設置一個靜態(tài)變量 boolean connected,在調用 unbindService() 前先作出判斷
public class MainActivity extends Activity { private MyServiceConnection serviceConnection; private static boolean connected; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); serviceConnection=new MyServiceConnection(); } public void btnBind_onclick(View view){ connected=true; //綁定 MyService Intent intent=new Intent(this,MyService.class); Log.i(Context.ACTIVITY_SERVICE, "----------onClick bindService-----------"); //通過bindService(intent,serviceConnection,int)方式啟動Service bindService(intent,this.serviceConnection,Context.BIND_AUTO_CREATE); } public void btnUnbind_onclick(View view){ Log.i(Context.ACTIVITY_SERVICE, "----------onClick unbindService----------"); if(connected){ unbindService(serviceConnection); connected=false; } } }
3.3 Service 服務的綜合運用
在前兩節(jié)只是從初級階段介紹了Service服務的使用原理,無論是使用startService()或者bindService()啟動服務,Service服務的運行都是階段性,當使用stopService()、unbindService()后,Service服務就會結束。然而從現實應用層面上看,Service 服務很多時候是長駐后臺的,它會記錄程序運行的流程,當今的狀態(tài)等重要信息。此時,更多的使用方式就是結合startService()、bindService()兩種方式調用Service服務,startService()負責管理Service服務的啟動,輸入初始化參數,bindService()負責定時對Service服務進行檢測。而且流程是有規(guī)律性,以startService()啟動服務后,每使用bindService()綁定服務,就通過serviceConnection對服務進行檢測,然后以unbindService()結束綁定。注意,此時服務并未結束,而是長期運行于后臺,直到系統(tǒng)以stopService()方法結束服務后,Service才會最終完結。
public class MainActivity extends Activity { private MyServiceConnection serviceConnection; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); serviceConnection=new MyServiceConnection(); } public void btnBind_onclick(View view){ //綁定 MyService Intent intent=new Intent(this,MyService.class); Log.i(Context.ACTIVITY_SERVICE, "----------onClick bindService-----------"); //通過bindService(intent,serviceConnection,int)方式啟動Service bindService(intent,this.serviceConnection,Context.BIND_AUTO_CREATE); } public void btnUnbind_onclick(View view){ Log.i(Context.ACTIVITY_SERVICE, "----------onClick unbindService----------"); unbindService(serviceConnection); } public void btnStart_onclick(View view){ //通過Intent綁定MyService,加入初始參數 Intent intent=new Intent(MainActivity.this,MyService.class); intent.putExtra("param",0.88); Log.i(Context.ACTIVITY_SERVICE, "----------onClick startService-----------"); //啟動MyService startService(intent); } public void btnStop_onclick(View view){ Intent intent=new Intent(MainActivity.this,MyService.class); Log.i(Context.ACTIVITY_SERVICE, "----------onClick stopService------------"); //停止MyService stopService(intent); } } public class MyService extends Service{ private MyBinder myBinder; private double param; @Override public void onStart(Intent intent, int startId){ Log.i(Context.ACTIVITY_SERVICE,"Service onStart"); super.onStart(intent, startId); } @Override public int onStartCommand(Intent intent, int flags, int startId) { Log.i(Context.ACTIVITY_SERVICE, "Service onStartCommand"); //獲取Context.startService設置的param初始值 this.param=intent.getDoubleExtra("param",1.0); return super.onStartCommand(intent, flags, startId); } @Override public IBinder onBind(Intent intent) { Log.i(Context.ACTIVITY_SERVICE,"Service onBind"); return this.myBinder; } @Override public boolean onUnbind(Intent intent){ Log.i(Context.ACTIVITY_SERVICE,"Service onUnbind"); return super.onUnbind(intent); } @Override public void onCreate(){ super.onCreate(); Log.i(Context.ACTIVITY_SERVICE,"Service onCreate"); myBinder=new MyBinder(); } @Override public void onDestroy() { Log.i(Context.ACTIVITY_SERVICE, "Service onDestroy"); super.onDestroy(); } //獲取處理后的值 public double getValue(int value){ return value*param; } public class MyBinder extends Binder { public MyService getService(){ return MyService.this; } } } public class MyServiceConnection implements ServiceConnection{ @Override public void onServiceConnected(ComponentName name, IBinder service){ Log.i(Context.ACTIVITY_SERVICE, "Service Connected"); //通過IBinder獲取Service句柄 MyService.MyBinder myBinder=(MyService.MyBinder)service; MyService myService=myBinder.getService(); //生成隨機數輸入 Random random=new Random(); double value=myService.getValue(random.nextInt(10)*1000); //顯示計算結果 Log.i(Context.ACTIVITY_SERVICE,String.valueOf(value)); } @Override public void onServiceDisconnected(ComponentName name) { Log.i(Context.ACTIVITY_SERVICE, "Service Disconnected"); } }
通過startService() 啟動服務后,多次使用bindService()綁定服務,unbindService()解除綁定,最后通過stopService()結束服務后,可以看到下面的結果
這時候 Service 的onBind()方法和onUnbind()方法只在第一個bindService流程中觸發(fā),其后多次調用bindService(),此事件都不會被觸發(fā),而只會觸發(fā)onServiceConnected()事件。這是因為在默認情況下,系統(tǒng)在綁定時會先搜索IBinder接口,如果Service已經綁定了Binder對象,系統(tǒng)就會直接跳過onBind()方法。
既然 onBind(),onUnbind()方法只會在第一次啟動綁定時被調用,如果在多次綁定時需要有不同的處理方式又該如何,還好Android為大家預備了一個備用方法void onRebind(intent),Service服務中 boolean onUnbind(intent)的默認返回值為false,只要將此方法的返回值修改為true,則系統(tǒng)在第二次調用Context.bindService()開始,就會激活Service.onRebind(intent)方法。在此對上面的方法作出少量修改,就會看到下面的處理結果。
public class MyService extends Service{ ........... @Override public void onRebind(Intent intent){ Log.i(Context.ACTIVITY_SERVICE,"Service onRebind"); super.onRebind(intent); } @Override public boolean onUnbind(Intent intent){ Log.i(Context.ACTIVITY_SERVICE,"Service onUnbind"); //將返回值設置為true return true; } ........... ........... }
運行結果
注意:此使用方法只適用 startService()、bindServcie()同時被調用的情況下,如果只調用其中一個方法,無論onUnbind()返回值為何值都無法觸發(fā)onRebind()方法
回到目錄
四、通過多線程方式處理 Service 的延時性操作
4.1 以 Runnable接口實現 Service 多線程操作
由于Android 系統(tǒng)的資源有限,而且對屏幕顯示,事件發(fā)應,用戶體現都有較高的要求,所以在CPU、RAM、GPU、GPU都有獨立的運行機制。當主線程中存在大文件讀取、圖片批量處理、網絡連接超時等操作時,一旦時間超過5秒,Android 系統(tǒng)就會出現 “設置運行緩慢” 的提示,Logcat日志上也會顯示 “The application may be doing too much work on its main thread” 等提示。在開發(fā)Service服務時,若存在此類操作時,開發(fā)人員就應該嘗試使用多線程方式進行開發(fā),避免主線程被長時間占用。下文將以簡單的 Runnable 接口方式實現多線程作為例子。
public class MainActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); } public void btnStart_onclick(View view){ Intent intent=new Intent(MainActivity.this,MyService.class); Log.i(Context.ACTIVITY_SERVICE, "----------onClick startService-----------------"); startService(intent); } public void btnStop_onclick(View view){ Intent intent=new Intent(MainActivity.this,MyService.class); Log.i(Context.ACTIVITY_SERVICE, "----------onClick stopService------------------"); stopService(intent); } } public class MyService extends Service{ @Override public void onCreate(){ Log.i(Context.ACTIVITY_SERVICE,"Service onCreate"); super.onCreate(); } @Override public int onStartCommand(Intent intent, int flags, int startId) { Log.i(Context.ACTIVITY_SERVICE, "Service onStartCommand"); Log.i(Context.ACTIVITY_SERVICE,"Main thread id is "+Thread.currentThread().getId()); //以異步方式進行模擬操作 Thread background=new Thread(new AsyncRunnable()); background.start(); return super.onStartCommand(intent, flags, startId); } @Override public void onDestroy() { Log.i(Context.ACTIVITY_SERVICE, "Service onDestroy"); super.onDestroy(); } } public class AsyncRunnable implements Runnable { @Override public void run() { try { Log.i(Context.ACTIVITY_SERVICE,"Async thread id is "+Thread.currentThread().getId()); //虛擬操作 for(int n=0;n<8;n++){ Thread.sleep(1000); Log.i(Context.ACTIVITY_SERVICE,"****Do Work****"); } } catch (InterruptedException e) { // TODO 自動生成的 catch 塊 e.printStackTrace(); } } }
請留意運行結果,主線程與onStartCommand()方法內部操作存在于不同的線程當中完成
4.2 IntentService 服務簡介
在Service服務中出現延時性操作是普遍遇到的情況,有見及此 Android 系統(tǒng)早為開發(fā)人員提供了一個Service的子類IntentService,當IntentService執(zhí)行 startService()方法時,系統(tǒng)將使用一個循環(huán)程序將該服務加入到一個子線程隊列當中,以便執(zhí)行服務當中的操作。下面為大家提供 IntentService的源代碼,讓各位更好的理解IntentService的運行方式。
public abstract class IntentService extends Service { private volatile Looper mServiceLooper; private volatile ServiceHandler mServiceHandler; private String mName; private boolean mRedelivery; private final class ServiceHandler extends Handler { public ServiceHandler(Looper looper) { super(looper); } @Override public void handleMessage(Message msg) { onHandleIntent((Intent)msg.obj); stopSelf(msg.arg1); } } public IntentService(String name) { super(); mName = name; } public void setIntentRedelivery(boolean enabled) { mRedelivery = enabled; } @Override public void onCreate() { super.onCreate(); HandlerThread thread = new HandlerThread("IntentService[" + mName + "]"); thread.start(); mServiceLooper = thread.getLooper(); mServiceHandler = new ServiceHandler(mServiceLooper); } @Override public void onStart(Intent intent, int startId) { Message msg = mServiceHandler.obtainMessage(); msg.arg1 = startId; msg.obj = intent; mServiceHandler.sendMessage(msg); } @Override public int onStartCommand(Intent intent, int flags, int startId) { onStart(intent, startId); return mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY; } @Override public void onDestroy() { mServiceLooper.quit(); } @Override public IBinder onBind(Intent intent) { return null; } protected abstract void onHandleIntent(Intent intent); }
從代碼中可以看到,系統(tǒng)沒有在onStartCommand()中創(chuàng)建新線程,而是在onCreate()方法中建立了獨立的工作線程,這是由于onCreate()方法只會在新建服務時被調用一次,可見這樣的目的是為了讓系統(tǒng)在單個線程中執(zhí)行多個異步任務。當系統(tǒng)調用Context.startService()方法時,系統(tǒng)將通過onStart()方法使用異步方式,調用ServiceHandler.handleMessage(msg)進行處理,而handleMessage(msg)正是調用了虛擬方法onHandleIntent(intent),然后以stopSelf()結束服務。所以用戶只需要在繼承類中重寫onHandleIntent(intent)方法,便可以以異步方法執(zhí)行IntentService。
4.3 IntentService 應用
以下面一個簡單的例子說明一下IntentService的應用,建立一個MyIntentService類繼承IntentService,實現onHandleIntent(Message msg)方法。然后在MainActivity活動分別3次以不同參數調用intentService,觀察其運行的線程狀態(tài)。
public class MyIntentService extends IntentService { public MyIntentService() { super(null); } @Override protected void onHandleIntent(Intent intent) { String msg=intent.getStringExtra("msg"); Log.i(Context.ACTIVITY_SERVICE,msg+"'s thread id is "+Thread.currentThread().getId()); } } public class MainActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); } public void btnStart_onclick(View view){ Log.i(Context.ACTIVITY_SERVICE, "----------onClick startService--------------"); Log.i(Context.ACTIVITY_SERVICE,"Main thread id is "+Thread.currentThread().getId()); Intent intent1=new Intent(this,MyIntentService.class); intent1.putExtra("msg", "intentService1"); startService(intent1); Intent intent2=new Intent(this,MyIntentService.class); intent2.putExtra("msg", "intentService2"); startService(intent2); Intent intent3=new Intent(this,MyIntentService.class); intent3.putExtra("msg", "intentService3"); startService(intent3); } public void btnStop_onclick(View view){ Intent intent=new Intent(MainActivity.this,MyIntentService.class); Log.i(Context.ACTIVITY_SERVICE, "----------onClick stopService-------------"); stopService(intent); } }
在AndroidManifest.xml 文件設置服務
..........
從運行結果中可以看出,同一時間多次啟動startService()調用intentService,它們都將運行于同一個異步線程當中,這一點在這里得到了證實。
回到目錄
五、淺談 Remote Service 原理
5.1 跨進程通信的使用場景
以上章節(jié)所舉的例子都是使用Local Service 技術,Serivce服務端與Client客戶端都是在于同一進程當中,當APP被卸御,Service服務也被同時卸御。要是想把服務端與客戶端分別放在不同的進程當中進行跨進程信息交換的話,就需要使用到下面介紹的遠程通信服務 Remote Service。使用Remote Service可以把服務端與客戶端分離,當一方被卸御,另一方不會被影響。當今有很多企業(yè)都有多個獨立的APP,如阿里巴巴旗下就天貓、淘寶、聚劃算、支付寶等多個APP,這時候就有需要把Service服務放在一獨立的后臺進程當中,作為多個APP之間信息交換的橋梁。這樣如用戶信息,用戶登錄,身份驗證等多個共用的模塊都可以在Service服務中實現,以供不同的APP進行調用。而且當APP被關閉時,Service服務還會寄存在后臺當中,對用戶的操作進行檢測。如今越來越多的企業(yè)都使用這種開發(fā)方式,以收集用戶像所在地點,通信錄,短信,彩信等個人信息,方便企業(yè)針對用戶的個人資料進行產品推廣。
5.2 Remote Service 技術背景
Android 系統(tǒng)與 Windows 系統(tǒng)的通信原則基本一致,進程就是安全策略的邊界,不同的APP屬于不同進程 Process,一個進程不能直接訪問其他進程的資源。需要實現多進程間的通信,就要使用IPC(Inter Process Commnication)進程間通信技術。Android 系統(tǒng)的 IPC 機制是基于 RPC (Remote Proceduce Call) 遠程過程調用協議建立的,與 Java 使用的 RMI(Rmote Methed Invocation)遠程方法調用相比,不同之處在于Android的IPC機制是基于AIDL(Android Interface Definition Language)接口定義語言定制進程間的通訊規(guī)則的。系統(tǒng)會基于 AIDL 規(guī)則把信息進行序列化處理,然后發(fā)送到另一個進程當中,Android 系統(tǒng)把這種基于跨進程通信的服務稱作Remote Service 。
5.3 IPC 運作原理
從底層架構分析, Android 系統(tǒng)中 IPC 的運作主要依賴于 “ServiceManager” 和 “Binder Driver” 兩個核心元件,下面給大家簡單介紹一下它們的運作原理:
ServiceManager 簡介
ServiceManager是Android系統(tǒng)內的服務管理器,主要負責管理 Service 服務的管理,注冊,調用等任務。在Google提供的Android原始代碼中可以找到(文件夾路徑:frameworks/base/cmds/servicemanager),有C語言開發(fā)基礎且有興趣的朋友可以下載看一下,當中包含了幾個核心的函數:
int svcmgr_handler(struct binder_state *bs, struct binder_txn *txn, struct binder_io *msg, struct binder_io *reply)
int do_add_service(struct binder_state *bs, uint16_t *s, unsigned len, void *ptr, unsigned uid)
void *do_find_service(struct binder_state *bs, uint16_t *s, unsigned len)
void binder_loop(struct binder_state *bs, binder_handler func)
ServiceManager 啟動后會通過 binder_loop 循環(huán)對 Binder Driver 進行監(jiān)聽,當發(fā)現了有新的Service服務請求后,就會調用 svcmgr_handler() 函數對檢測的Service服務進行處理,通過*do_find_service()函數可以在svclist集中檢測Service服務,若當前svclist服務集中未存在當前服務,就會通過do_add_service()進行注冊,把當前服務及其唯一標識符加入到svclist中,這樣當前的 Service 服務被綁定后就完成在ServiceManager的注冊。Binder Driver 會按照規(guī)定的格式把它轉化為 Binder 實體發(fā)送到內核當中,當被 Client 調用時 ServiceManager 會根據 Service 服務的標識符在 svclist 中找到該 Binder 實體,并把 Binder 實體的引用發(fā)送給Client。完成計算后 Binder Driver 會進行數據處理,把計算結果發(fā)回到Client客戶端。由于Binder實體是以強類型的形式存在,所以即使被多次引用,系統(tǒng)都會指向同一個Binder實體,除非所有都結束鏈接,否則Binder實體會一直存在。
圖 5.3
Binder Driver簡介
Binder Driver運行于Android內核當中,它以 “字符驅動設備” 中的 “misc設備注冊” 存在于設備目錄 dev/binder,由于權限問題,在一般手機中沒有權限進行復制,對此有興趣的朋友可以在google 提供的 android 源代碼中查看。它提供open(),mmap(),poll(),ioctl() 等函數進行標準化文件操作,負責進程之間Binder通信的建立,Binder實體在進程之間的傳遞,Binder實體引用的計數管理,數據包在進程之間的傳遞與交互等一系列底層操作。
5.4 Remote Service 常用接口
在 5.3 節(jié)以 Android 底層結構的方式簡單介紹了一下 IPC 通信的原理,下面將以 JAVA 應用層方式再作介紹。
IBinder 接口
IBinder 是 Remote Service 遠程服務的常用接口,Binder是它的實現類,它是為高性能而設計的輕量級遠程調用機制的核心部分。IBinder 內部比較重要的方法就是 boolean transact(int code, Parcel data, Parcel reply, int flags) ,它負責在服務器與客戶端之間進行信息交換,調用遠程方法進行處理,然后把返回值轉換成可序列化對象送回客戶端。
5.5 Remote Service 開發(fā)實例
首先新建一個項目作為服務端, 建立 AIDL 文件 ITimerService.aidl,系統(tǒng)會根據接口描述自動在gen文件夾內生成對應的類文件 ITimerService.java ,當中 Stub 擴展了 android.os.Binder 并利用 transact ()實現了 ITimerService 接口中方法的遠程調用。
package com.example.remoteservice; interface ITimerService{ String getTimeNow(); }
gen\com\example\remoteservice\ITimerService.java (自動生成)
package com.example.remoteservice; public interface ITimerService extends android.os.IInterface { /** Local-side IPC implementation stub class. */ public static abstract class Stub extends android.os.Binder implements com.example.remoteservice.ITimerService { private static final java.lang.String DESCRIPTOR = "com.example.remoteservice.ITimerService"; /** Construct the stub at attach it to the interface. */ public Stub() { this.attachInterface(this, DESCRIPTOR); } /** * Cast an IBinder object into an com.example.remoteservice.ITimerService interface, * generating a proxy if needed. */ public static com.example.remoteservice.ITimerService asInterface(android.os.IBinder obj) { if ((obj==null)) { return null; } android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR); if (((iin!=null)&&(iin instanceof com.example.remoteservice.ITimerService))) { return ((com.example.remoteservice.ITimerService)iin); } return new com.example.remoteservice.ITimerService.Stub.Proxy(obj); } @Override public android.os.IBinder asBinder() { return this; } @Override public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException { switch (code) { case INTERFACE_TRANSACTION: { reply.writeString(DESCRIPTOR); return true; } case TRANSACTION_getTimeNow: { data.enforceInterface(DESCRIPTOR); java.lang.String _result = this.getTimeNow(); reply.writeNoException(); reply.writeString(_result); return true; } } return super.onTransact(code, data, reply, flags); } private static class Proxy implements com.example.remoteservice.ITimerService { private android.os.IBinder mRemote; Proxy(android.os.IBinder remote) { mRemote = remote; } @Override public android.os.IBinder asBinder() { return mRemote; } public java.lang.String getInterfaceDescriptor() { return DESCRIPTOR; } @Override public java.lang.String getTimeNow() throws android.os.RemoteException { android.os.Parcel _data = android.os.Parcel.obtain(); android.os.Parcel _reply = android.os.Parcel.obtain(); java.lang.String _result; try { _data.writeInterfaceToken(DESCRIPTOR); mRemote.transact(Stub.TRANSACTION_getTimeNow, _data, _reply, 0); _reply.readException(); _result = _reply.readString(); } finally { _reply.recycle(); _data.recycle(); } return _result; } } static final int TRANSACTION_getTimeNow = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0); } public java.lang.String getTimeNow() throws android.os.RemoteException; }
然后建立服務TimerService,建立內置類TimerServiceImpl實現接口ITimerService中的方法,由于使用 Remote Service 只能使用 bindService()方式對服務進行遠程綁定,所以TimerService中須利用 onBind() 方法綁定 TimerServiceImpl 對象。
public class TimerService extends Service { @Override public IBinder onBind(Intent intent) { // TODO 自動生成的方法存根 return new TimerServiceImpl(); } &n
本文標題:Android綜合揭秘——全面剖釋Service服務
文章URL:http://weahome.cn/article/jeosic.html