服務(wù)(Service)

Service是一種可以在后臺(tái)執(zhí)行耗時(shí)操作而沒有用戶界面的應(yīng)用組件锚烦。它默認(rèn)運(yùn)行在主線程中邦投,不可以直接進(jìn)行耗時(shí)操作,關(guān)于在Service中進(jìn)行耗時(shí)操作詳見本文末尾 —— IntentService膨更。

Android四大組件中只有Activity和Service是Context的子類

如果用戶在應(yīng)用管理界面手動(dòng)停止了Service所在進(jìn)程挺邀,Service就會(huì)停止;如果是內(nèi)存不足導(dǎo)致Android系統(tǒng)殺死了Service所在進(jìn)程灯萍,Service也會(huì)停止轧铁,只是當(dāng)內(nèi)存充足時(shí)系統(tǒng)又會(huì)重啟該Service所在進(jìn)程(服務(wù)進(jìn)程),Service也會(huì)被重新啟動(dòng)旦棉。

Android進(jìn)程優(yōu)先級(jí)

  1. 前臺(tái)進(jìn)程(Foreground process):優(yōu)先級(jí)最高齿风,最重要并且最后一個(gè)被Android系統(tǒng)殺死药薯。滿足下面任意一條的進(jìn)程就是前臺(tái)進(jìn)程

    • 擁有一個(gè)正在與用戶交互的Activity(onResume()被調(diào)用)
    • 擁有一個(gè)與其他進(jìn)程中正在與用戶交互的Activity綁定的Service
    • 擁有一個(gè)正在執(zhí)行onCreate()onStartCommand()救斑、onDestroy()其中一個(gè)生命周期方法的Service(Service在執(zhí)行生命周期方法時(shí)童本,短暫提高其所在進(jìn)程的優(yōu)先級(jí),以保證進(jìn)程不會(huì)被系統(tǒng)殺死)
    • 擁有一個(gè)正在執(zhí)行onReceive()的BroadcastReceiver(BroadcastReceiver在收到廣播時(shí)脸候,短暫提高其所在進(jìn)程的優(yōu)先級(jí)穷娱,以保證進(jìn)程不會(huì)被系統(tǒng)殺死)
  2. 可見進(jìn)程(Visible process):滿足下面任意一條的進(jìn)程就是可見進(jìn)程

    • 擁有一個(gè)不在前臺(tái)(失去焦點(diǎn))但是對(duì)用戶依然可見的Activity(onPause()被調(diào)用)
    • 擁有一個(gè)與其他進(jìn)程中可見Activity綁定的Service
    • 擁有一個(gè)調(diào)用了service.startForeground(id, notification)運(yùn)行在前臺(tái)的Service
  3. 服務(wù)進(jìn)程(Service process):擁有一個(gè)通過content.startService(intent)啟動(dòng)的Service,不到萬不得已時(shí)不會(huì)被Android系統(tǒng)殺死运沦。即使在內(nèi)存不足時(shí)被系統(tǒng)殺死了鄙煤,等到內(nèi)存充足時(shí)仍然可以被重新啟動(dòng),繼續(xù)運(yùn)行該Service茶袒。只有服務(wù)進(jìn)程才可以用來做文件下載梯刚、音樂播放等后臺(tái)操作

  4. 后臺(tái)進(jìn)程(Background process):擁有一個(gè)用戶不可見的Activity(onStop()被調(diào)用),很容易被Android系統(tǒng)殺死薪寓,且不會(huì)被重新啟動(dòng)

  5. 空進(jìn)程(Empty process):不含有任何活動(dòng)的應(yīng)用組件(主要是Activity和Service亡资,BroadcastReceiver的生命周期很短),保留空進(jìn)程的唯一目的就是作為緩存向叉,以加快下次在此進(jìn)程中運(yùn)行組件的啟動(dòng)速度锥腻,優(yōu)先級(jí)最低,最容易被Android系統(tǒng)殺死母谎,且不會(huì)被重新啟動(dòng)

