小男孩‘自慰网亚洲一区二区,亚洲一级在线播放毛片,亚洲中文字幕av每天更新,黄aⅴ永久免费无码,91成人午夜在线精品,色网站免费在线观看,亚洲欧洲wwwww在线观看

分享

Android組件系列_Android Service組件深入解析

 jiffes 2019-01-14

文章來源:http://www.cnblogs.com/smyhvae/p/4070518.html

 

【前言】

花了周末兩天的時(shí)間,整理了一下作為Android四大組件之一的Service的基礎(chǔ)知識(shí),通過這篇文章,應(yīng)該可以明白:對Service的理解、在什么地方使用、怎么使用、要注意哪些問題等。

【本文主要內(nèi)容】

一、Service的基本概念(四大組件之一)
二、定義(啟動(dòng))一個(gè)Service

  • 1、如何定義(啟動(dòng))一個(gè)Service:
  • 2、停止一個(gè)started服務(wù)有兩種方法
  • 3、onStartCommand方法的返回值

三、IntentService

  • 1、IntentService的引入
  • 2、IntentService的作用
  • 3、IntentService的用法
  • 4、Service和Thread的關(guān)系

四、使用Bind Service完成Service和Activity之間的通信

  • 1、Bind Service的介紹
  • 2、實(shí)現(xiàn)Service和Activity之間通信步驟
  • 3、started服務(wù)與bind服務(wù)的區(qū)別
  • 4、Service的生命周期

五、使用Bind Service完成IPC進(jìn)程間通信:(在同一個(gè)APP內(nèi)模擬)

  • 1、Bind Service的介紹
  • 2、在客戶端綁定一個(gè)服務(wù)的步驟
  • 3、IPC(Inter-Process Communication)進(jìn)程間通信機(jī)制
  • 4、AIDL(Android Interface Definition Language)Android接口定義語言
  • 5、IPC(進(jìn)程間通訊)具體的步驟如下
  • 6、讓Activity與一個(gè)遠(yuǎn)程Service建立關(guān)聯(lián)的步驟:(在同一個(gè)APP內(nèi)模擬)
  • 7、AIDL支持的自定義數(shù)據(jù)類型


六、使用Bind Service完成IPC進(jìn)程間通信:(兩個(gè)APP之間)
七、Messenger的使用

 

【正文】

一、Service的基本概念(四大組件之一)

Service是Android中實(shí)現(xiàn)程序后臺(tái)運(yùn)行的解決方案,非常適合用于去執(zhí)行哪些不需要和用戶交互而且還要求長期運(yùn)行的任務(wù)。不能運(yùn)行在一個(gè)獨(dú)立的進(jìn)程當(dāng)中,而是依賴與創(chuàng)建服務(wù)時(shí)所在的應(yīng)用程序進(jìn)程。只能在后臺(tái)運(yùn)行,并且可以和其他組件進(jìn)行交互。

Service可以在很多場合使用,比如播放多媒體的時(shí)候用戶啟動(dòng)了其他Activity,此時(shí)要在后臺(tái)繼續(xù)播放;比如檢測SD卡上文件的變化;比如在后臺(tái)記錄你的地理信息位置的改變等等,總之服務(wù)是藏在后臺(tái)的。

服務(wù)不會(huì)自動(dòng)開啟線程,我們需要在服務(wù)的內(nèi)部手動(dòng)創(chuàng)建子線程,并在這里執(zhí)行具體的任務(wù)。關(guān)于多線程的知識(shí):可以參考另外一篇文章:Android多線程----異步消息處理機(jī)制之Handler詳解

 

二、定義(啟動(dòng))一個(gè)Service:

1、如何定義(啟動(dòng))一個(gè)Service:

核心步驟如下:

  • 創(chuàng)建一個(gè)類繼承android.app.Service類,實(shí)現(xiàn)抽象方法onBind(),重寫onCreate()、onStartCommand()、onDestry();
  • 在清單文件中配置Service。

新建一個(gè)Android項(xiàng)目ServiceTest,具體步驟如下:

(1)新建一個(gè)MyService類,繼承自Service,并重寫父類的onCreate()、onStartCommand()和onDestroy()方法,代碼如下:

復(fù)制代碼
 1 package com.example.servicetest;
 2 
 3 import android.app.Service;
 4 import android.content.Intent;
 5 import android.os.IBinder;
 6 import android.util.Log;
 7 
 8 public class MyService extends Service {  
 9       
10     public static final String TAG = "MyService";  
11   
12     //創(chuàng)建服務(wù)時(shí)調(diào)用
13     @Override  
14     public void onCreate() {  
15         super.onCreate();  
16         Log.d(TAG, "onCreate");  
17     }  
18   
19     //服務(wù)執(zhí)行的操作
20     @Override  
21     public int onStartCommand(Intent intent, int flags, int startId) {  
22         Log.d(TAG, "onStartCommand");  
23         return super.onStartCommand(intent, flags, startId);  
24     }  
25       
26     //銷毀服務(wù)時(shí)調(diào)用
27     @Override  
28     public void onDestroy() {  
29         super.onDestroy();  
30         Log.d(TAG, "onDestroy");  
31     }  
32   
33     @Override  
34     public IBinder onBind(Intent intent) {  
35         return null;  
36     }  
37 }
復(fù)制代碼

可以看到,我們只是在onCreate()、onStartCommand()和onDestroy()方法中分別打印了一句話,并沒有進(jìn)行其它任何的操作,注意代碼注釋中這三個(gè)方法的作用

onBind()方法是Service中唯一的一個(gè)抽象方法,所以必須要在子類里實(shí)現(xiàn)。我們知道,Service可以有兩種啟動(dòng)方式:一種是startService(),另一種是bindService()。第二種啟動(dòng)方式才會(huì)用到onBind()方法。我們這先用第一種方式啟動(dòng)Service,所以暫時(shí)忽略onBind()方法。

(2)在清單文件中聲明:(和Activity標(biāo)簽并列)

 <service android:name=".MyService">        </service>

(3)修改activity_main.xml代碼,如下:

復(fù)制代碼
<LinearLayout xmlns:android="http://schemas./apk/res/android"  
    android:layout_width="match_parent"  
    android:layout_height="match_parent"  
    android:orientation="vertical" >  
  
    <Button  
        android:id="@+id/button1_start_service"  
        android:layout_width="match_parent"  
        android:layout_height="wrap_content"  
        android:text="Start Service" />  
  
    <Button  
        android:id="@+id/button2_stop_service"  
        android:layout_width="match_parent"  
        android:layout_height="wrap_content"  
        android:text="Stop Service" />  
  
</LinearLayout> 
復(fù)制代碼

我們在布局文件中加入了兩個(gè)按鈕,一個(gè)用于啟動(dòng)Service,一個(gè)用于停止Service。

(4)在MainActivity作為程序的主Activity,在里面加入啟動(dòng)Service和停止Service的邏輯,代碼如下:

復(fù)制代碼
 1 package com.example.servicetest;
 2 
 3 import android.app.Activity;
 4 import android.content.Intent;
 5 import android.os.Bundle;
 6 import android.view.View;
 7 import android.view.View.OnClickListener;
 8 import android.widget.Button;
 9 
10 
11 public class MainActivity extends Activity implements OnClickListener {  
12       
13     private Button button1_start_service;  
14   
15     private Button button2_stop_service;  
16   
17     @Override  
18     protected void onCreate(Bundle savedInstanceState) {  
19         super.onCreate(savedInstanceState);  
20         setContentView(R.layout.activity_main);  
21         button1_start_service = (Button) findViewById(R.id.button1_start_service);  
22         button2_stop_service = (Button) findViewById(R.id.button2_stop_service);  
23         button1_start_service.setOnClickListener(this);  
24         button2_stop_service.setOnClickListener(this);  
25     }  
26   
27     @Override  
28     public void onClick(View v) {  
29         switch (v.getId()) {  
30         case R.id.button1_start_service:  
31             Intent startIntent = new Intent(this, MyService.class);  
32             startService(startIntent);  
33             break;  
34         case R.id.button2_stop_service:  
35             Intent stopIntent = new Intent(this, MyService.class);  
36             stopService(stopIntent);  
37             break;  
38         default:  
39             break;  
40         }  
41     }  
42   
43 }
復(fù)制代碼

核心代碼:31行至32行、35行至36行。

可以看到,在Start Service按鈕的點(diǎn)擊事件里,我們構(gòu)建出了一個(gè)Intent對象,并調(diào)用startService()方法來啟動(dòng)MyService。然后在Stop Serivce按鈕的點(diǎn)擊事件里,我們同樣構(gòu)建出了一個(gè)Intent對象,并調(diào)用stopService()方法來停止MyService。代碼的邏輯非常簡單。

這樣的話,一個(gè)簡單的帶有Service功能的程序就寫好了。

啟動(dòng)和停止服務(wù):

定義好服務(wù)之后,接下來看一下如何啟動(dòng)和停止一個(gè)服務(wù),這主要是借助Intent來實(shí)現(xiàn)的。注意startService()和stopService()方法都是定義在Context類當(dāng)中的,所以可以在MainActivity中直接調(diào)用這兩個(gè)方法。

運(yùn)行上面的程序,點(diǎn)擊button1_start_service按鈕,啟動(dòng)服務(wù),后臺(tái)打印日志如下:

說明服務(wù)啟動(dòng)成功。

那么如果我再連續(xù)點(diǎn)三次button1_start_service按鈕,后臺(tái)增加的日志如下:

事實(shí)上,onCreate()方法只會(huì)在Service第一次被創(chuàng)建的時(shí)候調(diào)用,而onStartCommand()方法在每次啟動(dòng)服務(wù)的時(shí)候都會(huì)調(diào)用。

我們還可以在正在“設(shè)置--應(yīng)用---運(yùn)行”中找到這個(gè)服務(wù),如下圖所示:

點(diǎn)開上圖中的紅框部分,可以看到:

如果我們再點(diǎn)擊button2_stop_service按鈕或者點(diǎn)擊上圖中的“Stop”,MyService服務(wù)就停止掉了:

需要注意的是:

  • 服務(wù)對象同時(shí)只會(huì)有一個(gè)
  • 默認(rèn)情況下,一個(gè)started的Service與啟動(dòng)他的組件在同一個(gè)線程中。上面的實(shí)例中,服務(wù)就是在主線程中運(yùn)行的,如果是在服務(wù)中完成耗時(shí)操作的話,容易造成主線程阻塞。

2、停止一個(gè)started服務(wù)有兩種方法:

(1)在外部使用stopService()

(2)在服務(wù)內(nèi)部(onStartCommand方法內(nèi)部)使用stopSelf()方法。

3、onStartCommand方法的返回值:

