你所不知道的Service

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

  1. 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)行通信。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容