Android Service(服務(wù))詳解-附異步下載Demo

原創(chuàng)作品,轉(zhuǎn)載請(qǐng)注明出處O
Service是Android四大組件之一,它主要是去執(zhí)行耗時(shí)操作(不需要與用戶交互并且要長(zhǎng)期運(yùn)行的任務(wù))。

Service不依賴于用戶界面,但依賴于創(chuàng)建服務(wù)的應(yīng)用進(jìn)程,同時(shí)也不會(huì)自動(dòng)開(kāi)啟線程,所以我們需要在服務(wù)內(nèi)部手動(dòng)創(chuàng)建線程,否則就可能會(huì)出現(xiàn)線程阻塞。


在具體講解Service之前,我們先引入幾個(gè)關(guān)于Android多線程的知識(shí)點(diǎn):

1.Android多線程基本用法

多線程的作用就是避免在主線程做耗時(shí)操作而導(dǎo)致線程阻塞,從而影響用戶體驗(yàn)。

1.繼承Thread,重寫(xiě)run方法

public class MyThread extends Thread {

    @Override
    public void run() {
        //具體實(shí)現(xiàn)代碼
    }
}

調(diào)用   new MyThread().start();

2.實(shí)現(xiàn)Runnable接口

public class MyThread implements Runnable {

    @Override
    public void run() {
        //具體實(shí)現(xiàn)代碼
    }
}

調(diào)用  MyThread myThread = new MyThread();
     new Thread(myThread).start();

3.匿名類方式

new Thread(new Runnable() {
            @Override
            public void run() {
                //具體實(shí)體
            }
        }).start();

4.切記不可在子線程更新UI


2.異步消息處理

Android中的異步消息處理主要有4部分:

1.Message

Message是在線程之間傳遞的消息,它可以在內(nèi)部攜帶少量信息,用于在不同線程之間交換數(shù)據(jù)。

2.Handler

Handler主要用于發(fā)送和處理消息。

3.MessageQueue

MessageQueue是消息隊(duì)列的意思,主要用于存放所有通過(guò)Handler發(fā)送的消息。這部分消息會(huì)一直存在于消息隊(duì)列中,等待處理。

4.Looper

Looper是每個(gè)線程中的MessageQueue的管家,調(diào)用Looper的loop()方法后,就會(huì)進(jìn)入到一個(gè)無(wú)限循環(huán)中,每當(dāng)MessageQueue中存在一條消息,就將它取出,傳遞到Handler的handleMessage()方法中。

整個(gè)流程如圖所示(請(qǐng)忽略我清新脫俗的配圖)


3.AsyncTask

AsyncTask的實(shí)現(xiàn)原理也是基于異步消息處理機(jī)制

由于AsyncTask是一個(gè)抽象類,所以必須創(chuàng)建一個(gè)子類繼承它。在繼承時(shí)可以為AsyncTask類指定3個(gè)泛型參數(shù):

  • Params 在執(zhí)行AsyncTask時(shí)需要傳入的參數(shù),可用于后臺(tái)任務(wù)。
  • Progress 后臺(tái)任務(wù)執(zhí)行時(shí),可以使用這里指定的泛型作為進(jìn)度單位
  • Result 任務(wù)執(zhí)行完成時(shí),可以使用這里指定的泛型作為返回值類型
public class MyTask extends AsyncTask<Void,Integer,Boolean>{
    
}

現(xiàn)在創(chuàng)建完MyTask如果要使用還需要重寫(xiě)里面的方法,常用的有以下4個(gè):

1.onPreExecute()

后臺(tái)任務(wù)執(zhí)行前調(diào)用,用于界面上的一些初始化操作。

2.doInBackground(Params...)

代碼執(zhí)行在子線程,處理耗時(shí)操作。返回類型根據(jù)AsyncTask類的第三個(gè)泛型參數(shù)決定(上文中提到的Result)。
不可進(jìn)行UI操作,可通過(guò)publishProgress() 從而調(diào)用下一個(gè)方法onProgressUpdate()操作UI

3.onProgressUpdate(Progress...)

在這個(gè)方法中對(duì)UI進(jìn)行操作

4.onPostExecute(Result)

后臺(tái)任務(wù)執(zhí)行完成時(shí)調(diào)用此方法

  • 重寫(xiě)方法后變成這樣
public class MyTask extends AsyncTask<Void,Integer,Boolean>{

    @Override
    protected void onPreExecute() {
        //顯示對(duì)話框等UI操作
    }

