深入理解IntentService(HandlerThread的经典应用)

IntentService 简介

public abstract class IntentService extends Service {
        .......
}

IntentService是一个抽象类,继承Service。

IntentService因为是一个Service,在后台不会轻易被系统杀死。他能够在onHandleIntent中接收intent请求。然后在子线程中按顺序处理。

特点:

优先级比较高的,用于串行执行异步任务,并且是一个会自尽的Service(stopSelf())。当任务执行完成以后,IntentService会自动停止。

启动IntentService与启动普通的Service一样。可以启动IntentService多次。每一个耗时的操作都会以工作队列的方式在IntentService的OnHandleIntent回调中执行,并且只有一个工作线程。(由HandlerThread开启一个新的线程)

请求都在一个单线程中,不会阻塞应用程序的主线程(UI Thread),同一时间只处理一个请求。 那么,用 IntentService 有什么好处呢?首先,我们省去了在 Service 中手动开线程的麻烦,第二,当操作完成时,我们不用手动停止 Service。

而Service 是长期运行在后台的应用程序组件。

Service 不是一个单独的进程,它和应用程序在同一个进程中,Service 也不是一个线程,它和线程没有任何关系,所以它不能直接处理耗时操作。如果直接把耗时操作放在 Service 的 onStartCommand() 中,很容易引起 ANR .如果有耗时操作就必须开启一个单独的线程来处理。

源码解析:

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);
        }
    }

    //子类需要重写的构造函数,参数是服务的名称
    public IntentService(String name) {
        super();
        mName = name;
    }

    //设置当前服务被意外关闭后是否重新
    //如果设置为 true,onStartCommand() 方法将返回 Service.START_REDELIVER_INTENT,这样当
    //当前进程在 onHandleIntent() 方法返回前销毁时,会重启进程,重新使用之前的 Intent 启动这个服务
    //(如果有多个 Intent,只会使用最后的一个)
    //如果设置为 false,onStartCommand() 方法返回 Service.START_NOT_STICKY,当进程销毁后也不重启服务
    public void setIntentRedelivery(boolean enabled) {
        mRedelivery = enabled;
    }

    @Override
    public void onCreate() {
        super.onCreate();
        //创建时启动一个 HandlerThread
        HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
        thread.start();

        //拿到 HandlerThread 中的 Looper,然后创建一个子线程中的 Handler
        mServiceLooper = thread.getLooper();
        mServiceHandler = new ServiceHandler(mServiceLooper);
    }

    @Override
    public void onStart(@Nullable Intent intent, int startId) {
        //将 intent 和 startId 以消息的形式发送到 Handler
        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(@Nullable Intent intent, int flags, int startId) {
        onStart(intent, startId);
        return mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY;
    }

    @Override
    public void onDestroy() {
        mServiceLooper.quit();    //值得学习的,在销毁时退出 Looper
    }

    @Override
    @Nullable
    public IBinder onBind(Intent intent) {
        return null;
    }

    @WorkerThread
    protected abstract void onHandleIntent(@Nullable Intent intent);
}

工作流程:

  • 创建了一个 HandlerThread 默认的工作线程
  • 使用 HandlerThread 的 Looper 创建了一个 Handler,这个 Handler 执行在子线程
  • 在 onStartCommand() 中调用 onStart(),然后在 onStart() 中将 intent 和 startId 以消息的形式发送到 Handler
  • 在 Handler 中将消息队列中的 Intent 按顺序传递给 onHandleIntent() 方法
  • 在处理完所有启动请求后自动停止服务,不需要我们调用 stopSelf()

这个 stopSelf() 方法传递了一个 id,这个 id 是启动服务时 IActivityManager 分配的 id,当我们调用 stopSelf(id) 方法结束服务时,IActivityManager 会对比当前 id 是否为最新启动该服务的 id,如果是就关闭服务。只有当最后一次启动 IntentService 的任务执行完毕才会关闭这个服务。IntentService 中除了 onHandleIntent 方法其他都是运行在主线程的

案例:

public class DownLoadService extends IntentService {
    private static final String TAG = "DownloadService";
    public static final String DOWNLOADURL = "download_url";
    public static final int DOWN_START = 0;
    public static final int DOWN_FINISH = 1;

    private static  Handler mUIHandler;

    /**
     * Creates an IntentService.  Invoked by your subclass's constructor.
     *
     * @param name Used to name the worker thread, important only for debugging.
     */
    public DownLoadService() {
        super(TAG);
    }

    @Override
    protected void onHandleIntent(@Nullable Intent intent) {
        String url = intent.getStringExtra(DOWNLOADURL);
        if (mUIHandler != null) {
            //下载开始,通知主线程
            Message startMsg = mUIHandler.obtainMessage(DOWN_START, "\n 开始下载 @" + System.currentTimeMillis() + "\n" + url);
            mUIHandler.sendMessage(startMsg);
        }

        SystemClock.sleep(4000);    //模拟下载

        if (mUIHandler != null) {
            //下载完成,通知主线程
            Message finishMsg = mUIHandler.obtainMessage(DOWN_FINISH, "\n 下载完成 @" + System.currentTimeMillis() + "\n" + url);
            mUIHandler.sendMessage(finishMsg);
        }

    }

