Service概述

知識點列表

1.生命周期
2.啟動方式
3.跨進程通信
4.雙向通信

一 生命周期

Service的生命周期跟啟動方式有關(guān),啟動方式有倆種纷铣,startService和bindService却邓,對應生命周期如下


Service生命周期

二 啟動方式

startService()

通過startService啟動后炸宵,service會一直無限期運行下去,只有外部調(diào)用了stopService()或stopSelf()方法時辆憔,該Service才會停止運行并銷毀钉稍。

調(diào)用startService的時候
第一次調(diào)用
構(gòu)造方法——onCreate()——onStartCommand()

當?shù)诙握{(diào)用
直接調(diào)用onStartCommand()

當調(diào)用stopService()的時候
直接調(diào)用onDestory()

bindService()

bindService啟動的服務和調(diào)用者之間是典型的client-server模式配深。調(diào)用者是client,service則是server端嫁盲。service只有一個篓叶,但綁定到service上面的client可以有一個或很多個烈掠,這里所提到的client指的是組件,比如某個Activity缸托。

bindService啟動服務的生命周期與其綁定的client息息相關(guān)左敌,client也可以明確調(diào)用Context的unbindService()方法與Service解除綁定。當所有的client與service解除綁定的時候俐镐,才會調(diào)用onUnbind,然后 Service會自行銷毀矫限。

startService和bindService區(qū)別

client可以通過IBinder接口獲取Service實例,從而實現(xiàn)在client端直接調(diào)用Service中的方法以實現(xiàn)靈活交互佩抹,這在通過startService方法啟動中是無法實現(xiàn)的叼风。

三 跨進程通信

Service跨進程通信,設計到三個東西棍苹,Service无宿,Client,AIDL枢里,client通過bindService的方式可以和Service進行AIDL跨進程通信孽鸡,在Service中定義好AIDL的Stub實現(xiàn)類,通過在Service的onBind回調(diào)中返回IBinder對象栏豺,client綁定時彬碱,獲取到該IBinder對象,即可進行傳遞參數(shù)

AIDL文件

在android studio中奥洼,app/src/main巷疼,下面新建一個aidl文件夾,再在該aidl文件夾下按照包名再新建一個文件夾灵奖,這里面用來寫aidl文件

interface IMyAidlInterface {

    int calculate(int x, int y);

}

建好之后嚼沿,build一下,在build/generated/aidl_source_output_dir/debug/out/包名/桑寨,路徑下伏尼,會生成真正用來通訊的文件忿檩,這其實就是AIDL的實際通訊過程尉尾,android為了簡化,只需要我們定義好AIDL接口燥透,自動給我們生成該類沙咏,實際上可以直接編寫該類就可以進行通訊,里面包含一個內(nèi)部靜態(tài)類Stub班套,我們需要再定義個實現(xiàn)類繼承該Stub肢藐,如下

public class MyAidlServiceImpl extends IMyAidlInterface.Stub {

    private final String TAG = "MyAidlServiceImpl";

    public MyAidlServiceImpl() {
    }

    @Override
    public int calculate(int x, int y) throws RemoteException {
        Log.d(TAG, x + y + "");
        return x + y;
    }

}
Service

Service里面用來通過onBind方法來傳遞該AIDL,實現(xiàn)通信

public class MyService extends Service {

    private MyAidlServiceImpl service;

    @Override
    public void onCreate() {
        super.onCreate();
        service = new MyAidlServiceImpl();
    }

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return service;
    }
}
Client

客戶端需要通過bindService來獲取到Service的IMyAidlInterface吱韭,即可調(diào)用對應方法實現(xiàn)通信

public class MainActivity extends Activity {