    @Override
    protected Boolean doInBackground(Void... voids) {
        //執(zhí)行后臺(tái)任務(wù)
        //不可操作UI
        publishProgress(integer);//通過(guò)此方法調(diào)用onProgressUpdate
        return ture;
    }

    @Override
    protected void onProgressUpdate(Integer... values) {
        //獲取后臺(tái)執(zhí)行進(jìn)度
        //可操作UI
    }

    @Override
    protected void onPostExecute(Boolean aBoolean) {
        //后臺(tái)任務(wù)執(zhí)行完成
        //可操作UI
    }
}

啟動(dòng)AsyncTask,只需要簡(jiǎn)單的一句

new MyAsyncTask().execute();

上面我們介紹了Android多線程編程的幾個(gè)知識(shí)點(diǎn),接下來(lái)一起學(xué)習(xí)Service的基本用法。


  • 我們先來(lái)定義一個(gè)Service
public class MyService extends Service{
      public MyService(){
      }

      @Override
      public IBinder onBind(Intent intent){
            throw new UnsupportedOperationException("Not yet implemented");
      }
}

可以看到MyService繼承自Service類,里面只有一個(gè)onBind()方法,onBind()是Service中唯一的一個(gè)抽象方法,所以子類中必須要實(shí)現(xiàn)。

  • 此時(shí)的服務(wù)還是空的,如果我們要在里面處理一些事情,還需要實(shí)現(xiàn)幾個(gè)服務(wù)中常用的方法
public class MyService extends Service{
      public MyService(){
      }

      @Override
      public IBinder onBind(Intent intent){
            throw new UnsupportedOperationException("Not yet implemented");
      }

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

      @Override
      public void onDestroy(){
            super.onDestroy();
      }
}

可以看到MyService中又重寫(xiě)了onCreate()、onStartCommand()、onDestroy()
onCreate() 會(huì)在服務(wù)創(chuàng)建的時(shí)候調(diào)用
onStartCommand() 會(huì)在每次服務(wù)啟動(dòng)的時(shí)候調(diào)用
onDestroy() 會(huì)在服務(wù)銷毀的時(shí)候調(diào)用

  • 此時(shí)MyService類已經(jīng)創(chuàng)建完成,但要讓其生效還需要在AndroidManifest.xml文件中注冊(cè)
 <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

        <service
            android:name=".MyService"
            android:enabled="true"
            android:exported="true"></service>
    </application>

細(xì)心的盆友可能看到注冊(cè)的MyService還附帶有兩個(gè)屬性
enabled 是否啟動(dòng)此服務(wù)
exported 是否允許其它程序訪問(wèn)此服務(wù)

  • 現(xiàn)在,服務(wù)已經(jīng)定義好了,如何啟動(dòng)呢(啟動(dòng)完別忘記關(guān)掉啊
//啟動(dòng)
Intent startIntent = new Intent(context,MyService.class);
startService(startIntent);

//停止
Intent stopIntent = new Intent(context,MyService.class);
stopService(stopIntent);

看到這里是不是覺(jué)得很簡(jiǎn)單,不過(guò)有一點(diǎn)要注意下onCreate()只在服務(wù)第一次創(chuàng)建的時(shí)候調(diào)用,而onStartCommand()會(huì)在每次啟動(dòng)的時(shí)候都調(diào)用。

  • 既然Service已經(jīng)定義完成,那么怎么讓它起作用呢?這時(shí)候onBind()就樣登場(chǎng)了
public class MyService extends Service{
      ···
   private DownloadBinder mBinder = new DownloadBinder();

   class DownloadBinder extends Binder{
        public void startDownload(){
            Log.d("MyService","startDownload executed");
        }

        public int getProgress(){
            Log.d("MyService","getProgress executed");
            return 0;
        }

    }

    @Override
    public IBinder onBind(Intent intent) {
        return mBinder;
    }
      ···
}

我們新建了DownloadBinder類,繼承Binder,并提供了兩個(gè)方法startDownload()啟動(dòng)下載,getProgress()獲取下載進(jìn)度(當(dāng)然只是執(zhí)行打印日志,并沒(méi)有實(shí)現(xiàn)具體方法)

  • 萬(wàn)事俱備只欠調(diào)用,直接上代碼
···
    private MyService.DownloadBinder downloadBinder;

    private ServiceConnection connection = new ServiceConnection() {//創(chuàng)建一個(gè)ServiceConnection
        @Override
        public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
            //服務(wù)成功綁定時(shí)調(diào)用
            downloadBinder = (MyService.DownloadBinder) iBinder;
            downloadBinder.startDownload();
            downloadBinder.getProgress();

        }

        @Override
        public void onServiceDisconnected(ComponentName componentName) {//斷開(kāi)連接時(shí)調(diào)用
        }
    };