對(duì)于優(yōu)先級(jí)相同的進(jìn)程瘦黑,當(dāng)內(nèi)存不足時(shí),Android系統(tǒng)會(huì)依據(jù)LRU算法決定殺死哪一個(gè)進(jìn)程


Service的定義

創(chuàng)建一個(gè)類XxxService繼承Service奇唤,并重寫onBind()

public class XxxService extends Service {

    /**
     * bindService時(shí)才會(huì)回調(diào)幸斥,必須實(shí)現(xiàn)的方法
     *
     * @param intent
     * @return
     */
    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }
}

在清單文件中配置該Service

<service android:name=".XxxService"></service>

Service的兩種啟動(dòng)方式

從Android 5.0開始,Google要求必須使用顯式Intent啟動(dòng)Service
在Android系統(tǒng)中啟動(dòng)Service有如下兩種方式:

1. startService

通過content.startService(intent)啟動(dòng)Service咬扇,將觸發(fā)Service的生命周期方法:onCreate() —> onStartCommand()
因?yàn)镾ervice沒有前臺(tái)界面所以Google使用onStartCommand()替代onStart()甲葬,它有一個(gè)int類型的返回值,如果返回START_STICKY懈贺,意味著如果Service所在進(jìn)程因?yàn)橄到y(tǒng)內(nèi)存不足而被殺掉经窖,當(dāng)內(nèi)存充足時(shí)系統(tǒng)還會(huì)嘗試重新創(chuàng)建這個(gè)Service,重新創(chuàng)建Service又會(huì)回調(diào)onStartCommand()梭灿,但這次傳入onStartCommand()中的Intent將為null

Intent intent = new Intent(this, XxxService.class);
startService(intent);

重復(fù)的startService()不會(huì)回調(diào)onCreate()画侣,只會(huì)回調(diào)onStartCommand()

不再使用時(shí),可以通過service.stopSelf()context.stopService(intent)停止Service堡妒,將觸發(fā)Service的生命周期方法:onDestroy()

Intent intent = new Intent(this, XxxService.class);
stopService(intent);

通過startService()啟動(dòng)的Service與啟動(dòng)它的Activity沒有任何關(guān)系配乱,即使Activity被銷毀了,Service也不會(huì)停止。另外宪卿,通過這種方式啟動(dòng)Service,該Service所在進(jìn)程的優(yōu)先級(jí)將不會(huì)低于服務(wù)進(jìn)程万栅。

2. bindService

通過content.bindService(intent, conn, flags)啟動(dòng)Service佑钾,需要定義ServiceConnection接口的實(shí)現(xiàn)類

public class XxxConnection implements ServiceConnection {


    /**
     * 當(dāng)?shù)絊ervice的連接被建立了(Service的onBind()執(zhí)行成功)
     * 并返回了一個(gè)非空的IBinder對(duì)象,此方法才會(huì)回調(diào)
     *
     * @param name
     * @param service   這個(gè)對(duì)象就是onBind()返回的中間人IBinder
     */
    @Override
    public void onServiceConnected(ComponentName name, IBinder service) {

    }

    /**
     * 當(dāng)?shù)絊ervice的連接因?yàn)楫惓6袛喾沉#朔椒ú艜?huì)回調(diào)休溶,正常的解綁不會(huì)回調(diào)此方法
     *
     * @param name
     */
    @Override
    public void onServiceDisconnected(ComponentName name) {

    }
}

然后創(chuàng)建該實(shí)現(xiàn)類的對(duì)象

XxxConnection conn = new XxxConnection();

通過bindService啟動(dòng)(綁定)Service,將觸發(fā)Service的生命周期方法:onCreate() —> onBind()

Intent intent = new Intent(this, XxxService.class);
bindService(intent, conn, BIND_AUTO_CREATE);  // BIND_AUTO_CREATE表示如果XxxService不存在則自動(dòng)創(chuàng)建它