    private final String TAG = "MainActivityInformation";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        findViewById(R.id.method_one).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                connect();
                if (aidlInterface == null) {
                    return;
                }
                try {
                    Toast.makeText(MainActivity.this, "計算成功: " + aidlInterface.calculate(10, 20), Toast.LENGTH_SHORT).show();
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
            }
        });
    }

    private IMyAidlInterface aidlInterface;

    private ServiceConnection connection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            aidlInterface = IMyAidlInterface.Stub.asInterface(service);
            runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    Toast.makeText(MainActivity.this, "連接成功", Toast.LENGTH_SHORT).show();
                }
            });
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            aidlInterface = null;
            runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    Toast.makeText(MainActivity.this, "連接斷開", Toast.LENGTH_SHORT).show();
                }
            });
        }
    };

}

四 雙向通信

單向通信指的是吆豹,數(shù)據(jù)只能從client流向Service鱼的,而雙向通信則是指數(shù)據(jù)可以在client和Service之間互相流通,雙向通信通過inout和回調(diào)監(jiān)聽可以實現(xiàn)雙向通信
提供一個用來傳遞的序列化的類

public class Person implements Parcelable {

    public String name;
    public int age;
    public String describe;

    public Person(){

    }

    ...

    @Override
    public String toString() {
        return "我叫" + name + ",今年" + age + "歲," + describe;
    }

    ...
}

然后定義對應的aidl文件

package visual.share.aidlservice;
parcelable Person;

一個用來監(jiān)聽和回調(diào)數(shù)據(jù)的AIDL

interface IMyAidlInterface {

    void inOutData(String name, int age, String describe, inout Person data);

    void registerCallBack(PersonResultListen listen);

    void unregisterCallBack(PersonResultListen listen);

    void callBackData();

}

一個回調(diào)監(jiān)聽

interface PersonResultListen {

    void result(inout Person person);

}

然后來實現(xiàn)IMyAidlInterface的實現(xiàn)類痘煤,如下

public class MyAidlServiceImpl extends IMyAidlInterface.Stub {

    private RemoteCallbackList<PersonResultListen> remoteCallbackList = new RemoteCallbackList<>();
    
    @Override
    public void inOutData(String name, int age, String describe, Person data) throws RemoteException {
        data.name = name;
        data.age = age;
        data.describe = describe;
    }

    @Override
    public void registerCallBack(PersonResultListen listen) throws RemoteException {
        if (listen != null) {
            remoteCallbackList.register(listen);
        }
    }

    @Override
    public void unregisterCallBack(PersonResultListen listen) throws RemoteException {
        if (listen != null){
            remoteCallbackList.unregister(listen);
        };
    }

    @Override
    public void callBackData() throws RemoteException {
        synchronized (remoteCallbackList){
            int count = remoteCallbackList.beginBroadcast();
            if (count > 0){
                Person person = new Person();
                person.name = "監(jiān)聽回調(diào)";
                person.age = 20;
                person.describe = "監(jiān)聽回調(diào)的描述";
                for (int i = 0; i < count; i++) {
                    remoteCallbackList.getBroadcastItem(i).result(person);
                }
            }
            remoteCallbackList.finishBroadcast();
        }
    }
}

這里我們的inOutData方法凑阶,里面用到了inout標識,用來實現(xiàn)雙向通信衷快,callBackData方法用來實現(xiàn)接口回調(diào)宙橱,而registerCallBack和unregisterCallBack則是用來注冊和取消注冊這個監(jiān)聽回調(diào),需要注意的時候蘸拔,這里使用的是RemoteCallbackList來存儲回調(diào)隊列师郑,這個類專門用來處理跨進程通信的回調(diào)
定義一個Service,用來獲取我們這個AIDL實現(xiàn)類

public class MyService extends Service {

    private MyAidlServiceImpl service;

    @Override
    public void onCreate() {
        super.onCreate();
        service = new MyAidlServiceImpl();
    }

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return service;
    }

}

然后在應用中就可以直接使用

public class MainActivity extends Activity {

    private final String TAG = "MainActivity";

    private Intent intent;