···
    //bind
    Intent bindIntent = new Intent(this, MyService.class);
    bindService(bindIntent, connection, BIND_AUTO_CREATE);
···    
    //unbind
    unbindService(connection);

上文中我想細(xì)心的朋友已經(jīng)注意到了,服務(wù)也有自己的生命周期
官網(wǎng)解釋
官方文檔
服務(wù)可以由系統(tǒng)運(yùn)行有兩個(gè)原因。如果有人調(diào)用Context.startService(),系統(tǒng)將檢索服務(wù)(創(chuàng)建它并調(diào)用它的onCreate()方法),然后使用客戶機(jī)提供的參數(shù)調(diào)用onStartCommand(Intent,int,int)方法。此服務(wù)將繼續(xù)運(yùn)行到Context.stopService()或stopSelf()。注意,多個(gè)調(diào)用Context.startService()不嵌入(盡管他們導(dǎo)致多個(gè)相應(yīng)的調(diào)用onStartCommand()),所以不管多少次啟動(dòng)服務(wù)將停止一次Context.stopService()或stopSelf();然而,服務(wù)可以使用他們stopSelf(int)方法,以確保服務(wù)不停止,直到開(kāi)始意圖已經(jīng)處理。
開(kāi)始服務(wù),主要有兩個(gè)額外的操作模式,他們可以決定在運(yùn)行,這取決于他們從onStartCommand返回值():START_STICKY用于顯式地根據(jù)需要啟動(dòng)和停止服務(wù),而START_NOT_STICKY或START_REDELIVER_INTENT應(yīng)該只用于服務(wù)仍然運(yùn)行在處理任何命令發(fā)送給他們。有關(guān)語(yǔ)義的更多細(xì)節(jié),請(qǐng)參閱相關(guān)文檔。
客戶端還可以使用Context.bindService()來(lái)獲得對(duì)服務(wù)的持久連接。同樣,如果服務(wù)尚未運(yùn)行(調(diào)用onCreate()),但不調(diào)用onStartCommand(),也會(huì)創(chuàng)建服務(wù)??蛻舳藢⒔邮誌Binder對(duì)象,該對(duì)象將從其onBind(Intent)方法返回服務(wù),允許客戶端調(diào)用該服務(wù)。只要建立連接(不管客戶機(jī)是否保留對(duì)服務(wù)的IBinder的引用),服務(wù)就會(huì)繼續(xù)運(yùn)行。通常IBinder返回的是在aidl中編寫(xiě)的復(fù)雜接口。
服務(wù)既可以啟動(dòng),也可以連接到它。在這種情況下,系統(tǒng)將保持服務(wù)運(yùn)行,只要它已經(jīng)啟動(dòng),或者有一個(gè)或多個(gè)與上下文相關(guān)的連接。BIND_AUTO_CREATE標(biāo)記。一旦這些情況都不存在,服務(wù)的onDestroy()方法被調(diào)用,服務(wù)實(shí)際上被終止。從onDestroy()返回時(shí),所有清除(停止線程、未注冊(cè)的接收者)都應(yīng)該完成。


  • 了解了服務(wù)的基本使用和生命周期,我們來(lái)學(xué)習(xí)一些新的技巧

1.前臺(tái)服務(wù)

服務(wù)的系統(tǒng)優(yōu)先級(jí)比較低,當(dāng)系統(tǒng)內(nèi)存不足時(shí),可能會(huì)回收掉正在后臺(tái)運(yùn)行的服務(wù)。前臺(tái)服務(wù)會(huì)一直有一個(gè)正在運(yùn)行的圖標(biāo)顯示在系統(tǒng)的狀態(tài)欄顯示,已避免被后臺(tái)回收。相信這樣的例子在你的手機(jī)上很常見(jiàn)。
現(xiàn)在就讓我們的服務(wù)具有前臺(tái)能力,直接上代碼

 @Override
    public void onCreate() {
        super.onCreate();
        Log.d("MyService","onCreate executed");
        Intent intent = new Intent(this,MainActivity.class);
        PendingIntent pendingIntent = PendingIntent.getActivity(this,0,intent,0);
        Notification notification = new NotificationCompat.Builder(this)
                .setContentTitle("This is content title")
                .setContentText("This is content text")
                .setWhen(System.currentTimeMillis())
                .setSmallIcon(R.mipmap.ic_launcher)
                .setLargeIcon(BitmapFactory.decodeResource(getResources(),R.mipmap.ic_launcher))
                .setContentIntent(pendingIntent)
                .build();
        startForeground(1,notification);
    }