重復(fù)的bindService不會(huì)回調(diào)onCreate()onBind().

通過context.unbindService(conn)停止(解綁)Service將觸法生命周期方法:onUnbind() —> onDestroy()

unbindService(conn);

通常不推薦使用ApplicationContext去bindService扰她,如果通過這種方式bind了一個(gè)Local Service兽掰,此后每次start Remote Service都會(huì)拋出異常:android.os.BinderProxy cannot be cast to XxxService$XxxServiceBinder
如果Service想要同整個(gè)應(yīng)用的生命周期一致,可與MainActivity進(jìn)行bind

通過bindService()啟動(dòng)Service徒役,也叫綁定(多個(gè)Activity可以綁定一個(gè)Service)孽尽,它使Service與啟動(dòng)它的Activity建立連接:如果Activity被銷毀了,Service也會(huì)被解綁并銷毀忧勿;但是如果Service被銷毀了杉女,Activity則不會(huì)被銷毀。另外鸳吸,通過這種方式啟動(dòng)(綁定)Service熏挎,該Service所在的進(jìn)程優(yōu)先級(jí)不變(仍取決于啟動(dòng)服務(wù)的Activity)。

startService的應(yīng)用:后臺(tái)操作

Android系統(tǒng)通話有三種狀態(tài):空閑晌砾、響鈴坎拐、摘機(jī),我們可以在摘機(jī)時(shí)通過Service進(jìn)行后臺(tái)錄音來實(shí)現(xiàn)通話錄音機(jī)养匈。

定義一個(gè)RecorderService讓其在創(chuàng)建時(shí)就開始監(jiān)聽電話狀態(tài)

public class RecorderService extends Service {


    @Override
    public void onCreate() {
        super.onCreate();
        TelephonyManager tm = (TelephonyManager) getSystemService(TELEPHONY_SERVICE); // 獲取電話管理器
        tm.listen(new MyListener(), PhoneStateListener.LISTEN_CALL_STATE);  // 監(jiān)聽電話狀態(tài)
    }

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

自定義電話狀態(tài)監(jiān)聽器哼勇,當(dāng)響鈴時(shí)進(jìn)行音頻錄制的初始化(申請(qǐng)硬件資源),摘機(jī)時(shí)開始錄制呕乎,空閑時(shí)回收音頻錄制所占用的資源

class MyListener extends PhoneStateListener {


    /**
     * 電話狀態(tài)改變時(shí)回調(diào)
     *
     * @param state
     * @param incomingNumber
     */
    @Override
    public void onCallStateChanged(int state, String incomingNumber) {
        super.onCallStateChanged(state, incomingNumber);
        switch (state) {    // 判斷當(dāng)前是什么狀態(tài)
            case TelephonyManager.CALL_STATE_IDLE:  // 空閑
                if (mediaRecorder != null) {
                    mediaRecorder.stop();       // 停止錄制音頻
                    mediaRecorder.release();    // 釋放錄音所占用的硬件資源(C代碼所占用的)
                    mediaRecorder = null;       // 等待垃圾回收器回收java對(duì)象資源
                }
                break;
            case TelephonyManager.CALL_STATE_RINGING:   // 響鈴猴蹂,進(jìn)行音頻錄制的初始化
                if (mediaRecorder == null) {
                    mediaRecorder = new MediaRecorder();
                    mediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);        // 設(shè)置音頻源為麥克風(fēng)
                    mediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);// 設(shè)置所錄制的音頻文件格式為3gp
                    mediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);// 設(shè)置所錄制音頻的編碼格式AMR_NB(3gp文件格式的一種最常見的音頻編碼格式)
                    mediaRecorder.setOutputFile("sdcard/voice.3gp");// 設(shè)置所錄制的音頻文件的保存位置
                    try {
                        mediaRecorder.prepare();// 準(zhǔn)備錄制音頻
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
                break;
            case TelephonyManager.CALL_STATE_OFFHOOK:   // 摘機(jī)
                if (mediaRecorder == null) {
                    mediaRecorder.start();  // 開始錄制音頻
                }
                break;
        }
    }
}

