Android IPC機(jī)制(一)——綁定Service實(shí)現(xiàn)本地通信

寫(xiě)作原因:跨進(jìn)程通信的實(shí)現(xiàn)和理解是Android進(jìn)階中重要的一環(huán)。下面博主分享IPC一些相關(guān)知識(shí)、操作及自己在學(xué)習(xí)IPC過(guò)程中的一些理解回怜。
這一章是為下面的Messenger和AIDL的使用做準(zhǔn)備仓洼,主要講解Android Service的綁定和Activity與本地Service之間通信相關(guān)知識(shí)。有興趣的可以關(guān)注博主的博客或者之后的文章扇单。

簡(jiǎn)介

我們都知道啟動(dòng)Service有兩種方式:startService()和bindService()。相比第一種方式奠旺,bindService()能夠更加靈活地實(shí)現(xiàn)與啟動(dòng)端Activity的數(shù)據(jù)通信蜘澜,第一種啟動(dòng)方式啟動(dòng)Activity與Service之間并沒(méi)有直接關(guān)聯(lián),難以直接實(shí)現(xiàn)通信(當(dāng)然了响疚,使用BroadCast或者事件總線(xiàn)也是可以實(shí)現(xiàn)的)鄙信。而使用綁定的方式啟動(dòng)Service則可以實(shí)現(xiàn)Service之間的通信。下面就講述一下綁定Service實(shí)現(xiàn)本地通信的流程忿晕。

以Activity調(diào)用Service中的方法為例装诡,具體流程參考博主繪制的流程圖:

service01

實(shí)例

結(jié)合圖片我們來(lái)模擬開(kāi)啟一個(gè)后臺(tái)執(zhí)行更新功能為例來(lái)講解本地Service的綁定。

先分析一下該需求的場(chǎng)景:首先我們需要點(diǎn)擊Start按鈕開(kāi)啟后臺(tái)更新數(shù)據(jù)杏糙,后臺(tái)將數(shù)據(jù)返回給Activity并在ProgressBar展示進(jìn)度慎王;當(dāng)我們點(diǎn)擊Pause時(shí)后臺(tái)暫停更新;點(diǎn)擊Stop按鈕時(shí)關(guān)閉后臺(tái)宏侍。本例中也用到Service通過(guò)BroadCast向Activity傳遞數(shù)據(jù)赖淤。由于只是實(shí)例,對(duì)于線(xiàn)程操作一塊有一些bug谅河,希望大家能夠幫忙指正咱旱。

Activity的布局如下:


service02

Service實(shí)現(xiàn)

先看看Service的實(shí)現(xiàn):
思路是這樣的:先繼承Binder類(lèi)創(chuàng)建MyBinder類(lèi)确丢,把MyBinder看做Service與Activity通信的代理人,所以在MyBinder內(nèi)部中寫(xiě)好方法間接調(diào)用Service中的方法以供Activity去調(diào)用(如本例中callPauseUpgrade())吐限。關(guān)于IBinder對(duì)象的獲取除了直接重寫(xiě)后面還有兩種方式鲜侥,這里先不闡述了。在Service中我們開(kāi)啟一個(gè)Thread诸典,在這個(gè)Thread中模擬持續(xù)更新進(jìn)度條直到isStop為False或者progress大于等于100時(shí)描函,然后將進(jìn)度廣播出去,讓Activity接收到廣播進(jìn)行進(jìn)度條更新狐粱。Service中供Activity調(diào)用的方法實(shí)現(xiàn)暫停和停止Thread的功能舀寓,具體過(guò)程參照代碼。

public class UpgradeService extends Service {
    private Thread thread;
    private Intent intent;
    private int progress;
    private Boolean isStop;
    public class MyBinder extends Binder{
        public void callPauseUpgrade(){
            pauseUpgrade();
        }

        public void callStopUpgrade() {
            stopUpgrade();
        }
    }

    private void stopUpgrade() {
        progress = 0;
        isStop = false;
        intent.putExtra("progress",progress);
        sendBroadcast(intent);
    }

    @Override
    public void onCreate() {
        super.onCreate();
        progress = 0;
        isStop = true;
        intent = new Intent();
        thread = new Thread(new Runnable() {
            @Override
            public void run() {
                while(isStop){
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    progress = progress +5;
                    intent.putExtra("progress",progress);
                    intent.setAction("UPGRADE_ACTION");
                    sendBroadcast(intent);
                    if(progress>=100) break;
                }

            }
        });
        thread.start();
    }

    private void pauseUpgrade() {
        //TODO:Pause the upgrade
        Toast.makeText(getApplicationContext(),"暫停",Toast.LENGTH_SHORT).show();
        isStop = false;
    }

    @Override
    public IBinder onBind(Intent intent) {
        return new MyBinder();
    }
}

Activity實(shí)現(xiàn)

下面是在客戶(hù)端實(shí)現(xiàn)與Service的通信代碼肌蜻。主要思路是:先注冊(cè)廣播實(shí)現(xiàn)Service數(shù)據(jù)的返回互墓;單擊Start按鈕時(shí)實(shí)現(xiàn)與Service的綁定,創(chuàng)建ServiceConnection對(duì)象實(shí)現(xiàn)ServiceConnection接口蒋搜,分別回調(diào)綁定成功和失敗兩種情況下的邏輯篡撵。當(dāng)成功時(shí)獲取MyBinder對(duì)象,并將設(shè)置的isBound值設(shè)為true豆挽;當(dāng)失敗時(shí)將isBound設(shè)置為false育谬。單擊Pause調(diào)用MyBinder對(duì)象的callPauseUpgrade()方法間接調(diào)用Service中的pauseUpgrade()方法。單擊Stop調(diào)用callStopUpgrade()方法并且解除綁定祷杈。注意解除綁定事件只能執(zhí)行一次斑司,否則程序會(huì)崩潰。具體實(shí)現(xiàn)并不難但汞,主要是認(rèn)真理解上面的流程圖即可。