onStartCommand方法執(zhí)行時(shí),返回的是一個(gè)int型。這個(gè)整型可以有三個(gè)返回值:START_NOT_STICKY、START_STICKY、START_REDELIVER_INTENT

  • START_NOT_STICKY:“非粘性的”。使用這個(gè)返回值時(shí),如果在執(zhí)行完onStartCommand方法后,服務(wù)被異常kill掉,系統(tǒng)不會(huì)自動(dòng)重啟該服務(wù)。
  • START_STICKY:如果Service進(jìn)程被kill掉,保留Service的狀態(tài)為開始狀態(tài),但不保留遞送的intent對象。隨后系統(tǒng)會(huì)嘗試重新創(chuàng)建Service,由于服務(wù)狀態(tài)為開始狀態(tài),所以創(chuàng)建服務(wù)后一定會(huì)調(diào)用onStartCommand(Intent,int,int)方法。如果在此期間沒有任何啟動(dòng)命令被傳遞到Service,那么參數(shù)Intent將為null。
  • START_REDELIVER_INTENT:重傳Intent。使用這個(gè)返回值時(shí),系統(tǒng)會(huì)自動(dòng)重啟該服務(wù),并將Intent的值傳入。

 

三、IntentService

1、IntentService的引入:

我們在第一段中就已經(jīng)說了,服務(wù)中的代碼默認(rèn)運(yùn)行在主線程中,如果直接在服務(wù)里執(zhí)行一些耗時(shí)操作,容易造成ANR(Application Not Responding)異常,所以就需要用到多線程的知識(shí)了。

因此一個(gè)比較標(biāo)準(zhǔn)的服務(wù)可以這樣寫

復(fù)制代碼
 1 package com.example.servicetest;
 2 
 3 import android.app.Service;
 4 import android.content.Intent;
 5 import android.os.IBinder;
 6 
 7 public class MyService extends Service {  
 8       
 9     public static final String TAG = "MyService";   
10   
11     //服務(wù)執(zhí)行的操作
12     @Override  
13     public int onStartCommand(Intent intent, int flags, int startId) {  
14         new Thread(new Runnable() {
15             public void run() {
16                 //處理具體的邏輯
17                 stopSelf();  //服務(wù)執(zhí)行完畢后自動(dòng)停止
18             }
19         }).start();        
20         return super.onStartCommand(intent, flags, startId);  
21     }
22 
23     @Override
24     public IBinder onBind(Intent intent) {
25         // TODO Auto-generated method stub
26         return null;
27     }      
28  
29 }
復(fù)制代碼

核心代碼:14至19行,在子線程中處理具體的邏輯。

需要注意的是,如果沒有第17行的stopSelf(),服務(wù)一旦啟動(dòng)后,就會(huì)一直處于運(yùn)行狀態(tài),必須調(diào)用stopService()或者stopSelf()方法才能讓服務(wù)停止下來;所以我們添加了17行的stopSelf(),服務(wù)執(zhí)行完畢后會(huì)自動(dòng)停止。

雖說上面的這種寫法并不復(fù)雜,但總會(huì)有一些程序猿忘記開啟線程,或者忘記調(diào)用stopSelf()方法。為了可以簡單地創(chuàng)建一個(gè)異步的、會(huì)自動(dòng)停止的服務(wù),Android專門提供了一個(gè)IntentService類,這個(gè)類就很好的解決了上面所提到的兩種尷尬。另外,可以啟動(dòng)IntentService多次,而每一個(gè)耗時(shí)操作會(huì)以工作隊(duì)列的方式在IntentService的onHandleIntent()回調(diào)方法中執(zhí)行,并且每次只會(huì)執(zhí)行一個(gè)工作線程,執(zhí)行完第一個(gè)后,再執(zhí)行第二個(gè),以此類推。

2、IntentService的作用:

當(dāng)我們需要這樣一次性完成的任務(wù)時(shí),就可以使用IntentService來完成

3、IntentService的用法:

我們在上面的項(xiàng)目ServiceTest基礎(chǔ)上進(jìn)行修改,步驟如下:

(1)新建一個(gè)MyIntentService類,繼承自IntentService,并重寫父類的onHandleIntent()方法,代碼如下:

復(fù)制代碼
 1 package com.example.servicetest;
 2 
 3 import android.app.IntentService;
 4 import android.content.Intent;
 5 import android.util.Log;
 6 
 7 public class MyIntentService extends IntentService{
 8 
 9     public MyIntentService() {
10         super("MyIntentService");//調(diào)用父類有參構(gòu)造函數(shù)。這里我們手動(dòng)給服務(wù)起個(gè)名字為:MyIntentService
11         // TODO Auto-generated constructor stub
12     }
13 
14     //該方法在會(huì)在一個(gè)單獨(dú)的線程中執(zhí)行,來完成工作任務(wù)。任務(wù)結(jié)束后,該Service自動(dòng)停止
15     @Override
16     protected void onHandleIntent(Intent intent) {
17         // TODO Auto-generated method stub
18         for(int i = 0;i<3;i++) {
19             //打印當(dāng)前線程的id
20             Log.d("MyIntentService","IntentService線程的id是:"+Thread.currentThread().getId());
21             try {
22                 Thread.sleep(1000);
23             } catch (InterruptedException e) {
24                 // TODO Auto-generated catch block
25                 e.printStackTrace();
26             }
27         }        
28     }
29 
30     @Override
31     public void onDestroy() {
32         // TODO Auto-generated method stub
33         super.onDestroy();
34         Log.d("MyIntentService","onDestroy");
35     }
36 }
復(fù)制代碼

這里首先要提供一個(gè)無參的構(gòu)造方法,并且必須在其內(nèi)部調(diào)用父類的有參構(gòu)造方法(9至12行),我們在第10行手動(dòng)將服務(wù)的名字改為“MyIntentService”。

然后在子類中實(shí)現(xiàn)onHandleIntent()這個(gè)抽象方法,可以在這個(gè)方法里去處理一些具體的邏輯,我們就用三次for循環(huán),打印當(dāng)前線程的id,每次延時(shí)1秒。

因?yàn)檫@個(gè)服務(wù)在運(yùn)行結(jié)束后會(huì)自動(dòng)停止,所以我們在onDestroy()方法中打印日志驗(yàn)證一下。

(2)在清單文件中對服務(wù)進(jìn)行注冊服務(wù):

<service android:name=".MyIntentService"> </service>

(3)在activity_main.xml中添加一個(gè)按鈕button3_stop_intentservice,用于啟動(dòng)MyIntentService服務(wù),代碼略。

(4)在MainActivity里面加入啟動(dòng)IntentService的邏輯,核心代碼如下:

1       case R.id.button3_stop_intentservice:
2             Log.d("MainActivity","主線程的id是:"+Thread.currentThread().getId());
3             Intent intentService = new Intent(this,MyIntentService.class);
4             startService(intentService);
5         default:

我們在第02行中,打印主線程的id。

運(yùn)行程序,點(diǎn)擊按鈕button3_stop_intentservice,顯示如下:

由此可見,啟動(dòng)一個(gè)IntentService和啟動(dòng)一個(gè)普通的Service,步驟是一樣的。

4、Service和Thread的關(guān)系:

不少Android初學(xué)者都可能會(huì)有這樣的疑惑,Service和Thread到底有什么關(guān)系呢?什么時(shí)候應(yīng)該用Service,什么時(shí)候又應(yīng)該用Thread?答案可能會(huì)有點(diǎn)讓你吃驚,因?yàn)镾ervice和Thread之間沒有任何關(guān)系!

之所以有不少人會(huì)把它們聯(lián)系起來,主要就是因?yàn)镾ervice的后臺(tái)概念。Thread我們大家都知道,是用于開啟一個(gè)子線程,在這里去執(zhí)行一些耗時(shí)操作就不會(huì)阻塞主線程的運(yùn)行。而Service我們最初理解的時(shí)候,總會(huì)覺得它是用來處理一些后臺(tái)任務(wù)的,一些比較耗時(shí)的操作也可以放在這里運(yùn)行,這就會(huì)讓人產(chǎn)生混淆了。但是,如果我告訴你Service其實(shí)是運(yùn)行在主線程里的,你還會(huì)覺得它和Thread有什么關(guān)系嗎?

其實(shí),后臺(tái)和子線程是兩個(gè)完全不同的概念:

Android的后臺(tái)就是指,它的運(yùn)行是完全不依賴UI的。即使Activity被銷毀,或者程序被關(guān)閉,只要進(jìn)程還在,Service就可以繼續(xù)運(yùn)行。比如說一些應(yīng)用程序,始終需要與服務(wù)器之間始終保持著心跳連接,就可以使用Service來實(shí)現(xiàn)。你可能又會(huì)問,Service既然是運(yùn)行在主線程里,在這里一直執(zhí)行著心跳連接,難道就不會(huì)阻塞主線程的運(yùn)行嗎?當(dāng)然會(huì),但是我們可以在Service中再創(chuàng)建一個(gè)子線程,然后在這里去處理耗時(shí)邏輯就沒問題了。

既然在Service里也要?jiǎng)?chuàng)建一個(gè)子線程,那為什么不直接在Activity里創(chuàng)建呢?這是因?yàn)锳ctivity很難對Thread進(jìn)行控制,當(dāng)Activity被銷毀之后,就沒有任何其它的辦法可以再重新獲取到之前創(chuàng)建的子線程的實(shí)例;而且在一個(gè)Activity中創(chuàng)建的子線程,另一個(gè)Activity無法對其進(jìn)行操作。但是Service就不同了,所有的Activity都可以與Service進(jìn)行關(guān)聯(lián),然后可以很方便地操作其中的方法,即使Activity被銷毀了,之后只要重新與Service建立關(guān)聯(lián),就又能夠獲取到原有的Service中Binder的實(shí)例。因此,使用Service來處理后臺(tái)任務(wù),Activity就可以放心地finish,完全不需要擔(dān)心無法對后臺(tái)任務(wù)進(jìn)行控制的情況。

所以說,一個(gè)比較標(biāo)準(zhǔn)的Service,就可以寫成本段中第1節(jié)的樣子。

 

四、使用Bind Service完成Service和Activity之間的通信

有沒有什么辦法能讓它們倆的關(guān)聯(lián)更多一些呢?比如說在Activity中指揮Service去干什么,Service就去干什么。當(dāng)然可以,只需要讓Activity和Service建立關(guān)聯(lián)就好了。

1、Bind Service的介紹:

應(yīng)用程序組件(客戶端)通過調(diào)用bindService()方法能夠綁定服務(wù),然后Android系統(tǒng)會(huì)調(diào)用服務(wù)的onBind()回調(diào)方法,則個(gè)方法會(huì)返回一個(gè)跟服務(wù)器端交互的Binder對象。