注意不要忘了在清單文件中配置RecorderService以及申請(qǐng)所需要的權(quán)限

<uses-permission android:name="android.permission.READ_PHONE_STATE"/>       <!-- 讀取通話狀態(tài)權(quán)限 -->
<uses-permission android:name="android.permission.RECORD_AUDIO"/>           <!-- 錄制音頻權(quán)限 -->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> <!-- 寫SD卡權(quán)限 -->

<service android:name=".RecorderService"></service>     <!-- 配置RecorderService -->

bindService的應(yīng)用:調(diào)用Service中的方法

在應(yīng)用中啟動(dòng)Service,系統(tǒng)會(huì)自動(dòng)為我們創(chuàng)建這個(gè)Service對(duì)象楣嘁,但是我們無法直接拿到這個(gè)Service對(duì)象的引用磅轻,也就無法在前臺(tái)調(diào)用這個(gè)Service中的非靜態(tài)方法。

想要找人辦個(gè)證逐虚,但不認(rèn)識(shí)領(lǐng)導(dǎo)聋溜,需要通過中間人馮秘書牽線!
把Service看成一個(gè)領(lǐng)導(dǎo)叭爱,服務(wù)中有一個(gè)banZheng()撮躁,用bindService()綁定Service時(shí),會(huì)觸發(fā)Service的onBind()买雾,此方法會(huì)返回一個(gè)中間人Ibinder對(duì)象把曼,前臺(tái)可以在bindService()時(shí)傳入的ServiceConnection實(shí)現(xiàn)類對(duì)象的onServiceConnected()中拿到這個(gè)Ibinder對(duì)象杨帽,通過這個(gè)對(duì)象就可以訪問Service中的banZheng()

定義一個(gè)服務(wù)LeaderService嗤军,在服務(wù)中定義一個(gè)FengMiShu繼承Binder(實(shí)現(xiàn)了Ibinder接口)作為中間人注盈,以在onBind()中返回該類對(duì)象。

public class LeaderService extends Service {


    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return new FengMiShu();     // 返回中間人IBinder對(duì)象
    }

    /**
     * 辦證
     */
    public void banzheng() {
        System.out.println("成功辦證");
    }


    /**
     * 馮秘書(中間人)
     */
    class FengMiShu extends Binder implements PublicBusiness {

        /**
         * 中間人的牽線
         */
        @Override
        public void qianXian() {
            banzheng();             // 調(diào)用領(lǐng)導(dǎo)的辦證
        }

        /**
         * 沒有抽象到接口中叙赚,不能隨便調(diào)用(撿肥皂不是對(duì)公業(yè)務(wù))
         */
        public void jianFeiZao() {

        }
    }
}

qianXian()抽象到PublicBusiness接口中

public interface PublicBusiness {

    void qianXian();  // 把需要被Activity調(diào)用的方法抽象到接口中(牽線屬于對(duì)公業(yè)務(wù))
}

在Activity中綁定服務(wù)時(shí)傳入一個(gè)ServiceConnection實(shí)現(xiàn)類的對(duì)象老客,如果不考慮解綁服務(wù)可直接用匿名內(nèi)部類定義

Intent intent = new Intent(this, LeaderService.class);

bindService(intent, new ServiceConnection() {

    /**
     * 到Service的連接被建立了(Service的onBind()執(zhí)行成功),并返回了一個(gè)非空的IBinder對(duì)象震叮,此方法才會(huì)回調(diào)
     *
     * @param name
     * @param service   這個(gè)對(duì)象就是onBind()返回的中間人IBinder
     */
    @Override
    public void onServiceConnected (ComponentName name, IBinder service){
        publicBusiness = (PublicBusiness) service; // 拿到中間人馮秘書胧砰,強(qiáng)轉(zhuǎn)成PublicBusiness使其只能調(diào)用對(duì)公業(yè)務(wù)
    }

    /**
     * 到Service的連接因?yàn)楫惓6袛啵朔椒ú艜?huì)回調(diào)苇瓣,正常的解綁不會(huì)回調(diào)此方法
     *
     * @param name
     */
    @Override
    public void onServiceDisconnected (ComponentName name){

    }

},BIND_AUTO_CREATE);