public class MainActivity extends AppCompatActivity implements View.OnClickListener{
    private Button mBtStart;
    private Button mBtStop;
    private Button mBtPause;
    private ProgressBar mPbProgress;
    private UpgradeService.MyBinder myBinder;
    private boolean isBound;
    private UpgradeReceiver upgradeReceiver;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        IntentFilter intentFilter = new IntentFilter();
        intentFilter.addAction("UPGRADE_ACTION");
        upgradeReceiver = new UpgradeReceiver();
        registerReceiver(upgradeReceiver,intentFilter);
        initView();
        initEvent();

    }
    public class UpgradeReceiver extends BroadcastReceiver{

        @Override
        public void onReceive(Context context, Intent intent) {
            mPbProgress.setProgress(intent.getIntExtra("progress",0));
        }
    }
    @Override
    protected void onDestroy() {
        super.onDestroy();
        unregisterReceiver(upgradeReceiver);
    }

    private void initEvent() {
        mBtPause.setOnClickListener(this);
        mBtStart.setOnClickListener(this);
        mBtStop.setOnClickListener(this);
    }

    private void initView() {
        mBtPause = (Button) findViewById(R.id.bt_pause);
        mBtStart = (Button) findViewById(R.id.bt_start);
        mBtStop = (Button) findViewById(R.id.bt_stop);
        mPbProgress = (ProgressBar) findViewById(R.id.pb_progress);
    }

    @Override
    public void onClick(View v) {
        switch(v.getId()){
            case R.id.bt_pause:
                if(isBound&&myBinder!=null){
                    myBinder.callPauseUpgrade();
                }
                break;
            case R.id.bt_start:
                bindService(new Intent(MainActivity.this,UpgradeService.class),conn,BIND_AUTO_CREATE);
                break;
            case R.id.bt_stop:
                if(isBound) {
                    myBinder.callStopUpgrade();
                    unbindService(conn);
                    isBound = false;
                }
                break;
        }
    }

    private ServiceConnection conn = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            if(service != null){
                myBinder = (UpgradeService.MyBinder) service;
                isBound = true;
            }
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            isBound = false;
        }
    };
}

總結(jié)

這篇文章是為下面的Messenger和AIDL跨進(jìn)程通信做準(zhǔn)備的互站,實(shí)際上個(gè)人感覺(jué)真正開(kāi)發(fā)時(shí)可以使用EventBus或者RxJava取代這種同進(jìn)程各個(gè)組件通信的問(wèn)題私蕾,有興趣的讀者可以自行嘗試。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末胡桃,一起剝皮案震驚了整個(gè)濱河市踩叭,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌翠胰,老刑警劉巖容贝,帶你破解...
    沈念sama閱讀 211,561評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異之景,居然都是意外死亡斤富,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,218評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門(mén)锻狗,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)满力,“玉大人焕参,你說(shuō)我怎么就攤上這事∮投睿” “怎么了叠纷?”我有些...
    開(kāi)封第一講書(shū)人閱讀 157,162評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)潦嘶。 經(jīng)常有香客問(wèn)我涩嚣,道長(zhǎng),這世上最難降的妖魔是什么掂僵? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,470評(píng)論 1 283
  • 正文 為了忘掉前任缓艳,我火速辦了婚禮,結(jié)果婚禮上看峻,老公的妹妹穿的比我還像新娘阶淘。我一直安慰自己,他們只是感情好互妓,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,550評(píng)論 6 385
  • 文/花漫 我一把揭開(kāi)白布溪窒。 她就那樣靜靜地躺著,像睡著了一般冯勉。 火紅的嫁衣襯著肌膚如雪澈蚌。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 49,806評(píng)論 1 290
  • 那天灼狰,我揣著相機(jī)與錄音宛瞄,去河邊找鬼。 笑死交胚,一個(gè)胖子當(dāng)著我的面吹牛份汗,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播蝴簇,決...
    沈念sama閱讀 38,951評(píng)論 3 407
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼杯活,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了熬词?” 一聲冷哼從身側(cè)響起旁钧,我...
    開(kāi)封第一講書(shū)人閱讀 37,712評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎互拾,沒(méi)想到半個(gè)月后歪今,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,166評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡颜矿,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,510評(píng)論 2 327
  • 正文 我和宋清朗相戀三年寄猩,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片或衡。...
    茶點(diǎn)故事閱讀 38,643評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡焦影,死狀恐怖车遂,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情斯辰,我是刑警寧澤舶担,帶...
    沈念sama閱讀 34,306評(píng)論 4 330
  • 正文 年R本政府宣布,位于F島的核電站彬呻,受9級(jí)特大地震影響衣陶,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜闸氮,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,930評(píng)論 3 313
  • 文/蒙蒙 一剪况、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧蒲跨,春花似錦译断、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,745評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至巡语,卻和暖如春翎蹈,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背男公。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,983評(píng)論 1 266
  • 我被黑心中介騙來(lái)泰國(guó)打工荤堪, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人枢赔。 一個(gè)月前我還...
    沈念sama閱讀 46,351評(píng)論 2 360
  • 正文 我出身青樓澄阳,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親糠爬。 傳聞我的和親對(duì)象是個(gè)殘疾皇子寇荧,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,509評(píng)論 2 348

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