這個(gè)綁定是異步的,bindService()方法立即返回,并且不給客戶端返回IBinder對象。要接收IBinder對象,客戶端必須創(chuàng)建一個(gè)ServiceConnection類的實(shí)例,并且把這個(gè)實(shí)例傳遞給bindService()方法。ServiceConnection對象包含了一個(gè)系統(tǒng)調(diào)用的傳遞IBinder對象的回調(diào)方法。

注意:只有Activity、Service、Content Provider能夠綁定服務(wù);BroadcastReceiver廣播接收器不能綁定服務(wù)

2、實(shí)現(xiàn)Service和Activity之間通信步驟:

我們依然在第二段中的項(xiàng)目ServiceTest基礎(chǔ)上進(jìn)行修改。

觀察上面第二段中MyService中的代碼,你會(huì)發(fā)現(xiàn)一直有一個(gè)onBind()方法我們都沒有使用到,這個(gè)方法其實(shí)就是用于和Activity建立關(guān)聯(lián)的,修改MyService中的代碼,如下所示:

復(fù)制代碼
 1 package com.example.servicetest;
 2 
 3 import android.app.Service;
 4 import android.content.Intent;
 5 import android.os.Binder;
 6 import android.os.IBinder;
 7 import android.util.Log;
 8 
 9 public class MyService extends Service {  
10       
11     public static final String TAG = "MyService";  
12   
13     private MyBinder mBinder = new MyBinder();  
14   
15     @Override  
16     public void onCreate() {  
17         super.onCreate();  
18         Log.d(TAG, "onCreate");  
19     }  
20   
21     @Override  
22     public int onStartCommand(Intent intent, int flags, int startId) {  
23         Log.d(TAG, "onStartCommand");  
24         return super.onStartCommand(intent, flags, startId);  
25     }  
26   
27     @Override  
28     public void onDestroy() {  
29         super.onDestroy();  
30         Log.d(TAG, "onDestroy");  
31     }  
32   
33     @Override  
34     public IBinder onBind(Intent intent) {  
35         return mBinder;  //在這里返回新建的MyBinder類
36     }  
37   
38     //MyBinder類,繼承Binder:讓里面的方法執(zhí)行下載任務(wù),并獲取下載進(jìn)度
39     class MyBinder extends Binder {  
40   
41         public void startDownload() {  
42             Log.d("TAG", "startDownload() executed");  
43             // 執(zhí)行具體的下載任務(wù)  
44         }
45         public int getProgress(){
46             Log.d("TAG", "getProgress() executed");  
47             return 0;
48         }
49   
50     }  
51   
52 }  
復(fù)制代碼

38至50行:新建一個(gè)MyBinder類,繼承Binder:讓里面的方法執(zhí)行下載任務(wù),并獲取下載進(jìn)度。當(dāng)然,這里只是兩個(gè)模擬方法,并沒有實(shí)現(xiàn)真正的功能,我們通過打印日志的形式來體現(xiàn)。

接著創(chuàng)建MyBinder的實(shí)例(13行),然后在onBind()方法里返回這個(gè)實(shí)例(35行)。

核心代碼是35行,返回這個(gè)mBinder,是一個(gè)IBinder類型,就可以把這個(gè)IBinder類型傳遞到MainActivity中,從而調(diào)用Service里面的方法。下面就要看一看,在MainActivity是如何調(diào)用Service里面的兩個(gè)方法的。

(2)檢查清單文件,是否已經(jīng)對Service進(jìn)行注冊:

<service android:name=".MyService" > </service> 

(3)在activity_main.xml中繼續(xù)添加兩個(gè)按鈕button3_bind_service和button4_unbind_service,用于綁定服務(wù)和取消綁定服務(wù)。最終,activity_main.xml的完整代碼如下:

復(fù)制代碼
<LinearLayout xmlns:android="http://schemas./apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >

    <Button
        android:id="@+id/button1_start_service"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Start Service" />

    <Button
        android:id="@+id/button2_stop_service"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Stop Service" />

    <Button
        android:id="@+id/button3_bind_service"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="bind Service" />

    <Button
        android:id="@+id/button4_unbind_service"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="unbind Service" />

</LinearLayout> 
復(fù)制代碼

(4)接下來再修改MainActivity中的代碼,讓MainActivity和MyService之間建立關(guān)聯(lián),代碼如下所示:

復(fù)制代碼
 1 package com.example.servicetest;
 2 
 3 import android.app.Activity;
 4 import android.content.ComponentName;
 5 import android.content.Intent;
 6 import android.content.ServiceConnection;
 7 import android.os.Bundle;
 8 import android.os.IBinder;
 9 import android.util.Log;
10 import android.view.View;
11 import android.view.View.OnClickListener;
12 import android.widget.Button;
13 
14 public class MainActivity extends Activity implements OnClickListener {
15 
16     private Button button1_start_service;
17     private Button button2_stop_service;
18     private Button button3_bind_service;
19     private Button button4_unbind_service;
20 
21     private MyService.MyBinder myBinder;
22     
23     //匿名內(nèi)部類:服務(wù)連接對象
24     private ServiceConnection connection = new ServiceConnection() {
25         
26         //當(dāng)服務(wù)異常終止時(shí)會(huì)調(diào)用。注意,解除綁定服務(wù)時(shí)不會(huì)調(diào)用
27         @Override
28         public void onServiceDisconnected(ComponentName name) {
29         }
30         
31         //和服務(wù)綁定成功后,服務(wù)會(huì)回調(diào)該方法
32         @Override
33         public void onServiceConnected(ComponentName name, IBinder service) {
34             myBinder = (MyService.MyBinder) service;
35             //在Activity中調(diào)用Service里面的方法
36             myBinder.startDownload();
37             myBinder.getProgress();
38         }
39     };
40 
41     @Override
42     protected void onCreate(Bundle savedInstanceState) {
43         super.onCreate(savedInstanceState);
44         setContentView(R.layout.activity_main);
45         button1_start_service = (Button) findViewById(R.id.button1_start_service);
46         button2_stop_service = (Button) findViewById(R.id.button2_stop_service);
47         button3_bind_service = (Button) findViewById(R.id.button3_bind_service);
48         button4_unbind_service = (Button) findViewById(R.id.button4_unbind_service);
49 
50         button1_start_service.setOnClickListener(this);
51         button2_stop_service.setOnClickListener(this);
52         button3_bind_service.setOnClickListener(this);
53         button4_unbind_service.setOnClickListener(this);
54     }
55 
56     @Override
57     public void onClick(View v) {
58         switch (v.getId()) {
59         case R.id.button1_start_service:
60             Intent startIntent = new Intent(this, MyService.class);
61             startService(startIntent);
62             break;
63         case R.id.button2_stop_service:
64             Intent stopIntent = new Intent(this, MyService.class);
65             stopService(stopIntent);
66             break;
67         case R.id.button3_bind_service:
68             Intent bindIntent = new Intent(this, MyService.class);
69             bindService(bindIntent, connection, BIND_AUTO_CREATE);
70             break;
71         case R.id.button4_unbind_service:
72             unbindService(connection);
73             break;
74 
75         default:
76             break;
77         }
78     }
79 
80 }
復(fù)制代碼

可以看到,這里我們首先創(chuàng)建了一個(gè)ServiceConnection的匿名類(24行),在里面重寫了onServiceConnected()方法和onServiceDisconnected()方法,如果當(dāng)前Activity與服務(wù)連接成功后,服務(wù)會(huì)回調(diào)onServiceConnected()方法,

在onServiceConnected()方法中,我們又通過向下轉(zhuǎn)型得到了MyBinder的實(shí)例(34行),有了這個(gè)實(shí)例,Activity和Service之間的關(guān)系就變得非常緊密了?,F(xiàn)在我們可以在Activity中根據(jù)具體的場景來調(diào)用MyBinder中的任何public方法(36、37行),即實(shí)現(xiàn)了Activity指揮Service干什么Service就去干什么的功能。

當(dāng)然,現(xiàn)在Activity和Service其實(shí)還沒關(guān)聯(lián)起來了呢,這個(gè)功能是在Bind Service按鈕的點(diǎn)擊事件里完成的。可以看到,這里我們?nèi)匀皇菢?gòu)建出了一個(gè)Intent對象,然后調(diào)用bindService()方法將Activity和Service進(jìn)行綁定。bindService()方法接收三個(gè)參數(shù),第一個(gè)參數(shù)就是剛剛構(gòu)建出的Intent對象,第二個(gè)參數(shù)是前面創(chuàng)建出的ServiceConnection的實(shí)例,第三個(gè)參數(shù)是一個(gè)標(biāo)志位,這里傳入BIND_AUTO_CREATE表示在Activity和Service建立關(guān)聯(lián)后會(huì)自動(dòng)創(chuàng)建Service(即使之前沒有創(chuàng)建Service也沒有關(guān)系),這會(huì)使得MyService中的onCreate()方法得到執(zhí)行,但onStartCommand()方法不會(huì)執(zhí)行。

然后如何我們想解除Activity和Service之間的關(guān)聯(lián)怎么辦呢?調(diào)用一下unbindService()方法就可以了,這也是Unbind Service按鈕的點(diǎn)擊事件里實(shí)現(xiàn)的邏輯。

現(xiàn)在讓我們重新運(yùn)行一下程序吧,在MainActivity中點(diǎn)擊一下Bind Service按鈕,LogCat里的打印日志如下圖所示:

可以看到,只點(diǎn)擊了Bind Service按鈕,但是oncreate()方法得到了執(zhí)行,而onStartCommand()方法不會(huì)執(zhí)行。

另外需要注意,任何一個(gè)Service在整個(gè)應(yīng)用程序范圍內(nèi)都是通用的,即MyService不僅可以和MainActivity建立關(guān)聯(lián),還可以和任何一個(gè)Activity建立關(guān)聯(lián),而且在建立關(guān)聯(lián)時(shí)它們都可以獲取到相同的MyBinder實(shí)例。

如何銷毀Service:

根據(jù)上面第一段的知識(shí),我們介紹了銷毀Service最簡單的一種情況:現(xiàn)在卸載程序,重新運(yùn)行程序,點(diǎn)擊Start Service按鈕啟動(dòng)Service,再點(diǎn)擊Stop Service按鈕停止Service,這樣MyService就被銷毀了:

現(xiàn)在回到本段內(nèi)容。卸載程序,重新開始。那么如果我們只點(diǎn)擊的Bind Service按鈕呢?由于在綁定Service的時(shí)候指定的標(biāo)志位是BIND_AUTO_CREATE,說明點(diǎn)擊Bind Service按鈕的時(shí)候Service也會(huì)被創(chuàng)建,這時(shí)應(yīng)該怎么銷毀Service呢?其實(shí)也很簡單,點(diǎn)擊一下Unbind Service按鈕,將Activity和Service的關(guān)聯(lián)解除就可以了:

以上這兩種銷毀的方式都很好理解。那么如果我們既點(diǎn)擊了Start Service按鈕,又點(diǎn)擊了Bind Service按鈕會(huì)怎么樣呢?這個(gè)時(shí)候你會(huì)發(fā)現(xiàn),不管你是單獨(dú)點(diǎn)擊Stop Service按鈕還是Unbind Service按鈕,Service都不會(huì)被銷毀,必要將Unbind Service按鈕和Stop Service按鈕都點(diǎn)擊一下(沒有先后順序),Service才會(huì)被銷毀。也就是說,點(diǎn)擊Stop Service按鈕只會(huì)讓Service停止,點(diǎn)擊Unbind Service按鈕只會(huì)讓Service和Activity解除關(guān)聯(lián),一個(gè)Service必須要在既沒有和任何Activity關(guān)聯(lián)又處理停止?fàn)顟B(tài)的時(shí)候才會(huì)被銷毀。

點(diǎn)擊Unbind Service按鈕后,再次點(diǎn)擊Unbind Service按鈕按鈕引發(fā)的問題:

假設(shè)現(xiàn)在Service和Activity已經(jīng)相關(guān)聯(lián)了,點(diǎn)擊Unbind Service按鈕能夠解除綁定,如果繼續(xù)點(diǎn)擊Unbind Service按鈕,程序會(huì)異常退出,這說明代碼不夠完善,我們需要在代碼中加一個(gè)判斷是否綁定的標(biāo)記mBound。在改MainActivity中增加一部分代碼,最終改MainActivity的完整代碼如下:(加粗字體是添加的內(nèi)容)

復(fù)制代碼
 1 package com.example.servicetest02;
 2 
 3 import android.app.Activity;
 4 import android.content.ComponentName;
 5 import android.content.Intent;
 6 import android.content.ServiceConnection;
 7 import android.os.Bundle;
 8 import android.os.IBinder;
 9 import android.util.Log;
10 import android.view.View;
11 import android.view.View.OnClickListener;
12 import android.widget.Button;
13 
14 public class MainActivity extends Activity implements OnClickListener {
15     private Button button1_start_service;
16     private Button button2_stop_service;
17     private Button button3_bind_service;
18     private Button button4_unbind_service;
19     private MyService.MyBinder myBinder;
20     
21     boolean mBound = false; //一開始,并沒有和Service綁定.這個(gè)參數(shù)是用來顯示綁定狀態(tài)
22     
23     //匿名內(nèi)部類:服務(wù)連接對象
24     private ServiceConnection connection = new ServiceConnection() {
25         
26         //當(dāng)服務(wù)異常終止時(shí)會(huì)調(diào)用。注意,解除綁定服務(wù)時(shí)不會(huì)調(diào)用
27         @Override
28         public void onServiceDisconnected(ComponentName name) {
29             mBound = false; //服務(wù)異常終止時(shí),狀態(tài)為未綁定
30         }
31         
32         //和服務(wù)綁定成功后,服務(wù)會(huì)回調(diào)該方法
33         @Override
34         public void onServiceConnected(ComponentName name, IBinder service) {
35             myBinder = (MyService.MyBinder) service;
36             //在Activity中調(diào)用Service里面的方法
37             myBinder.startDownload();
38             myBinder.getProgress();
39             mBound = true; //true說明是綁定狀態(tài)
40         }
41     };
42     @Override
43     protected void onCreate(Bundle savedInstanceState) {
44         super.onCreate(savedInstanceState);
45         setContentView(R.layout.activity_main);
46         button1_start_service = (Button) findViewById(R.id.button1_start_service);
47         button2_stop_service = (Button) findViewById(R.id.button2_stop_service);
48         button3_bind_service = (Button) findViewById(R.id.button3_bind_service);
49         button4_unbind_service = (Button) findViewById(R.id.button4_unbind_service);
50         button1_start_service.setOnClickListener(this);
51         button2_stop_service.setOnClickListener(this);
52         button3_bind_service.setOnClickListener(this);
53         button4_unbind_service.setOnClickListener(this);
54     }
55     @Override
56     public void onClick(View v) {
57         switch (v.getId()) {
58         case R.id.button1_start_service:
59             Intent startIntent = new Intent(this, MyService.class);
60             startService(startIntent);
61             break;
62         case R.id.button2_stop_service:
63             Intent stopIntent = new Intent(this, MyService.class);
64             stopService(stopIntent);
65             break;
66         case R.id.button3_bind_service:
67             Intent bindIntent = new Intent(this, MyService.class);
68             bindService(bindIntent, connection, BIND_AUTO_CREATE);
69             break;
70         case R.id.button4_unbind_service:
71             //如果和Service是綁定的狀態(tài),就解除綁定。
72             if(mBound){
73                 unbindService(connection);
74                 mBound=false;
75             }
76             break;
77 
78         default:
79             break;
80         }
81     }
82 }
復(fù)制代碼

添加的代碼是第21行、29行、72行至74行。

這樣的話,連續(xù)點(diǎn)擊Unbind Service按鈕,就不會(huì)使程序出現(xiàn)異常。

3、started服務(wù)與bind服務(wù)的區(qū)別:

區(qū)別一:生命周期

  • 通過started方式的服務(wù)會(huì)一直運(yùn)行在后臺(tái),需要由組件本身或外部組件來停止服務(wù)才會(huì)以結(jié)束運(yùn)行
  • bind方式的服務(wù),生命周期就要依賴綁定的組件

區(qū)別二:參數(shù)傳遞

  • started服務(wù)可以給啟動(dòng)的服務(wù)對象傳遞參數(shù),但無法獲取服務(wù)中方法的返回值
  • bind服務(wù)可以給啟動(dòng)的服務(wù)對象傳遞參數(shù),也可以通過綁定的業(yè)務(wù)對象獲取返回結(jié)果

實(shí)際開發(fā)中的技巧;

  • 第一次先使用started方式來啟動(dòng)一個(gè)服務(wù)
  • 之后可以使用bind的方式綁定服務(wù),從而可以直接調(diào)用業(yè)務(wù)方法獲取返回值

4、Service的生命周期:

一旦在項(xiàng)目的任何位置調(diào)用了Context的startService()方法,相應(yīng)的服務(wù)就會(huì)啟動(dòng)起來,并回調(diào)onstartCommand()方法。如果這個(gè)服務(wù)之前還沒有創(chuàng)建過,onCreate()方法會(huì)先于onstartCommand()方法執(zhí)行。服務(wù)啟動(dòng)過后,會(huì)一直保持運(yùn)行狀態(tài),直到stopService()或stopself()方法被調(diào)用。注意雖然每次調(diào)用一次startService()方法,onstartCommand()方法就會(huì)以執(zhí)行一次,但實(shí)際上每個(gè)服務(wù)都只會(huì)存在一個(gè)實(shí)例。所以不管你調(diào)用了多少次startService()方法,只需調(diào)用一次stopService()或stopself()方法,服務(wù)就會(huì)停止。

另外,還可以調(diào)用Context的bindService()來獲取一個(gè)服務(wù)的持久連接,這時(shí)就會(huì)回調(diào)服務(wù)中的onBind()方法。類似地,如果這個(gè)服務(wù)之前還沒有創(chuàng)建過,onCreate()方法會(huì)先于onBind()方法執(zhí)行。之后調(diào)用方可以獲取到onBind()方法里返回的IBinder對象的實(shí)例,這樣,就能自由地和服務(wù)進(jìn)行通信了。只要調(diào)用方和服務(wù)之間的連接沒有斷開,服務(wù)就會(huì)一直保持運(yùn)行狀態(tài)。

 

五、使用Bind Service完成IPC進(jìn)程間通信:(在同一個(gè)APP內(nèi)模擬)

既然是在在同一個(gè)APP內(nèi)模擬進(jìn)程間通信,其實(shí)就是完成進(jìn)程內(nèi)通信,但是原理都是一樣的嘛。

也就是說,要實(shí)現(xiàn):讓Activity與一個(gè)遠(yuǎn)程Service建立關(guān)聯(lián),這就要使用AIDL來進(jìn)行跨進(jìn)程通信了(IPC)。這里把Bind Service及其他的概念再重復(fù)一下:

1、Bind Service的介紹:

應(yīng)用程序組件(客戶端)通過調(diào)用bindService()方法能夠綁定服務(wù),然后Android系統(tǒng)會(huì)調(diào)用服務(wù)的onBind()回調(diào)方法,則個(gè)方法會(huì)返回一個(gè)跟服務(wù)器端交互的Binder對象。

這個(gè)綁定是異步的,bindService()方法立即返回,并且不給客戶端返回IBinder對象。要接收IBinder對象,客戶端必須創(chuàng)建一個(gè)ServiceConnection類的實(shí)例,并且把這個(gè)實(shí)例傳遞給bindService()方法。ServiceConnection對象包含了一個(gè)系統(tǒng)調(diào)用的傳遞IBinder對象的回調(diào)方法。

注意:只有Activity、Service、Content Provider能夠綁定服務(wù);BroadcastReceiver廣播接收器不能綁定服務(wù)。

2、在客戶端綁定一個(gè)服務(wù)的步驟:

(1)實(shí)現(xiàn)ServiceConnection抽象類。實(shí)現(xiàn)過程中,必須重寫一下兩個(gè)回調(diào)方法:

  • onServiceConnected()  和服務(wù)綁定成功后,系統(tǒng)會(huì)調(diào)用這個(gè)方法來發(fā)送由服務(wù)的onBind()方法返回的IBinder對象
  • onServiceDisconnected()   當(dāng)服務(wù)異常終止時(shí)會(huì)調(diào)用(如服務(wù)崩潰或被殺死時(shí))。注意,在客戶端解除綁定時(shí)不會(huì)調(diào)用該方法。

(2)調(diào)用bindService()方法來傳遞ServiceConnection類的實(shí)現(xiàn);

(3)當(dāng)系統(tǒng)調(diào)用你的onServiceConnected()回調(diào)方法時(shí),你就可以開始使用接口中定義的方法來調(diào)用服務(wù)了

(4)調(diào)用unbindService()方法斷開與服務(wù)的鏈接。

注:bindService()和unbindService()方法都是Context類中的方法。

3、IPC(Inter-Process Communication)進(jìn)程間通信機(jī)制:

在同一進(jìn)程中,各個(gè)組件進(jìn)行通信是十分方便的,普通的函數(shù)調(diào)用就可以解決;但是對于不同的進(jìn)程中的組件來說,要進(jìn)行通信,就需要用到Android的IPC機(jī)制了。

對應(yīng)用開發(fā)者來說,Android的IBinder/Binder框架實(shí)現(xiàn)了Android的IPC通信。當(dāng)然,IBinder/Binder框架也可以用來實(shí)現(xiàn)進(jìn)程內(nèi)通信(本地通信),也可以實(shí)現(xiàn)進(jìn)程間通信(遠(yuǎn)程通信)

