Android IntentService詳解(源碼分析)

目錄: java

目錄

1. 前言

本文是對 IntentService 的深刻學習,包含其基本使用方法、IntentService更新處理UI工做,以及對 IntentService 的源碼分析。android

1.1 定義

IntentService 是 Service 的子類,繼承於 Service 類,用於處理後臺異步請求任務。異步

用戶經過調用 Context.StartService(Intent) 發送請求,服務根據請求啓動,使用工做線程依次處理每一個 Intent任務請求,並在處理完全部任務請求後自身中止服務。ide

使用時,擴展 IntentService ,即實現它的子類並具體實現 onHandleIntent(android.content.Intent) 方法。IntentService 接收 Intent,啓動工做線程,並在適當時機中止服務。函數

全部的請求都在同一個工做線程上處理,一次處理一個請求,因此處理完因此的請求可能會花費很長的時間,但因爲 IntentService 是另外建立的線程來工做,因此保證不會阻止App 的主線程,防止 App 出現 ANR。oop

1.2 使用場景

用於處理後臺長時間的耗時操做,如:下載文件、播放音樂...源碼分析

2. 使用方法

因爲 IntentService 是抽象類,因此在實際使用中,咱們須要建立一個 IntentService 子類來具體實現。學習

使用步驟分爲兩步:ui

  1. 建立 IntentService 子類,並在清單文件中註冊
  2. 在 Activity 中經過調用 startService(Intent) 方法發送任務請求。

例子: 咱們模擬在後臺進行下載以及讀取文件的耗時操做,觀察打印出來的 Log 信息。this

以下所示:

MyIntentService處理耗時操做的日誌信息
IntentService 的執行流程爲:onCreate() -> onStartCommand() -> onStart() -> onHandleIntent -> ···處理完全部請求··· -> onDestroy()

具體代碼以下所示:分 Java & Kotlin 兩個版本展現。

2.1 Java版本

步驟一:建立 MyIntentService

public class MyIntentService extends IntentService {

    private static final String TAG = "MyIntentService";
    public static final String DOWNLOAD_ACTION = "DOWNLOAD_ACTION";
    public static final String READ_ACTION = "READ_ACTION";
    public static final String TEST_AUTHOR = "TEST_AUTHOR";

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

