Service 知識梳理(1) - Service 問題整理

一蹲诀、Service 常見問題

由于Service比較簡單岸晦,就不介紹基礎(chǔ)的東西了禁悠,這篇文章主要是用于記錄使用Service過程中遇到的一些問題念祭。

1.1 描述 Service 的生命周期

Service的生命周期如下圖所示:


Service的啟動方式有兩種,startServicebindService碍侦,先 單獨 地看一下這兩種方式的調(diào)用粱坤。

  • startService:如果該Service沒有運行,那么會先調(diào)用onCreate方法瓷产,之后再回調(diào)onStartCommand方法比规,如果再次啟動已經(jīng)在運行的Service,仍然會回調(diào)該方法拦英。如果調(diào)用了stopService,那么會回調(diào)onDestroy方法测秸。
  • bindService:如果該Service沒有運行疤估,那么會先調(diào)用onCreate方法灾常,之后再回調(diào)onBind方法,如果再次啟動已經(jīng)在運行的Service铃拇,仍然會回調(diào)钞瀑。如果調(diào)用了unBindService,那么會回調(diào)onBindonDestroy方法慷荔。

假如同時通過startbind方式啟動了Service雕什,那么必須保證stopServiceunBindService都調(diào)用后,Service才會被銷毀显晶。

1.2 onStartCommand 返回值的含義

onStartCommand決定了Service被系統(tǒng)殺死后的處理行為贷岸。

START_NOT_STICKY

如果ServiceonStartCommand返回之后被殺死,它不會重啟磷雇。

START_STICKY

如果ServiceonStartCommand返回之后被殺死偿警,并在稍后 嘗試重新創(chuàng)建 這個Service,依次回調(diào)onCreate唯笙、onStartCommand螟蒸,onStartCommand當中傳入的Intent將為null

START_REDELIVER_INTENT

如果ServiceonStartCommand返回之后被殺死崩掘,那么系統(tǒng)會重新創(chuàng)建這個Service七嫌,依次回調(diào)onCreateonStartCommand苞慢,onStartCommand當中的Intent為最后一次傳遞的Intent诵原。

1.3 同一進程通過 Service 進行通信

前面我們有談到Service啟動的兩種方式,下面我們介紹一下在同一進程中枉疼,如何和Service進行交互皮假。

1.3.1 startService 下的交互

通過startService啟動服務(wù)的時候,只能通過onStartCommand當中的Intent進行交互骂维,Service根據(jù)Intent中的action區(qū)分行為惹资,intent的數(shù)據(jù)作為輸入?yún)?shù)。

這種方式的優(yōu)點是 簡單航闺,缺點是 這種通信方式是單向的褪测,只能由調(diào)用者告訴Service做什么,Service無法返回給調(diào)用者信息潦刃。

/**
 * @author lizejun
 **/
public class CommandWorkerService extends Service {

    private static final String TAG = CommandWorkerService.class.getSimpleName();

    public static final String ACTION_LOG = "com.android.action.log";
    public static final String ACTION_KEY_LOG_MSG = "com.android.action.log.msg";

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        handleIntent(intent);
        return super.onStartCommand(intent, flags, startId);
    }

    private void handleIntent(Intent intent) {
        String action = intent.getAction();
        if (!TextUtils.isEmpty(action)) {
            switch (action) {
                case ACTION_LOG:
                    Log.d(TAG, intent.getStringExtra(ACTION_KEY_LOG_MSG));
                    break;
                default:
                    break;
            }
        }
    }

}

調(diào)用者的處理方式:

public class MainActivity extends AppCompatActivity {


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        startService();
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        stopService();
    }

    private void startService() {
        Intent intent = new Intent(this, CommandWorkerService.class);
        intent.setAction(CommandWorkerService.ACTION_LOG);
        intent.putExtra(CommandWorkerService.ACTION_KEY_LOG_MSG, "Call CommandWorkerService");
        startService(intent);
    }

    private void stopService() {
        Intent intent = new Intent(this, CommandWorkerService.class);
        stopService(intent);
    }
}

1.3.2 bindService 下的交互

首先定義調(diào)用者和Service之間交互的接口侮措。

/**
 * 契約類,定義和 Service 之間交互的接口乖杠。
 *
 * @author lizejun
 **/
public interface IBindWorker {
    
    /**
     * 調(diào)用方法分扎。
     */
    int add(int a, int b);
}

