目錄
- Service 介紹
- Service兩種啟動方式
- 使用
- 測試
- IntentService
- Activity與Service之間的通信
- 繼承Binder類
- Messenger
- AIDL
Service 介紹
A Service is an application component that can perform long-running operations in the background and does not provide a user interface. Another application component can start a service and it will continue to run in the background even if the user switches to another application. Additionally, a component can bind to a service to interact with it and even perform interprocess communication (IPC). For example, a service might handle network transactions, play music, perform file I/O, or interact with a content provider, all from the background.
Service是一個沒有用戶界面的應用程序組件,可以在后臺長時間運行。另外一個應用程序組件可以啟動一個Service,它可以在用戶切換到其他應用的時候依舊保持在后臺運行。另外,一個組件可以綁定到Service上,與它進行通信,甚至是IPC(進程間通信)。
Service兩種啟動方式
- startService
-
bindService
其生命周期官方API介紹:
Paste_Image.png
需要注意的點:
1.通過startService啟動的Service,只有調用了stopService(外部組件調用)或stopSelf(Service內部自己調用),才會停止。
2.通過startService啟動的Service,在Service運行中無法與Service進行交互,即外部組件只能控制其開關,無法進行交互。
3.通過startService啟動的Service,與外部組件之間沒有關系,外部組件的生死跟它沒有聯系。
3.通過startService啟動的Service,啟動之后重復啟動的話不會觸發(fā)onCreate方法,但是會重復觸發(fā)onStartCommand,其實貌似也算是數據交流了吧,不過是單向的。
4.bindService啟動的Service表示將一個Service綁定到一個組件上,其生命周期與該組件的生命周期綁定在一起,比如綁到一個Activity,Activity在Destroy后Service跟著就Destroy了。
4.注意是不能綁定廣播的,因為廣播發(fā)完了其生命就到頭了,常用的是綁Activity,還可以是Service。
5.bindService啟動的Service在使用完之后可以解除綁定,當一個Service上的所有綁定的組件都解綁之后,它就會被銷毀。
6.可以同時使用兩種啟動方式,此時的生命周期就變的有些復雜了,兩種關聯到一起,總結來說的話,先startService與先bindService兩種方式達到的效果是一樣的,即此時unbindService的話,Service并不會結束,而是要等到stopService才會結束(onDestroy);若是此時stopService,也不會結束,而是要等到unbindService時才會結束(由于已經調用過stopService,此時會直接onDestroy)。
7.onRebind調用時機:
Paste_Image.png
當舊client與service之間的關聯在onUnbind中都結束之后,新client綁定時,
必須是onUnbind返回true,且服務在解綁之后沒有銷毀
使用
-
startService
創(chuàng)建類繼承Service,在啟動組件中調用:Intent intent1 = new Intent(ServiceActivity1.this, MyService1.class); startService(intent1);
停止方法同上
-
bindService
1.創(chuàng)建類繼承Service,其中默認會有一個onBind方法,返回是一個IBinder類型對象,這個返回值就是與組件之間通信的關鍵??梢酝ㄟ^自定義內部類繼承Binder,在這個類中返回Service,然后在組件中通過返回的IBinder類型對象獲取到Service對象從而進行操作;
2.在組件中新建一個ServiceConnection對象,必須重寫兩個方法,onServiceConnected(建立連接(bind_service)時調用)、onServiceDisconnected(一般都不調用,除非是意外情況,unbind_service并不調用這個),在onServiceConnected中獲取到Service對象進行操作。
MyService:public class MyService1 extends Service { @Override public void onCreate() { super.onCreate(); Log.i("test_out","----->onCreate"); } @Override public int onStartCommand(Intent intent, int flags, int startId) { Log.i("test_out","----->onStartCommand"); return super.onStartCommand(intent, flags, startId); } @Override public void onDestroy() { super.onDestroy(); Log.i("test_out","----->onDestroy"); } public class MyBinder extends Binder { public MyService1 getService(){ return MyService1.this; } } private MyBinder mBinder = new MyBinder(); @Override public IBinder onBind(Intent intent) { Log.i("test_out","----->onBind"); return mBinder; } @Override public boolean onUnbind(Intent intent) { Log.i("test_out","----->onUnbind"); return true; } @Override public void onRebind(Intent intent) { super.onRebind(intent); Log.i("test_out","----->onRebind"); } public int getCount(){ return (int) (Math.random() * 10); } }
在Activity中建立連接:
ServiceConnection serviceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
mService = ((MyService1.MyBinder)service).getService();
textView.setText("" + mService.getCount());
}
@Override
public void onServiceDisconnected(ComponentName name) {
Log.i("test_out","----->onServiceDisconnected");
}
};
啟動與停止:
bindService(intent3, serviceConnection, Service.BIND_AUTO_CREATE);
第一個參數是intent,第二個是上面的serviceConnection,第三個表示綁定方式,Service.BIND_AUTO_CREATE表示綁定時不存在的話就自動創(chuàng)建。
解綁:unbindService(serviceConnection);
測試
startService -> stopService
startService -> startService -> startService -> stopService
果然重復start會調用onStartCommand,那么就相當于可以發(fā)指令。
bindService -> unbindService
bindService -> 按下返回鍵
startService -> bindService -> unbindService
startService -> bindService -> unbindService -> bindService -> unbindService
startService -> bindService -> stopService
此時unbindService :
IntentService
Android封裝好的Service,在其中的onHandleIntent(Intent intent)中處理需要在子線程中處理的邏輯,在處理完畢后,會自動onDestroy。
Activity與Service之間的通信
一般來講,我們將綁定到Service上的組件稱為客戶端,Service稱為服務端,他們之間的通信可以在同一個進程,也可以在不同的進程。
要進行通信,那個前面已經提到,就要在客戶端獲取到一個IBinder對象,而獲取這個對象的方式有三種:
繼承Binder類,使用Messenger類,使用AIDL
第一種上面已經用過了,不過這種方式是適用于同進程內通信,因為不同的進程使用的是不同的內存區(qū)域。
Messenger
其實現是通過Message以及Handler來進行通信,Handler應該都有所了解,一般使用最多的都是用來解決非UI線程更新UI的問題,Handler機制有待仔細研究一下,后面寫一篇。
其實其底層實現也是通過AIDL。
服務端(Service):
基本流程跟上面的繼承Binder類的方式差不多,如下:
首先創(chuàng)建一個Service,在其中實現一個Handler,用于接受消息。
然后通過Handler創(chuàng)建一個Messenger實例,在onBind中返回Messager實例的Binder。
public class MyService2 extends Service {
class ServiceHandler extends Handler{
@Override
public void handleMessage(Message msg) {
if(msg.what == 1){
Log.i("test_out","------>receive data from client!");
}
}
}
final Messenger messager = new Messenger(new ServiceHandler());
@Override
public IBinder onBind(Intent intent) {
Log.i("test_out","----->onBind");
return messager.getBinder();
}
@Override
public boolean onUnbind(Intent intent) {
Log.i("test_out","----->onUnbind");
return true;
}
}
客戶端:
通過serviceConnection中的onServiceConnected獲取到服務端傳來的Binder對象,實例化Client端的Messenger。
創(chuàng)建Message,通過Messager發(fā)送消息。
ServiceConnection serviceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
mMessager = new Messenger(service);
}
@Override
public void onServiceDisconnected(ComponentName name) {
Log.i("test_out","----->onServiceDisconnected");
}
};
public void sendToMyService(){
Message msg = new Message();
msg.what = 1;
try {
mMessager.send(msg);
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
protected void onStart() {
super.onStart();
Intent intent = new Intent();
intent.setAction("com.gsq.service2");
intent.setClassName("com.example.gsq.servicetest", "com.example.gsq.servicetest.service.MyService2");
bindService(intent, serviceConnection, BIND_AUTO_CREATE);
}
@Override
protected void onStop() {
super.onStop();
unbindService(serviceConnection);
}
對了,這里我的客戶端是另外一個APK,驗證進程間通信,那么就要在Service注冊的時候指定其action,在Intent跳轉的時候設置完整包名和action,這個在前面學習Intent中有介紹的。
<service android:name=".service.MyService2"
android:exported="true"
android:permission="com.gsq.permission.service2">
<intent-filter>
<action android:name="com.gsq.service2" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</service>
注意,這里寫了android:exported之后,AS會提示你:
Exported services (services which either set exported=true or contain an intent-filter and do not specify exported=false) should define a permission that an entity must have in order to launch the service or bind to it. Without this, any application can use this service.
大意就是,最好是加一個權限,android:permission。
自定義權限過程:在被調用的應用中先定義
<permission
android:name="com.gsq.permission.service2"
android:protectionLevel="normal">
</permission>
然后使用,android:permission="com.gsq.permission.service2
然后在調用者(客戶端)聲明:
<uses-permission android:name="com.gsq.permission.service2" />
坑:自定義權限名稱一定要是 *.permission.*
好了,這樣子Messenger的使用流程就說完了,下面就可以看一下結果了。
啟動第二個應用:
點擊button:
OK ! 大功告成!
再點一下:
關閉:
上面就實現了客戶端向服務端發(fā)送消息。
下面就來實現一下服務端在受到消息之后給一個response。
流程跟上面類似,要想發(fā)送消息就要有一個Messenger,可是我們前面不是分別在服務端創(chuàng)建了一個Messenger了么,然后將這個Messenger返回給客戶端,這樣客戶端就可以通過這個Messenger向服務器端發(fā)送消息,那么既然已經通道已經存在了,我們能不能在服務端使用這個Messenger來想客戶端發(fā)送消息呢?
答案是否定的。因為這個Messenger是在服務端的,而且是通過服務端的handler實例化的,然后通過binder傳到客戶端,也就是說告訴客戶端你就用這個給我發(fā)消息就行了,我就能收到,但是服務端并不知道客戶端是誰,誰拿到了這個Messenger,只知道處理通過這個Messenger傳遞來的消息。
那么按照這個分析的話,要想服務端向客戶端發(fā)送消息方式就顯而易見了,就是在客戶端實例化一個Messenger,然后傳遞給服務端,告訴它用這個Messenger給我發(fā)消息就行了,那么客戶端可以通過binder獲取到服務端的Messenger實例,那么服務端怎么獲取到客戶端的實例呢?這個就很簡單了,因為前面通道已經創(chuàng)建了,只需要在客戶端向服務端發(fā)送消息的時候將客戶端自己的Messenger傳遞過去就行了。而Message類剛好有一個參數replyTo,這個參數就是Messenger類型的,那么我們只需要在客戶端建一個handler,然后實例化一個Messenger傳遞過去就ok了。
分析完畢,下面是實現:
首先是客戶端代碼:
//創(chuàng)建一個處理服務端發(fā)來的消息的handler
private class MyHandler extends Handler{
@Override
public void handleMessage(Message msg) {
switch(msg.what){
case 2:
Log.i("test_out", "------>receive response from server!");
break;
default:
super.handleMessage(msg);
}
}
}
然后在向服務端發(fā)送數據的時候給msg指定replyTo:
//在client向服務端發(fā)送消息的時候告訴服務端通過這個messenger進行回復
msg.replyTo = new Messenger(new MyHandler());
接下來是服務端代碼,收到消息后獲取到messenger并發(fā)送消息即可:
class ServiceHandler extends Handler{
@Override
public void handleMessage(Message msg) {
if(msg.what == 1){
Log.i("test_out","------>receive data from client!");
Messenger messengerFromClient = msg.replyTo;
Message replyMessage = new Message();
replyMessage.what = 2;
try {
messengerFromClient.send(replyMessage);
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
}
接下來就是測試了:
先啟動服務端,按下home,再啟動客戶端:
此時服務端:
然后在客戶端點擊發(fā)送消息按鈕:
再看一下客戶端:
這樣就完成了雙向通信。
小結:要實現雙向通信需要首先在服務端創(chuàng)建一個handler,通過這個handler實例化一個Messenger,通過binder將這個Messenger返回給客戶端,客戶端收到Messenger之后即可向服務端發(fā)消息,此時在客戶端創(chuàng)建一個handler,并實例化一個Messenger,在向服務端發(fā)消息時將這個Messenger傳遞過去,服務端收到消息的同時也得到了客戶端的Messenger,就可以發(fā)消息給客戶端。
Messenger是使用串行的方式來進行通信,數據量比較大時顯然就有點吃力了,而且主要是用來傳遞消息,但是我們還會出現在客戶端調用服務端的方法的情況,這種情況下Messenger就無能為力了,這時候就需要AIDL了,而且Messenger底層實現的本質上也是AIDL。
AIDL
由于水平很菜,感覺AIDL太高深了,后面單獨寫一個。
參考:
http://www.lxweimin.com/p/a8e43ad5d7d2
http://blog.csdn.net/luoyanglizi/article/details/51586437
http://blog.csdn.net/luoyanglizi/article/details/51594016
非常感謝這位大神。