從Android SDK中對IBinder/Binder的解釋可知,IBinder/Binder是Android遠(yuǎn)程對象的基本接口,它是Android用于提供高性能IPC通信而設(shè)計(jì)的一套輕量級遠(yuǎn)程調(diào)用機(jī)制的核心部分。該接口描述了與一個(gè)遠(yuǎn)程對象進(jìn)行通信的抽象協(xié)議。

4、AIDL(Android Interface Definition Language)Android接口定義語言:

AIDL它可以用于讓某個(gè)Service與多個(gè)應(yīng)用程序組件之間進(jìn)行跨進(jìn)程通信,從而可以實(shí)現(xiàn)多個(gè)應(yīng)用程序共享同一個(gè)Service的功能。

AIDL支持的類型:八大基本數(shù)據(jù)類型、String類型、CharSequence、List、Map、自定義。

來看下面的這張?jiān)韴D:

上圖中,如果A應(yīng)用程序想訪問B應(yīng)用程序中的業(yè)務(wù)對象,可以先讓A綁定B應(yīng)用中的Service,然后通過Service去訪問B中的業(yè)務(wù)對象。我們可以用AIDL來描述需要被別人調(diào)用的接口(即B中的業(yè)務(wù)對象)。

5、IPC(進(jìn)程間通訊)具體的步驟如下:

  • 使用AIDL定義業(yè)務(wù)接口,通過ADT工具來生成一個(gè)java類,此類實(shí)現(xiàn)了進(jìn)程間遠(yuǎn)程通訊的代理
  • 編寫自己的業(yè)務(wù)類(繼承生成的類中的Stub)來實(shí)現(xiàn)業(yè)務(wù)接口功能
  • 再通過綁定Service的方式來暴露此業(yè)務(wù)對象,給其它組件提供功能
  • 調(diào)用者組件通過bindService方法綁定服務(wù),從而獲取綁定成功后的遠(yuǎn)程業(yè)務(wù)對象或本地業(yè)務(wù)對象,然后就可以調(diào)用相關(guān)功能。
  • 注意:一般在使用完綁定服務(wù)后,需要解除綁定。

下面就通過代碼來實(shí)現(xiàn)。

6、讓Activity與一個(gè)遠(yuǎn)程Service建立關(guān)聯(lián)的步驟:(在同一個(gè)APP內(nèi)模擬)

新建一個(gè)全新的Android工程ServiceTest02。

(1)新建IPerson.aidl文件,代碼如下所示:

復(fù)制代碼
1 package com.example.servicetest02;  
2 interface IPerson{  
3     void setName(String name); 
4     void setSex(String sex);
5     void setAge(int age);
6     String getPerson();
7 }
復(fù)制代碼

這個(gè)文件里,添加我們需要的業(yè)務(wù)方法。第01行是包名。注意不要寫public等修飾符。(如果這個(gè)文件寫錯(cuò)了,程序會(huì)報(bào)錯(cuò),后面的Java文件也不會(huì)自從生成)

文件保存之后,ADT會(huì)在gen目錄下自動(dòng)生成一個(gè)對應(yīng)的Java文件,如下圖所示:

之后,程序運(yùn)行的時(shí)候使用的是這個(gè)Java文件,與aidl文件就沒有關(guān)系了。

我們來大致分析一下這個(gè)自動(dòng)生成的Java文件。完整版代碼如下:

IPerson.java

分析:

這個(gè)Java文件實(shí)際上是一個(gè)接口,同時(shí)生成了在aidl文件中定義的四個(gè)方法,并拋出了遠(yuǎn)程調(diào)用的異常。我們按住Ctrl鍵,點(diǎn)開上圖中藍(lán)框部分的IInterface,查看一下源代碼:

可以看到,在IInterface接口里,定義了一個(gè)接口IBinder,這是IPC機(jī)制的核心接口。

再回來看IPerson.java文件的第9行定義了這樣一個(gè)抽象類:

上圖中的Stub類可以比作存根。Stub類繼承了Binder類,同時(shí)實(shí)現(xiàn)了IPerson接口(沒有實(shí)現(xiàn)IPerson里的方法)。所以進(jìn)一步理解為:Stub既是IPerson里的內(nèi)部類,也是一個(gè)IPerson。

(2)新建PersonImpl類,繼承IPerson.Stub類,重寫父類里的方法。代碼如下:(也就是說,根據(jù)上面的java類,生成業(yè)務(wù)對象,即原理圖中B應(yīng)用的業(yè)務(wù)對象)

復(fù)制代碼
 1 package com.example.servicetest02;
 2 
 3 import android.os.RemoteException;
 4 
 5 public class PersonImpl extends IPerson.Stub{
 6     
 7     private String name;
 8     private String sex;
 9     private int age;
10 
11     @Override
12     public void setName(String name) throws RemoteException {
13         // TODO Auto-generated method stub
14         this.name = name;
15         
16     }
17 
18     @Override
19     public void setSex(String sex) throws RemoteException {
20         // TODO Auto-generated method stub
21         this.sex = sex;
22         
23     }
24     @Override
25     public void setAge(int age) throws RemoteException {
26         // TODO Auto-generated method stub
27         this.age = age;
28         
29     }
30 
31     @Override
32     public String getPerson() throws RemoteException {
33         // TODO Auto-generated method stub
34         return "name="+name+",sex="+sex+",age="+age;
35     }
36 }
復(fù)制代碼

(3)新建類MyService,代碼如下:

復(fù)制代碼
 1 package com.example.servicetest02;
 2 
 3 import android.app.Service;
 4 import android.content.Intent;
 5 import android.os.IBinder;
 6 import android.util.Log;
 7 
 8 public class MyService extends Service {  
 9       
10     public static final String TAG = "MyService";  
11   
12      PersonImpl mBinder = new PersonImpl();
13   
14     @Override  
15     public void onCreate() {  
16         super.onCreate();  
17         Log.d(TAG, "onCreate");  
18     }  
19   
20     @Override  
21     public int onStartCommand(Intent intent, int flags, int startId) {  
22         Log.d(TAG, "onStartCommand");  
23         return super.onStartCommand(intent, flags, startId);  
24     }  
25   
26     @Override  
27     public void onDestroy() {  
28         super.onDestroy();  
29         Log.d(TAG, "onDestroy");  
30     }  
31   
32     @Override  
33     public IBinder onBind(Intent intent) { 
34         Log.d("MyService", "onBind");
35         return mBinder;  //在這里返回新建的MyBinder類
36     }
37   
38 }  
復(fù)制代碼

核心代碼:12行和35行。

因?yàn)镻ersonImpl類繼承了IPerson.Stub,而Stub繼承了Binder,Binder又實(shí)現(xiàn)了IBinder。所以,PersonImpl可以理解為一個(gè)IBinder。于是可以在第35行返回PersonImpl的實(shí)例。

(4)在清單文件中添加權(quán)限:

<service android:name=".MyService"> </service>

 

現(xiàn)在,B應(yīng)用的業(yè)務(wù)對象和服務(wù)建立好了。B應(yīng)用的業(yè)務(wù)對象通過與Service綁定,讓Service把業(yè)務(wù)對象暴露給了A應(yīng)用或者其他的應(yīng)用。也就是說,Service最終并沒有實(shí)現(xiàn)業(yè)務(wù)功能。

如果要讓A應(yīng)用來訪問,該怎么做呢?

(5)在activity_main.xml中添加兩個(gè)按鈕button_bind_service和button_unbind_service,用于綁定遠(yuǎn)程服務(wù)和取消綁定。activity_main.xml的代碼如下:

復(fù)制代碼
<LinearLayout xmlns:android="http://schemas./apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >

    <Button
        android:id="@+id/button_bind_service"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="bind Service" />
    
    <Button
        android:id="@+id/button_unbind_service"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="unbind Service" />

</LinearLayout>
復(fù)制代碼

(6)MainActivity中的代碼,如下所示:

復(fù)制代碼
 1 package com.example.servicetest02;
 2 
 3 import android.app.Activity;
 4 import android.content.ComponentName;
 5 import android.content.Intent;
 6 import android.content.ServiceConnection;
 7 import android.os.Bundle;
 8 import android.os.IBinder;
 9 import android.os.RemoteException;
10 import android.util.Log;
11 import android.view.View;
12 import android.view.View.OnClickListener;
13 import android.widget.Button;
14 
15 public class MainActivity extends Activity implements OnClickListener {
16 
17     public static final String TAG = "MainActivity";
18     private Button button_bind_service;
19     private Button button_unbind_service;
20 
21     private IPerson person;
22 
23     boolean mBound = false; // 一開始,并沒有和Service綁定.這個(gè)參數(shù)是用來判斷綁定狀態(tài)
24 
25     // 匿名內(nèi)部類:服務(wù)連接對象
26     private ServiceConnection connection = new ServiceConnection() {
27 
28         // 當(dāng)服務(wù)異常終止時(shí)會(huì)調(diào)用。注意,解除綁定服務(wù)時(shí)不會(huì)調(diào)用
29         @Override
30         public void onServiceDisconnected(ComponentName name) {
31             mBound = false; // 服務(wù)異常終止時(shí),狀態(tài)為未綁定
32         }
33 
34         // 和服務(wù)綁定成功后,服務(wù)會(huì)回調(diào)該方法。在這個(gè)方法里調(diào)用的業(yè)務(wù)對象中的內(nèi)容
35         @Override
36         public void onServiceConnected(ComponentName name, IBinder service) {
37             Log.d(TAG, "onServiceConnected");
38             person = IPerson.Stub.asInterface(service); // 得到person對象
39             Log.d("person", "person對象的內(nèi)存地址是" + person); // 打印出person對象的內(nèi)存地址
40             try {
41                 person.setName("生命壹號(hào)");
42                 person.setAge(22);
43                 person.setSex("男");
44                 String p = person.getPerson();
45                 Log.d("person", "person的信息是" + p);
46             } catch (RemoteException e) {
47                 // TODO Auto-generated catch block
48                 e.printStackTrace();
49             }
50             mBound = true; //true說明是綁定狀態(tài)
51 
52         }
53     };
54 
55     @Override
56     protected void onCreate(Bundle savedInstanceState) {
57         super.onCreate(savedInstanceState);
58         setContentView(R.layout.activity_main);
59         button_bind_service = (Button) findViewById(R.id.button_bind_service);
60         button_unbind_service = (Button) findViewById(R.id.button_unbind_service);
61         button_bind_service.setOnClickListener(this);
62         button_unbind_service.setOnClickListener(this);
63 
64     }
65 
66     @Override
67     public void onClick(View v) {
68         switch (v.getId()) {
69         case R.id.button_bind_service:
70             Intent bindIntent = new Intent(this, MyService.class);
71             bindService(bindIntent, connection, BIND_AUTO_CREATE);
72             break;
73         case R.id.button_unbind_service:
74             // 如果和Service是綁定的狀態(tài),就解除綁定。
75             if (mBound) {
76                 unbindService(connection);
77                 mBound = false;
78             }
79             break;
80 
81         default:
82             break;
83         }
84     }
85 
86 }
復(fù)制代碼