    /** * 進行一些耗時操做 * @param intent 經過startService(Intent intent)方法傳入 */
    @Override
    protected void onHandleIntent(Intent intent) {
        Log.e(TAG, "onHandleIntent: ");
        if (intent != null) {
            final String action = intent.getAction();
            String author = intent.getExtras().getString(TEST_AUTHOR);
            //模擬下載動做
            if (DOWNLOAD_ACTION.equals(action)) {
                for (int i = 0; i < 5; i++) {
                    try {
                        //線程等待1s,模擬耗時操做
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    Log.e(TAG, author + " " + action + " " + i);
                }
            }
            //模擬讀操做
            if (READ_ACTION.equals(action)) {
                for (int i = 0; i < 5; i++) {
                    try {
                        //線程等待2s,模擬耗時操做
                        Thread.sleep(2000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    Log.e(TAG, author + " " + action + " " + i);
                }
            }
        }

    }

    @Override
    public void onCreate() {
        super.onCreate();
        Toast.makeText(this, "onCreate", Toast.LENGTH_SHORT).show();
        Log.e(TAG, "onCreate: ");
    }

    @Override
    public int onStartCommand(@Nullable Intent intent, int flags, int startId) {
        Toast.makeText(this, "onStartCommand", Toast.LENGTH_SHORT).show();
        Log.e(TAG, "onStartCommand: ");
        return super.onStartCommand(intent, flags, startId);
    }

    @Override
    public void onStart(@Nullable Intent intent, int startId) {
        super.onStart(intent, startId);
        Log.e(TAG, "onStart: ");
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        Log.e(TAG, "onDestroy: ");
        Toast.makeText(this, "onDestroy", Toast.LENGTH_SHORT).show();
    }
}
複製代碼

並記得在清單文件中註冊,這一步你也能夠經過 Android Studio 來幫咱們完成

<service android:name=".intentservice.MyIntentService" android:exported="false">
</service>
複製代碼

步驟二:經過 startService(Intent) 發送任務請求

public class IntentServiceActivity extends AppCompatActivity {

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

        //模擬 Jere 作下載動做
        Intent intent = new Intent(this, MyIntentService.class);
        intent.setAction(MyIntentService.DOWNLOAD_ACTION);
        intent.putExtra(MyIntentService.TEST_AUTHOR, "Jere");
        startService(intent);

        //模擬 James 作讀取動做
        Intent jamesIntent = new Intent(this, MyIntentService.class);
        jamesIntent.setAction(MyIntentService.READ_ACTION);
        jamesIntent.putExtra(MyIntentService.TEST_AUTHOR, "James");
        startService(jamesIntent);
    }
}
複製代碼

2.2 Koltin版本

步驟一:建立 MyIntentService

class MyIntentService : IntentService("MyIntentService") {
    companion object {
        private val TAG: String = "MyIntentService"
        val DOWNLOAD_ACTION = "DOWNLOAD_ACTION"
        val READ_ACTION = "READ_ACTION"
        val TEST_AUTHOR = "TEST_AUTHOR"
    }


    override fun onHandleIntent(intent: Intent?) {
        val action: String? = intent?.action
        val author: String? = intent?.extras?.getString(TEST_AUTHOR)

        when (intent?.action) {
            DOWNLOAD_ACTION -> {
                for (i in 0..10) {
                    Thread.sleep(1000)
                    Log.e(TAG, "$author $action $i")
                }
            }
            READ_ACTION -> {
                for (j in 0..10) {
                    Thread.sleep(2000)
                    Log.e(TAG, "$author $action $j")
                }
            }
        }
    }

    override fun onCreate() {
        super.onCreate()
        Log.e(TAG, "onCreate")
        Toast.makeText(this, "conCreate", Toast.LENGTH_SHORT).show()
    }

    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        Log.e(TAG, "onStartCommand")
        Toast.makeText(this, "onStartCommand", Toast.LENGTH_SHORT).show()
        return super.onStartCommand(intent, flags, startId)
    }

    override fun onDestroy() {
        super.onDestroy()
        Log.e(TAG, "onDestroy")
        Toast.makeText(this, "onDestroy", Toast.LENGTH_SHORT).show()
    }
}
複製代碼

在清單文件中註冊:

<service android:name=".multiplethread.intentservice.MyIntentService" android:exported="false">
</service>
複製代碼

步驟二:經過 startService(Intent) 發送任務請求

class TestIntentServiceActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_test_intent_service)

        val intent = Intent()
        intent.setClass(this, MyIntentService::class.java)
        intent.action = MyIntentService.DOWNLOAD_ACTION
        intent.putExtra(MyIntentService.TEST_AUTHOR, "Jere")
        startService(intent)

        val jamesIntent = Intent()
        jamesIntent.setClass(this, MyIntentService::class.java)
        jamesIntent.action = MyIntentService.READ_ACTION
        jamesIntent.putExtra(MyIntentService.TEST_AUTHOR, "James")
        startService(jamesIntent)
    }
}
複製代碼

2.3 IntentService更新處理UI工做

思考: 在上面的例子中,咱們是經過在打印出來的日誌信息來觀察後臺異步任務的執行狀況,那 IntentService 能夠處理 UI 工做嗎?

答: IntentService 是服務端,確定是不能夠處理 UI 工做的,可是他能夠經過其對應的客戶端,也就是 Activity 來更新處理 UI 工做。

客戶端與服務端之間的通訊
客戶端與服務端之間的通訊,咱們使用 Messenger , 關於 Messenger 內容請看另外一篇博客 Android Messenger初探,具體實現以下所示:

例子: 咱們在 IntentService 模擬下載任務,經過 ProgressBar 顯示下載進度。

效果圖以下所示:

IntentService更新處理UI工做
具體代碼以下所示:分Java & Kotlin 兩個版本展現。

2.3.1 Java版本

  1. 在 Activity 中發送請求時,新建一個 Messenger 對象,傳遞給 IntentService。
public class IntentServiceActivity extends AppCompatActivity {
    private ProgressBar downloadProgressBar;

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

        downloadProgressBar = findViewById(R.id.download_progress_bar);

        //用於建立 Messenger,接收 IntentService 回覆的消息
        MessengerHandler messengerHandler = new MessengerHandler(this);

