日本综合一区二区|亚洲中文天堂综合|日韩欧美自拍一区|男女精品天堂一区|欧美自拍第6页亚洲成人精品一区|亚洲黄色天堂一区二区成人|超碰91偷拍第一页|日韩av夜夜嗨中文字幕|久久蜜综合视频官网|精美人妻一区二区三区

RELATEED CONSULTING
相關咨詢
選擇下列產(chǎn)品馬上在線溝通
服務時間:8:30-17:00
你可能遇到了下面的問題
關閉右側工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營銷解決方案
Android面試,與Service交互方式

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

Service與Thread的區(qū)別

Thread:Thread 是程序執(zhí)行的最小單元,可以用 Thread 來執(zhí)行一些異步的操作。

Service:Service 是Android的一種機制,當它運行的時候如果是Local Service,那么對應的 Service 是運行在主進程的 main 線程上的。如果是Remote Service,那么對應的 Service 則是運行在獨立進程的 main 線程上。

Thread 的運行是獨立的,也就是說當一個 Activity 被 finish 之后,如果沒有主動停止 Thread 或者 Thread 里的 run 方法沒有執(zhí)行完畢的話,Thread 也會一直執(zhí)行。因此這里會出現(xiàn)一個問題:當 Activity 被 finish 之后,不再持有該 Thread 的引用,也就是不能再控制該Thread。另一方面,沒有辦法在不同的 Activity 中對同一 Thread 進行控制。

例如:如果 一個Thread 需要每隔一段時間連接服務器校驗數(shù)據(jù),該Thread需要在后臺一直運行。這時候如果創(chuàng)建該Thread的Activity被結束了而該Thread沒有停止,那么將沒有辦法再控制該Thread,除非kill掉該程序的進程。這時候如果創(chuàng)建并啟動一個 Service ,在 Service 里面創(chuàng)建、運行并控制該 Thread,這樣便解決了該問題(因為任何 Activity 都可以控制同一個Service,而系統(tǒng)也只會創(chuàng)建一個對應 Service 的實例)。

因此可以把 Service 想象成一種消息服務,可以在任何有 Context 的地方調(diào)用 Context.startService、Context.stopService、Context.bindService、Context.unbindService來控制它,也可以在 Service 里注冊 BroadcastReceiver,通過發(fā)送 broadcast 來達到控制的目的,這些都是 Thread 做不到的。

Service的生命周期

1. 被啟動的服務(startService())的生命周期。

如果一個Service被某個Activity 調(diào)用Context.startService() 方法啟動,那么不管是否有Activity使用bindService()綁定或unbindService()解除綁定到該Service,該Service都在后臺運行。如果一個Service被多次執(zhí)行startService(),它的onCreate()方法只會調(diào)用一次,也就是說該Service只會創(chuàng)建一個實例,而它的onStartCommand()將會被調(diào)用多次(對應調(diào)用startService()的次數(shù))。該Service將會一直在后臺運行,直到被調(diào)用stopService(),或自身的stopSelf方法。當然如果系統(tǒng)資源不足,系統(tǒng)也可能結束服務。

2. 被綁定的服務(bindService())的生命周期。

如果一個Service被調(diào)用 Context.bindService ()方法綁定啟動,不管調(diào)用bindService()調(diào)用幾次,onCreate()方法都只會調(diào)用一次,而onStartCommand()方法始終不會被調(diào)用,這時會調(diào)用onBind()方法。當連接建立之后,Service將會一直運行,除非調(diào)用Context.unbindService() 斷開連接或者之前調(diào)用bindService() 的 Context 不存在了(如該Activity被finish),系統(tǒng)將會自動停止Service,對應onDestroy()將被調(diào)用。

3. 被啟動又被綁定的服務的生命周期。

如果一個Service又被啟動又被綁定,則該Service將會一直在后臺運行。調(diào)用unbindService()將不會停止Service,而必須調(diào)用stopService()或Service的stopSelf()方法來停止服務。

4. 當服務被停止時清除服務。

當一個Service被終止時,Service的onDestroy()方法將會被調(diào)用,在這里應當做一些清除工作,如停止在Service中創(chuàng)建并運行的線程等。

