异步任务:Handler,AsyncTask,HandlerThread,IntentService 的使用

999 阅读2分钟

Android中主线程(UI)不能执行耗时操作,所以耗时操作任务需要放在子线程中执行,但是主线程有需要根据子线程任务执行情况来更新UI,所以就涉及到异步任务的执行。

而Android中常见的异步执行任务有以下4种方式:Handler,AsyncTask,HandlerThread,IntentService。

下面介绍下这四种异步方式的使用。

一.Handler

线程间通信。

Message:消息的载体
MessageQueue:消息队列,负责存储消息
Handler:负责发送和处理消息(发送和处理不在同一个线程)
Looper:通过死循环来取出消息并且通知Handler处理

Handler将发送的消息Message保存到消息队列MessageQueue中,Looper不断取出消息队列中的消息交给Handler处理。

使用代码:

Handler handler;

...

handler = new Handler(){
    public void handleMessage(Message msg){
        Log.d(TAG,"currentThread:"+Thread.currentThread().getName()+"   HandlerThread,接收到:msg="+msg);
    }
 };

new Thread(){
    @Override
    public void run() {
        try {
            Thread.sleep(3000);
            Message message = new Message();
            message.obj = "我是Handler";
            handler.sendMessage(message);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}.start();

二.AsyncTask

一种轻量级的异步任务类,它封装了线程池和Handler。通过直接继承它可以很方便地实现后台异步任务的执行和进度的回调更新UI。 使用代码:

public class MyAsyncTask extends AsyncTask<String, Integer, Long> {
    public static final String TAG = "MyAsyncTask";

    @Override
    protected Long doInBackground(String... params) {
        int count = 100;
        long total = 0;
        for(int i = 0; i < count; i++) {
            // 模拟耗时操作
            try{
                Thread.sleep(1000);
            }catch (InterruptedException e) {
                e.printStackTrace();
            }

            // 更新进度给onProgressUpdate
            publishProgress((int) ((i / (float) count) * 100));
            total ++;
            Log.d(TAG,""+((i / (float) count) * 100));
            // 是否停止异步任务
            if(isCancelled()) {
                Log.d(TAG,"AsyncTask 任务取消!!!");
                break;
            }
        }
        // 返回结果给onPostExecute
        return total;
    }
}

开启AsyncTask任务代码:

HandlerThread myHandlerThread;
...

mMyAsyncTask = new MyAsyncTask();
// 开始任务
mMyAsyncTask.execute();

终止AsyncTask任务:

// 停止任务
if (mMyAsyncTask != null) {
    mMyAsyncTask.cancel(true);
}

执行后Log信息:

-------------mMyAsyncTask.execute()--------------
......
2021-08-07 18:46:13.910 9129-9679/ D/MyAsyncTask: 17.0
2021-08-07 18:46:14.921 9129-9679/ D/MyAsyncTask: 18.0
2021-08-07 18:46:15.941 9129-9679/ D/MyAsyncTask: 19.0
2021-08-07 18:46:16.900 9129-9679/ D/MyAsyncTask: 20.0
----------mMyAsyncTask.cancel(true);------------
2021-08-07 18:46:16.900 9129-9679/ D/MyAsyncTask: AsyncTask 任务取消!!!

分析日志信息,日志操作与操作一一对应。需要注意下执行后mMyAsyncTask.cancel(true),在AsyncTask中的doInBackground方法isCancelled()=true,从而break跳出任务循环。

三.HandlerThread

继承了Thread,它本质上就是具有消息循环的Thread,它内部建立了Looper。

使用代码:

myHandlerThread = new HandlerThread("handler_thread") ;
myHandlerThread.start();

// 给Handler指定myHandlerThread的Looper
handler =new Handler(myHandlerThread.getLooper()){
    @Override
    public void handleMessage(Message msg) {
        super.handleMessage(msg);
        // 这里接收Handler发来的消息,运行在handler_thread线程中
        Log.d(TAG,"currentThread:"+Thread.currentThread().getName()+"   HandlerThread,接收到:msg="+msg);
    }
};
new Thread(new Runnable() {
    @Override
    public void run() {
        // 在子线程给Handler发送数据
        try {
            Thread.sleep(3000);
            Message message = new Message();
            message.obj = "我是HandlerThread";
            handler.sendMessage(message) ;
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}).start();

//退出页面,退出HandlerThread
if (myHandlerThread !=null) {
    myHandlerThread.quit();
}

执行后Log信息:

MainActivity: currentThread:handler_thread   HandlerThread,接收到:msg=
{ when=-1ms what=0 obj=我是HandlerThread target=com.example.myfirstproject.async.MainActivity$6 }

参考Handler.在Handler里handler = new Handler()不带参数,是因为主线程(UI)在framework层源码中已经Looper.prepare(),也就是Handler的handleMessage处理消息代码是在Main主线程。

再来看HandlerThreadhandler =new Handler(myHandlerThread.getLooper()),使用的是HandlerThread的名字是"handler_thread"ThreadLooper,最后从日志看出该handleMessagehandler_thread名字的线程。

四.IntentService

一种特殊的Service,它继承了Service并且它是一个抽象类,系统对其进行了封装使其可以更方便地执行后台任务,内部采用了HandlerThread来执行任务。当任务执行完毕后IntentService会自动退出onDestory。

使用代码:

public class MyIntentService extends IntentService {
    private static final String TAG = "MyIntentService";

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

    public MyIntentService() {
        super("");
    }

    @Override
    protected void onHandleIntent(Intent intent) {
        String action = intent.getStringExtra("action");
        try {
            Thread.sleep(2000);
        }
        catch(InterruptedException e) {
            e.printStackTrace();
        }
        Log.d(TAG, "onHandleIntent被调用, action:" + action);
    }
    @Override
    public void onDestroy() {
        Log.d(TAG, "onDestroy被调用");
        super.onDestroy();
    }

}

注意下MyIntentService正确的构造方法名是不带参数的public MyIntentService(),而非IDE代码生成的构造方法public MyIntentService(String name)。不然会App会崩溃并抛出异常。

因为IntentService是一种特殊Service,也就是还是Service,所以需要在清单文件配置:

<service android:name=".xxx.MyIntentService" />

启动IntentService代码,同启动Service方式:

Intent intent = new Intent(MainActivity.this, MyIntentService.class);
intent.putExtra("action","action_IntentService_1");
startService(intent);
intent.putExtra("action","action_IntentService_2");
startService(intent);
intent.putExtra("action","action_IntentService_3");
startService(intent);

执行后Log信息:

18:56:11.820 10804-10988/ D/MyIntentService: onHandleIntent被调用, action:action_IntentService_1
18:56:13.825 10804-10988/ D/MyIntentService: onHandleIntent被调用, action:action_IntentService_2
18:56:15.827 10804-10988/ D/MyIntentService: onHandleIntent被调用, action:action_IntentService_3
18:56:15.832 10804-10804/ D/MyIntentService: onDestroy被调用

从日志能看出IntentService执行完任务后,IntentService调用了onDestroy。

即:当任务执行完毕后IntentService会自动退出onDestory


完结!!!