核心代碼是第38行:可以看到,這里首先使用了MyAIDLService.Stub.asInterface()方法將傳入的IBinder對象傳換成了IPerson對象,接下來就可以調(diào)用在IPerson.aidl文件中定義的所有接口了(41至44行)。調(diào)用之后,我們在后臺(tái)打印輸出。

運(yùn)行程序,點(diǎn)擊按鈕,效果如下:

由此可見,我們確實(shí)已經(jīng)成功實(shí)現(xiàn)跨進(jìn)程通信了,在一個(gè)進(jìn)程中訪問到了另外一個(gè)進(jìn)程中的方法。 注意,這個(gè)Service是運(yùn)行在主線程當(dāng)中的,畢竟我們是在本地模擬的嘛。

另外注意藍(lán)色箭頭處,可以看出,這個(gè)person其實(shí)就是personImpl,因?yàn)槭窃诒镜卣{(diào)用。所以說,目前的跨進(jìn)程通信其實(shí)并沒有什么實(shí)質(zhì)上的作用,因?yàn)檫@只是在一個(gè)Activity里調(diào)用了同一個(gè)應(yīng)用程序的Service里的方法。而跨進(jìn)程通信的真正意義是為了讓一個(gè)應(yīng)用程序去訪問另一個(gè)應(yīng)用程序中的Service,以實(shí)現(xiàn)共享Service的功能。那么下面我們自然要學(xué)習(xí)一下,如何才能在其它的應(yīng)用程序中調(diào)用到MyService里的方法。

繼續(xù)回顧第(1)步中自動(dòng)生成的IPerson.java文件,截取第22至32行,摘抄如下:

復(fù)制代碼
 1         public static com.example.servicetest02.IPerson asInterface(
 2                 android.os.IBinder obj) {
 3             if ((obj == null)) {
 4                 return null;
 5             }
 6             android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
 7             if (((iin != null) && (iin instanceof com.example.servicetest.IPerson))) {
 8                 return ((com.example.servicetest.IPerson) iin);
 9             }
10             return new com.example.servicetest.IPerson.Stub.Proxy(obj);
11         }
復(fù)制代碼

代碼解釋:

上方第06行的iin代表的是,查詢本地對象返回的結(jié)果。

07行:如果iin不為空,并且iin為IPerson,那就將iin強(qiáng)制轉(zhuǎn)換為IPerson(08行)。很顯然,這里是進(jìn)程內(nèi)通信(本地通信)要用到的。也就是本段中的例子。

10行:代表的是進(jìn)程間通信(遠(yuǎn)程通信),此時(shí)第07行的if語句不成立,于是返回第10行的代理對象Proxy(obj)。

 

實(shí)現(xiàn)進(jìn)程間通信的兩個(gè)辦法:

如果要實(shí)現(xiàn)進(jìn)程間通信,就必須讓MainActivity和Service相互獨(dú)立。有兩個(gè)辦法:

(1)辦法一:將本地的Service設(shè)置為遠(yuǎn)程。只需要在清單文件中注冊Service的時(shí)候?qū)⑺腶ndroid:process屬性指定成:remote就可以了,代碼如下所示:

<service android:name=".MyService" android:process=":remote"> </service>

后臺(tái)打印日志如下:

上圖的紅框部分顯示,Service和Activity并非在同一個(gè)線程內(nèi),連包名都不一樣。而IPeron也并非是本地的IPeron。

如果將這種方法應(yīng)用到上面的第四段中,情形是這樣的:

點(diǎn)擊button1_start_service,服務(wù)啟動(dòng)。既然已經(jīng)將Service的android:process屬性指定成:remote,此時(shí)Service和Activity不在同一個(gè)線程內(nèi),那么即使在Service的onStartCommand()方法中執(zhí)行耗時(shí)操作而不重新開啟子線程,程序也不會(huì)阻塞。

但是,如果點(diǎn)擊button3_bind_service按鈕綁定服務(wù),程序會(huì)崩潰的。這是因?yàn)?,目前MyService已經(jīng)是一個(gè)遠(yuǎn)程Service了,Activity和Service運(yùn)行在兩個(gè)不同的進(jìn)程當(dāng)中,這時(shí)就不能再使用傳統(tǒng)的建立關(guān)聯(lián)的方式,程序也就崩潰了。

現(xiàn)在我們總結(jié)一下:

第四段中使用的是傳統(tǒng)的方式和Service建立關(guān)聯(lián),默認(rèn)MainActivity和MyService在同一個(gè)線程內(nèi),如果將Service的android:process屬性指定成:remote,此時(shí)MainActivity和MyService將在不同的線程內(nèi),但是無法綁定服務(wù)。

本段中(第五段)使用的是IPC跨進(jìn)程通信,MainActivity和MyService在不同的進(jìn)程中,可以綁定遠(yuǎn)程服務(wù)。

(2)辦法二:新建另外一個(gè)工程,真正實(shí)現(xiàn)遠(yuǎn)程通信。這就是我們下一段(第六段)要講的內(nèi)容。

我們還是先回過頭來再鞏固一下本段中AIDL的知識(shí)吧。

 

7、AIDL支持的自定義數(shù)據(jù)類型:

我們在本段中的第4小結(jié)講到,AIDL支持的類型:八大基本數(shù)據(jù)類型、String類型、CharSequence、List、Map、自定義,那我們就來詳細(xì)說下這個(gè)自定義數(shù)據(jù)類型。

由于這是在不同的進(jìn)程之間傳遞數(shù)據(jù),Android對這類數(shù)據(jù)的格式支持是非常有限的,基本上只能傳遞Java的基本數(shù)據(jù)類型、字符串、List或Map等。那么如果我想傳遞一個(gè)自定義的類該怎么辦呢?這就必須要讓這個(gè)類去實(shí)現(xiàn)Parcelable接口,并且要給這個(gè)類也定義一個(gè)同名的AIDL文件進(jìn)行聲明。這部分內(nèi)容并不復(fù)雜,而且和Service關(guān)系不大。具體操作如下:

重新建一個(gè)工程ServiceTest03。步驟如下:

(1)新建一個(gè)Student類去實(shí)現(xiàn)Parcelable接口。Student類是作為傳遞的自定義類:

復(fù)制代碼
 1 package com.example.servicetest;
 2 
 3 import android.os.Parcel;
 4 import android.os.Parcelable;
 5 
 6 public class Student implements Parcelable {
 7     private String name;
 8     private String sex;
 9 
10     public Student() {
11         super();
12     }
13 
14     public String getName() {
15         return name;
16     }
17 
18     public void setName(String name) {
19         this.name = name;
20     }
21 
22     public String getSex() {
23         return sex;
24     }
25 
26     public void setSex(String sex) {
27         this.sex = sex;
28     }
29 
30     @Override
31     public int describeContents() {
32         // TODO Auto-generated method stub
33         return 0;
34     }
35 
36     // 重寫父類的方法:將需要傳送的數(shù)據(jù)放進(jìn)來
37     @Override
38     public void writeToParcel(Parcel dest, int flags) {
39         // TODO Auto-generated method stub
40         dest.writeString(name);
41         dest.writeString(name);
42     }
43 
44     public static final Parcelable.Creator<Student> CREATOR = new Parcelable.Creator<Student>() {
45         public Student createFromParcel(Parcel in) {
46             Student s = new Student();
47             s.setName(in.readString());
48             s.setSex(in.readString());
49             return s;
50         }
51 
52         public Student[] newArray(int size) {
53             return new Student[size];
54         }
55     };
56 
57 }
復(fù)制代碼

我們在這個(gè)類中放入了name和age這兩個(gè)參數(shù),并實(shí)現(xiàn)了Parcelable接口。注意第44行至55行代碼的修改。

接著,新建一個(gè)和類同名的aidl文件,即新建Student.aidl,代碼如下:

parcelable Student;

 

注意這個(gè)parcelable的第一個(gè)字母是小寫。

繼續(xù),新建IStudent.aidl,作為需要遠(yuǎn)程傳遞的業(yè)務(wù)方法。代碼如下:

復(fù)制代碼
1 package com.example.servicetest03;
2 
3 import com.example.servicetest03.Student;
4 interface IStudent{
5     void setStudent(String name,String sex);
6     Student getStudent();
7 }
復(fù)制代碼

核心代碼是第03行,雖然Student類文件和本文件是在同一個(gè)包下,但是依然要導(dǎo)包,否則將無法識(shí)別Student類。然后在第06行代碼中,就可以把Student這個(gè)類傳遞出去了。注意了,第06行返回的是Student類型,這不就是ADIL所支持的自定義類型嘛。

文件結(jié)構(gòu)如下:

綜上所述,傳遞自定義類,有三個(gè)步驟:

  • 自定義類實(shí)現(xiàn)Parcelable接口
  • 新建同名的aidl文件,聲明這個(gè)Parcelable類型的自定義類
  • 在需要遠(yuǎn)程傳遞的aidl文件中導(dǎo)包,引用進(jìn)來

那么接下來的步驟就和本段中的第6小節(jié)一樣了,就不再多解釋了,這里只貼代碼:

(2)新建StudentImpl類,繼承IStudent.Stub類。代碼如下:(也就是說,根據(jù)步驟(1)中的java類,生成業(yè)務(wù)對象,即原理圖中B應(yīng)用的業(yè)務(wù)對象)

復(fù)制代碼
 1 package com.example.servicetest03;
 2 
 3 import android.os.RemoteException;
 4 
 5 //業(yè)務(wù)對象的實(shí)現(xiàn)
 6 public class StudentImpl extends IStudent.Stub{
 7 
 8     private Student student;
 9     
10     public StudentImpl(){
11         student = new Student();
12     }
13     @Override
14     public void setStudent(String name, String sex) throws RemoteException {
15         // TODO Auto-generated method stub
16         student.setName(name);
17         student.setSex(sex);
18         
19     }
20 
21     @Override
22     public Student getStudent() throws RemoteException {
23         // TODO Auto-generated method stub
24         return student;
25     }
26 
27 }
復(fù)制代碼

(3)新建Service類,代碼如下:

復(fù)制代碼
 1 package com.example.servicetest03;
 2 
 3 import android.app.Service;
 4 import android.content.Intent;
 5 import android.os.IBinder;
 6 import android.util.Log;
 7 
 8 public class MyService extends Service {  
 9       
10     public static final String TAG = "MyService";  
11   
12     private  StudentImpl studentImpl ;
13   
14     @Override  
15     public void onCreate() {  
16         super.onCreate();  
17         Log.d(TAG, "onCreate");  
18     }  
19   
20     @Override  
21     public int onStartCommand(Intent intent, int flags, int startId) {  
22         Log.d(TAG, "onStartCommand");  
23         return super.onStartCommand(intent, flags, startId);  
24     }  
25   
26     @Override  
27     public void onDestroy() {  
28         super.onDestroy();  
29         Log.d(TAG, "onDestroy");  
30     }  
31   
32     @Override  
33     public IBinder onBind(Intent intent) { 
34         Log.d("MyService", "onBind");
35         studentImpl = new StudentImpl();
36         return studentImpl;  //在這里返回新建的MyBinder類
37     }
38   
39 }
復(fù)制代碼

核心代碼:12行、35行、36行。

(4)在清單文件中添加權(quán)限:

<service android:name=".MyService"> </service> 

現(xiàn)在,B應(yīng)用的業(yè)務(wù)對象和服務(wù)建立好了。B應(yīng)用的業(yè)務(wù)對象通過與Service綁定,讓Service把業(yè)務(wù)對象暴露給了A應(yīng)用或者其他的應(yīng)用。也就是說,Service最終并沒有實(shí)現(xiàn)業(yè)務(wù)功能。

如果要讓A應(yīng)用來訪問,該怎么做呢?

(5)在activity_main.xml中添加兩個(gè)按鈕button1_setStudent和button2_getStudent。activity_main.xml的代碼如下:

復(fù)制代碼
<LinearLayout xmlns:android="http://schemas./apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >
    <Button
        android:id="@+id/button1_setStudent"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="調(diào)用setStudent方法" />
    
    <Button
        android:id="@+id/button2_getStudent"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="調(diào)用getStudent方法" />
</LinearLayout>
復(fù)制代碼

注:布局文件里不再添加綁定服務(wù)和取消綁定的按鈕,我們稍后在Activity的生命周期里完成這件事。

(6)MainActivity中的代碼,如下所示:

復(fù)制代碼
 1 package com.example.servicetest03;
 2 
 3 import android.app.Activity;
 4 import android.content.ComponentName;
 5 import android.content.Intent;
 6 import android.content.ServiceConnection;
 7 import android.os.Bundle;
 8 import android.os.IBinder;
 9 import android.os.RemoteException;
10 import android.view.View;
11 import android.view.View.OnClickListener;
12 import android.widget.Button;
13 import android.widget.Toast;
14 
15 
16 public class MainActivity extends Activity implements OnClickListener {
17     public static final String TAG = "MainActivity";
18     private Button button1_setStudent;
19     private Button button2_getStudent;
20     private IStudent studentImpl;
21     boolean mBound = false; // 一開始,并沒有和Service綁定.這個(gè)參數(shù)是用來判斷綁定狀態(tài)
22 
23     @Override
24     protected void onCreate(Bundle savedInstanceState) {
25         super.onCreate(savedInstanceState);
26         setContentView(R.layout.activity_main);
27         button1_setStudent = (Button) findViewById(R.id.button1_setStudent);
28         button2_getStudent = (Button) findViewById(R.id.button2_getStudent);
29         button1_setStudent.setOnClickListener(this);
30         button2_getStudent.setOnClickListener(this);
31     }    
32 
33     
34     // 匿名內(nèi)部類:服務(wù)連接對象
35     private ServiceConnection connection = new ServiceConnection() {
36         // 當(dāng)服務(wù)異常終止時(shí)會(huì)調(diào)用。注意,解除綁定服務(wù)時(shí)不會(huì)調(diào)用
37         @Override
38         public void onServiceDisconnected(ComponentName name) {
39             mBound = false; // 服務(wù)異常終止時(shí),狀態(tài)為未綁定
40         }
41         // 和服務(wù)綁定成功后,服務(wù)會(huì)回調(diào)該方法。在這個(gè)方法里調(diào)用的業(yè)務(wù)對象中的內(nèi)容
42         @Override
43         public void onServiceConnected(ComponentName name, IBinder service) {
44             studentImpl = IStudent.Stub.asInterface(service); // 得到person對象
45             mBound = true; //true說明是綁定狀態(tài)
46         }
47     };
48     
49     //程序啟動(dòng)時(shí),開始綁定服務(wù)
50     @Override
51     protected void onStart() {
52         super.onStart();
53         Intent bindIntent = new Intent(this, MyService.class);
54         bindService(bindIntent, connection, BIND_AUTO_CREATE);
55     }
56     
57     //程序退出時(shí),取消綁定服務(wù)
58     @Override
59     protected void onDestroy() {
60         super.onDestroy();
61         // 如果和Service是綁定的狀態(tài),就解除綁定。
62         if (mBound) {
63             unbindService(connection);
64             mBound = false;
65         }
66     }
67     
68     
69     @Override
70     public void onClick(View v) {
71         switch (v.getId()) {
72         //點(diǎn)擊button1_setStudent按鈕,設(shè)置Student的值
73         case R.id.button1_setStudent:
74             try {
75                 studentImpl.setStudent("生命壹號(hào)", "男");
76                 Toast.makeText(this, "設(shè)置成功", Toast.LENGTH_SHORT).show();
77             } catch (RemoteException e) {
78                 // TODO Auto-generated catch block
79                 e.printStackTrace();
80             }
81             break;
82         //點(diǎn)擊button2_getStudent按鈕,獲取Student的值
83         case R.id.button2_getStudent:
84             Student s;
85             try {
86                 s = studentImpl.getStudent();
87                 Toast.makeText(this, "name="+s.getName()+",sex="+s.getSex(), Toast.LENGTH_SHORT).show();
88             } catch (RemoteException e) {
89                 // TODO Auto-generated catch block
90                 e.printStackTrace();
91             }
92             break;
93 
94         default:
95             break;
96         }
97     }
98 }
復(fù)制代碼

核心代碼是第44行。

我們在第75行、86至87行使用到了IStudent中的業(yè)務(wù)方法。

運(yùn)行程序,點(diǎn)擊第一個(gè)按鈕,然后點(diǎn)擊第二個(gè)按鈕,效果如下:

這樣,Acitivity就成功調(diào)用了遠(yuǎn)程Service的自定義類。

 

六、使用Bind Service完成IPC進(jìn)程間通信:(兩個(gè)APP之間)

上一段中的跨進(jìn)程通信其實(shí)并沒有什么實(shí)質(zhì)上的作用,因?yàn)檫@只是在一個(gè)Activity里調(diào)用了同一個(gè)應(yīng)用程序的Service里的方法。而跨進(jìn)程通信的真正意義是為了讓一個(gè)應(yīng)用程序去訪問另一個(gè)應(yīng)用程序中的Service,以實(shí)現(xiàn)共享Service的功能。那么下面我們自然要學(xué)習(xí)一下,如何才能在其它的應(yīng)用程序中調(diào)用到MyService里的方法。

在第四段中我們已經(jīng)知道,如果想要讓Activity與Service之間建立關(guān)聯(lián),需要調(diào)用bindService()方法,并將Intent作為參數(shù)傳遞進(jìn)去,在Intent里指定好要綁定的Service,核心代碼如下:

Intent bindIntent = new Intent(this, MyService.class);
bindService(bindIntent, connection, BIND_AUTO_CREATE);

這里在構(gòu)建Intent的時(shí)候是使用MyService.class來指定要綁定哪一個(gè)Service的,但是在另一個(gè)應(yīng)用程序中去綁定Service的時(shí)候并沒有MyService這個(gè)類,這時(shí)就必須使用到隱式Intent了。

具體步驟如下:

我們在第六段中的MyService02這個(gè)工程文件中進(jìn)行修改。代碼實(shí)現(xiàn)如下:

(1)現(xiàn)在修改AndroidManifest.xml中的代碼,給MyService加上一個(gè)action,如下所示:

1         <service android:name=".MyService" >
2             <intent-filter>
3                 <action android:name="com.example.servicetest02.MyService" />
4             </intent-filter>
5         </service> 

這就說明,MyService可以響應(yīng)帶有com.example.servicetest02.MyService這個(gè)action的Intent。

現(xiàn)在重新運(yùn)行一下MyService02這個(gè)程序,這樣就把遠(yuǎn)程Service端的工作全部完成了。

然后新建一個(gè)新的工程,起名為ClientTest,我們就嘗試在這個(gè)程序中遠(yuǎn)程調(diào)用MyService中的方法。

ClientTest中的Activity如果想要和MyService建立關(guān)聯(lián)其實(shí)也不難,首先需要將IPerson.aidl文件從ServiceTest02項(xiàng)目中拷貝過來,注意要將原有的包路徑一起拷貝過來,完成后項(xiàng)目的結(jié)構(gòu)如下圖所示:

(2)在activity_main.xml中添加兩個(gè)按鈕button_bind_service和button_unbind_service,用于綁定遠(yuǎn)程服務(wù)和取消綁定。activity_main.xml的代碼如下:

復(fù)制代碼
<LinearLayout xmlns:android="http://schemas./apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >
    <Button
        android:id="@+id/button_bind_service"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="bind Service" />
    
    <Button
        android:id="@+id/button_unbind_service"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="unbind Service" />
</LinearLayout>
復(fù)制代碼

(3)在MainActivity中加入和遠(yuǎn)程的MyService建立關(guān)聯(lián)的代碼,如下所示:

復(fù)制代碼
 1 package com.example.clienttest;
 2 
 3 import android.app.Activity;
 4 import android.content.ComponentName;
 5 import android.content.Intent;
 6 import android.content.ServiceConnection;
 7 import android.os.Bundle;
 8 import android.os.IBinder;
 9 import android.os.RemoteException;
