service常見的有2種方式跳仿,本地service以及remote service留美。
這2種的生命周期贸呢,同activity的通信方式等荧恍,都不相同瓷叫。
關(guān)于這2種service如何使用,這里不做介紹送巡,只是介紹一些被遺漏的地方
1.遠(yuǎn)程Service(AIDL方式)
ServiceConActivity:
package com.joyfulmath.samples.basecontrol;import android.app.Activity;import android.content.ComponentName;import android.content.Intent;import android.content.ServiceConnection;import android.os.IBinder;import android.os.RemoteException;import com.joyfulmath.samples.R;import com.joyfulmath.samples.TraceLog;import org.androidannotations.annotations.Click;import org.androidannotations.annotations.EActivity;/** * Created by Administrator on 2016/10/11 0011. * service connect activity samples */@EActivity(R.layout.activity_connect_service)public class ServiceConActivity extends Activity { private ISamplesAidlInterface binder; private ServiceConnection connection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { TraceLog.i(); binder = ISamplesAidlInterface.Stub.asInterface(service); if(binder!=null) { try { binder.registerCallBack(mCallBack); } catch (RemoteException e) { e.printStackTrace(); } } } @Override public void onServiceDisconnected(ComponentName name) { TraceLog.i(); binder = null; } }; private ICallBack.Stub mCallBack = new ICallBack.Stub() { @Override public void onServiceStateChanged(int s) throws RemoteException { TraceLog.i(String.valueOf(s)); } }; public void bindSamplesService() { TraceLog.i(); Intent intent = new Intent(getApplicationContext(),ServiceSamples.class);// intent.setAction("com.joyfulmath.service.samples"); intent.putExtra("cookie","origin"); bindService(intent,connection,BIND_AUTO_CREATE); } public void unBindSamplesService() { TraceLog.i(); if(binder!=null) { try { binder.unRegisterCallBack(mCallBack); } catch (RemoteException e) { e.printStackTrace(); } } unbindService(connection); } @Click(R.id.btn_connect) void connectClick() { TraceLog.i(); bindSamplesService(); } @Click(R.id.btn_unconnect) void unConnectClick() { TraceLog.i(); unBindSamplesService(); } @Click(R.id.btn_do) void doAction() { if(binder!=null) { try { int r = binder.doBackground("action"); TraceLog.i(String.valueOf(r)); } catch (RemoteException e) { e.printStackTrace(); } } } @Override protected void onDestroy() { super.onDestroy(); TraceLog.i(); unBindSamplesService(); }}
ServiceSamples
package com.joyfulmath.samples.basecontrol;import android.app.Service;import android.content.Intent;import android.os.IBinder;import android.os.RemoteCallbackList;import android.os.RemoteException;import android.support.annotation.Nullable;import com.joyfulmath.samples.TraceLog;/** * Created by Administrator on 2016/10/11 0011. */public class ServiceSamples extends Service { private SamplesBinder samplesBinder = null; private RemoteCallbackList<ICallBack> mCallbacks = new RemoteCallbackList<>(); private String tag = ""; @Nullable @Override public IBinder onBind(Intent intent) { tag = intent.getStringExtra("cookie"); TraceLog.i(tag); return samplesBinder; } @Override public int onStartCommand(Intent intent, int flags, int startId) { tag = intent.getStringExtra("cookie"); TraceLog.i(tag); return super.onStartCommand(intent, flags, startId); } @Override public boolean onUnbind(Intent intent) { TraceLog.i(tag); return super.onUnbind(intent); } @Override public void onDestroy() { super.onDestroy(); TraceLog.i(tag); mCallbacks.kill(); } @Override public void onCreate() { super.onCreate(); samplesBinder = new SamplesBinder(); TraceLog.i(tag); } public class SamplesBinder extends ISamplesAidlInterface.Stub{ @Override public int doBackground(String action) throws RemoteException { TraceLog.i(tag); return -1; } @Override public void findPerson(PersonCall p) throws RemoteException { notifyFindPerson(); } @Override public void registerCallBack(ICallBack cb) throws RemoteException { mCallbacks.register(cb); } @Override public void unRegisterCallBack(ICallBack cb) throws RemoteException { mCallbacks.unregister(cb); } } private void notifyFindPerson() throws RemoteException { try{ synchronized (this){ int n = mCallbacks.beginBroadcast(); for(int i=0;i<n;i++){ mCallbacks.getBroadcastItem(i).onServiceStateChanged(0x11); } mCallbacks.finishBroadcast(); } }catch (RemoteException e) { TraceLog.i(tag+":"+e.getMessage()); } }}
這是簡單的service & activity交互的代碼摹菠。
在看關(guān)鍵的AIDL代碼:
// ISamplesAidlInterface.aidlpackage com.joyfulmath.samples.basecontrol;import com.joyfulmath.samples.basecontrol.PersonCall;import com.joyfulmath.samples.basecontrol.ICallBack;// Declare any non-default types here with import statementsinterface ISamplesAidlInterface { int doBackground(in String action); void findPerson(in PersonCall p); void registerCallBack(ICallBack cb); void unRegisterCallBack(ICallBack cb);}
// PersonCall.aidlpackage com.joyfulmath.samples.basecontrol;parcelable PersonCall;
// ICallBack.aidlpackage com.joyfulmath.samples.basecontrol;// Declare any non-default types here with import statementsinterface ICallBack { void onServiceStateChanged(int s);}
這里有3個問題,我們從頭往下看骗爆,就能明白次氨。
1)為什么在其他APK調(diào)用該service的時候,aidl的文件包必須一致
2)為什么要自定義PersonCall.aidl
- ICallBack是什么玩意摘投。
4)多個APK連接同一個service煮寡,該service會產(chǎn)生多個實(shí)例嗎。怎么保證不沖突呢犀呼?
其實(shí)1) & 2)的問題是一樣的幸撕,都是基于java的classloader原理。
同一個類外臂,必須在同一個包內(nèi)坐儿,并且由同一個classloader加載,才能表示他們是同一個類宋光。
所以AIDL在拷貝的時候挑童,必須保證是同一個包名(AIDL在打包的時候會生成java文件。)
并且自定義的參數(shù)class跃须,必須有AIDL定義站叼,才能讓其他APK可以理解該類。當(dāng)然為了傳輸菇民,需要繼承自pracacle
3)關(guān)于service回調(diào)的工作尽楔,是由RemoteCallbackList 專門用來回調(diào)通知client端投储。
首先在client端定義的listener,遠(yuǎn)端是沒有實(shí)體對象的阔馋,所以在作為參數(shù)傳入到遠(yuǎn)端的時候玛荞,會復(fù)制一份,并且與binder綁定呕寝。
下面來看看真正的干貨勋眯,第4個問題:
我們分成幾個小問題來解答。
I下梢,如果service和activity不在同一個app客蹋,那么activity可以通過startservice or bindservice的方式啟動該service嗎?如果不行孽江,怎么啟動該service讶坯。
經(jīng)測試,可以通過bindservice的方式啟動岗屏。
II辆琅,如果2個client同時對同一個service做bind操作,會有什么結(jié)果这刷?
binderservice都會返回成功操作婉烟,并且前一個client,沒有收到disconnect的通知暇屋。
此時service的操作隅很,會返回對后面一個client傳遞的參數(shù)的操作,也就是只有一份service實(shí)例率碾,會同時binder2個client叔营,but只會處理后面一個client的行為。
所以此時所宰,service應(yīng)該阻止由其他client端輸入的請求绒尊,并且可以提供接口給到client,由他決定是否關(guān)閉這個binder仔粥。
下面是bindservice的flag參數(shù)說明:
常量名
值
含義
BIND_ABOVE_CLIENT
8
如果當(dāng)綁定服務(wù)期間遇到OOM需要?dú)⑺肋M(jìn)程婴谱,客戶進(jìn)程會先于服務(wù)進(jìn)程被殺死。
BIND_ADJUST_WITH_ACTIVITY
128
允許客戶進(jìn)程提升被綁定服務(wù)進(jìn)程的優(yōu)先級
BIND_ALLOW_OOM_MANAGEMENT
16
如果綁定服務(wù)期間遇到OOM需要?dú)⑺肋M(jìn)程躯泰,被綁定的服務(wù)進(jìn)程會被OOM列入獵殺對象中谭羔。
BIND_AUTO_CREATE
1
若綁定服務(wù)時服務(wù)未啟動,則會自動啟動服務(wù)麦向。 注意瘟裸,這種情況下服務(wù)的onStartCommand
仍然未被調(diào)用(它只會在顯式調(diào)用startService
時才會被調(diào)用)。
BIND_DEBUG_UNBIND
2
使用此標(biāo)志綁定服務(wù)之后的unBindService
方法會無效诵竭。 這種方法會引起內(nèi)存泄露话告,只能在調(diào)試時使用兼搏。
BIND_IMPORTANT
64
被綁定的服務(wù)進(jìn)程優(yōu)先級會被提到FOREGROUND級別
BIND_NOT_FOREGROUND
4
被綁定的服務(wù)進(jìn)程優(yōu)先級不允許被提到FOREGROUND級別
BIND_WAIVE_PRIORITY
32
被綁定的服務(wù)進(jìn)程不會被OOM列入獵殺對象中。
可以看到沙郭,他們是可以組合使用的佛呻。
如果在第三方APP 使用service
第一步:在java同級目錄下,創(chuàng)建aidl文件夾
第二步:把AIDL文件copy該目錄下病线,注意保持包名一致吓著。
第三步:把自定義的class,copy到j(luò)ava目錄下送挑,包名一致绑莺。
第四步:啟動service需要用顯示的定義(android5.0開始):
public void bindSamplesService() { TraceLog.i();// ComponentName name = new ComponentName("com.joyfulmath.samples","ServiceSamples"); Intent intent = new Intent();// intent.setComponent(name); intent.setAction("com.joyfulmath.service.samples"); intent.setPackage("com.joyfulmath.samples"); intent.putExtra("cookie","third"); bindService(intent,connection,BIND_AUTO_CREATE|BIND_ADJUST_WITH_ACTIVITY); }
android 5.1上,親測让虐,該方式可行紊撕,使用componentName不行罢荡,需進(jìn)一步研究赡突。
2.startservice
startservice可以跨進(jìn)程調(diào)用,也就是調(diào)用其他app的service区赵。
public void bindSamplesService() { TraceLog.i(); Intent intent = new Intent(); intent.setAction("com.joyfulmath.service.samples"); intent.setPackage("com.joyfulmath.samples"); intent.putExtra("cookie","third");// bindService(intent,connection,BIND_AUTO_CREATE|BIND_ADJUST_WITH_ACTIVITY); startService(intent); }
關(guān)于startservice惭缰,你所不知道的內(nèi)容如下:
public abstract ComponentName startService(Intent service);
該方法還會返回一個ComponentName ,這個name就是表示package+name笼才,因?yàn)閏lassname會重復(fù)漱受。
* @return If the service is being started or is already running, the * {@link ComponentName} of the actual service that was started is * returned; else if the service does not exist null is returned.
注釋說的很清楚。
* <p>This function will throw {@link SecurityException} if you do not * have permission to start the given service.
沒有權(quán)限骡送,就會報安全異常昂羡。
跨進(jìn)程啟動service的流程:
如果考慮到進(jìn)程,那么我們就應(yīng)該暫時撇開四大組件的概念摔踱。
從操作系統(tǒng)虐先,進(jìn)程線程的本質(zhì)來考慮問題。
Activity是生存在一個ActivityThread派敷。它就是一個app(一般對應(yīng)一個進(jìn)程)的主線程蛹批。
那么service在哪里,也在主線程中篮愉「郑可以通過tracelong來認(rèn)證這個結(jié)論。
所以說试躏,service雖然是有獨(dú)立生命周期的一大組件猪勇,但是它默認(rèn)還是在主線程中。所以也會ANR颠蕴。
既然要跨進(jìn)程埠对,必然也需要binder機(jī)制络断,可能我們看不到而已。
大致流程如下:
從主進(jìn)程調(diào)用到AMS進(jìn)程(SystemServer進(jìn)程)项玛,創(chuàng)建新的進(jìn)程貌笨。這個過程需要用到binder通信。
從新進(jìn)程回調(diào)AMS襟沮,獲取新進(jìn)程的一些信息锥惋。關(guān)鍵是這些信息是從源進(jìn)程傳遞過來+manifest注冊的。
從AMS回到新進(jìn)程开伏,直到新進(jìn)程啟動(同時包括service啟動)
這三步都是跨進(jìn)程啟動service的過程膀跌,都需要binder機(jī)制來通信。
具體詳細(xì)流程固灵,后續(xù)會繼續(xù)分析捅伤。
3.process lifecycle
關(guān)于service對應(yīng)的lifecycle已經(jīng)在activity那篇里說明了。
Android 四大組件之Activity(續(xù)2)
4.binder機(jī)制
關(guān)于這塊之前以及有相關(guān)博文巫玻,接下來打算再詳細(xì)分析下丛忆。binder機(jī)制是android最重要的基石。
server 會在通過servermanger注冊它仍秤,然后提供遠(yuǎn)程調(diào)用的句柄熄诡,通過binder機(jī)制
client獲取servermanger不需要通過binder,應(yīng)為servermanger是默認(rèn)的句柄為0诗力,可以直接獲取到凰浮。
所以說,servermanager是在等待client端發(fā)送請求苇本,然后它去尋找以及注冊的server袜茧,得到它的遠(yuǎn)程對象,進(jìn)行通信瓣窄。