        //模擬 Jere 作下載動做
        Intent intent = new Intent(this, MyIntentService.class);
        intent.setAction(MyIntentService.DOWNLOAD_ACTION);
        intent.putExtra(MyIntentService.TEST_AUTHOR, "Jere");
        //將 Messenger 傳遞給 IntentService,讓其回覆消息回來
        intent.putExtra(MyIntentService.TEST_MESSENGER, new Messenger(messengerHandler));
        startService(intent);
    }

    /** * 用於建立 Messenger 對象 * * 靜態內部類,防止內存泄漏 */
    public static class MessengerHandler extends Handler {
        private WeakReference<IntentServiceActivity> weakReference;

        MessengerHandler(IntentServiceActivity activity) {
            weakReference = new WeakReference<>(activity);
        }

        @Override
        public void handleMessage(@NonNull Message msg) {
            super.handleMessage(msg);
            //msg 爲 IntentService 回覆的消息,包含 Bundle 等信息。
            Bundle bundle = msg.getData();
            //獲取 IntentService 傳遞過來的 下載進度 參數
            int downloadProgressBarValue = bundle.getInt(MyIntentService.DOWNLOAD_PROGRESS_VALUE_KEY);

            //將下載進度設置成 ProgressBar 的進度,顯示出來。
            IntentServiceActivity activity = weakReference.get();
            if (activity != null && !activity.isFinishing()) {
                activity.downloadProgressBar.setProgress(downloadProgressBarValue);
            }

        }
    }

}
複製代碼
  1. 在 IntentService 中獲取 Messenger 對象,並回復消息給 Activity。
public class MyIntentService extends IntentService {

    private static final String TAG = "MyIntentService";
    public static final String DOWNLOAD_ACTION = "DOWNLOAD_ACTION";
    public static final String READ_ACTION = "READ_ACTION";
    public static final String TEST_AUTHOR = "TEST_AUTHOR";
    public static final String TEST_MESSENGER = "TEST_MESSENGER";
    public static final String DOWNLOAD_PROGRESS_VALUE_KEY = "DOWNLOAD_PROGRESS_VALUE";

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

    /** * 進行一些耗時操做 * @param intent 經過startService(Intent intent)方法傳入 */
    @Override
    protected void onHandleIntent(Intent intent) {
        Log.e(TAG, "onHandleIntent: ");
        if (intent != null) {
            final String action = intent.getAction();
            String author = intent.getExtras().getString(TEST_AUTHOR);

            //模擬下載動做
            if (DOWNLOAD_ACTION.equals(action)) {
                for (int i = 0; i <= 6; i++) {
                    try {
                        //線程等待1s,模擬耗時操做
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    Log.e(TAG, author + " " + action + " " + i);
                    //獲取從 Activity 傳入的 Messenger
                    Messenger messenger = (Messenger) intent.getExtras().get(TEST_MESSENGER);
                    //新建消息,設置下載進度參數
                    Message msg = new Message();
                    Bundle bundle = new Bundle();
                    bundle.putInt(DOWNLOAD_PROGRESS_VALUE_KEY, i);
                    msg.setData(bundle);
                    try {
                        //回覆消息
                        messenger.send(msg);
                    } catch (RemoteException e) {
                        e.printStackTrace();
                    }
                }
            }
        }

    }

    @Override
    public void onCreate() {
        super.onCreate();
        Toast.makeText(this, "onCreate", Toast.LENGTH_SHORT).show();
        Log.e(TAG, "onCreate: ");
    }

    @Override
    public int onStartCommand(@Nullable Intent intent, int flags, int startId) {
        Toast.makeText(this, "onStartCommand", Toast.LENGTH_SHORT).show();
        Log.e(TAG, "onStartCommand: ");
        return super.onStartCommand(intent, flags, startId);
    }

    @Override
    public void onStart(@Nullable Intent intent, int startId) {
        super.onStart(intent, startId);
        Toast.makeText(this, "onStart", Toast.LENGTH_SHORT).show();
        Log.e(TAG, "onStart: ");
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        Log.e(TAG, "onDestroy: ");
        Toast.makeText(this, "onDestroy", Toast.LENGTH_SHORT).show();
    }

}
複製代碼

這樣就實現了 IntentService 更新處理 UI 工做,具體效果看上圖 IntentService更新處理UI工做

2.3.2 Kotlin版本

一樣,Kotlin的實現方式爲:

  1. 在 Activity 中發送請求時,新建一個 Messenger 對象,傳遞給 IntentService。
class TestIntentServiceActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_test_intent_service)

        val intent = Intent()
        intent.setClass(this, MyIntentService::class.java)
        intent.action = MyIntentService.DOWNLOAD_ACTION_KEY
        intent.putExtra(MyIntentService.TEST_AUTHOR_KEY, "Jere")
        //將 Messenger 傳遞給 IntentService, 使其傳遞消息回來,實現客戶端與服務端之間進行溝通
        intent.putExtra(MyIntentService.TEST_MESSENGER_KEY, Messenger(MessengerHandler(this)))
        startService(intent)
    }

    /** * 此 Handler 用於建立 Messenger 對象,接收 IntentService 回覆回來的消息 * * 靜態內部類,防止內存泄漏 */
    class MessengerHandler(activity: TestIntentServiceActivity) : Handler() {
        var weakReference: WeakReference<TestIntentServiceActivity> = WeakReference(activity)

        override fun handleMessage(msg: Message) {
            super.handleMessage(msg)
            //msg 爲 IntentService 回覆的消息,包含 Bundle 等信息。
            val bundle: Bundle = msg.data
            //獲取 IntentService 傳遞過來的 下載進度 參數
            val downloadProgressBarValue: Int =
                bundle.get(MyIntentService.DOWNLOAD_PROGRESS_BAR_VALUE_KEY) as Int
            val activity: TestIntentServiceActivity? = weakReference.get()
            //將下載進度設置成 ProgressBar 的進度,顯示出來。
            if (activity != null && !activity.isFinishing) {
                activity.intentServiceDownloadProgressBar.progress = downloadProgressBarValue
            }
        }
    }
}
複製代碼
  1. 在 IntentService 中獲取 Messenger 對象,並回復消息給 Activity。
class MyIntentService : IntentService("MyIntentService") {
    companion object {
        private val TAG: String = "MyIntentService"
        val DOWNLOAD_ACTION_KEY = "DOWNLOAD_ACTION"
        val READ_ACTION_KEY = "READ_ACTION"
        val TEST_AUTHOR_KEY = "TEST_AUTHOR"
        val TEST_MESSENGER_KEY = "TEST_MESSENGER"
        val DOWNLOAD_PROGRESS_BAR_VALUE_KEY = "DOWNLOAD_PROGRESS_BAR_VALUE"
    }

    override fun onHandleIntent(intent: Intent?) {
        val action: String? = intent?.action
        val author: String? = intent?.extras?.getString(TEST_AUTHOR_KEY)

        when (intent?.action) {
            DOWNLOAD_ACTION_KEY -> {
                for (i in 0..6) {
                    Thread.sleep(1000)
                    Log.e(TAG, "$author $action $i")
                    //獲取從 Activity 中傳入的 Messenger 對象
                    val messenger: Messenger = intent.extras?.get(TEST_MESSENGER_KEY) as Messenger
                    //新建一個 Message 對象
                    val msg: Message = Message()
                    //爲 Message 對象設置 下載進度 參數
                    val bundle: Bundle = Bundle()
                    bundle.putInt(DOWNLOAD_PROGRESS_BAR_VALUE_KEY, i)
                    msg.data = bundle
                    //Messenger 回覆消息給 Activity
                    messenger.send(msg)
                }
            }
            READ_ACTION_KEY -> {
                for (j in 0..10) {
                    Thread.sleep(2000)
                    Log.e(TAG, "$author $action $j")
                }
            }
        }
    }

    override fun onCreate() {
        super.onCreate()
        Log.e(TAG, "onCreate")
        Toast.makeText(this, "onCreate", Toast.LENGTH_SHORT).show()
    }

    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        Log.e(TAG, "onStartCommand")
        Toast.makeText(this, "onStartCommand", Toast.LENGTH_SHORT).show()
        return super.onStartCommand(intent, flags, startId)
    }

    override fun onStart(intent: Intent?, startId: Int) {
        super.onStart(intent, startId)
        Log.e(TAG, "onStart")
        Toast.makeText(this, "onStart", Toast.LENGTH_SHORT).show()
    }

    override fun onDestroy() {
        super.onDestroy()
        Log.e(TAG, "onDestroy")
        Toast.makeText(this, "onDestroy", Toast.LENGTH_SHORT).show()
    }
}
複製代碼

具體效果看上圖 IntentService更新處理UI工做