Process的生命周期

當Service運行在低內(nèi)存的環(huán)境時,系統(tǒng)會kill掉一些進程。因此進程的優(yōu)先級將會狠重要:

1. 如果Service當前正在執(zhí)行onCreate()、onStartCommand()、onDestroy()方法,那麼此時主進程將會成為前臺進程來保證代碼可以執(zhí)行完成而避免被kill。

2. 如果Service已經(jīng)啟動,那么主進程將會比其他可見的進程的重要性低,但比其他看不見的進程高。這裡說的可見指的是對用戶來講,可見的進程優(yōu)先級永遠是最高的,用戶至上嘛。但只有少部分進程始終是用戶可見的,因此除非系統(tǒng)處於極度低內(nèi)存的時候,不然 service是不會被kill的。

3. 如果有Client端連到Service,那么Service永遠比Client端重要。

4. Service可以使用startForeground()將Service放到前臺狀態(tài)。這樣在低內(nèi)存時被kill的幾率更低,但如果在極低內(nèi)存的情況下,該Service理論上還是會被kill掉。但這個情況基本不用考慮。

廣播交互

提到Activity與Service的交互,可能狠多人首先想到的就是BroadCast——廣播。在Android中,廣播是系統(tǒng)提供的一種很好的交互方式。比如:在電池電量過低,開機完成等情況下,系統(tǒng)都會發(fā)出相應的系統(tǒng)廣播,我們的應用程序只需要注冊相應的廣播接收器,就可以接收到這些系統(tǒng)的廣播。同時,我們也可以定義自己的廣播,這樣在不同的Activity、Service以及應用程序之間,就可以通過廣播來實現(xiàn)交互。我們通過模擬應用程序后臺下載的情況來分析Service與Activity的交互方式。

當我們點擊StartService按鈕之后,界面上的進度條將會每隔一秒加1。因為是模擬下載,因此下載動作我們在Service中通過一個Timer定時器來實現(xiàn),在Timer中對一個整型數(shù)據(jù)i進行自加(i++),然后Client端獲取Server端的i值并顯示在界面上,從而達到模擬的目的。

1.1. 實現(xiàn)原理

Server端將目前的下載進度,通過廣播的方式發(fā)送出來,Client端注冊此廣播的監(jiān)聽器,當獲取到該廣播后,將廣播中當前的下載進度解析出來并更新到界面上。

1.2. 實現(xiàn)步驟