    public static void setUiHandler(Handler handler) {
        mUIHandler = handler;
    }

}

Activity:

public class MainActivity extends RxAppCompatActivity implements View.OnClickListener, Handler.Callback {
    TextView mTvStartMsg;
    TextView mTvFinishMsg;
    Button mBtnStartDownload;

    private Handler mUIHandler;

    private List<String> urlList = Arrays.asList("https://ws1.sinaimg.cn/large/610dc034ly1fgepc1lpvfj20u011i0wv.jpg",
            "https://ws1.sinaimg.cn/large/d23c7564ly1fg6qckyqxkj20u00zmaf1.jpg",
            "https://ws1.sinaimg.cn/large/610dc034ly1fgchgnfn7dj20u00uvgnj.jpg");

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mTvStartMsg = (TextView) findViewById(R.id.tv_start_msg);
        mTvFinishMsg = (TextView) findViewById(R.id.tv_finish_msg);
        mBtnStartDownload = (Button) findViewById(R.id.btn_start_download);

        mBtnStartDownload.setOnClickListener(this);

        mUIHandler = new Handler(this);
        DownLoadService.setUiHandler(mUIHandler);

    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.tv_start_msg:

                break;
            case R.id.tv_finish_msg:
                break;
            case R.id.btn_start_download:
                Intent intent = new Intent(this,DownLoadService.class);
                for (String url : urlList) {
                    intent.putExtra(DownLoadService.DOWNLOADURL, url);
                    startService(intent);
                    SystemClock.sleep(4000);
                    Log.i("onion","startService:"+url);
                }

                mBtnStartDownload.setEnabled(false);

                break;
        }

    }

    @Override
    public boolean handleMessage(Message msg) {
        switch (msg.what) {
            case DownLoadService.DOWN_FINISH:
                Log.i("onion","开始下载: "+ msg.obj);

                break;
            case DownLoadService.DOWN_START:
                Log.i("onion","下载完成: "+ msg.obj);

                break;
        }

        return true;
    }


}

结果:

07-18 08:29:19.942 4418-4418/lib.com.myapplication I/onion: startService:https://ws1.sinaimg.cn/large/610dc034ly1fgepc1lpvfj20u011i0wv.jpg
07-18 08:29:23.944 4418-4418/lib.com.myapplication I/onion: startService:https://ws1.sinaimg.cn/large/d23c7564ly1fg6qckyqxkj20u00zmaf1.jpg
07-18 08:29:27.945 4418-4418/lib.com.myapplication I/onion: startService:https://ws1.sinaimg.cn/large/610dc034ly1fgchgnfn7dj20u00uvgnj.jpg
07-18 08:29:27.948 4418-4418/lib.com.myapplication I/onion: 下载完成: 
     开始下载 @1531916967948
    https://ws1.sinaimg.cn/large/610dc034ly1fgepc1lpvfj20u011i0wv.jpg
07-18 08:29:31.949 4418-4418/lib.com.myapplication I/onion: 开始下载: 
     下载完成 @1531916971948
    https://ws1.sinaimg.cn/large/610dc034ly1fgepc1lpvfj20u011i0wv.jpg
    下载完成: 
     开始下载 @1531916971949
    https://ws1.sinaimg.cn/large/d23c7564ly1fg6qckyqxkj20u00zmaf1.jpg
07-18 08:29:35.949 4418-4418/lib.com.myapplication I/onion: 开始下载: 
     下载完成 @1531916975949
    https://ws1.sinaimg.cn/large/d23c7564ly1fg6qckyqxkj20u00zmaf1.jpg
07-18 08:29:35.950 4418-4418/lib.com.myapplication I/onion: 下载完成: 
     开始下载 @1531916975950
    https://ws1.sinaimg.cn/large/610dc034ly1fgchgnfn7dj20u00uvgnj.jpg
07-18 08:29:39.951 4418-4418/lib.com.myapplication I/onion: 开始下载: 
     下载完成 @1531916979951
    https://ws1.sinaimg.cn/large/610dc034ly1fgchgnfn7dj20u00uvgnj.jpg

IntentService内部的HandlerThread 继承自 Thread,内部封装了 Looper,在这里新建线程并启动,所以启动 IntentService 不需要新建线程。IntentService 中使用的 Handler、Looper、MessageQueue 机制把消息发送到子线程中去执行的,所以多次启动 IntentService 不会重新创建新的服务和新的线程,只是把消息加入消息队列中等待执行,而如果服务停止,会清除消息队列中的消息,后续的事件得不到执行。

猜你喜欢

转载自blog.csdn.net/chenpdsu/article/details/81104123