最后在Activity需要調(diào)用LeaderService的banzheng()處調(diào)用以下代碼

publicBusiness.qianXian();  // 馮秘書的對(duì)公業(yè)務(wù)牽線
//    publicBusiness.jianFeiZao();    // 非對(duì)公業(yè)務(wù)尉间,調(diào)用不了

Service的混合啟動(dòng):音樂播放器

實(shí)現(xiàn)音樂播放時(shí),要保證在Activity銷毀后音樂仍在后臺(tái)播放击罪,進(jìn)程不會(huì)變成空進(jìn)程而在內(nèi)存不足時(shí)被系統(tǒng)殺死乌妒,必須要通過startService()把進(jìn)程變成服務(wù)進(jìn)程⊥獾耍可是音樂Service中的方法撤蚊,需要被前臺(tái)Activity所調(diào)用(例如當(dāng)用戶點(diǎn)擊開始或暫停按鈕要分別觸發(fā)Service中的播放音樂和暫停音樂),又必須要用bindService()綁定服務(wù)损话,獲取Binder對(duì)象侦啸。所以需要用到上述兩種方式混合啟動(dòng)音樂Service,并且startService()必須在bindService()之前執(zhí)行丧枪。詳見多媒體編程:二光涂、音頻播放

混合啟動(dòng)服務(wù)時(shí)先startService(),再bindService()拧烦;停止服務(wù)時(shí)先unbindService()忘闻,再stopService()


Local Service 與 Remote Service

  • 對(duì)于Web開發(fā),Local Service是指提供服務(wù)的程序在本地恋博,而Remote Service是指提供服務(wù)的程序在服務(wù)器

  • 對(duì)于Android開發(fā)齐佳,Local Service和Remote Service都運(yùn)行在我們的手機(jī)上

Local Service

Local Service是指與啟動(dòng)它的組件都在同一進(jìn)程的Service,前面啟動(dòng)的Service均是Local Service.

Remote Service

Remote Service是指與啟動(dòng)它的組件不在同一進(jìn)程的Service债沮,它分為兩種:

  • 同一應(yīng)用中的Remote Service:在清單文件中明確指定了Service所在的進(jìn)程炼吴,對(duì)于該應(yīng)用中位于不同進(jìn)程的其它組件,該Service就是一個(gè)Remote Service

    <service
        android:name=".RemoteService"
        android:process=":newProcess" />
    
  • 不同應(yīng)用中的Remote Service:在清單文件中配置了intent-filter子節(jié)點(diǎn)(此時(shí) android:exported="true")疫衩,并指定action硅蹦,對(duì)于其他應(yīng)用(一般位于不同進(jìn)程,特例Android中如何設(shè)置兩個(gè)應(yīng)用程序?yàn)橥粋€(gè)進(jìn)程?)中的組件童芹,該Service就是一個(gè)Remote Service

    <service android:name=".RemoteService">
        <intent-filter>
            <action android:name="a.b.c" />
        </intent-filter>
    </service>
    

    從Android 5.0以后涮瞻,開發(fā)者只能顯式的(指定包名)跨應(yīng)用啟動(dòng)Remote Service,下面是通過startService的方式跨應(yīng)用啟動(dòng)Remote Service的代碼:

    Intent intent = new Intent();
    intent.setPackage("edu.neu.steve.remoteservice");// Android 5.0之  后必須指明遠(yuǎn)程Service所在的應(yīng)用包名    
    intent.setAction("a.b.c");        // 與遠(yuǎn)程Service注冊(cè)時(shí)的action匹配
    startService(intent);
    