1.2.1 在Client端中通過startService()啟動Service。

 
 
 
 
  1. if(v == startBtn){   
  2.     Log.i(TAG, "start button clicked...pid: "+Process.myPid());   
  3.     mIntent.setClass(BroadCastService.this, DownLoadService.class);   
  4.     startService(mIntent);   

這里的mIntent = new Intent();Process.myPid()方法可以獲取當前進程的ID號。

1.2.2 DownLoadService接到啟動的命令之后,執(zhí)行onCreate()方法,并在其中開啟timer計數(shù)模擬下載。

 
 
 
 
  1. @Override   
  2. public void onCreate() {   
  3.     super.onCreate();   
  4.     Log.i(TAG, "DownLoadService.onCreate()...pid: "+Process.myPid());   
  5.     intent = new Intent("com.seven.broadcast");   
  6.     mTimer = new Timer();   
  7.     mTimer.schedule(new MyTimerTask(), 0 , TIME * 1000);   

這里的intent是Server端向Client端傳送數(shù)據(jù)用的,使用的action是”com.seven.broadcast”,Client端只有註冊了相應action才能夠接收到Server端的廣播,并解析其中的內(nèi)容。Process.myPid()是獲取當前進程的ID。

1.2.3 在Server端的timer計數(shù)其中發(fā)送廣播,告知Client端目前下載進度。

 
 
 
 
  1. class MyTimerTask extends TimerTask{   
  2.     @Override   
  3.     public void run() {   
  4.         if(i==100){   
  5.             i=0;   
  6.         }   
  7.         intent.putExtra("CurrentLoading", i);   
  8.         sendBroadcast(intent);   
  9.         i++;   
  10.         Log.e(TAG, "i= "+i);   
  11.     }   

通過intent.putExtra(key,value);設置intent的值,然后通過sendBroadcast(intent)方法,將廣播發(fā)送出去。

1.2.4 在Client端通過匿名內(nèi)部類的方式實例化BroadcastReceiver并覆寫其中的onReceive()方法。

 
 
 
 
  1. BroadcastReceiver receiver = new BroadcastReceiver() {   
  2.     @Override   
  3.     public void onReceive(Context context, Intent intent) {   
  4.         if(MYACTION.equals(intent.getAction())){   
  5.             Log.i(TAG, "get the broadcast from DownLoadService...");   
  6.             curLoad = intent.getIntExtra("CurrentLoading", ERROR);   
  7.             mHandler.sendMessage(mHandler.obtainMessage());   
  8.         }   
  9.     }   
  10. }; 

在onReceive()方法中,判斷是否為Server端發(fā)送的廣播,如果是則對廣播中攜帶的intent數(shù)據(jù)進行解包處理。這裡也可以單獨寫一個類繼承自BroadcastReceiver,在其中覆寫onReceive()方法,在Client端中實例化其對象,同樣可以達到相應的效果,這樣做可以為后面實現(xiàn)靜態(tài)注冊廣播。

1.2.5 更新主介面下載進度。

 
 
 
 
  1. Handler mHandler = new Handler(){   
  2.     @Override   
  3.     public void handleMessage(Message msg) {   
  4.         super.handleMessage(msg);   
  5.         Log.i(TAG, "current loading: "+curLoad);   
  6.         if(curLoad<0||curLoad>100){   
  7.             Log.e(TAG, "ERROR: "+curLoad);   
  8.             return;   
  9.         }   
  10.         mProgressBar.setProgress(curLoad);   
  11.         mTextView.setText(curLoad+"%");   
  12.     }   
  13. }; 

這里對獲取到的進度進行了一次判斷,如果獲取到的值沒有異常,那么將會顯示到界面,并更新進度條的進度,如果異常則返回。

1.2.6 一定要對Broadcast進行注冊和取消注冊。只有注冊之后相應的broadcast之后才能接收到廣播注冊方法有兩種。

動態(tài)注冊/取消注冊:

 
 
 
 
  1. @Override   
  2. protected void onResume() {   
  3.     super.onResume();   
  4.     Log.i(TAG, "register the broadcast receiver...");   
  5.     IntentFilter filter = new IntentFilter();   
  6.     filter.addAction(MYACTION);   
  7.     registerReceiver(receiver, filter);   
  8. }   
  9. @Override   
  10. protected void onDestroy() {   
  11.     super.onDestroy();   
  12.     Log.i(TAG, "unregister the broadcast receiver...");   
  13.     unregisterReceiver(receiver);   

動態(tài)註冊可以隨時註冊隨時取消。

靜態(tài)註冊:

 
 
 
 
  1.    
  2.        
  3.            
  4.        
  5.  

注:這里的MyBroadcastReceiver是一個繼承自BroadcastReceiver的類。靜態(tài)注冊只要注冊了一次那么只要該程序沒有被卸載那么該廣播將一直有效。

最后貼出整個AndroidManifest.xml文件

 
 
 
 
  1.    
  2.     
  3.               android:label="@string/app_name">   
  4.            
  5.                
  6.                
  7.            
  8.        
  9.        
  10.  

這里的android:process =”:remote”可以使該Service運行在單獨進程中,從而可以模擬跨進程通信。

1.3 小結

通過廣播的方式實現(xiàn)Activity與Service的交互操作簡單且容易實現(xiàn),可以勝任簡單級的應用。但缺點也十分明顯,發(fā)送廣播受到系統(tǒng)制約。系統(tǒng)會優(yōu)先發(fā)送系統(tǒng)級廣播,在某些特定的情況下,我們自定義的廣播可能會延遲。同時在廣播接收器中不能處理長耗時操作,否則系統(tǒng)會出現(xiàn)ANR即應用程序無響應。

 #p#

共享文件交互

2這里提到的共享文件指的是Activity和Service使用同一個文件來達到傳遞數(shù)據(jù)的目的。我們使用SharedPreferences來實現(xiàn)共享,當然也可以使用其它IO方法實現(xiàn),通過這種方式實現(xiàn)交互時需要注意,對于文件的讀寫的時候,同一時間只能一方讀一方寫,不能兩方同時寫。

2.1 實現(xiàn)原理

Server端將當前下載進度寫入共享文件中,Client端通過讀取共享文件中的下載進度,并更新到主界面上。

2.2 實現(xiàn)步驟

2.2.1 在Client端通過startService()啟動Service。

 
 
 
 
  1. if(startSerBtn==v){   
  2.     Log.i(TAG, "Start Button Clicked.");   
  3.     if(intent!=null){   
  4.     startService(intent);   
  5.     timer.schedule(new MyTimerTask(), 0, TIME * 1000);   
  6.     }   

2.2.2 Server端收到啟動intent之后執(zhí)行onCreate()方法,并開啟timer,模擬下載,以及初始化SharedPreferences對象preferences。

 
 
 
 
  1. @Override   
  2. public void onCreate() {   
  3.     super.onCreate();   
  4.     Log.i(TAG, "DownLoadService.onCreate()...");   
  5.     preferences = getSharedPreferences("CurrentLoading_SharedPs", 0);   
  6.     timer = new Timer();   
  7.     timer.schedule(new MyTimerTask(), 0, TIME*1000);   

2.2.3 開始計數(shù)并將下載進度寫入shared_prefs文件夾下的xml文件中,內(nèi)容以鍵值對的方式保存。

 
 
 
 
  1. class MyTimerTask extends TimerTask{   
  2.     @Override   
  3.     public void run() {   
  4.         setCurrentLoading();   
  5.         if(100==i){   
  6.             i=0;   
  7.         }   
  8.         i++;   
  9.     }          
  10. }      
  11. private void setCurrentLoading() {   
  12.     preferences.edit().putInt("CurrentLoading", i).commit();   

對於SharedPreferences的使用需要注意一下幾點:

首先,使用sharedPreferences前需要獲取文件引用。

 
 
 
 
  1. preferences = getSharedPreferences("CurrentLoading_SharedPs", 0); 

其次,使用sharedpreferences寫數(shù)據(jù)方式。

 
 
 
 
  1. preferences.edit().putInt("CurrentLoading", i).commit(); 

最后,讀取數(shù)據(jù)的方式。

 
 
 
 
  1. int couLoad = preferences.getInt("CurrentLoading", 0); 

2.2.4 Client端通過讀取/data/data/com.seven.servicetestdemo/shared_prefs文件夾下的xml文件,并取得里面的鍵值對,從而獲取到當前的下載進度,并更新到主界面上。

 
 
 
 
  1. Handler mHandler = new Handler(){   
  2.     @Override   
  3.     public void handleMessage(Message msg) {   
  4.         super.handleMessage(msg);   
  5.         int couLoad = preferences.getInt("CurrentLoading", 0);   
  6.         mProgressBar.setProgress(couLoad);   
  7.         currentTv.setText(couLoad+"%");   
  8.     }   
  9.  }; 

2.3 小結

因為方法簡單,因此就不貼出AndroidManifest.xml文件了。對於這種方式實現(xiàn)Activity與Service的交互,可以說很方便,就像使用管道,一個往裡寫,一個往外讀。但這種方式也有缺陷,寫入數(shù)據(jù)較為復雜以及數(shù)據(jù)量較大時,就有可能導致寫入與讀數(shù)據(jù)出不一致的錯誤。同時因為經(jīng)過了一個中轉站,這種操作將更耗時。

#p#

Messenger交互(信使交互)

3Messenger翻譯過來指的是信使,它引用了一個Handler對象,別人能夠向它發(fā)送消息(使用mMessenger.send(Message msg)方法)。該類允許跨進程間基于Message通信,在服務端使用Handler創(chuàng)建一個 Messenger,客戶端只要獲得這個服務端的Messenger對象就可以與服務端通信了。也就是說我們可以把Messenger當做Client端與Server端的傳話筒,這樣就可以溝通交流了。

3.1 實現(xiàn)原理

在Server端與Client端之間通過一個Messenger對象來傳遞消息,該對象類似于信息中轉站,所有信息通過該對象攜帶。

3.2 Messenger的一般用法

(1). 在Server端創(chuàng)建信使對象。

mMessenger = new Messenger(mHandler)

(2). Client端使用bindService()綁定Server端。

(3). Server端的onBind()方法返回一個binder對象。

return mMessenger.getBinder();

(4). Client端使用返回的binder對象得到Server端信使。

 
 
 
 
  1. public void onServiceConnected(ComponentName name, IBinder service) {     
  2.               rMessenger = new Messenger(service);         
  3.              ......   
  4.  } 
這里雖然是new了一個Messenger,但我們查看它的實現(xiàn)

 

 
 
 
 
  1. public Messenger(IBinder target) { mTarget = IMessenger.Stub.asInterface(target);  } 

發(fā)現(xiàn)它的mTarget是通過AIDL得到的,實際上就是遠程創(chuàng)建的那個。

(5). Client端可以使用這個Server端的信使對象向Server端發(fā)送消息。

rMessenger.send(msg);

這樣Server端的Handler對象就能收到消息了,然后可以在其handlerMessage(Message msg)方法中進行處理。經(jīng)過這5個步驟之后只有Client端向Server端發(fā)送消息,這樣的消息傳遞是單向的,那么如何實現(xiàn)消息的雙向傳遞呢?

首先需要在第5步做修改,在send(msg)前通過msm.replyTo = mMessenger將Client端自己的信使設置到消息中,這樣Server端接收到消息時同時也得到了Client端的信使對象,然后Server端也可以通過使用得到的Client端的信使對象來項Client端發(fā)送消息 cMessenger = msg.replyTo2 cMessenger.send(message);

這樣即完成了從Server端向Client端發(fā)送消息的功能,這樣Client端可以在自己的Handler對象的handlerMessage()方法中接收服務端發(fā)送來的message進行處理。

3.3 實現(xiàn)步驟

3.3.1 創(chuàng)建并初始化Server端的信使對象。

 
 
 
 
  1. private Handler mHandler = new Handler(){   
  2.     @Override   
  3.     public void handleMessage(Message msg) {   
  4.         super.handleMessage(msg);   
  5.         switch (msg.what) {   
  6.         case TEST:   
  7.             Log.e(TAG, "Get Message from MainActivity.");   
  8.             cMessenger = msg.replyTo;   
  9.             mTimer.schedule(new MyTimerTask(), 1000,TIME * 1000);   
  10.             break;   
  11.             default:   
  12.                 break;   
  13.             }   
  14.         }          
  15. };   
  16. //It's the messenger of server   
  17. private Messenger mMessenger = new Messenger(mHandler); 

   3.3.2 在Client端使用bindService()方法綁定Server端。

 
 
 
 
  1. private void doBindService(){   
  2.         Log.i(TAG, "doBindService()...");   
  3.     mIsBind = bindService(intent, serConn, BIND_AUTO_CREATE);//if bind success return true   
  4.         Log.e(TAG, "Is bind: "+mIsBind);   

3.3.3 在Server端的onBind()方法中返回一個binder對象。

 
 
 
 
  1. @Override   
  2. public IBinder onBind(Intent intent) {   
  3.     Log.i(TAG, "MessengerService.onBind()...");   
  4.     return mMessenger.getBinder();   

這裡的mMessenger就是Server端的信使對象。

3.3.4 Client端使用ServiceConnected()方法來獲取Server端的信使對象。

 
 
 
 
  1. private ServiceConnection serConn = new ServiceConnection() {      
  2.     @Override   
  3.     public void onServiceDisconnected(ComponentName name) {   
  4.         Log.i(TAG, "onServiceDisconnected()...");   
  5.         rMessenger = null;   
  6.     }          
  7.     @Override   
  8.     public void onServiceConnected(ComponentName name, IBinder service) {   
  9.         Log.i(TAG, "onServiceConnected()...");   
  10.     rMessenger = new Messenger(service);//get the object of remote service   
  11.     mMessenger = new Messenger(mHandler);//initial the object of local service   
  12.     sendMessage();   
  13.     }   
  14. }; 

獲取Server端的信使對象的同時,也初始化Client端的自己的信使對象,并且通過sendMessage()方法發(fā)送消息給Server端,表示可以開始下載了。

3.3.5 Client端使用獲取到的rMessenger來發(fā)送消息給Server端,同時將Client端的信使封裝到消息中,一并發(fā)送給Server端。

 
 
 
 
  1. private void sendMessage() {   
  2.     Message msg = Message.obtain(null, MessengerService.TEST);//MessengerService.TEST=0   
  3.     msg.replyTo = mMessenger;   
  4.     try {   
  5.         rMessenger.send(msg);   
  6.     } catch (RemoteException e) {   
  7.         e.printStackTrace();   
  8.     }   

這里的MessengerService.TEST為Server端里的一個靜態(tài)常量。Msg.replyTo=mMessenger;表示發(fā)送給Server端的信息里攜帶Client端的信使。

3.3.6 Server端獲取Client端發(fā)送的消息并得到Client端的信使對象。

 
 
 
 
  1. private Handler mHandler = new Handler(){   
  2.     @Override   
  3.     public void handleMessage(Message msg) {   
  4.         super.handleMessage(msg);   
  5.         switch (msg.what) {   
  6.         case TEST:   
  7.             Log.e(TAG, "Get Message from MainActivity.");   
  8.             cMessenger = msg.replyTo;//get the messenger of client   
  9.             mTimer.schedule(new MyTimerTask(), 1000,TIME * 1000);   
  10.             break;   
  11.         default:   
  12.             break;   
  13.         }   
  14.     }   
  15. }; 

在接收到Client端的信息之后,Server端開啟timer模擬下載,并接收Client端的信使對象。

3.3.7 Server端向Client端發(fā)送數(shù)據(jù)。

 
 
 
 
  1. class MyTimerTask extends TimerTask {   
  2.     @Override   
  3.     public void run() {   
  4.         if (i == 100) {   
  5.             i = 0;   
  6.         }   
  7.         try {   
  8.             //send the message to the client   
  9.         Message message = Message.obtain(null, MessengerService.TEST,i, 0);   
  10.             cMessenger.send(message);   
  11.         } catch (RemoteException e) {   
  12.                 e.printStackTrace();   
  13.         }   
  14.             i++;   
  15.     }   

直接使用接收到的Client端的信使對象來發(fā)送當前下載進度給Client端。

3.3.8 Client端接收來自Server端的數(shù)據(jù)。

 
 
 
 
  1. private Handler mHandler = new Handler(){   
  2.     @Override   
  3.     public void handleMessage(Message msg) {   
  4.         super.handleMessage(msg);   
  5.         switch (msg.what) {   
  6.         case MessengerService.TEST:   
  7.             Log.e(TAG, "Get Message From MessengerService. i= "+msg.arg1);   
  8.             int curLoad = msg.arg1;   
  9.             mTextView.setText(curLoad+"%");   
  10.             mProgressBar.setProgress(curLoad);   
  11.             break;   
  12.         default:   
  13.             break;   
  14.         }   
  15.     }   
  16. }; 

Client端的接收和Server端的接收狠類似。接收到Server端傳過來的數(shù)據(jù)之后進行介面更新,以及下載進度更新。

以下是AndroidManifest.xml文件:

 
 
 
 
  1.    
  2.       package="com.seven.messengerservicedemo"   
  3.       android:versionCode="1"   
  4.       android:versionName="1.0">   
  5.        
  6.        
  7.         
  8.                   android:label="@string/app_name">   
  9.                
  10.                    
  11.                    
  12.                
  13.            
  14.        
  15.            
  16.        
  17.            
  18.        
  19.    
  20.  

這里在Service的註冊中加入了過濾動作,只有相匹配的action才能啟動相應的Service。

3.4 小結

通過Messenger來實現(xiàn)Activity和Service的交互,稍微深入一點我們就可以知道,其實Messenger也是通過AIDL來實現(xiàn)的。對於前兩種實現(xiàn)方式,Messenger方式總體上來講也是比較容易理解的,這就和平時使用Handler和Thread通信一個道理。

#p#

自定義接口交互

4何謂自定義接口呢,其實就是我們自己通過接口的實現(xiàn)來達到Activity與Service交互的目的,我們通過在Activity和Service之間架設一座橋樑,從而達到數(shù)據(jù)交互的目的,而這種實現(xiàn)方式和AIDL非常類似(后文會說到)。

4.1 實現(xiàn)原理

自定義一個接口,該接口中有一個獲取當前下載進度的空方法。Server端用一個類繼承自Binder并實現(xiàn)該接口,覆寫了其中獲取當前下載進度的方法。Client端通過ServiceConnection獲取到該類的對象,從而能夠使用該獲取當前下載進度的方法,最終實現(xiàn)實時交互。

4.2 實現(xiàn)步驟

4.2.1 新建一個Interface,并在其中創(chuàng)建一個用于獲取當前下載進度的的空方法getCurrentLoad()

 
 
 
 
  1. public interface ICountService {   
  2.     public int getCurrentLoad();   

4.2.2 新建Server端DownService實現(xiàn)ICountService并在其中通過一個內(nèi)部類ServiceBinder繼承自Binder并實現(xiàn)ICoutService接口。

 
 
 
 
  1. public class DownLoadService extends Service implements ICountService{   
  2. private ServiceBinder serviceBinder = new ServiceBinder();     
  3. public class ServiceBinder extends Binder implements ICountService{   
  4.     @Override   
  5.     public int getCurrentLoad() {   
  6.         Log.i(TAG, "ServiceBinder getCurrentLoad()... i=:"+i);   
  7.         return i;   
  8.     }      
  9. }   
  10. @Override   
  11. public int getCurrentLoad() {   
  12.     return 0;   
  13. }   

在Server端中,實現(xiàn)獲取下載進度的空方法getCurrentLoad();這是Eclipse自動生成的,重點不在這裡。我們需要在ServiceBinder類中覆寫getCurrentLoad()方法,這裡我們返回當前的下載進度i。

4.2.3 Client端使用bindService()綁定Server端。

 
 
 
 
  1. if (startSerBtn == v) {   
  2.     Log.i(TAG, "Start Button Clicked.");   
  3.     bindService(intent, serConn, BIND_AUTO_CREATE);   
  4.     timer.schedule(new MyTimerTask(), 1000, TIME * 1000);//這里一定要延遲一下再開始獲取數(shù)據(jù),不然會報空指針異常   

在Client端綁定Server端的同時,延遲1s開始獲取下載進度。其中的intent = new Intent(“com.seven.test”);com.seven.test該字符串要與在AndroidManifest.xml中申明的一致

4.2.4 Server端返回binder對象。

 
 
 
 
  1. @Override   
  2. public IBinder onBind(Intent intent) {   
  3.     Log.i(TAG, "DownLoadService.onBind()...");   
  4.     return serviceBinder;   

這里的serviceBinder因為繼承了Binder因此也是Binder對象。

4.2.5 Client端通過ServiceConnection來獲取Server端的binder對象。

 
 
 
 
  1. private ServiceConnection serConn = new ServiceConnection() {   
  2. @Override   
  3.     public void onServiceDisconnected(ComponentName name) {   
  4.         iCountService = null;   
  5.     }          
  6.     @Override   
  7.     public void onServiceConnected(ComponentName name, IBinder service) {   
  8.         Log.i(TAG, "onServiceConnected()...");   
  9.         iCountService = (ICountService)service;   
  10.     }   
  11. }; 

獲取的過程是在bindService()過程中完成的,這里的iCountService是接口ICountService的對象,在這里得到實例化。

4.2.6 在綁定完成之后,Server端會開啟下載,在實際情況中Server端會開啟獨立線程用于下載,這里用i++來代替。

 
 
 
 
  1. @Override   
  2. public void onCreate() {   
  3.     super.onCreate();   
  4.     Log.i(TAG, "DownLoadService.onCreate()...");   
  5.     timer = new Timer();   
  6.     timer.schedule(new MyTimerTask(), 0, TIME*1000);   
  7. }   
  8. class MyTimerTask extends TimerTask{   
  9.     @Override   
  10.     public void run() {   
  11.         if(100==i){   
  12.             i=0;   
  13.         }   
  14.         i++;   
  15.     }   

bindService()方法執(zhí)行之后會調(diào)用DownLoadService中的onCreate()方法,在其onCreate()方法中開啟timer使得i++。

4.2.7 Server端已經(jīng)開啟了下載,那么Client端需要及時獲取下載進度并在主界面上更新。

 
 
 
 
  1. Handler mHandler = new Handler(){   
  2.     @Override   
  3.     public void handleMessage(Message msg) {   
  4.         super.handleMessage(msg);   
  5.         Log.i(TAG, "handleMessage...");   
  6.         int curLoad = iCountService.getCurrentLoad();   
  7.         mProgressBar.setProgress(curLoad);   
  8.         currentTv.setText(curLoad+"%");   
  9.     }   
  10.  };   
  11. class MyTimerTask extends TimerTask{   
  12.     @Override   
  13.     public void run() {   
  14.         mHandler.sendMessage(mHandler.obtainMessage());   
  15.     }   

Client端的Timer在bindService()完成之后1秒再開始獲取下載進度,獲取方法是直接通過int curLoad = iCountService.getCurrentLoad();這里的getCurrentLoad()方法是DownLoadService內(nèi)部類ServiceBinder中的方法。Client端將獲取到的下載進度更新到介面上并更新進度條。

4.3 小結

通過上面的例子可以知道,這種方法簡單實用,擴展性強,但其也有一些缺點,比如需要延遲一些再開始獲取Server端的數(shù)據(jù),從而無法完全實現(xiàn)從零開始同步更新。綜其所述,通過自定義接口實現(xiàn)Activity與Service交互的方法還是比較實用的。適用於同進程中通信,不能進行跨進程通信。

#p#

AIDL交互

AIDL是Android Interface Definition Language的首字母縮寫, 也就是Android接口定義語言。提及AIDL就不得不說下Android的服務,Android 支持兩種服務類型的服務即本地服務和遠程服務。

本地服務無法供在設備上運行的其他應用程序訪問,也就是說只能該應用程序內(nèi)部調(diào)用,比如某些應用程序中的下載類服務,這些服務只能由內(nèi)部調(diào)用。而對于遠程服務,除了可以由本應用程序調(diào)用,還可以允許其他應用程序訪問。遠程服務一般通過AIDL來實現(xiàn),可以進行進程間通信,這種服務也就是遠程服務。

本地服務與遠程服務還是有一些重要的區(qū)別。具體來講,如果服務完全只供同一進程中的組件使用(運行后臺任務),客戶端一邊通過調(diào)用 Context.startService()來啟動該服務。這種類型的服務為本地服務,它的一般用途是后臺執(zhí)行長耗時操作。而遠程服務一般通過bindService()方法啟動,主要為不同進程間通信。我們也將遠程服務稱為AIDL支持服務,因為客戶端使用 AIDL 與服務通信。Android中對于遠程服務有多種叫法:遠程服務、AIDL服務、外部服務和RPC服務。

5.1 AIDL實現(xiàn)流程圖

這屬于代理/存根結構,通過這張AIDL的流程圖,很容易發(fā)現(xiàn)Android實現(xiàn)IPC其實是在原來的C/S框架上加入了代理/存根結構。

比如,你到自動取款機上去取款。那么你就是客戶(Client),取款機就是你的代理(Proxy);你不會在乎錢具體放在那里,你只想將你的錢從取款機中取出來。你同銀行之間的操作完全是取款機代理實現(xiàn)。你的取款請求通過取款機傳到另一邊,即銀行的服務器(Server)。它也沒有必要知道你在哪兒取錢,它所關心的是你的身份和你取款多少。當它確認你的權限,就進行相應的操作,返回操作結果給取款機,取款機根據(jù)服務器返回結果,從保險柜里取出相應數(shù)量的錢給你。你取出卡后,操作完成。取款機不是直接同服務器連接的,他們之間還有一個“存根(Stub)”,取款機與存根通信,服務器與存根通信,從某種意義上說存根就是服務器的代理。

6

5.3 實現(xiàn)原理7

AIDL屬于Android的IPC機
分享名稱:Android面試,與Service交互方式
文章分享:http://www.dlmjj.cn/article/dppiggj.html