10 import android.util.Log;
11 import android.view.View;
12 import android.view.View.OnClickListener;
13 import android.widget.Button;
14 
15 import com.example.servicetest02.IPerson;
16 
17 
18 public class MainActivity extends Activity implements OnClickListener {
19     public static final String TAG = "MainActivity";
20     private Button button_bind_service;
21     private Button button_unbind_service;
22     private IPerson person;
23     boolean mBound = false; // 一開始,并沒有和Service綁定.這個(gè)參數(shù)是用來判斷綁定狀態(tài)
24     // 匿名內(nèi)部類:服務(wù)連接對象
25     private ServiceConnection connection = new ServiceConnection() {
26         // 當(dāng)服務(wù)異常終止時(shí)會(huì)調(diào)用。注意,解除綁定服務(wù)時(shí)不會(huì)調(diào)用
27         @Override
28         public void onServiceDisconnected(ComponentName name) {
29             mBound = false; // 服務(wù)異常終止時(shí),狀態(tài)為未綁定
30         }
31         // 和服務(wù)綁定成功后,服務(wù)會(huì)回調(diào)該方法。在這個(gè)方法里調(diào)用的業(yè)務(wù)對象中的內(nèi)容
32         @Override
33         public void onServiceConnected(ComponentName name, IBinder service) {
34             Log.d(TAG, "onServiceConnected");
35             person = IPerson.Stub.asInterface(service); // 得到person對象
36             Log.d("person", "person對象的內(nèi)存地址是" + person); // 打印出person對象的內(nèi)存地址
37             try {
38                 person.setName("生命壹號(hào)");
39                 person.setAge(22);
40                 person.setSex("男");
41                 String p = person.getPerson();
42                 Log.d("person", "person的信息是" + p);
43             } catch (RemoteException e) {
44                 // TODO Auto-generated catch block
45                 e.printStackTrace();
46             }
47             mBound = true; //true說明是綁定狀態(tài)
48         }
49     };
50     @Override
51     protected void onCreate(Bundle savedInstanceState) {
52         super.onCreate(savedInstanceState);
53         setContentView(R.layout.activity_main);
54         button_bind_service = (Button) findViewById(R.id.button_bind_service);
55         button_unbind_service = (Button) findViewById(R.id.button_unbind_service);
56         button_bind_service.setOnClickListener(this);
57         button_unbind_service.setOnClickListener(this);
58     }
59     @Override
60     public void onClick(View v) {
61         switch (v.getId()) {
62         case R.id.button_bind_service:
63             Intent bindIntent = new Intent("com.example.servicetest02.MyService");
64             bindService(bindIntent, connection, BIND_AUTO_CREATE);
65             break;
66         case R.id.button_unbind_service:
67             // 如果和Service是綁定的狀態(tài),就解除綁定。
68             if (mBound) {
69                 unbindService(connection);
70                 mBound = false;
71             }
72             break;
73         default:
74             break;
75         }
76     }
77 }
復(fù)制代碼

這部分代碼大家一定會(huì)非常眼熟吧?沒錯(cuò),這和在ServiceTest02的MainActivity中的代碼幾乎是完全相同的,只是在讓Activity和Service建立關(guān)聯(lián)的時(shí)候我們使用了隱式Intent,將Intent的action指定成了com.example.servicetest02.MyAIDLService(63行)。

在當(dāng)前Activity和MyService建立關(guān)聯(lián)之后,我們?nèi)匀皇钦{(diào)用了setName、setAge、setSex、getPerson()這幾個(gè)方法,遠(yuǎn)程的MyService會(huì)對傳入的參數(shù)進(jìn)行處理并返回結(jié)果,然后將結(jié)果打印出來。

這樣的話,ClientTest中的代碼也就全部完成了,現(xiàn)在運(yùn)行一下這個(gè)項(xiàng)目,然后點(diǎn)擊Bind Service按鈕,此時(shí)就會(huì)去和遠(yuǎn)程的MyService建立關(guān)聯(lián),觀察LogCat中的打印信息如下所示:

注意紅框部分,包名是不一樣的哦。由此可見,我們確實(shí)已經(jīng)成功實(shí)現(xiàn)跨進(jìn)程通信了,在一個(gè)程序中訪問到了另外一個(gè)程序中的方法。

 

七、Messenger的使用:

public final class Messenger extends Object implements Parcelable

 

介紹:Messenger實(shí)現(xiàn)了IPC通信,底層也是使用了AIDL方式。和AIDL方式不同的是,Messenger方式是利用Handler形式處理,因此,它是線程安全的,這也表示它不支持并發(fā)處理;而AIDL方式是非線程安全的,支持并發(fā)處理,因此,我們使用AIDL方式時(shí),需要保證代碼的線程安全。大部分情況下,應(yīng)用中不需要并發(fā)處理,因此我們通常只需要使用Messenger方式。

過程:在進(jìn)程A中創(chuàng)建一個(gè)Message,將這個(gè)Message對象通過Messenger.send(message)方法傳遞到進(jìn)程B的消息隊(duì)列里,然后交給Handler去處理。

當(dāng)然,Message對象本身是無法被傳遞到進(jìn)程B的,send(message)方法會(huì)使用一個(gè)Pacel對象對Message對象編集,再將Pacel對象傳遞到進(jìn)程B中,然后解編集,得到一個(gè)和進(jìn)程A中的Message對象內(nèi)容一樣的對象。

關(guān)于多線程的Handler機(jī)制,如果不清楚的話,可以參考本人另外一篇博客:

使用Messenger來實(shí)現(xiàn)IPC的步驟:

  • 在Service中創(chuàng)建一個(gè)Messenger對象并綁定一個(gè)Handler
  • 在onBind方法中通過Messenger.getIbinder方法返回一個(gè)IBinder對象。
  • 在調(diào)用的組件中的ServiceConnection的onServiceConnected事件方法中根據(jù)iBinder對象來創(chuàng)建一個(gè)Messenger對象。這樣,兩個(gè)Messenger就同時(shí)綁定到一個(gè)IBinder上,從而實(shí)現(xiàn)通信。
  • 在調(diào)用的組件中使用Messenger的send方法來發(fā)送消息到Service的Messenger對象中。

那我們通過代碼來實(shí)現(xiàn)以下吧。新建一個(gè)全新的工程MessengerTest。步驟如下:

(1)新建一個(gè)MessengerService類,繼承Service類,代碼如下:

復(fù)制代碼
 1 package com.example.messengertest;
 2 
 3 import android.app.Service;
 4 import android.content.Intent;
 5 import android.os.Handler;
 6 import android.os.IBinder;
 7 import android.os.Message;
 8 import android.os.Messenger;
 9 import android.util.Log;
10 import android.widget.Toast;
11 
12 public class MessengerService extends Service{
13 
14     public static final int MSG_SAY_HELLO = 1;
15     
16     private Handler handler = new Handler() {
17         public void handleMessage(Message msg) {
18             switch (msg.what) {
19             case MSG_SAY_HELLO:
20                 //在服務(wù)里定義業(yè)務(wù)方法
21                 Toast.makeText(MessengerService.this, "hello", Toast.LENGTH_SHORT).show();
22                 Log.d("MessengerService", "MessengerService thread id is " + Thread.currentThread().getId()); //打印MessengerService的線程id
23                 break;
24             default:
25                 break;
26             }
27         }
28     };
29     
30     
31     private Messenger messenger = new Messenger(handler);
32     
33     
34     @Override
35     public IBinder onBind(Intent intent) {
36         // TODO Auto-generated method stub
37         return messenger.getBinder();
38     }
39 }
復(fù)制代碼

核心代碼:16至28行、31行、37行。

37行中,將IBinder類型返回之后,就已經(jīng)和Messenger進(jìn)行綁定了。

(2)在清單文件中注冊服務(wù):(和Activity標(biāo)簽并列)

<service android:name=".MessengerService"> </service>

 

(3)修改activity_main.xml代碼,添加一個(gè)按鈕,用于發(fā)送Message,代碼如下:

復(fù)制代碼
<LinearLayout xmlns:android="http://schemas./apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >
    
    <Button
        android:id="@+id/button_messenger"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="使用messenger" />
</LinearLayout>
復(fù)制代碼

(4)在MainActivity作為程序的主Activity,在里面加入發(fā)送Message消息和建立Service連接的邏輯,代碼如下:

復(fù)制代碼
 1 package com.example.messengertest;
 2 
 3 import android.app.Activity;
 4 import android.content.ComponentName;
 5 import android.content.Intent;
 6 import android.content.ServiceConnection;
 7 import android.os.Bundle;
 8 import android.os.IBinder;
 9 import android.os.Message;
10 import android.os.Messenger;
11 import android.os.RemoteException;
12 import android.util.Log;
13 import android.view.View;
14 import android.view.View.OnClickListener;
15 import android.widget.Button;
16 
17 public class MainActivity extends Activity implements OnClickListener {
18 
19     private Button button_messenger;
20 
21     private Messenger messenger;
22     boolean mBound = false;
23 
24     @Override
25     protected void onCreate(Bundle savedInstanceState) {
26         super.onCreate(savedInstanceState);
27         setContentView(R.layout.activity_main);
28         button_messenger = (Button) findViewById(R.id.button_messenger);
29         button_messenger.setOnClickListener(this);
30     }
31 
32     @Override
33     protected void onStart() {
34         // TODO Auto-generated method stub
35         super.onStart();
36         Intent bindIntent = new Intent(this, MessengerService.class);
37         bindService(bindIntent, connection, BIND_AUTO_CREATE);
38     }
39 
40     @Override
41     protected void onDestroy() {
42         // TODO Auto-generated method stub
43         super.onDestroy();
44         if (mBound) {
45             unbindService(connection);
46             mBound = false;
47         }
48     }
49 
50     private ServiceConnection connection = new ServiceConnection() {
51         @Override
52         public void onServiceConnected(ComponentName name, IBinder service) {
53             // TODO Auto-generated method stub
54             messenger = new Messenger(service);            
55             
56             mBound = true;
57         }
58 
59         @Override
60         public void onServiceDisconnected(ComponentName name) {
61             // TODO Auto-generated method stub
62             mBound = false;
63 
64         }
65 
66     };
67 
68     //點(diǎn)擊按鈕,發(fā)送Message消息,在MessengerService里接收,從而執(zhí)行Service里面的方法
69     @Override
70     public void onClick(View v) {
71         switch (v.getId()) {
72         case R.id.button_messenger:
73             Message  msg  = Message.obtain();
74             msg.what = MessengerService.MSG_SAY_HELLO;
75             try {
76                 messenger.send(msg);
77                   Log.d("MainActivity", "MainActivity thread id is " + Thread.currentThread().getId()); //打印MainActivity的線程id
78             } catch (RemoteException e) {
79                 // TODO Auto-generated catch block
80                 e.printStackTrace();
81             }
82             break;
83 
84         default:
85             break;
86         }
87     }
88 
89 }
復(fù)制代碼

我們在上一步的MessengerService類新建了一個(gè)Messenger,在這里又新建另一個(gè)Messenger(54行)。兩個(gè)Messenger綁定了同一個(gè)服務(wù),Activity就可以和Service實(shí)現(xiàn)通訊了。

點(diǎn)擊按鈕(72行),發(fā)送消息,讓MessengerService類里的Messenger去接收,然后交給handler去處理,從而執(zhí)行handleMessage()里方法,也就是說,執(zhí)行了Service里面的方法。

運(yùn)行程序,點(diǎn)擊按鈕,顯示效果如下:

后臺(tái)打印日志如下:

說明這個(gè)MessengerService和普通Service一樣,也是運(yùn)行在主線程當(dāng)中的。

當(dāng)然了,這里的Messenger的實(shí)現(xiàn)比較簡單,如果以后需要實(shí)現(xiàn)復(fù)雜IPC訪問,還是需要自己去寫AIDL才更加直接有效,可控性強(qiáng)。

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多