AIDL

AIDL(Android Interface Defination Language)即Android接口定義語言假褪,用于Android進(jìn)程間通信署咽。
下面使用AIDL實(shí)現(xiàn)在Client端Bind Server端的Remote Service,從而調(diào)用Server端提供的遠(yuǎn)程服務(wù)嗜价,這里假設(shè)Client和Server不在同一應(yīng)用中。

Server端
  1. 在java目錄上右鍵幕庐,創(chuàng)建一個(gè)AIDL文件PublicBusiness.aidl久锥,并在其中聲明中間人的對(duì)公業(yè)務(wù)qianXian(),然后Make一下异剥,會(huì)自動(dòng)生成一個(gè)PublicBusiness.java(位于app/build/generated/source/aidl/debug下)
interface PublicBusiness {

    void qianXian();
}

aidl接口中所有成員都是public的瑟由,不需要也不能自己設(shè)置訪問修飾符

  1. 在RemoteService內(nèi)定義中間人FengMiShu直接繼承抽象類PublicBusiness.Stub(查看生成的PublicBusiness.java源碼可知,這個(gè)內(nèi)部類Stub繼承自Binder并實(shí)現(xiàn)了PublicBusiness接口)冤寿,實(shí)現(xiàn)對(duì)公業(yè)務(wù)qianXian()歹苦,然后在onBind()中返回FengMiShu的實(shí)例。RemoteService在清單文件中的配置同上
public class RemoteService extends Service {


    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return new FengMiShu();  // 返回中間人的實(shí)例
    }

    public void remoteBanzheng() {
        System.out.println("領(lǐng)導(dǎo)在國外遠(yuǎn)程辦證");
    }


    /**
     * 中間人馮秘書直接繼承Stub并實(shí)現(xiàn)qianXian()
     */
    class FengMiShu extends PublicBusiness.Stub {
        @Override
        public void qianXian() {
            remoteBanzheng();
        }
    }
}
Client端
  1. 由于Client和Server不在同一應(yīng)用中督怜,需要把Server端的PublicBusiness.aidl文件復(fù)制一份放到Client端殴瘦,保持該AIDL文件所在包名與Server端的AIDL文件所在包名一致。(如果Client和Server在同一應(yīng)用中号杠,則跳過該步)

  2. 在客戶端bind RemoteService蚪腋,綁定成功后使用PublicBusiness.Stub.asInterface()將從onServiceConnected()中獲取到的中間人IBinder對(duì)象強(qiáng)轉(zhuǎn)成PublicBusiness類型(不同進(jìn)程中實(shí)際上返回的是一個(gè)代理),在需要時(shí)調(diào)用這個(gè)PublicBusiness對(duì)象的qianXian()即可實(shí)現(xiàn)遠(yuǎn)程辦證

public class MainActivity extends AppCompatActivity {

    public PublicBusiness publicBusiness;
    private MyConnection myConn;

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

        myConn = new MyConnection();
    }

    class MyConnection implements ServiceConnection {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            publicBusiness = PublicBusiness.Stub.asInterface(service);  // 強(qiáng)轉(zhuǎn)成PublicBusiness
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {

        }
    }

    /**
     * 綁定服務(wù)
     *
     * @param v
     */
    public void bind(View v) {
        Intent intent = new Intent();
        intent.setPackage("edu.neu.steve.remoteservice");
        intent.setAction("a.b.c");
        bindService(intent, myConn, BIND_AUTO_CREATE);
    }

    /**
     * 解綁服務(wù)
     *
     * @param v
     */
    public void unbind(View v) {
        unbindService(myConn);
    }

    /**
     * 在Activity中辦證
     *
     * @param v
     */
    public void banZheng(View v) {
        try {
            publicBusiness.qianXian();  // 中間人前線姨蟋,領(lǐng)導(dǎo)在國外遠(yuǎn)程辦證
        } catch (RemoteException e) {
            e.printStackTrace();
        }
    }
}

