你所不知道的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)系作者
  • 序言:七十年代末笛厦,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子康栈,更是在濱河造成了極大的恐慌递递,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,723評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件啥么,死亡現(xiàn)場離奇詭異登舞,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)悬荣,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,485評論 2 382
  • 文/潘曉璐 我一進(jìn)店門菠秒,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事践叠⊙早停” “怎么了?”我有些...
    開封第一講書人閱讀 152,998評論 0 344
  • 文/不壞的土叔 我叫張陵禁灼,是天一觀的道長管挟。 經(jīng)常有香客問我,道長弄捕,這世上最難降的妖魔是什么僻孝? 我笑而不...
    開封第一講書人閱讀 55,323評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮守谓,結(jié)果婚禮上穿铆,老公的妹妹穿的比我還像新娘。我一直安慰自己斋荞,他們只是感情好荞雏,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,355評論 5 374
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著平酿,像睡著了一般凤优。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上染服,一...
    開封第一講書人閱讀 49,079評論 1 285
  • 那天别洪,我揣著相機(jī)與錄音叨恨,去河邊找鬼柳刮。 笑死,一個胖子當(dāng)著我的面吹牛痒钝,可吹牛的內(nèi)容都是我干的秉颗。 我是一名探鬼主播,決...
    沈念sama閱讀 38,389評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼送矩,長吁一口氣:“原來是場噩夢啊……” “哼蚕甥!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起栋荸,我...
    開封第一講書人閱讀 37,019評論 0 259
  • 序言:老撾萬榮一對情侶失蹤菇怀,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后晌块,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體爱沟,經(jīng)...
    沈念sama閱讀 43,519評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,971評論 2 325
  • 正文 我和宋清朗相戀三年匆背,在試婚紗的時候發(fā)現(xiàn)自己被綠了呼伸。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,100評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡钝尸,死狀恐怖括享,靈堂內(nèi)的尸體忽然破棺而出搂根,到底是詐尸還是另有隱情,我是刑警寧澤铃辖,帶...
    沈念sama閱讀 33,738評論 4 324
  • 正文 年R本政府宣布剩愧,位于F島的核電站,受9級特大地震影響娇斩,放射性物質(zhì)發(fā)生泄漏隙咸。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,293評論 3 307
  • 文/蒙蒙 一成洗、第九天 我趴在偏房一處隱蔽的房頂上張望五督。 院中可真熱鬧,春花似錦瓶殃、人聲如沸充包。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,289評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽基矮。三九已至,卻和暖如春冠场,著一層夾襖步出監(jiān)牢的瞬間家浇,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,517評論 1 262
  • 我被黑心中介騙來泰國打工碴裙, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留钢悲,地道東北人。 一個月前我還...
    沈念sama閱讀 45,547評論 2 354
  • 正文 我出身青樓舔株,卻偏偏與公主長得像莺琳,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子载慈,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,834評論 2 345

推薦閱讀更多精彩內(nèi)容