實現(xiàn)Service,在onBind方法中胧洒,返回一個Binder的子類畏吓,同時實現(xiàn)了契約類的接口墨状。

/**
 * @author lizejun
 **/
public class BindWorkerService extends Service {

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

    private class BindWorker extends Binder implements IBindWorker {

        @Override
        public int add(int a, int b) {
            return a + b;
        }
    }
}

實現(xiàn)調(diào)用者,在bindService方法調(diào)用時菲饼,會要求傳入ServiceConnection的子類肾砂,在該子類的onServiceConnected會返回一個IBinder,這個就是與Service交互的橋梁宏悦,將它轉(zhuǎn)型為契約類镐确,通過它來調(diào)用相應(yīng)的接口。

public class MainActivity extends AppCompatActivity {

    private ServiceConnection mConnection;
    private IBindWorker mWorker;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        bindService();
        findViewById(R.id.tv_hello).setOnClickListener(new View.OnClickListener() {

            @Override
            public void onClick(View v) {
                if (mWorker != null) {
                    //調(diào)用接口方法饼煞。
                    Log.d("IBindWorker", "result=" + mWorker.add(1, 2));
                }
            }
        });
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        unBindService();
    }

    private void bindService() {
        Intent intent = new Intent(this, BindWorkerService.class);
        mConnection = new BindWorkerConnection();
        bindService(intent, mConnection, Service.BIND_AUTO_CREATE);
    }

    private void unBindService() {
        unbindService(mConnection);
    }

    private class BindWorkerConnection implements ServiceConnection {

        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            //通過返回的 IBinder 子類源葫,與 Service 進行交互。
            mWorker = (IBindWorker) service;
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {

        }
    }
}

1.4 不同進程通過 Service 進行通信

在不同的進程之間進行通信派哲,需要采用AIDL來實現(xiàn)臼氨,這個在 Framework 源碼解析知識梳理(3) - 應(yīng)用進程之間的通信實現(xiàn) 中有詳細的介紹。

1.5 Service 和 Thread 對比

1.5.1 定義區(qū)別

  • Thread是線程執(zhí)行的最小單元芭届,也是分配CPU的基本單位储矩。
  • ServiceAndroid的四大組件之一,通過Binder實現(xiàn)沒有UI的后臺服務(wù)褂乍。

1.5.2 使用場景

  • 由于Android中不允許在主線程執(zhí)行耗時的操作持隧,因此常通過Thread將耗時的任務(wù)放在子線程中進行。
  • Service默認是運行于主線程中的逃片,因此不可以在其中執(zhí)行耗時的操作屡拨,否則會產(chǎn)生ANR臊恋,它適合于長時間運行在后臺承耿,且不需要交互的場景。

1.6 前臺服務(wù)

前臺服務(wù)相比于普通的后臺Service有更高的優(yōu)先級里烦,因此在內(nèi)存不足時损离,也不會考慮將其終止哥艇。前臺服務(wù)要求提供通知欄。

  • startForeground(int id, Notification notification):將當前服務(wù)設(shè)為前臺服務(wù)僻澎,id參數(shù)表示唯一標識通知的整數(shù)貌踏,不允許為0Notification是一個通知欄的通知窟勃。
  • stopForeground(boolean removeNotification)從前臺刪除服務(wù)祖乳,boolean表示是否也刪除狀態(tài)欄通知,該方法并不會停止服務(wù)秉氧。

1.7 Android 5.0 之后不允許隱式啟動服務(wù)

Android 5.0之后眷昆,啟動服務(wù)的時候必須要提供component或者package,否則會拋出異常。

1.8 IntentService

IntentService繼承于Service隙赁,因此我們可以像start普通Service那樣啟動它垦藏。

它常被用于在 后臺執(zhí)行單次的耗時任務(wù),任務(wù)的執(zhí)行邏輯放在onHandleIntent方法當中伞访。它內(nèi)部是采用HandlerThread來實現(xiàn)的,因此如果有多個任務(wù)轰驳,那么將會排隊等待執(zhí)行厚掷。

onHandleIntent執(zhí)行完后,會調(diào)用stopSelf(int startId)级解。

public abstract class IntentService extends Service {

    private volatile Looper mServiceLooper;
    private volatile ServiceHandler mServiceHandler;
    private String mName;
    private boolean mRedelivery;

    private final class ServiceHandler extends Handler {
        public ServiceHandler(Looper looper) {
            super(looper);
        }

