Android IntentService詳解(源碼分析)

目錄:

目錄

1. 前言

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

1.1 定義

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

用戶通過調用 Context.StartService(Intent) 發送請求,服務根據請求啟動,使用工作線程依次處理每個 Intent任務請求,并在處理完所有任務請求后自身停止服務。

使用時,擴展 IntentService ,即實現它的子類并具體實現 onHandleIntent(android.content.Intent) 方法。IntentService 接收 Intent,啟動工作線程,并在適當時機停止服務。

所有的請求都在同一個工作線程上處理,一次處理一個請求,所以處理完所以的請求可能會花費很長的時間,但由于 IntentService 是另外創建的線程來工作,所以保證不會阻止App 的主線程,防止 App 出現 ANR。

1.2 使用場景

用于處理后臺長時間的耗時操作,如:下載文件、播放音樂...

2. 使用方法

由于 IntentService 是抽象類,所以在實際使用中,我們需要創建一個 IntentService 子類來具體實現。

使用步驟分為兩步:

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

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

如下所示:


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工作.gif

具體代碼如下所示:分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~!

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。