|
文章來源:http://www.cnblogs.com/smyhvae/p/4070518.html
【前言】 花了周末兩天的時(shí)間,整理了一下作為Android四大組件之一的Service的基礎(chǔ)知識(shí),通過這篇文章,應(yīng)該可以明白:對Service的理解、在什么地方使用、怎么使用、要注意哪些問題等。 【本文主要內(nèi)容】 一、Service的基本概念(四大組件之一)
三、IntentService
四、使用Bind Service完成Service和Activity之間的通信
五、使用Bind Service完成IPC進(jìn)程間通信:(在同一個(gè)APP內(nèi)模擬)
【正文】 一、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: 核心步驟如下:
新建一個(gè)Android項(xiàng)目ServiceTest,具體步驟如下: (1)新建一個(gè)MyService類,繼承自Service,并重寫父類的onCreate()、onStartCommand()和onDestroy()方法,代碼如下: 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 } 可以看到,我們只是在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代碼,如下: <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> 我們在布局文件中加入了兩個(gè)按鈕,一個(gè)用于啟動(dòng)Service,一個(gè)用于停止Service。 (4)在MainActivity作為程序的主Activity,在里面加入啟動(dòng)Service和停止Service的邏輯,代碼如下: 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 } 核心代碼: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ù)就停止掉了:
需要注意的是:
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
三、IntentService 1、IntentService的引入: 我們在第一段中就已經(jīng)說了,服務(wù)中的代碼默認(rèn)運(yùn)行在主線程中,如果直接在服務(wù)里執(zhí)行一些耗時(shí)操作,容易造成ANR(Application Not Responding)異常,所以就需要用到多線程的知識(shí)了。 因此一個(gè)比較標(biāo)準(zhǔn)的服務(wù)可以這樣寫: 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 } 核心代碼: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()方法,代碼如下: 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 } 這里首先要提供一個(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中的代碼,如下所示: 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 } 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的完整代碼如下: <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> (4)接下來再修改MainActivity中的代碼,讓MainActivity和MyService之間建立關(guān)聯(lián),代碼如下所示: 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 } 可以看到,這里我們首先創(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)容) 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 } 添加的代碼是第21行、29行、72行至74行。 這樣的話,連續(xù)點(diǎn)擊Unbind Service按鈕,就不會(huì)使程序出現(xiàn)異常。 3、started服務(wù)與bind服務(wù)的區(qū)別: 區(qū)別一:生命周期
區(qū)別二:參數(shù)傳遞
實(shí)際開發(fā)中的技巧;
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)方法:
(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)程間通訊)具體的步驟如下:
下面就通過代碼來實(shí)現(xiàn)。 6、讓Activity與一個(gè)遠(yuǎn)程Service建立關(guān)聯(lián)的步驟:(在同一個(gè)APP內(nèi)模擬) 新建一個(gè)全新的Android工程ServiceTest02。 (1)新建IPerson.aidl文件,代碼如下所示: 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 } 這個(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ù)對象) 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 } (3)新建類MyService,代碼如下: 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 } 核心代碼: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的代碼如下: <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> (6)MainActivity中的代碼,如下所示: 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 } 核心代碼是第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行,摘抄如下: 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 } 代碼解釋: 上方第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類是作為傳遞的自定義類: 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 } 我們在這個(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ù)方法。代碼如下: 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 } 核心代碼是第03行,雖然Student類文件和本文件是在同一個(gè)包下,但是依然要導(dǎo)包,否則將無法識(shí)別Student類。然后在第06行代碼中,就可以把Student這個(gè)類傳遞出去了。注意了,第06行返回的是Student類型,這不就是ADIL所支持的自定義類型嘛。 文件結(jié)構(gòu)如下:
綜上所述,傳遞自定義類,有三個(gè)步驟:
那么接下來的步驟就和本段中的第6小節(jié)一樣了,就不再多解釋了,這里只貼代碼: (2)新建StudentImpl類,繼承IStudent.Stub類。代碼如下:(也就是說,根據(jù)步驟(1)中的java類,生成業(yè)務(wù)對象,即原理圖中B應(yīng)用的業(yè)務(wù)對象) 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 } (3)新建Service類,代碼如下: 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 } 核心代碼: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的代碼如下: <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> 注:布局文件里不再添加綁定服務(wù)和取消綁定的按鈕,我們稍后在Activity的生命周期里完成這件事。 (6)MainActivity中的代碼,如下所示: 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 } 核心代碼是第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的代碼如下: <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> (3)在MainActivity中加入和遠(yuǎn)程的MyService建立關(guān)聯(lián)的代碼,如下所示: 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 } 這部分代碼大家一定會(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的步驟:
那我們通過代碼來實(shí)現(xiàn)以下吧。新建一個(gè)全新的工程MessengerTest。步驟如下: (1)新建一個(gè)MessengerService類,繼承Service類,代碼如下: 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 } 核心代碼: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,代碼如下: <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> (4)在MainActivity作為程序的主Activity,在里面加入發(fā)送Message消息和建立Service連接的邏輯,代碼如下: 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 } 我們在上一步的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)。 |
|
|