IntentService

在Android開發(fā)中屉凯,我們一般需要在Service中開啟一個(gè)子線程來執(zhí)行類似下載這樣的后臺(tái)耗時(shí)操作,因?yàn)锳ctivity可能會(huì)被用戶退出眼溶,而BroadcastReceiver的生命周期本身就很短悠砚,在Service中開啟子線程進(jìn)行耗時(shí)操作可以避免應(yīng)用進(jìn)程變?yōu)榭者M(jìn)程在內(nèi)存不足時(shí)被系統(tǒng)殺死。如果仍然擔(dān)心Service所在進(jìn)程被殺堂飞,還可以通過調(diào)用service.startForeground(id, notification)提升應(yīng)用進(jìn)程的優(yōu)先級(jí)灌旧,這是一種比較常見的進(jìn)程保活方式绰筛。

然而自己去管理Service的生命周期以及子線程并非是個(gè)優(yōu)雅的做法节榜,好在Android給我們提供了IntentService,IntentService是Service的子類别智,用來處理異步請(qǐng)求宗苍,在IntentService內(nèi)有一個(gè)worker線程來處理耗時(shí)操作。使用IntentService,我們不需要在Service中自己去開啟一個(gè)子線程讳窟,也不需要考慮在什么時(shí)候停止Service.

擴(kuò)展IntentService實(shí)現(xiàn)Service無須重寫onBind()让歼、onStartCommand(),只需要重寫onHandleIntent()

public class MyIntentService extends IntentService {


    /**
     * IntentService的構(gòu)造函數(shù)一定是參數(shù)為空的構(gòu)造函數(shù)
     * 然后再在其中調(diào)用super("name")這種形式的構(gòu)造函數(shù)
     * 因?yàn)镮ntentService的實(shí)例化是系統(tǒng)用參數(shù)為空的構(gòu)造函數(shù)來完成的
     */
    public MyIntentService() {
        super("MyIntentService");
    }

    /**
     * IntentService會(huì)使用單獨(dú)的線程來執(zhí)行該方法內(nèi)的代碼
     * 該方法內(nèi)可以執(zhí)行任何耗時(shí)任務(wù)丽啡,比如下載文件等谋右,此處讓線程暫停20秒來模擬耗時(shí)操作
     *
     * @param intent
     */
    @Override
    protected void onHandleIntent(Intent intent) {
        long endTime = System.currentTimeMillis() + 20 * 1000;
        while (System.currentTimeMillis() < endTime) {
            synchronized (this) {
                try {
                    wait(endTime - System.currentTimeMillis());
                } catch (Exception e) {
                }
            }
        }
        System.out.println("---耗時(shí)任務(wù)執(zhí)行完成---");
    }
}

不要忘了在清單文件中配置IntentService,因?yàn)樗^承于Service补箍,所以它仍是一個(gè)Service

<service android:name=".MyIntentService"></service>

客戶端可以通過startService(Intent)方法傳遞請(qǐng)求給IntentService

Intent intent = new Intent(this, MyIntentService.class);// 創(chuàng)建需要啟動(dòng)的IntentService的Intent
startService(intent);       // 啟動(dòng)IntentService

