IntentService

IntentService overview

In this article, we will look at the application of HandlerThread to IntentService. Again, let’s look at the IntentService features:

It is essentially a special Service that inherits from Service and is itself an abstract class. It can be used to perform time-consuming asynchronous tasks in the background and stop automatically when the task is completed. It has a high priority and is not easily killed by the system (because it inherits from Service). So it’s a good place to do asynchronous tasks that have a high priority that are implemented asynchronously internally by HandlerThread and Handler so when you create an IntentService, you just implement onHandleIntent and its constructor, and onHandleIntent is an asynchronous method, You can perform time-consuming operations

General usage of IntentService

IntentService: IntentService: IntentService: IntentService: IntentService: IntentService

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"); } @override protected void onHandleIntent(Override protected void onHandleIntent) Bitmap =downloadUrlBitmap(intent.getStringExtra(DOWNLOAD_URL)); Message msg1 = new Message(); msg1.what = intent.getIntExtra(INDEX_FLAG,0); msg1.obj =bitmap; // notify main thread to updateUI if(updateUI! =null){ updateUI.updateUI(msg1); } / / mUIHandler sendMessageDelayed (msg1, 1000); LogUtils.e("onHandleIntent"); } / / -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- only rewrite the method for test -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- @ 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; }}Copy the code

IntentService: IntentService: IntentService: IntentService: IntentService: IntentService: IntentService: IntentService: IntentService: IntentService: IntentService: IntentService The intent parameter can carry data passed from the activity. In this case, we use onHandleIntent to download the image asynchronously, and then call back the downloaded bitmap in a message (or broadcast). Finally, we use Handler to update the UI. Let’s look at the code for Acitvity:

activity_intent_service.xml

<? The 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>Copy the code

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 serviceActivity extends Activity implements MyIntentService.UpdateUI{/** ** 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; PutExtra (myintentService.download_url,url[I]); intent.putExtra(MyIntentService.INDEX_FLAG,i); startService(intent); } MyIntentService.setUpdateUI(this); } // The update must be done via Handler, which is an asynchronous method, Do not update the UI @ Override public void updateUI (Message Message) {mUIHandler. SendMessageDelayed (Message, the Message. What * 1000); }}Copy the code

The code is very simple. We start the IntentService multiple times through the for loop, and then download the image. Notice that even though we start the IntentService multiple times, there is only one instance of the IntentService, just like a traditional Service. Eventually IntentService calls onHandleIntent to perform the asynchronous task. Here we might also worry that the for loop starts the task and there is only one instance, so will the task be overwritten? IntentService (IntentService, IntentService, IntentService, IntentService, IntentService, IntentService, IntentService, IntentService, IntentService, IntentService, IntentService, IntentService, IntentService, IntentService) Ok, let’s run the code:

Update the image every second, and then look at a set of logs:

The Log shows that onCreate is only started once, onStartCommand and onStart are started multiple times, which confirms the previous statement that IntentService is started multiple times, but there is only one instance of IntentService, which is the same as a traditional Service. IntentService is automatically destroyed. IntentService: IntentService: IntentService Now let’s look at the source code for IntentService, which is actually quite simple with just over 100 lines of code.

IntentService source code parsing

Let’s start with IntentService’s onCreate method:

@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);
}
Copy the code

When IntentService is first started, its onCreate method will be called, internally creating a HandlerThread and starting it, then creating a ServiceHandler (inheriting Handler), Pass in the Looper object for the HandlerThread, so ServiceHandler becomes an execution class that can handle asynchronous threads. (Since the Looper object is bound to the HandlerThread, which is an asynchronous thread, IntentService starts the asynchronous task by passing the Looper object held by the HandlerThread to the Handler. The ServiceHandler holds the Looper object of the asynchronous thread and can execute the asynchronous task. OnStartCommand () : IntentService () : IntentService () : onStartCommand () : IntentService () : onStartCommand () : onStartCommand () : IntentService () : onStartCommand () : onStartCommand () : IntentService () : onStartCommand () : onStartCommand ();

@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;
}
Copy the code

In the onStart method, IntentService sends a message via mServiceHandler’s sendMessage method, This message will be sent to the HandlerThread for processing (since the HandlerThread holds the Looper object, the Looper pulls the message from the message queue for processing and then calls the handleMessage method of the mServiceHandler). Take a look at the ServiceHandler source code:

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); }}Copy the code

ServiceHandler is a handler class that handles an asynchronous intent. After an onHandleIntent is executed, the ServiceHandler class handles an asynchronous intent. IntentService tries to stop the service by stopSelf(int startId). StopSelf (int startId) is used instead of stopSelf() to stop the service because stopSelf() stops the service immediately, while stopSelf(int startId) waits for all messages to be processed before terminating the service. Finally, look at the declaration of the onHandleIntent method:

protected abstract void onHandleIntent(Intent intent);
Copy the code

IntentService onHandleIntent (IntentService onHandleIntent) : IntentService onHandleIntent (IntentService onHandleIntent) : IntentService onHandleIntent (IntentService onHandleIntent) : IntentService onHandleIntent (IntentService onHandleIntent) : IntentService onHandleIntent (IntentService onHandleIntent) : IntentService onHandleIntent (IntentService onHandleIntent) Note that if there is only one background task, the service will be destroyed after the onHandleIntent executes, but if there are multiple background tasks, the service will be destroyed after the onHandleIntent completes its last task. The IntentService is sent internally to the HandlerThread as a message, which is then processed by Looper in the Handler. Looper, on the other hand, fetches tasks sequentially from the message queue, which means that IntentService’s background tasks are executed sequentially. When multiple background tasks exist at the same time, they are queued in order of external calls, as illustrated in our previous use case. IntentService: IntentService: IntentService: IntentService

/*
 * 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);
}
Copy the code

— — — — — — — —

Copyright notice: This article is originally published BY CSDN blogger “Zejian_”. It is subject to CC 4.0 BY-SA copyright agreement. Please attach the link of original source and this statement.

The original link: blog.csdn.net/javazejian/…