    @SuppressLint("MissingInflatedId")
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        intent = new Intent(MainActivity.this, MyService.class);
        bindService();
        findViewById(R.id.inout_data).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (aidlInterface != null){
                    try {
                        Person person = new Person();
                        aidlInterface.inOutData("小明", 25, "長得很帥",person);
                        Toast.makeText(MainActivity.this, person.toString(), Toast.LENGTH_SHORT).show();
                    } catch (RemoteException e) {
                        throw new RuntimeException(e);
                    }
                }
            }
        });
        findViewById(R.id.callback_data).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (aidlInterface != null){
                    try {
                        aidlInterface.callBackData();
                    } catch (RemoteException e) {
                        throw new RuntimeException(e);
                    }
                }
            }
        });
    }

    private void bindService(){
        boolean flag = bindService(intent, connection,BIND_AUTO_CREATE);
        Log.d(TAG, "connect status" + flag);
    }

    private IMyAidlInterface aidlInterface;

    private ServiceConnection connection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            aidlInterface = IMyAidlInterface.Stub.asInterface(service);
            try {
                aidlInterface.asBinder().linkToDeath(deathRecipient, 0);
            } catch (RemoteException e) {
                throw new RuntimeException(e);
            }
            try {
                aidlInterface.registerCallBack(personResultListen);
            } catch (RemoteException e) {
                throw new RuntimeException(e);
            }
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            aidlInterface = null;
        }
    };

    private PersonResultListen.Stub personResultListen = new PersonResultListen.Stub() {
        @Override
        public void result(Person person) throws RemoteException {
            Toast.makeText(MainActivity.this, person.toString(), Toast.LENGTH_SHORT).show();
        }
    };

    private IBinder.DeathRecipient deathRecipient = new IBinder.DeathRecipient() {
        @Override
        public void binderDied() {
            try {
                aidlInterface.unregisterCallBack(personResultListen);
            } catch (RemoteException e) {
                throw new RuntimeException(e);
            }
            if (aidlInterface != null){
                aidlInterface.asBinder().unlinkToDeath(deathRecipient, 0);
            }
            bindService();
        }
    };
}

打開應用后就綁定服務调窍,這里我們添加監(jiān)聽是在綁定成功之后添加宝冕,并且使用了死亡代理,Binder.DeathRecipient陨晶,當和服務斷開的時候猬仁,再取消注冊的監(jiān)聽,布局如下

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".MainActivity">

    <Button
        android:id="@+id/inout_data"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginHorizontal="10dp"
        android:text="獲取inout數(shù)據(jù)" />

    <Button
        android:id="@+id/callback_data"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginHorizontal="10dp"
        android:text="獲取回調(diào)數(shù)據(jù)" />

</LinearLayout>

這里提供了倆個按鈕先誉,點擊后湿刽,分別獲得通過inout處理后的數(shù)據(jù),和通過回調(diào)返回的數(shù)據(jù)褐耳,實現(xiàn)了雙向通信诈闺。
最后測試demo結(jié)構(gòu)如下


測試demo

IntentService

Service因為是運行在主線程,不能處理耗時任務铃芦,否則會可能會出現(xiàn)ANR問題雅镊,IntentService 是繼承于 Service 并處理異步請求的一個類,在 IntentService 內(nèi)有一個子線程來處理耗時操作刃滓,啟動 IntentService 的方式和啟動傳統(tǒng) Service 一樣仁烹,同時,當任務執(zhí)行完后咧虎,IntentService 會自動停止卓缰,而不需要我們?nèi)ナ謩涌刂啤?/p>

踩坑

1.binderService和unbinderService需要用使用同一個ServiceConnection

如果不使用同一個,當調(diào)用unbindService時砰诵,會提示Service not registered錯誤

2.onServiceDisconnected不回調(diào)