IntentService會(huì)將該Intent加入到隊(duì)列中改执,然后開啟一條新的worker線程來處理該Intent以及onHandleIntent()中的耗時(shí)操作,不會(huì)阻塞主線程坑雅。
對(duì)于異步的startService()請(qǐng)求辈挂,IntentService會(huì)按次序依次處理隊(duì)列中的Intent,worker線程保證同一時(shí)刻只處理一個(gè)Intent裹粤。當(dāng)所有請(qǐng)求處理完成后终蒂,IntentService會(huì)自動(dòng)停止

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末遥诉,一起剝皮案震驚了整個(gè)濱河市拇泣,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌矮锈,老刑警劉巖霉翔,帶你破解...
    沈念sama閱讀 222,000評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異苞笨,居然都是意外死亡早龟,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,745評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門猫缭,熙熙樓的掌柜王于貴愁眉苦臉地迎上來葱弟,“玉大人,你說我怎么就攤上這事猜丹≈ゼ樱” “怎么了?”我有些...
    開封第一講書人閱讀 168,561評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵射窒,是天一觀的道長藏杖。 經(jīng)常有香客問我,道長脉顿,這世上最難降的妖魔是什么蝌麸? 我笑而不...
    開封第一講書人閱讀 59,782評(píng)論 1 298
  • 正文 為了忘掉前任,我火速辦了婚禮艾疟,結(jié)果婚禮上来吩,老公的妹妹穿的比我還像新娘敢辩。我一直安慰自己,他們只是感情好弟疆,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,798評(píng)論 6 397
  • 文/花漫 我一把揭開白布戚长。 她就那樣靜靜地躺著,像睡著了一般怠苔。 火紅的嫁衣襯著肌膚如雪同廉。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,394評(píng)論 1 310
  • 那天柑司,我揣著相機(jī)與錄音迫肖,去河邊找鬼。 笑死攒驰,一個(gè)胖子當(dāng)著我的面吹牛蟆湖,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播讼育,決...
    沈念sama閱讀 40,952評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼帐姻,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼稠集!你這毒婦竟也來了奶段?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,852評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤剥纷,失蹤者是張志新(化名)和其女友劉穎痹籍,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體晦鞋,經(jīng)...
    沈念sama閱讀 46,409評(píng)論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡蹲缠,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,483評(píng)論 3 341
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了悠垛。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片线定。...
    茶點(diǎn)故事閱讀 40,615評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖确买,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤南蓬,帶...
    沈念sama閱讀 36,303評(píng)論 5 350
  • 正文 年R本政府宣布唯鸭,位于F島的核電站,受9級(jí)特大地震影響搀缠,放射性物質(zhì)發(fā)生泄漏铛楣。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,979評(píng)論 3 334
  • 文/蒙蒙 一艺普、第九天 我趴在偏房一處隱蔽的房頂上張望簸州。 院中可真熱鬧鉴竭,春花似錦、人聲如沸勿侯。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,470評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽助琐。三九已至祭埂,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間兵钮,已是汗流浹背蛆橡。 一陣腳步聲響...
    開封第一講書人閱讀 33,571評(píng)論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留掘譬,地道東北人泰演。 一個(gè)月前我還...
    沈念sama閱讀 49,041評(píng)論 3 377
  • 正文 我出身青樓,卻偏偏與公主長得像葱轩,于是被迫代替她去往敵國和親睦焕。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,630評(píng)論 2 359

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

  • 服務(wù)是什么靴拱?Service是Android系統(tǒng)中的四大組件之一垃喊,主要有兩個(gè)應(yīng)用場(chǎng)景:后臺(tái)運(yùn)行和跨進(jìn)程訪問。Serv...
    飛行員suke閱讀 984評(píng)論 0 1
  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,302評(píng)論 25 707
  • 概述 進(jìn)程 :進(jìn)程是一個(gè)應(yīng)用程序運(yùn)行的載體袜炕。在Android中本谜,進(jìn)程的底層是Linux管理的。Android中應(yīng)用...
    嘯天AskSky閱讀 638評(píng)論 4 5
  • 有人如夢(mèng) 有人如風(fēng) 夢(mèng)醒時(shí)分偎窘,清風(fēng)拂面 淚光中閃爍著不悔還是無奈 就這樣吧 我已無言乌助,直面你的美
    arman007閱讀 251評(píng)論 0 1
  • 突然,我就多了一個(gè)小院陌知。表面上看起來他托,我只是多了一個(gè)小院∑推希可是赏参,我的內(nèi)心是狂喜的,在這個(gè)小院出現(xiàn)的時(shí)刻浙芙,已經(jīng)呈現(xiàn)出...
    梅園遺珠閱讀 507評(píng)論 3 3