新聞中心
五種交互方式,分別是:通過廣播交互、通過共享文件交互、通過Messenger(信使)交互、通過自定義接口交互、通過AIDL交互。(可能更多)

Service與Thread的區(qū)別
Thread:Thread 是程序執(zhí)行的最小單元,可以用 Thread 來執(zhí)行一些異步的操作。
Service:Service 是Android的一種機(jī)制,當(dāng)它運(yùn)行的時(shí)候如果是Local Service,那么對(duì)應(yīng)的 Service 是運(yùn)行在主進(jìn)程的 main 線程上的。如果是Remote Service,那么對(duì)應(yīng)的 Service 則是運(yùn)行在獨(dú)立進(jìn)程的 main 線程上。
Thread 的運(yùn)行是獨(dú)立的,也就是說當(dāng)一個(gè) Activity 被 finish 之后,如果沒有主動(dòng)停止 Thread 或者 Thread 里的 run 方法沒有執(zhí)行完畢的話,Thread 也會(huì)一直執(zhí)行。因此這里會(huì)出現(xiàn)一個(gè)問題:當(dāng) Activity 被 finish 之后,不再持有該 Thread 的引用,也就是不能再控制該Thread。另一方面,沒有辦法在不同的 Activity 中對(duì)同一 Thread 進(jìn)行控制。
例如:如果 一個(gè)Thread 需要每隔一段時(shí)間連接服務(wù)器校驗(yàn)數(shù)據(jù),該Thread需要在后臺(tái)一直運(yùn)行。這時(shí)候如果創(chuàng)建該Thread的Activity被結(jié)束了而該Thread沒有停止,那么將沒有辦法再控制該Thread,除非kill掉該程序的進(jìn)程。這時(shí)候如果創(chuàng)建并啟動(dòng)一個(gè) Service ,在 Service 里面創(chuàng)建、運(yùn)行并控制該 Thread,這樣便解決了該問題(因?yàn)槿魏?Activity 都可以控制同一個(gè)Service,而系統(tǒng)也只會(huì)創(chuàng)建一個(gè)對(duì)應(yīng) Service 的實(shí)例)。
因此可以把 Service 想象成一種消息服務(wù),可以在任何有 Context 的地方調(diào)用 Context.startService、Context.stopService、Context.bindService、Context.unbindService來控制它,也可以在 Service 里注冊(cè) BroadcastReceiver,通過發(fā)送 broadcast 來達(dá)到控制的目的,這些都是 Thread 做不到的。
Service的生命周期
1. 被啟動(dòng)的服務(wù)(startService())的生命周期。
如果一個(gè)Service被某個(gè)Activity 調(diào)用Context.startService() 方法啟動(dòng),那么不管是否有Activity使用bindService()綁定或unbindService()解除綁定到該Service,該Service都在后臺(tái)運(yùn)行。如果一個(gè)Service被多次執(zhí)行startService(),它的onCreate()方法只會(huì)調(diào)用一次,也就是說該Service只會(huì)創(chuàng)建一個(gè)實(shí)例,而它的onStartCommand()將會(huì)被調(diào)用多次(對(duì)應(yīng)調(diào)用startService()的次數(shù))。該Service將會(huì)一直在后臺(tái)運(yùn)行,直到被調(diào)用stopService(),或自身的stopSelf方法。當(dāng)然如果系統(tǒng)資源不足,系統(tǒng)也可能結(jié)束服務(wù)。
2. 被綁定的服務(wù)(bindService())的生命周期。
如果一個(gè)Service被調(diào)用 Context.bindService ()方法綁定啟動(dòng),不管調(diào)用bindService()調(diào)用幾次,onCreate()方法都只會(huì)調(diào)用一次,而onStartCommand()方法始終不會(huì)被調(diào)用,這時(shí)會(huì)調(diào)用onBind()方法。當(dāng)連接建立之后,Service將會(huì)一直運(yùn)行,除非調(diào)用Context.unbindService() 斷開連接或者之前調(diào)用bindService() 的 Context 不存在了(如該Activity被finish),系統(tǒng)將會(huì)自動(dòng)停止Service,對(duì)應(yīng)onDestroy()將被調(diào)用。
3. 被啟動(dòng)又被綁定的服務(wù)的生命周期。
如果一個(gè)Service又被啟動(dòng)又被綁定,則該Service將會(huì)一直在后臺(tái)運(yùn)行。調(diào)用unbindService()將不會(huì)停止Service,而必須調(diào)用stopService()或Service的stopSelf()方法來停止服務(wù)。
4. 當(dāng)服務(wù)被停止時(shí)清除服務(wù)。
當(dāng)一個(gè)Service被終止時(shí),Service的onDestroy()方法將會(huì)被調(diào)用,在這里應(yīng)當(dāng)做一些清除工作,如停止在Service中創(chuàng)建并運(yùn)行的線程等。
Process的生命周期
當(dāng)Service運(yùn)行在低內(nèi)存的環(huán)境時(shí),系統(tǒng)會(huì)kill掉一些進(jìn)程。因此進(jìn)程的優(yōu)先級(jí)將會(huì)狠重要:
1. 如果Service當(dāng)前正在執(zhí)行onCreate()、onStartCommand()、onDestroy()方法,那麼此時(shí)主進(jìn)程將會(huì)成為前臺(tái)進(jìn)程來保證代碼可以執(zhí)行完成而避免被kill。
2. 如果Service已經(jīng)啟動(dòng),那么主進(jìn)程將會(huì)比其他可見的進(jìn)程的重要性低,但比其他看不見的進(jìn)程高。這裡說的可見指的是對(duì)用戶來講,可見的進(jìn)程優(yōu)先級(jí)永遠(yuǎn)是最高的,用戶至上嘛。但只有少部分進(jìn)程始終是用戶可見的,因此除非系統(tǒng)處於極度低內(nèi)存的時(shí)候,不然 service是不會(huì)被kill的。
3. 如果有Client端連到Service,那么Service永遠(yuǎn)比Client端重要。
4. Service可以使用startForeground()將Service放到前臺(tái)狀態(tài)。這樣在低內(nèi)存時(shí)被kill的幾率更低,但如果在極低內(nèi)存的情況下,該Service理論上還是會(huì)被kill掉。但這個(gè)情況基本不用考慮。
廣播交互
提到Activity與Service的交互,可能狠多人首先想到的就是BroadCast——廣播。在Android中,廣播是系統(tǒng)提供的一種很好的交互方式。比如:在電池電量過低,開機(jī)完成等情況下,系統(tǒng)都會(huì)發(fā)出相應(yīng)的系統(tǒng)廣播,我們的應(yīng)用程序只需要注冊(cè)相應(yīng)的廣播接收器,就可以接收到這些系統(tǒng)的廣播。同時(shí),我們也可以定義自己的廣播,這樣在不同的Activity、Service以及應(yīng)用程序之間,就可以通過廣播來實(shí)現(xiàn)交互。我們通過模擬應(yīng)用程序后臺(tái)下載的情況來分析Service與Activity的交互方式。
當(dāng)我們點(diǎn)擊StartService按鈕之后,界面上的進(jìn)度條將會(huì)每隔一秒加1。因?yàn)槭悄M下載,因此下載動(dòng)作我們?cè)赟ervice中通過一個(gè)Timer定時(shí)器來實(shí)現(xiàn),在Timer中對(duì)一個(gè)整型數(shù)據(jù)i進(jìn)行自加(i++),然后Client端獲取Server端的i值并顯示在界面上,從而達(dá)到模擬的目的。
1.1. 實(shí)現(xiàn)原理
Server端將目前的下載進(jìn)度,通過廣播的方式發(fā)送出來,Client端注冊(cè)此廣播的監(jiān)聽器,當(dāng)獲取到該廣播后,將廣播中當(dāng)前的下載進(jìn)度解析出來并更新到界面上。
1.2. 實(shí)現(xiàn)步驟
1.2.1 在Client端中通過startService()啟動(dòng)Service。
- if(v == startBtn){
- Log.i(TAG, "start button clicked...pid: "+Process.myPid());
- mIntent.setClass(BroadCastService.this, DownLoadService.class);
- startService(mIntent);
- }
這里的mIntent = new Intent();Process.myPid()方法可以獲取當(dāng)前進(jìn)程的ID號(hào)。
1.2.2 DownLoadService接到啟動(dòng)的命令之后,執(zhí)行onCreate()方法,并在其中開啟timer計(jì)數(shù)模擬下載。
- @Override
- public void onCreate() {
- super.onCreate();
- Log.i(TAG, "DownLoadService.onCreate()...pid: "+Process.myPid());
- intent = new Intent("com.seven.broadcast");
- mTimer = new Timer();
- mTimer.schedule(new MyTimerTask(), 0 , TIME * 1000);
- }
這里的intent是Server端向Client端傳送數(shù)據(jù)用的,使用的action是”com.seven.broadcast”,Client端只有註冊(cè)了相應(yīng)action才能夠接收到Server端的廣播,并解析其中的內(nèi)容。Process.myPid()是獲取當(dāng)前進(jìn)程的ID。
1.2.3 在Server端的timer計(jì)數(shù)其中發(fā)送廣播,告知Client端目前下載進(jìn)度。
- class MyTimerTask extends TimerTask{
- @Override
- public void run() {
- if(i==100){
- i=0;
- }
- intent.putExtra("CurrentLoading", i);
- sendBroadcast(intent);
- i++;
- Log.e(TAG, "i= "+i);
- }
- }
通過intent.putExtra(key,value);設(shè)置intent的值,然后通過sendBroadcast(intent)方法,將廣播發(fā)送出去。
1.2.4 在Client端通過匿名內(nèi)部類的方式實(shí)例化BroadcastReceiver并覆寫其中的onReceive()方法。
- BroadcastReceiver receiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- if(MYACTION.equals(intent.getAction())){
- Log.i(TAG, "get the broadcast from DownLoadService...");
- curLoad = intent.getIntExtra("CurrentLoading", ERROR);
- mHandler.sendMessage(mHandler.obtainMessage());
- }
- }
- };
在onReceive()方法中,判斷是否為Server端發(fā)送的廣播,如果是則對(duì)廣播中攜帶的intent數(shù)據(jù)進(jìn)行解包處理。這裡也可以單獨(dú)寫一個(gè)類繼承自BroadcastReceiver,在其中覆寫onReceive()方法,在Client端中實(shí)例化其對(duì)象,同樣可以達(dá)到相應(yīng)的效果,這樣做可以為后面實(shí)現(xiàn)靜態(tài)注冊(cè)廣播。
1.2.5 更新主介面下載進(jìn)度。
- Handler mHandler = new Handler(){
- @Override
- public void handleMessage(Message msg) {
- super.handleMessage(msg);
- Log.i(TAG, "current loading: "+curLoad);
- if(curLoad<0||curLoad>100){
- Log.e(TAG, "ERROR: "+curLoad);
- return;
- }
- mProgressBar.setProgress(curLoad);
- mTextView.setText(curLoad+"%");
- }
- };
這里對(duì)獲取到的進(jìn)度進(jìn)行了一次判斷,如果獲取到的值沒有異常,那么將會(huì)顯示到界面,并更新進(jìn)度條的進(jìn)度,如果異常則返回。
1.2.6 一定要對(duì)Broadcast進(jìn)行注冊(cè)和取消注冊(cè)。只有注冊(cè)之后相應(yīng)的broadcast之后才能接收到廣播注冊(cè)方法有兩種。
動(dòng)態(tài)注冊(cè)/取消注冊(cè):
- @Override
- protected void onResume() {
- super.onResume();
- Log.i(TAG, "register the broadcast receiver...");
- IntentFilter filter = new IntentFilter();
- filter.addAction(MYACTION);
- registerReceiver(receiver, filter);
- }
- @Override
- protected void onDestroy() {
- super.onDestroy();
- Log.i(TAG, "unregister the broadcast receiver...");
- unregisterReceiver(receiver);
- }
動(dòng)態(tài)註冊(cè)可以隨時(shí)註冊(cè)隨時(shí)取消。
靜態(tài)註冊(cè):
注:這里的MyBroadcastReceiver是一個(gè)繼承自BroadcastReceiver的類。靜態(tài)注冊(cè)只要注冊(cè)了一次那么只要該程序沒有被卸載那么該廣播將一直有效。
最后貼出整個(gè)AndroidManifest.xml文件
- android:label="@string/app_name">
這里的android:process =”:remote”可以使該Service運(yùn)行在單獨(dú)進(jìn)程中,從而可以模擬跨進(jìn)程通信。
1.3 小結(jié)
通過廣播的方式實(shí)現(xiàn)Activity與Service的交互操作簡(jiǎn)單且容易實(shí)現(xiàn),可以勝任簡(jiǎn)單級(jí)的應(yīng)用。但缺點(diǎn)也十分明顯,發(fā)送廣播受到系統(tǒng)制約。系統(tǒng)會(huì)優(yōu)先發(fā)送系統(tǒng)級(jí)廣播,在某些特定的情況下,我們自定義的廣播可能會(huì)延遲。同時(shí)在廣播接收器中不能處理長(zhǎng)耗時(shí)操作,否則系統(tǒng)會(huì)出現(xiàn)ANR即應(yīng)用程序無響應(yīng)。
#p#
共享文件交互
2這里提到的共享文件指的是Activity和Service使用同一個(gè)文件來達(dá)到傳遞數(shù)據(jù)的目的。我們使用SharedPreferences來實(shí)現(xiàn)共享,當(dāng)然也可以使用其它IO方法實(shí)現(xiàn),通過這種方式實(shí)現(xiàn)交互時(shí)需要注意,對(duì)于文件的讀寫的時(shí)候,同一時(shí)間只能一方讀一方寫,不能兩方同時(shí)寫。
2.1 實(shí)現(xiàn)原理
Server端將當(dāng)前下載進(jìn)度寫入共享文件中,Client端通過讀取共享文件中的下載進(jìn)度,并更新到主界面上。
2.2 實(shí)現(xiàn)步驟
2.2.1 在Client端通過startService()啟動(dòng)Service。
- if(startSerBtn==v){
- Log.i(TAG, "Start Button Clicked.");
- if(intent!=null){
- startService(intent);
- timer.schedule(new MyTimerTask(), 0, TIME * 1000);
- }
- }
2.2.2 Server端收到啟動(dòng)intent之后執(zhí)行onCreate()方法,并開啟timer,模擬下載,以及初始化SharedPreferences對(duì)象preferences。
- @Override
- public void onCreate() {
- super.onCreate();
- Log.i(TAG, "DownLoadService.onCreate()...");
- preferences = getSharedPreferences("CurrentLoading_SharedPs", 0);
- timer = new Timer();
- timer.schedule(new MyTimerTask(), 0, TIME*1000);
- }
2.2.3 開始計(jì)數(shù)并將下載進(jìn)度寫入shared_prefs文件夾下的xml文件中,內(nèi)容以鍵值對(duì)的方式保存。
- class MyTimerTask extends TimerTask{
- @Override
- public void run() {
- setCurrentLoading();
- if(100==i){
- i=0;
- }
- i++;
- }
- }
- private void setCurrentLoading() {
- preferences.edit().putInt("CurrentLoading", i).commit();
- }
對(duì)於SharedPreferences的使用需要注意一下幾點(diǎn):
首先,使用sharedPreferences前需要獲取文件引用。
- preferences = getSharedPreferences("CurrentLoading_SharedPs", 0);
其次,使用sharedpreferences寫數(shù)據(jù)方式。
- preferences.edit().putInt("CurrentLoading", i).commit();
最后,讀取數(shù)據(jù)的方式。
- int couLoad = preferences.getInt("CurrentLoading", 0);
2.2.4 Client端通過讀取/data/data/com.seven.servicetestdemo/shared_prefs文件夾下的xml文件,并取得里面的鍵值對(duì),從而獲取到當(dāng)前的下載進(jìn)度,并更新到主界面上。
- Handler mHandler = new Handler(){
- @Override
- public void handleMessage(Message msg) {
- super.handleMessage(msg);
- int couLoad = preferences.getInt("CurrentLoading", 0);
- mProgressBar.setProgress(couLoad);
- currentTv.setText(couLoad+"%");
- }
- };
2.3 小結(jié)
因?yàn)榉椒ê?jiǎn)單,因此就不貼出AndroidManifest.xml文件了。對(duì)於這種方式實(shí)現(xiàn)Activity與Service的交互,可以說很方便,就像使用管道,一個(gè)往裡寫,一個(gè)往外讀。但這種方式也有缺陷,寫入數(shù)據(jù)較為復(fù)雜以及數(shù)據(jù)量較大時(shí),就有可能導(dǎo)致寫入與讀數(shù)據(jù)出不一致的錯(cuò)誤。同時(shí)因?yàn)榻?jīng)過了一個(gè)中轉(zhuǎn)站,這種操作將更耗時(shí)。
#p#
Messenger交互(信使交互)
3Messenger翻譯過來指的是信使,它引用了一個(gè)Handler對(duì)象,別人能夠向它發(fā)送消息(使用mMessenger.send(Message msg)方法)。該類允許跨進(jìn)程間基于Message通信,在服務(wù)端使用Handler創(chuàng)建一個(gè) Messenger,客戶端只要獲得這個(gè)服務(wù)端的Messenger對(duì)象就可以與服務(wù)端通信了。也就是說我們可以把Messenger當(dāng)做Client端與Server端的傳話筒,這樣就可以溝通交流了。
3.1 實(shí)現(xiàn)原理
在Server端與Client端之間通過一個(gè)Messenger對(duì)象來傳遞消息,該對(duì)象類似于信息中轉(zhuǎn)站,所有信息通過該對(duì)象攜帶。
3.2 Messenger的一般用法
(1). 在Server端創(chuàng)建信使對(duì)象。
mMessenger = new Messenger(mHandler)
(2). Client端使用bindService()綁定Server端。
(3). Server端的onBind()方法返回一個(gè)binder對(duì)象。
return mMessenger.getBinder();
(4). Client端使用返回的binder對(duì)象得到Server端信使。
這里雖然是new了一個(gè)Messenger,但我們查看它的實(shí)現(xiàn)
- public void onServiceConnected(ComponentName name, IBinder service) {
- rMessenger = new Messenger(service);
- ......
- }
- public Messenger(IBinder target) { mTarget = IMessenger.Stub.asInterface(target); }
發(fā)現(xiàn)它的mTarget是通過AIDL得到的,實(shí)際上就是遠(yuǎn)程創(chuàng)建的那個(gè)。
(5). Client端可以使用這個(gè)Server端的信使對(duì)象向Server端發(fā)送消息。
rMessenger.send(msg);
這樣Server端的Handler對(duì)象就能收到消息了,然后可以在其handlerMessage(Message msg)方法中進(jìn)行處理。經(jīng)過這5個(gè)步驟之后只有Client端向Server端發(fā)送消息,這樣的消息傳遞是單向的,那么如何實(shí)現(xiàn)消息的雙向傳遞呢?
首先需要在第5步做修改,在send(msg)前通過msm.replyTo = mMessenger將Client端自己的信使設(shè)置到消息中,這樣Server端接收到消息時(shí)同時(shí)也得到了Client端的信使對(duì)象,然后Server端也可以通過使用得到的Client端的信使對(duì)象來項(xiàng)Client端發(fā)送消息 cMessenger = msg.replyTo2 cMessenger.send(message);
這樣即完成了從Server端向Client端發(fā)送消息的功能,這樣Client端可以在自己的Handler對(duì)象的handlerMessage()方法中接收服務(wù)端發(fā)送來的message進(jìn)行處理。
3.3 實(shí)現(xiàn)步驟
3.3.1 創(chuàng)建并初始化Server端的信使對(duì)象。
- private Handler mHandler = new Handler(){
- @Override
- public void handleMessage(Message msg) {
- super.handleMessage(msg);
- switch (msg.what) {
- case TEST:
- Log.e(TAG, "Get Message from MainActivity.");
- cMessenger = msg.replyTo;
- mTimer.schedule(new MyTimerTask(), 1000,TIME * 1000);
- break;
- default:
- break;
- }
- }
- };
- //It's the messenger of server
- private Messenger mMessenger = new Messenger(mHandler);
3.3.2 在Client端使用bindService()方法綁定Server端。
- private void doBindService(){
- Log.i(TAG, "doBindService()...");
- mIsBind = bindService(intent, serConn, BIND_AUTO_CREATE);//if bind success return true
- Log.e(TAG, "Is bind: "+mIsBind);
- }
3.3.3 在Server端的onBind()方法中返回一個(gè)binder對(duì)象。
- @Override
- public IBinder onBind(Intent intent) {
- Log.i(TAG, "MessengerService.onBind()...");
- return mMessenger.getBinder();
- }
這裡的mMessenger就是Server端的信使對(duì)象。
3.3.4 Client端使用ServiceConnected()方法來獲取Server端的信使對(duì)象。
- private ServiceConnection serConn = new ServiceConnection() {
- @Override
- public void onServiceDisconnected(ComponentName name) {
- Log.i(TAG, "onServiceDisconnected()...");
- rMessenger = null;
- }
- @Override
- public void onServiceConnected(ComponentName name, IBinder service) {
- Log.i(TAG, "onServiceConnected()...");
- rMessenger = new Messenger(service);//get the object of remote service
- mMessenger = new Messenger(mHandler);//initial the object of local service
- sendMessage();
- }
- };
獲取Server端的信使對(duì)象的同時(shí),也初始化Client端的自己的信使對(duì)象,并且通過sendMessage()方法發(fā)送消息給Server端,表示可以開始下載了。
3.3.5 Client端使用獲取到的rMessenger來發(fā)送消息給Server端,同時(shí)將Client端的信使封裝到消息中,一并發(fā)送給Server端。
- private void sendMessage() {
- Message msg = Message.obtain(null, MessengerService.TEST);//MessengerService.TEST=0
- msg.replyTo = mMessenger;
- try {
- rMessenger.send(msg);
- } catch (RemoteException e) {
- e.printStackTrace();
- }
- }
這里的MessengerService.TEST為Server端里的一個(gè)靜態(tài)常量。Msg.replyTo=mMessenger;表示發(fā)送給Server端的信息里攜帶Client端的信使。
3.3.6 Server端獲取Client端發(fā)送的消息并得到Client端的信使對(duì)象。
- private Handler mHandler = new Handler(){
- @Override
- public void handleMessage(Message msg) {
- super.handleMessage(msg);
- switch (msg.what) {
- case TEST:
- Log.e(TAG, "Get Message from MainActivity.");
- cMessenger = msg.replyTo;//get the messenger of client
- mTimer.schedule(new MyTimerTask(), 1000,TIME * 1000);
- break;
- default:
- break;
- }
- }
- };
在接收到Client端的信息之后,Server端開啟timer模擬下載,并接收Client端的信使對(duì)象。
3.3.7 Server端向Client端發(fā)送數(shù)據(jù)。
- class MyTimerTask extends TimerTask {
- @Override
- public void run() {
- if (i == 100) {
- i = 0;
- }
- try {
- //send the message to the client
- Message message = Message.obtain(null, MessengerService.TEST,i, 0);
- cMessenger.send(message);
- } catch (RemoteException e) {
- e.printStackTrace();
- }
- i++;
- }
- }
直接使用接收到的Client端的信使對(duì)象來發(fā)送當(dāng)前下載進(jìn)度給Client端。
3.3.8 Client端接收來自Server端的數(shù)據(jù)。
- private Handler mHandler = new Handler(){
- @Override
- public void handleMessage(Message msg) {
- super.handleMessage(msg);
- switch (msg.what) {
- case MessengerService.TEST:
- Log.e(TAG, "Get Message From MessengerService. i= "+msg.arg1);
- int curLoad = msg.arg1;
- mTextView.setText(curLoad+"%");
- mProgressBar.setProgress(curLoad);
- break;
- default:
- break;
- }
- }
- };
Client端的接收和Server端的接收狠類似。接收到Server端傳過來的數(shù)據(jù)之后進(jìn)行介面更新,以及下載進(jìn)度更新。
以下是AndroidManifest.xml文件:
- package="com.seven.messengerservicedemo"
- android:versionCode="1"
- android:versionName="1.0">
- android:label="@string/app_name">
這里在Service的註冊(cè)中加入了過濾動(dòng)作,只有相匹配的action才能啟動(dòng)相應(yīng)的Service。
3.4 小結(jié)
通過Messenger來實(shí)現(xiàn)Activity和Service的交互,稍微深入一點(diǎn)我們就可以知道,其實(shí)Messenger也是通過AIDL來實(shí)現(xiàn)的。對(duì)於前兩種實(shí)現(xiàn)方式,Messenger方式總體上來講也是比較容易理解的,這就和平時(shí)使用Handler和Thread通信一個(gè)道理。
#p#
自定義接口交互
4何謂自定義接口呢,其實(shí)就是我們自己通過接口的實(shí)現(xiàn)來達(dá)到Activity與Service交互的目的,我們通過在Activity和Service之間架設(shè)一座橋樑,從而達(dá)到數(shù)據(jù)交互的目的,而這種實(shí)現(xiàn)方式和AIDL非常類似(后文會(huì)說到)。
4.1 實(shí)現(xiàn)原理
自定義一個(gè)接口,該接口中有一個(gè)獲取當(dāng)前下載進(jìn)度的空方法。Server端用一個(gè)類繼承自Binder并實(shí)現(xiàn)該接口,覆寫了其中獲取當(dāng)前下載進(jìn)度的方法。Client端通過ServiceConnection獲取到該類的對(duì)象,從而能夠使用該獲取當(dāng)前下載進(jìn)度的方法,最終實(shí)現(xiàn)實(shí)時(shí)交互。
4.2 實(shí)現(xiàn)步驟
4.2.1 新建一個(gè)Interface,并在其中創(chuàng)建一個(gè)用于獲取當(dāng)前下載進(jìn)度的的空方法getCurrentLoad()
- public interface ICountService {
- public int getCurrentLoad();
- }
4.2.2 新建Server端DownService實(shí)現(xiàn)ICountService并在其中通過一個(gè)內(nèi)部類ServiceBinder繼承自Binder并實(shí)現(xiàn)ICoutService接口。
- public class DownLoadService extends Service implements ICountService{
- private ServiceBinder serviceBinder = new ServiceBinder();
- public class ServiceBinder extends Binder implements ICountService{
- @Override
- public int getCurrentLoad() {
- Log.i(TAG, "ServiceBinder getCurrentLoad()... i=:"+i);
- return i;
- }
- }
- @Override
- public int getCurrentLoad() {
- return 0;
- }
- }
在Server端中,實(shí)現(xiàn)獲取下載進(jìn)度的空方法getCurrentLoad();這是Eclipse自動(dòng)生成的,重點(diǎn)不在這裡。我們需要在ServiceBinder類中覆寫getCurrentLoad()方法,這裡我們返回當(dāng)前的下載進(jìn)度i。
4.2.3 Client端使用bindService()綁定Server端。
- if (startSerBtn == v) {
- Log.i(TAG, "Start Button Clicked.");
- bindService(intent, serConn, BIND_AUTO_CREATE);
- timer.schedule(new MyTimerTask(), 1000, TIME * 1000);//這里一定要延遲一下再開始獲取數(shù)據(jù),不然會(huì)報(bào)空指針異常
- }
在Client端綁定Server端的同時(shí),延遲1s開始獲取下載進(jìn)度。其中的intent = new Intent(“com.seven.test”);com.seven.test該字符串要與在AndroidManifest.xml中申明的一致
4.2.4 Server端返回binder對(duì)象。
- @Override
- public IBinder onBind(Intent intent) {
- Log.i(TAG, "DownLoadService.onBind()...");
- return serviceBinder;
- }
這里的serviceBinder因?yàn)槔^承了Binder因此也是Binder對(duì)象。
4.2.5 Client端通過ServiceConnection來獲取Server端的binder對(duì)象。
- private ServiceConnection serConn = new ServiceConnection() {
- @Override
- public void onServiceDisconnected(ComponentName name) {
- iCountService = null;
- }
- @Override
- public void onServiceConnected(ComponentName name, IBinder service) {
- Log.i(TAG, "onServiceConnected()...");
- iCountService = (ICountService)service;
- }
- };
獲取的過程是在bindService()過程中完成的,這里的iCountService是接口ICountService的對(duì)象,在這里得到實(shí)例化。
4.2.6 在綁定完成之后,Server端會(huì)開啟下載,在實(shí)際情況中Server端會(huì)開啟獨(dú)立線程用于下載,這里用i++來代替。
- @Override
- public void onCreate() {
- super.onCreate();
- Log.i(TAG, "DownLoadService.onCreate()...");
- timer = new Timer();
- timer.schedule(new MyTimerTask(), 0, TIME*1000);
- }
- class MyTimerTask extends TimerTask{
- @Override
- public void run() {
- if(100==i){
- i=0;
- }
- i++;
- }
- }
bindService()方法執(zhí)行之后會(huì)調(diào)用DownLoadService中的onCreate()方法,在其onCreate()方法中開啟timer使得i++。
4.2.7 Server端已經(jīng)開啟了下載,那么Client端需要及時(shí)獲取下載進(jìn)度并在主界面上更新。
- Handler mHandler = new Handler(){
- @Override
- public void handleMessage(Message msg) {
- super.handleMessage(msg);
- Log.i(TAG, "handleMessage...");
- int curLoad = iCountService.getCurrentLoad();
- mProgressBar.setProgress(curLoad);
- currentTv.setText(curLoad+"%");
- }
- };
- class MyTimerTask extends TimerTask{
- @Override
- public void run() {
- mHandler.sendMessage(mHandler.obtainMessage());
- }
- }
Client端的Timer在bindService()完成之后1秒再開始獲取下載進(jìn)度,獲取方法是直接通過int curLoad = iCountService.getCurrentLoad();這里的getCurrentLoad()方法是DownLoadService內(nèi)部類ServiceBinder中的方法。Client端將獲取到的下載進(jìn)度更新到介面上并更新進(jìn)度條。
4.3 小結(jié)
通過上面的例子可以知道,這種方法簡(jiǎn)單實(shí)用,擴(kuò)展性強(qiáng),但其也有一些缺點(diǎn),比如需要延遲一些再開始獲取Server端的數(shù)據(jù),從而無法完全實(shí)現(xiàn)從零開始同步更新。綜其所述,通過自定義接口實(shí)現(xiàn)Activity與Service交互的方法還是比較實(shí)用的。適用於同進(jìn)程中通信,不能進(jìn)行跨進(jìn)程通信。
#p#
AIDL交互
AIDL是Android Interface Definition Language的首字母縮寫, 也就是Android接口定義語言。提及AIDL就不得不說下Android的服務(wù),Android 支持兩種服務(wù)類型的服務(wù)即本地服務(wù)和遠(yuǎn)程服務(wù)。
本地服務(wù)無法供在設(shè)備上運(yùn)行的其他應(yīng)用程序訪問,也就是說只能該應(yīng)用程序內(nèi)部調(diào)用,比如某些應(yīng)用程序中的下載類服務(wù),這些服務(wù)只能由內(nèi)部調(diào)用。而對(duì)于遠(yuǎn)程服務(wù),除了可以由本應(yīng)用程序調(diào)用,還可以允許其他應(yīng)用程序訪問。遠(yuǎn)程服務(wù)一般通過AIDL來實(shí)現(xiàn),可以進(jìn)行進(jìn)程間通信,這種服務(wù)也就是遠(yuǎn)程服務(wù)。
本地服務(wù)與遠(yuǎn)程服務(wù)還是有一些重要的區(qū)別。具體來講,如果服務(wù)完全只供同一進(jìn)程中的組件使用(運(yùn)行后臺(tái)任務(wù)),客戶端一邊通過調(diào)用 Context.startService()來啟動(dòng)該服務(wù)。這種類型的服務(wù)為本地服務(wù),它的一般用途是后臺(tái)執(zhí)行長(zhǎng)耗時(shí)操作。而遠(yuǎn)程服務(wù)一般通過bindService()方法啟動(dòng),主要為不同進(jìn)程間通信。我們也將遠(yuǎn)程服務(wù)稱為AIDL支持服務(wù),因?yàn)榭蛻舳耸褂?AIDL 與服務(wù)通信。Android中對(duì)于遠(yuǎn)程服務(wù)有多種叫法:遠(yuǎn)程服務(wù)、AIDL服務(wù)、外部服務(wù)和RPC服務(wù)。
5.1 AIDL實(shí)現(xiàn)流程圖
這屬于代理/存根結(jié)構(gòu),通過這張AIDL的流程圖,很容易發(fā)現(xiàn)Android實(shí)現(xiàn)IPC其實(shí)是在原來的C/S框架上加入了代理/存根結(jié)構(gòu)。
比如,你到自動(dòng)取款機(jī)上去取款。那么你就是客戶(Client),取款機(jī)就是你的代理(Proxy);你不會(huì)在乎錢具體放在那里,你只想將你的錢從取款機(jī)中取出來。你同銀行之間的操作完全是取款機(jī)代理實(shí)現(xiàn)。你的取款請(qǐng)求通過取款機(jī)傳到另一邊,即銀行的服務(wù)器(Server)。它也沒有必要知道你在哪兒取錢,它所關(guān)心的是你的身份和你取款多少。當(dāng)它確認(rèn)你的權(quán)限,就進(jìn)行相應(yīng)的操作,返回操作結(jié)果給取款機(jī),取款機(jī)根據(jù)服務(wù)器返回結(jié)果,從保險(xiǎn)柜里取出相應(yīng)數(shù)量的錢給你。你取出卡后,操作完成。取款機(jī)不是直接同服務(wù)器連接的,他們之間還有一個(gè)“存根(Stub)”,取款機(jī)與存根通信,服務(wù)器與存根通信,從某種意義上說存根就是服務(wù)器的代理。
6
5.3 實(shí)現(xiàn)原理7
AIDL屬于Android的IPC機(jī)
當(dāng)前名稱:Android面試,與Service交互方式
轉(zhuǎn)載來于:http://fisionsoft.com.cn/article/dppiggj.html


咨詢
建站咨詢