當bindService后征唬,如果Client和Service連接成功,會調(diào)用ServiceConnection的onServiceConnected方法茁彭,但是調(diào)用unbindService解除綁定時总寒,不會調(diào)用onServiceDisconnected方法,這是因為當Client和Service連接后理肺,只有到Service因為異常原因崩潰摄闸,才會調(diào)用onServiceDisconnected方法

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末善镰,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子年枕,更是在濱河造成了極大的恐慌媳禁,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,640評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件画切,死亡現(xiàn)場離奇詭異竣稽,居然都是意外死亡,警方通過查閱死者的電腦和手機霍弹,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,254評論 3 395
  • 文/潘曉璐 我一進店門毫别,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人典格,你說我怎么就攤上這事岛宦。” “怎么了耍缴?”我有些...
    開封第一講書人閱讀 165,011評論 0 355
  • 文/不壞的土叔 我叫張陵砾肺,是天一觀的道長。 經(jīng)常有香客問我防嗡,道長变汪,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,755評論 1 294
  • 正文 為了忘掉前任蚁趁,我火速辦了婚禮裙盾,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘他嫡。我一直安慰自己番官,他們只是感情好,可當我...
    茶點故事閱讀 67,774評論 6 392
  • 文/花漫 我一把揭開白布钢属。 她就那樣靜靜地躺著徘熔,像睡著了一般。 火紅的嫁衣襯著肌膚如雪淆党。 梳的紋絲不亂的頭發(fā)上酷师,一...
    開封第一講書人閱讀 51,610評論 1 305
  • 那天,我揣著相機與錄音宁否,去河邊找鬼窒升。 笑死缀遍,一個胖子當著我的面吹牛慕匠,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播域醇,決...
    沈念sama閱讀 40,352評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼台谊,長吁一口氣:“原來是場噩夢啊……” “哼蓉媳!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起锅铅,我...
    開封第一講書人閱讀 39,257評論 0 276
  • 序言:老撾萬榮一對情侶失蹤酪呻,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后盐须,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體玩荠,經(jīng)...
    沈念sama閱讀 45,717評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,894評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片冕房。...
    茶點故事閱讀 40,021評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡蚁袭,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出螟左,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 35,735評論 5 346
  • 正文 年R本政府宣布匆骗,位于F島的核電站,受9級特大地震影響誉简,放射性物質(zhì)發(fā)生泄漏碉就。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,354評論 3 330
  • 文/蒙蒙 一闷串、第九天 我趴在偏房一處隱蔽的房頂上張望铝噩。 院中可真熱鬧,春花似錦窿克、人聲如沸骏庸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,936評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽具被。三九已至,卻和暖如春只损,著一層夾襖步出監(jiān)牢的瞬間一姿,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,054評論 1 270
  • 我被黑心中介騙來泰國打工跃惫, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留叮叹,地道東北人。 一個月前我還...
    沈念sama閱讀 48,224評論 3 371
  • 正文 我出身青樓爆存,卻偏偏與公主長得像蛉顽,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子先较,可洞房花燭夜當晚...
    茶點故事閱讀 44,974評論 2 355

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

  • 服務基本上分為兩種形式 啟動 當應用組件(如 Activity)通過調(diào)用 startService() 啟動服務時...
    pifoo閱讀 1,272評論 0 8
  • 版權(quán)聲明:本文為博主原創(chuàng)文章,未經(jīng)博主允許不得轉(zhuǎn)載 PS:轉(zhuǎn)載請注明出處作者: TigerChain地址: htt...
    TigerChain閱讀 752評論 0 3
  • Service是一種可以在后臺執(zhí)行耗時操作而沒有用戶界面的應用組件曾棕。它默認運行在主線程中扣猫,不可以直接進行耗時操作,...
    stevewang閱讀 745評論 0 2
  • 1 Service是什么翘地? Service是一個可以在后臺執(zhí)行長時間運行操作而不操作用戶界面的應用組件申尤。服務可由其...
    WangGavin閱讀 3,626評論 0 3
  • 描述 Service通常總是稱之為“后臺服務”衙耕,其中“后臺”一詞是相對于前臺而言的瀑凝,具體是指其本身的運行并不依賴于...
    pkqgo閱讀 667評論 1 3