3. 源碼分析

上面介紹了 IntentService 的使用方法,接下來,咱們來看一下 IntentService 的源碼。若是你理解了 Hander、Looper、MessageQueue三者之間的關係,那麼理解 IntentService 的源代碼十分容易。(若是不理解,請看博客 Android Handler深刻學習(源碼分析)

根據上面的例子,咱們知道了 IntentService 的執行順序爲:

onCreate() -> onStartCommand() -> onStart() -> onHandleIntent -> ···處理完全部請求··· -> onDestroy()

IntentService 源碼以下:

public abstract class IntentService extends Service {
    private volatile Looper mServiceLooper;
    private volatile ServiceHandler mServiceHandler;
    private String mName;
    private boolean mRedelivery;

    //建立一個內部類,繼承於 Handler 類。
    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);
        }
    }

    /** * IntentService 構造函數,在你的子類構造函數中調用(在子類中經過 super(name) 方法調用) * * @param 參數 name 用於命名工做線程 * */
    public IntentService(String name) {
        super();
        mName = name;
    }

    @Override
    public void onCreate() {
        super.onCreate();
        //建立一個 HandlerThread 對象
        HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
        //啓動 HandlerThread 線程
        thread.start();
        //獲取 HandlerThread 中建立的 Looper 對象,賦值給全局變量 mServiceLooper。
        mServiceLooper = thread.getLooper();
        //建立一個 Handler 對象,並關聯剛剛經過 HandlerThread 建立的 Looper 對象,即關聯 mServiceLooper
        mServiceHandler = new ServiceHandler(mServiceLooper);
    }

    @Override
    public void onStart(@Nullable Intent intent, int startId) {
        //發送消息給 mServiceHanlder,
        Message msg = mServiceHandler.obtainMessage();
        msg.arg1 = startId;
        msg.obj = intent;
        mServiceHandler.sendMessage(msg);
    }

    /** * 你不該該在你的 IntentService 類中複寫 onStartCommand() 方法。 * 相反,你應該複寫 onHandleIntent() 方法,該方法會在 IntentService 接收到啓動請求時被調用。 */
    @Override
    public int onStartCommand(@Nullable Intent intent, int flags, int startId) {
        onStart(intent, startId);
        return mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY;
    }

    @Override
    public void onDestroy() {
        //退出 Looper 消息循環
        mServiceLooper.quit();
    }

    /** * 除非是綁定服務,不然你不須要實現此方法,由於此方法的默認實現返回 null. */
    @Override
    @Nullable
    public IBinder onBind(Intent intent) {
        return null;
    }

    /** * 此方法在工做線程中被調用用於處理請求,一次只能處理一個請求,處理工做運行於獨立於其餘應用程序的工做線程中。 * 所以,若是該段代碼花費了很長的一段時間,它會阻塞同一個 IntentService 的其餘請求,但不會阻塞其餘任何東西。 * 當全部的請求都被處理完,該 IntentService 就會中止運行,因此你不用調用 stopSelf() 方法。 * * @param intent 經過 startService(Intent) 方法傳入。若是服務在其進程消失後從新啓動,則此值可能爲空; */
    @WorkerThread
    protected abstract void onHandleIntent(@Nullable Intent intent);
}
複製代碼

總結:

  1. 在 onCreate() 方法中,咱們新建一個 HandlerThread 對象,而後啓用它,獲取其建立的 Looper 對象,而後新建立一個 Looper 對象關聯該 Looper 對象。關於 HandlerThread 的知識點請看另外一篇博客 Android HandlerThread詳解(源碼分析)
  2. 而後咱們在 onStart() 方法中,經過 Message,將 Intent任務請求 發送給 mServiceHandler,Intent任務請求就是咱們在 Activity 中經過 startService(Intent) 傳入的 Intent。
  3. mServiceHanlder 接受到任務請求,調用 onHandleIntent() 方法處理任務請求,處理完全部請求後,調用 stopSelf() 結束 IntentService。

至此 HandlerThread 的源碼也就分析結束了。 其實分享文章的最大目的正是等待着有人指出個人錯誤,若是你發現哪裏有錯誤,請毫無保留的指出便可,虛心請教。

另外,若是你以爲文章不錯,對你有所幫助,請給我點個贊,就當鼓勵,謝謝~Peace~!

相關文章
相關標籤/搜索