IntentService
1, IntentService overview
In the previous article, we talked about HandlerThread. In this article, we will take a look at the application of HandlerThread in IntentService. Before reading this article, we suggest to take a look at the HandlerThread in the previous article, which will help us better master IntentService. Similarly, let's take a look at the features of IntentService:
- It is essentially a special Service, which inherits from Service and is itself an abstract class
- It can be used to execute time-consuming asynchronous tasks in the background and will automatically stop when the task is completed
- It has high priority and is not easy to be killed by the system (inherited from Service), so it is more suitable for executing some high priority asynchronous tasks
- It implements asynchronous operation internally through HandlerThread and Handler
- When creating IntentService, you only need to implement onHandleIntent and construction method. onHandleIntent is an asynchronous method that can perform time-consuming operations
2, Routine usage of IntentService
After understanding the characteristics of IntentService, let's learn how to use it. Let's take a case first: IntentService implementation classes are as follows:
package com.zejian.handlerlooper; import android.app.IntentService; import android.content.Intent; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.os.IBinder; import android.os.Message; import com.zejian.handlerlooper.util.LogUtils; import java.io.BufferedInputStream; import java.io.IOException; import java.net.HttpURLConnection; import java.net.URL; /** * Created by zejian * Time 16/9/3. * Description: */ public class MyIntentService extends IntentService { public static final String DOWNLOAD_URL="download_url"; public static final String INDEX_FLAG="index_flag"; public static UpdateUI updateUI; public static void setUpdateUI(UpdateUI updateUIInterface){ updateUI=updateUIInterface; } public MyIntentService(){ super("MyIntentService"); } /** * Method for realizing asynchronous task * @param intent Activity The transmitted intent, and the data is encapsulated in intent */ @Override protected void onHandleIntent(Intent intent) { //Making network requests in child threads Bitmap bitmap=downloadUrlBitmap(intent.getStringExtra(DOWNLOAD_URL)); Message msg1 = new Message(); msg1.what = intent.getIntExtra(INDEX_FLAG,0); msg1.obj =bitmap; //Notify the main thread to update the UI if(updateUI!=null){ updateUI.updateUI(msg1); } //mUIHandler.sendMessageDelayed(msg1,1000); LogUtils.e("onHandleIntent"); } //----------------------Rewrite the method for testing only------------------------------------------ @Override public void onCreate() { LogUtils.e("onCreate"); super.onCreate(); } @Override public void onStart(Intent intent, int startId) { super.onStart(intent, startId); LogUtils.e("onStart"); } @Override public int onStartCommand(Intent intent, int flags, int startId) { LogUtils.e("onStartCommand"); return super.onStartCommand(intent, flags, startId); } @Override public void onDestroy() { LogUtils.e("onDestroy"); super.onDestroy(); } @Override public IBinder onBind(Intent intent) { LogUtils.e("onBind"); return super.onBind(intent); } public interface UpdateUI{ void updateUI(Message message); } private Bitmap downloadUrlBitmap(String urlString) { HttpURLConnection urlConnection = null; BufferedInputStream in = null; Bitmap bitmap=null; try { final URL url = new URL(urlString); urlConnection = (HttpURLConnection) url.openConnection(); in = new BufferedInputStream(urlConnection.getInputStream(), 8 * 1024); bitmap= BitmapFactory.decodeStream(in); } catch (final IOException e) { e.printStackTrace(); } finally { if (urlConnection != null) { urlConnection.disconnect(); } try { if (in != null) { in.close(); } } catch (final IOException e) { e.printStackTrace(); } } return bitmap; } }
It can be seen from the code that we inherit IntentService. There are two methods that must be implemented. One is the construction method, which must pass a string of thread name, and the other is the onHandleIntent(Intent intent) method for asynchronous processing. Its parameter intent can be attached with the data passed from the activity. Here, our case mainly uses onHandleIntent to download pictures asynchronously, then puts the downloaded bitmap in the message through callback listening, callback it to the activity (of course, it can also be completed by broadcasting), and finally update the UI through the Handler. Let's look at the code of Acitvity:
activity_intent_service.xml
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent"> <ImageView android:id="@+id/image" android:layout_width="match_parent" android:layout_height="match_parent" /> </LinearLayout>
IntentServiceActivity.java
package com.zejian.handlerlooper.util; import android.app.Activity; import android.content.Intent; import android.graphics.Bitmap; import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.widget.ImageView; import com.zejian.handlerlooper.MyIntentService; import com.zejian.handlerlooper.R; /** * Created by zejian * Time 16/9/3. * Description: */ public class IntentServiceActivity extends Activity implements MyIntentService.UpdateUI{ /** * Picture address collection */ private String url[] = { "https://img-blog.csdn.net/20160903083245762", "https://img-blog.csdn.net/20160903083252184", "https://img-blog.csdn.net/20160903083257871", "https://img-blog.csdn.net/20160903083257871", "https://img-blog.csdn.net/20160903083311972", "https://img-blog.csdn.net/20160903083319668", "https://img-blog.csdn.net/20160903083326871" }; private static ImageView imageView; private static final Handler mUIHandler = new Handler() { @Override public void handleMessage(Message msg) { imageView.setImageBitmap((Bitmap) msg.obj); } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_intent_service); imageView = (ImageView) findViewById(R.id.image); Intent intent = new Intent(this,MyIntentService.class); for (int i=0;i<7;i++) {//Cycle start task intent.putExtra(MyIntentService.DOWNLOAD_URL,url[i]); intent.putExtra(MyIntentService.INDEX_FLAG,i); startService(intent); } MyIntentService.setUpdateUI(this); } //It must be updated through the Handler. This method is asynchronous and cannot update the UI @Override public void updateUI(Message message) { mUIHandler.sendMessageDelayed(message,message.what * 1000); } }
The code is relatively simple. Start IntentService multiple times through the for loop, and then download pictures. Note that even if we start IntentService multiple times, there is only one instance of IntentService, which is the same as the traditional Service. Finally, IntentService will call onHandleIntent to perform asynchronous tasks. Here, we may also worry about the for loop to start the task, and there is only one instance. Will the task be overwritten? In fact, it won't, because the IntentService really performs asynchronous tasks is HandlerThread+Handler. Every time it starts, the task of downloading pictures will be added to the attached message queue, and finally executed by HandlerThread+Handler. OK ~, let's run the following code:

Update the picture every second, and then we look at a group of log s:

from the Log, we can see that onCreate is started only once, while onStartCommand and onStart are started multiple times, which confirms what we said before. There is only one instance of IntentService, which is the same as the traditional Service. After the final tasks are completed, IntentService is automatically destroyed. The above is how IntentService is used. How about it? It's relatively simple. Then we will analyze the source code of IntentService. In fact, it is relatively simple, with only more than 100 lines of code.
3, IntentService source code analysis
Let's first look at the onCreate method of IntentService:
@Override public void onCreate() { // TODO: It would be nice to have an option to hold a partial wakelock // during processing, and to have a static startService(Context, Intent) // method that would launch the service & hand off a wakelock. super.onCreate(); HandlerThread thread = new HandlerThread("IntentService[" + mName + "]"); thread.start(); mServiceLooper = thread.getLooper(); mServiceHandler = new ServiceHandler(mServiceLooper); }
When IntentService is started for the first time, its onCreate method will be called. It will internally create a HandlerThread and start it, then create a ServiceHandler (inherited Handler) and pass in the Looper object of HandlerThread. In this way, ServiceHandler will become an execution class that can handle asynchronous threads (because the Looper object is bound to the HandlerThread, and the HandlerThread is an asynchronous thread, after we pass the Looper object held by the HandlerThread to the Handler, the Looper of the asynchronous thread is held inside the ServiceHandler, and the asynchronous task can be executed naturally.) , how does IntentService start asynchronous tasks? In fact, after IntentService starts, it will call onStartCommand method, and onStartCommand method will call onStart method. Let's look at their source code:
@Override public void onStart(Intent intent, int startId) { Message msg = mServiceHandler.obtainMessage(); msg.arg1 = startId; msg.obj = intent; mServiceHandler.sendMessage(msg); } /** * You should not override this method for your IntentService. Instead, * override {@link #onHandleIntent}, which the system calls when the IntentService * receives a start request. * @see android.app.Service#onStartCommand */ @Override public int onStartCommand(Intent intent, int flags, int startId) { onStart(intent, startId); return mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY; }
From the source code, we can see that in the onStart method, IntentService sends a message through the sendMessage method of mServiceHandler, which will be sent to HandlerThread for processing (because HandlerThread holds Looper objects, so Looper actually takes the message out of the queue and then calls the handleMessage method of mServiceHandler). , let's look at the source code of ServiceHandler:
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); } }
In fact, this also shows that onHandleIntent is indeed an asynchronous processing method (ServiceHandler itself is an asynchronous processing handler class). After the execution of onHandleIntent method, IntentService will try to stop the service through stopSelf(int startId). stopSelf(int startId) instead of stopself () is used to stop the service because stopSelf() The service will be stopped immediately, and stopSelf(int startId) will wait for all messages to be processed before terminating the service. Finally, look at the declaration of onHandleIntent method:
protected abstract void onHandleIntent(Intent intent);
Now we know that the onHandleIntent method of IntentService is an abstract method, so we must implement this method when creating IntentService. According to the above analysis, onHandleIntent method is also an asynchronous method. Note that if there is only one background task, the service will be destroyed after onHandleIntent is executed, but if If there are multiple background tasks, the service will not be destroyed until onHandleIntent completes the last task. Finally, we need to know that the IntentService must be started every time a background task is executed, and the IntentService is internally sent to the HandlerThread through messages, and then the Looper in the Handler processes the messages, and the Looper is retrieved from the message queue in order Tasks, that is, the background tasks of IntentService are executed in sequence. When multiple background tasks exist at the same time, these background tasks will be queued for execution in the order of external calls. Our previous use cases also illustrate this. Finally, post all the source code of IntentService and let's feel it again:
/* * Copyright (C) 2008 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.app; import android.annotation.WorkerThread; import android.content.Intent; import android.os.Handler; import android.os.HandlerThread; import android.os.IBinder; import android.os.Looper; import android.os.Message; /** * IntentService is a base class for {@link Service}s that handle asynchronous * requests (expressed as {@link Intent}s) on demand. Clients send requests * through {@link android.content.Context#startService(Intent)} calls; the * service is started as needed, handles each Intent in turn using a worker * thread, and stops itself when it runs out of work. * * <p>This "work queue processor" pattern is commonly used to offload tasks * from an application's main thread. The IntentService class exists to * simplify this pattern and take care of the mechanics. To use it, extend * IntentService and implement {@link #onHandleIntent(Intent)}. IntentService * will receive the Intents, launch a worker thread, and stop the service as * appropriate. * * <p>All requests are handled on a single worker thread -- they may take as * long as necessary (and will not block the application's main loop), but * only one request will be processed at a time. * * <div class="special reference"> * <h3>Developer Guides</h3> * <p>For a detailed discussion about how to create services, read the * <a href="{@docRoot}guide/topics/fundamentals/services.html">Services</a> developer guide.</p> * </div> * * @see android.os.AsyncTask */ public abstract class IntentService extends Service { private volatile Looper mServiceLooper; private volatile ServiceHandler mServiceHandler; private String mName; private boolean mRedelivery; 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); } } /** * Creates an IntentService. Invoked by your subclass's constructor. * * @param name Used to name the worker thread, important only for debugging. */ public IntentService(String name) { super(); mName = name; } /** * Sets intent redelivery preferences. Usually called from the constructor * with your preferred semantics. * * <p>If enabled is true, * {@link #onStartCommand(Intent, int, int)} will return * {@link Service#START_REDELIVER_INTENT}, so if this process dies before * {@link #onHandleIntent(Intent)} returns, the process will be restarted * and the intent redelivered. If multiple Intents have been sent, only * the most recent one is guaranteed to be redelivered. * * <p>If enabled is false (the default), * {@link #onStartCommand(Intent, int, int)} will return * {@link Service#START_NOT_STICKY}, and if the process dies, the Intent * dies along with it. */ public void setIntentRedelivery(boolean enabled) { mRedelivery = enabled; } @Override public void onCreate() { // TODO: It would be nice to have an option to hold a partial wakelock // during processing, and to have a static startService(Context, Intent) // method that would launch the service & hand off a wakelock. super.onCreate(); HandlerThread thread = new HandlerThread("IntentService[" + mName + "]"); thread.start(); mServiceLooper = thread.getLooper(); mServiceHandler = new ServiceHandler(mServiceLooper); } @Override public void onStart(Intent intent, int startId) { Message msg = mServiceHandler.obtainMessage(); msg.arg1 = startId; msg.obj = intent; mServiceHandler.sendMessage(msg); } /** * You should not override this method for your IntentService. Instead, * override {@link #onHandleIntent}, which the system calls when the IntentService * receives a start request. * @see android.app.Service#onStartCommand */ @Override public int onStartCommand(Intent intent, int flags, int startId) { onStart(intent, startId); return mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY; } @Override public void onDestroy() { mServiceLooper.quit(); } /** * Unless you provide binding for your service, you don't need to implement this * method, because the default implementation returns null. * @see android.app.Service#onBind */ @Override public IBinder onBind(Intent intent) { return null; } /** * This method is invoked on the worker thread with a request to process. * Only one Intent is processed at a time, but the processing happens on a * worker thread that runs independently from other application logic. * So, if this code takes a long time, it will hold up other requests to * the same IntentService, but it will not hold up anything else. * When all requests have been handled, the IntentService stops itself, * so you should not call {@link #stopSelf}. * * @param intent The value passed to {@link * android.content.Context#startService(Intent)}. */ @WorkerThread protected abstract void onHandleIntent(Intent intent); }
The source code of this IntentService is analyzed. Well, this article is over.
This article is transferred from https://blog.csdn.net/javazejian/article/details/52426425 , in case of infringement, please contact to delete.