目錄: java
本文是對 IntentService 的深刻學習,包含其基本使用方法、IntentService更新處理UI工做,以及對 IntentService 的源碼分析。android
IntentService 是 Service 的子類,繼承於 Service 類,用於處理後臺異步請求任務。異步
用戶經過調用 Context.StartService(Intent) 發送請求,服務根據請求啓動,使用工做線程依次處理每一個 Intent任務請求,並在處理完全部任務請求後自身中止服務。ide
使用時,擴展 IntentService ,即實現它的子類並具體實現 onHandleIntent(android.content.Intent) 方法。IntentService 接收 Intent,啓動工做線程,並在適當時機中止服務。函數
全部的請求都在同一個工做線程上處理,一次處理一個請求,因此處理完因此的請求可能會花費很長的時間,但因爲 IntentService 是另外建立的線程來工做,因此保證不會阻止App 的主線程,防止 App 出現 ANR。oop
用於處理後臺長時間的耗時操做,如:下載文件、播放音樂...源碼分析
因爲 IntentService 是抽象類,因此在實際使用中,咱們須要建立一個 IntentService 子類來具體實現。學習
使用步驟分爲兩步:ui
例子: 咱們模擬在後臺進行下載以及讀取文件的耗時操做,觀察打印出來的 Log 信息。this
以下所示:
IntentService 的執行流程爲:onCreate() -> onStartCommand() -> onStart() -> onHandleIntent -> ···處理完全部請求··· -> onDestroy()具體代碼以下所示:分 Java & Kotlin 兩個版本展現。
步驟一:建立 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);
}
}
複製代碼
步驟一:建立 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)
}
}
複製代碼
思考: 在上面的例子中,咱們是經過在打印出來的日誌信息來觀察後臺異步任務的執行狀況,那 IntentService 能夠處理 UI 工做嗎?
答: IntentService 是服務端,確定是不能夠處理 UI 工做的,可是他能夠經過其對應的客戶端,也就是 Activity 來更新處理 UI 工做。
客戶端與服務端之間的通訊,咱們使用 Messenger , 關於 Messenger 內容請看另外一篇博客 Android Messenger初探,具體實現以下所示:例子: 咱們在 IntentService 模擬下載任務,經過 ProgressBar 顯示下載進度。
效果圖以下所示:
具體代碼以下所示:分Java & Kotlin 兩個版本展現。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);
}
}
}
}
複製代碼
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工做。
一樣,Kotlin的實現方式爲:
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
}
}
}
}
複製代碼
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工做。
上面介紹了 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);
}
複製代碼
總結:
至此 HandlerThread 的源碼也就分析結束了。 其實分享文章的最大目的正是等待着有人指出個人錯誤,若是你發現哪裏有錯誤,請毫無保留的指出便可,虛心請教。
另外,若是你以爲文章不錯,對你有所幫助,請給我點個贊,就當鼓勵,謝謝~Peace~!