看看是不是覺(jué)得很簡(jiǎn)單,只需用修改onCreate中的代碼,調(diào)用* startForeground()*后,我們的MyService就進(jìn)化成了一個(gè)前臺(tái)服務(wù),并在系統(tǒng)狀態(tài)欄顯示出來(lái)

image.png

上文我們提到,Service中的代碼是默認(rèn)運(yùn)行在主線程中的,如果直接在服務(wù)中進(jìn)行耗時(shí)操作,就準(zhǔn)備迎接ANR
現(xiàn)在我們上一篇講到的多線程就起到作用了

@Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.d("MyService","onStartCommand executed");
        new Thread(new Runnable() {
            @Override
            public void run() {
                //處理邏輯代碼
                stopSelf();
            }
        }).start();
        return super.onStartCommand(intent, flags, startId);
    }

在要進(jìn)行耗時(shí)操作的時(shí)候,我們new Thread()run()方法內(nèi)處理耗時(shí)操作,其中stopSelf()是為了讓服務(wù)在執(zhí)行完之后停止下來(lái),和stopService()同理。

  • 問(wèn)題來(lái)了,每次都要開(kāi)啟線程,關(guān)閉線程,不僅麻煩,忘了豈不是很尷尬。但是,Android還提供了一個(gè)類IntentService,這個(gè)類就能很好的解決這兩個(gè)問(wèn)題,下面我們就來(lái)創(chuàng)建一個(gè)IntentService類
public class MyIntentService extends IntentService {

    public MyIntentService() {
        super("MyIntentService");
    }

    @Override
    protected void onHandleIntent(@Nullable Intent intent) {
        //打印當(dāng)前線程的Id
        Log.d("MyIntentService","Thread id is" + Thread.currentThread().getId());
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        Log.d("MyIntentService","onDestroy executed");
    }
}

我們?cè)趏nHandleIntent()方法中打印當(dāng)前線程id和Activity所在線程id進(jìn)行對(duì)比

可以看到不僅所在線程不同,而且在執(zhí)行完打印操作后自動(dòng)停止了,完美解決問(wèn)題。

好了,服務(wù)的基本使用講到這里,后續(xù)會(huì)結(jié)合源碼來(lái)具體分析四大組件之一的Service.
我寫(xiě)了個(gè)Demo,涵蓋這本文涉及到的所有知識(shí)點(diǎn)
Demo下載 密碼:gxuc

每星期至少一篇跟新本系列,感興趣可以關(guān)注。
一起學(xué)習(xí),一起進(jìn)步。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 原文地址:Android Service完全解析,關(guān)于服務(wù)你所需知道的一切(上) 相信大多數(shù)朋友對(duì)Service這...
    AiPuff閱讀 4,310評(píng)論 11 98
  • Service的生命周期 service的生命周期,從它被創(chuàng)建開(kāi)始,到它被銷毀為止,可以有兩條不同的路徑: A s...
    _執(zhí)_念__閱讀 1,643評(píng)論 0 19
  • 時(shí)針已經(jīng)指向12了,安安輾轉(zhuǎn)反側(cè)了2個(gè)小時(shí),還是沒(méi)能安然入睡,懊惱的爬起來(lái),眼到之處無(wú)不雜亂。 衣柜的把手上掛著2...
    張瓓閱讀 452評(píng)論 0 0
  • 麻將是中國(guó)人自?shī)首詷?lè)的國(guó)粹,在桌上也領(lǐng)略到各色人等,百種形態(tài),若說(shuō)麻品似人品,雖牽強(qiáng),卻也不乏可陳之處。 麻將這東...
    飛城閱讀 1,450評(píng)論 0 1
  • 秋漸漸深了,呼吸時(shí)連空氣都多了一層涼意。在國(guó)慶節(jié)的尾聲里,我們還是聽(tīng)到了細(xì)細(xì)密密的秋雨聲,仿若它獨(dú)自彳亍在深...
    深深淺淺間閱讀 515評(píng)論 0 1

友情鏈接更多精彩內(nèi)容