        @Override
        public void handleMessage(Message msg) {
            onHandleIntent((Intent)msg.obj);
            stopSelf(msg.arg1);
        }
    }

這個方法和stopSelf()的區(qū)別在于冒黑,stopSelf(int startId) 在只有最后依次啟動服務(wù)的 ID 與它相同時,才會停止服務(wù)勤哗,也就是說抡爹,當最后一次發(fā)送給IntentService的任務(wù)被執(zhí)行完后,該服務(wù)會自動停止芒划。

二冬竟、參考文章

關(guān)于 Android Service真正的完全詳解,你需要知道的一切

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末民逼,一起剝皮案震驚了整個濱河市泵殴,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌拼苍,老刑警劉巖笑诅,帶你破解...
    沈念sama閱讀 211,123評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異疮鲫,居然都是意外死亡吆你,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,031評論 2 384
  • 文/潘曉璐 我一進店門俊犯,熙熙樓的掌柜王于貴愁眉苦臉地迎上來妇多,“玉大人,你說我怎么就攤上這事瘫析∑霭穑” “怎么了?”我有些...
    開封第一講書人閱讀 156,723評論 0 345
  • 文/不壞的土叔 我叫張陵贬循,是天一觀的道長咸包。 經(jīng)常有香客問我,道長杖虾,這世上最難降的妖魔是什么烂瘫? 我笑而不...
    開封第一講書人閱讀 56,357評論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上坟比,老公的妹妹穿的比我還像新娘芦鳍。我一直安慰自己,他們只是感情好葛账,可當我...
    茶點故事閱讀 65,412評論 5 384
  • 文/花漫 我一把揭開白布柠衅。 她就那樣靜靜地躺著,像睡著了一般籍琳。 火紅的嫁衣襯著肌膚如雪菲宴。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,760評論 1 289
  • 那天趋急,我揣著相機與錄音喝峦,去河邊找鬼。 笑死呜达,一個胖子當著我的面吹牛谣蠢,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播查近,決...
    沈念sama閱讀 38,904評論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼眉踱,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了嗦嗡?” 一聲冷哼從身側(cè)響起勋锤,我...
    開封第一講書人閱讀 37,672評論 0 266
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎侥祭,沒想到半個月后叁执,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,118評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡矮冬,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,456評論 2 325
  • 正文 我和宋清朗相戀三年谈宛,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片胎署。...
    茶點故事閱讀 38,599評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡吆录,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出琼牧,到底是詐尸還是另有隱情恢筝,我是刑警寧澤,帶...
    沈念sama閱讀 34,264評論 4 328
  • 正文 年R本政府宣布巨坊,位于F島的核電站撬槽,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏趾撵。R本人自食惡果不足惜侄柔,卻給世界環(huán)境...
    茶點故事閱讀 39,857評論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧暂题,春花似錦移剪、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,731評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至啸胧,卻和暖如春赶站,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背纺念。 一陣腳步聲響...
    開封第一講書人閱讀 31,956評論 1 264
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留想括,地道東北人陷谱。 一個月前我還...
    沈念sama閱讀 46,286評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像瑟蜈,于是被迫代替她去往敵國和親烟逊。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 43,465評論 2 348

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

  • 【Android Service】 Service 簡介(★★★) 很多情況下铺根,一些與用戶很少需要產(chǎn)生交互的應(yīng)用程...
    Rtia閱讀 3,143評論 1 21
  • 前言:本文所寫的是博主的個人見解宪躯,如有錯誤或者不恰當之處,歡迎私信博主位迂,加以改正访雪!原文鏈接,demo鏈接 Serv...
    PassersHowe閱讀 1,408評論 0 5
  • 服務(wù)基本上分為兩種形式 啟動 當應(yīng)用組件(如 Activity)通過調(diào)用 startService() 啟動服務(wù)時...
    pifoo閱讀 1,261評論 0 8
  • 上篇我們講解了Android中的5中等級的進程掂林,分別是:前臺進程臣缀、可見進程、服務(wù)進程泻帮、后臺進程精置、空進程。系統(tǒng)會按照...
    徐愛卿閱讀 3,850評論 6 33
  • 企業(yè)文化 破 (打破锣杂,突破脂倦,破解,破壞元莫,乘風破浪) (命若天定赖阻,我就破了這個天) 企業(yè)價值觀 渺小而不卑微,璀璨而...
    王昱鑫Fistar閱讀 319評論 0 2