美文网首页
Android异步任务实现详解

Android异步任务实现详解

作者: 雷涛赛文 | 来源:发表于2021-02-20 16:55 被阅读0次

       在Android开发中,主线程(UI线程)不能执行耗时的操作,通常将一些耗时的操作使用异步任务的方式进行处理,简单概括一下:
       1.只能在UI线程操作UI视图,不能在子线程中操作;
       2.不能在UI线程中执行耗时操作,否则会阻塞UI线程,引起ANR、卡顿等问题。

       接下来看一下在Android开发中,经常用到的处理耗时操作用的异步任务实现方式及实现原理:

一.Thread

       创建一个Thread是最简单直接的方式,在Thread内部去执行耗时的操作,实现方式如下:

Thread t = new Thread(new Runnable() {
    @Override
    public void run() {
        //执行耗时操作
    }
});
t.start();

       上面仅仅是在Thread内部执行耗时的操作,如果在执行完耗时操作后,需要UI来进行更新,那应该如何操作呢?接下来继续看:

二.Thread+Handler

       Android中提供了Handler机制来进行线程之间的通信,可以使用异步方式:Thread + handler 来进行异步任务执行及UI更新,实现方式如下:
       详细了解Handler的原理请参考Android Handler原理详解

new Thread() {
    public void run() {
        //执行耗时操作.....
        //更新UI
        mUIHandler.sendMessage(mUIHandler.obtainMessage(UIHandler.MSG_UPDATE_UI,2));
    }
}.start();

//UI线程Handler
private static class UIHandler extends Handler {
    private final static int MSG_UPDATE_UI = 1;

    private final WeakReference<HandlerFragment> mFragment;

    private UIHandler(HandlerFragment fragment) {
        mFragment = new WeakReference<>(fragment);
    }

    @Override
    public void handleMessage(Message msg) {
        HandlerFragment fragment = mFragment.get();

        switch (msg.what) {
            case MSG_UPDATE_UI:
                fragment.updateUI((int)msg.obj);
                break;
            default:
                break;
        }
    }
}

       从以上可以看到,在Thread内部执行耗时操作后,然后调用Handler来通知主线程更新UI。
       这样的话,每次执行耗时请求,都需要new一个Thread,会导致开销过大,可以通过以下方式来进行改进:

       子线程内部创建Looper+Handler
new Thread() {
    public void run() {
        Looper.prepare();
        mNoUIHandler = new NoUIHandler(handlerFragment);
        Looper.loop();
    }
}.start();

//工作线程Handler来执行耗时操作
private static class NoUIHandler extends Handler {
    private final static int MSG_HANDLE = 1;

    private final WeakReference<HandlerFragment> mFragment;

    private NoUIHandler(HandlerFragment fragment) {
        mFragment = new WeakReference<>(fragment);
    }

    @Override
    public void handleMessage(Message msg) {
        HandlerFragment fragment = mFragment.get();

        switch (msg.what) {
            case MSG_HANDLE:
                fragment.handleMsg();
                break;
            default:
                break;
        }
    }
}

private void handleMsg() {
      //执行耗时操作......
      //通知主线程更新UI
      mUIHandler.sendMessage(mUIHandler.obtainMessage(UIHandler.MSG_UPDATE_UI,1));
}

//启动耗时操作
mNoUIHandler.sendEmptyMessage(NoUIHandler.MSG_HANDLE);

       通过以下改善,在Thread内部创建Looper,然后创建Handler,这样的话Thread就不会退出,就不需要频繁创建Thread,此时Handler使用的是子线程创建的looper,从而Handler消息处理(耗时操作)就在子线程里面,执行完耗时操作,通知主线程来更新UI;
       这样的话,Thread一直不退出,是不是会造成内存泄露呢?
       如果不再使用的话,直接通过mNoUIHandler.getLooper().quitSafely()来让Looper.loop()结束循环,Thread也就退出了。

三.HandlerThread

       针对以上问题,Android也早就考虑到了,HandlerThread就出现了,结合了Thread+Looper+Handler进行封装实现,实现如下:

private HandlerThread mWorkHandlerThread;
private Handler mWorkHandler;

//首先创建HandlerThread
mWorkHandlerThread = new HandlerThread("handler thread");
//HandlerThread执行start()
mWorkHandlerThread.start();
//创建Handler将HandlerThread的Looper传入,从而在对应的线程内处理消息
mWorkHandler = new WorkHandler(mWorkHandlerThread.getLooper(), this);

//工作线程的Handler
private static class WorkHandler extends Handler {
    private static final int MSG_START = 200;
    private ControlService mService;

    private WorkHandler(Looper looper, ControlService service) {
        super(looper);
        mService = service;
    }

    @Override
    public void handleMessage(Message msg) {
        switch (msg.what) {
            case MSG_START:
                mService.createClient();
                break;
            default:
                break;
       }
    }
}

//发送处理消息
mWorkHandler.sendEmptyMessageDelayed(WorkHandler.MSG_START, delayTime);

       通过上述可以看到,通过HandlerThread来执行异步任务相对简单一些,不需要自己创建Looper,创建一个HandlerThread,然后执行start()即可,看一下HandlerThread内部的实现:

       HandlerThread源码分析
public class HandlerThread extends Thread {
    public HandlerThread(String name) {
        super(name);
        mPriority = Process.THREAD_PRIORITY_DEFAULT;
    }

    @Override
    public void run() {
        Looper.prepare();
        synchronized (this) {
            mLooper = Looper.myLooper();
            notifyAll();
        }
        ......
        Looper.loop();
    }

    public Looper getLooper() {
        if (!isAlive()) {
            return null;
        }
        
        // If the thread has been started, wait until the looper has been created.
        synchronized (this) {
            while (isAlive() && mLooper == null) {
                try {
                    wait();
                } catch (InterruptedException e) {
                }
            }
        }
        return mLooper;
    }

    public boolean quitSafely() {
        Looper looper = getLooper();
        if (looper != null) {
            looper.quitSafely();
            return true;
        }
        return false;
    }

       HandlerThread继承了Thread,当调用start()方法后,会回调到HandlerThread内部的run()方法,在run()方法内部,执行了Looper.prepare()及Looper.loop();
       提出一个小问题,getLooper()会不会返回null?
       答案是否定的,可以看到在getLooper()内部有加锁及wait()相关操作,如果Looper没有创建好的话,会执行wait()等待,当run()方法内创建完成后执行notifyAll(),从而确保getLooper()不为null。
       同理,如果不需要HandlerThread处理消息后,通过调用quitSafely()来退出Looper.loop()循环,从而结束HandlerThread。

四.IntentService

       IntentService继承Service类,启动了一个异步服务任务,内部是通过HandlerThread来实现异步处理任务,先看下代码实现:

Intent intent = new Intent(this, WorkService.class);
intent.putExtra("name", "获取图片信息");
intent.putExtra("url", baseUrl);
//启动IntentService
startService(intent);

public class WorkService extends IntentService {

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

    @Override
    public void onCreate() {
        super.onCreate();
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        return super.onStartCommand(intent, flags, startId);
    }

    @Override
    protected void onHandleIntent(@Nullable Intent intent) {
        String url = intent.getStringExtra("url");
        String name = intent.getStringExtra("name");
        //执行耗时操作
    }

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

       从以上可以看到,实现也比较简单,通过startService()来启动IntentService,然后在onHandleIntent()内部执行耗时操作就可以了,看一下IntentService内部的实现原理:

       IntentService源码分析
public abstract class IntentService extends Service {
    private volatile Looper mServiceLooper;
    private volatile ServiceHandler mServiceHandler;
    private String mName;

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

    @Override
    public void onCreate() {
        super.onCreate();
        HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
        thread.start();

        mServiceLooper = thread.getLooper();
        mServiceHandler = new ServiceHandler(mServiceLooper);
    }

    @Override
    public void onStart(@Nullable Intent intent, int startId) {
        Message msg = mServiceHandler.obtainMessage();
        msg.arg1 = startId;
        msg.obj = intent;
        mServiceHandler.sendMessage(msg);
    }

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

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

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

       通过上述代码可以看到,onHandleIntent()是一个抽象方法,需要子类来实现的,IntentService内部是通过HandlerThread来实现的,在执行startService()后,会IntentService的onCreate()内部创建HandlerThread,确保多次调用startService()仅创建一次HandlerThread相关的操作,每次在执行onStartCommand()后,都是通过mServiceHandler.sendMessage(),最终会回调子类实现的onHandleIntent(),从而在方法内部执行耗时操作就可以了。
       在执行完onHandleIntent()后,会自动调用stopSelf(msg.arg1)来结束该service,在onDestroy()内部会通过mServiceLooper.quit()来退出loop()循环,继而结束HandlerThread。

五.AsyncTask

       AsyncTask是一个封装好的轻量级异步任务处理类,轻量级,一个异步任务使用AsyncTask很方便,多个异步任务使用HandleThread更好。
       有一种场景非常适合使用AsyncTask:需要显示进度值的场景,使用这个封装好的类,方便理解,节省代码。
       先看一下AsyncTask 这个抽象类的定义,当我们定义一个类来继承AsyncTask时,需要为其指定3个泛型参数:

public abstract class AsyncTask<Params, Progress, Result>

       Params: 传递给异步任务执行时的参数的类型。
       Progress: 异步任务在执行的时候将执行的进度返回给UI线程的参数的类型。
       Result: 异步任务执行完后返回给UI线程的结果的类型。
       代码实现如下:

private class WorkTask extends AsyncTask<String, Integer, Bitmap> {

    //执行线程任务前的操作,工作在主线程
    @Override
    protected void onPreExecute() {
    }

    //接收输入参数、执行任务中的耗时操作、返回线程任务执行的结果
    //该方法必须复写
    @Override
    protected Bitmap doInBackground(String... strings) {
        //执行耗时操作.......
        //可调用publishProgress()显示进度, 之后将执行onProgressUpdate()
        publishProgress(10);
        return null;
    }

    //在主线程显示线程任务执行的进度
    @Override
    protected void onProgressUpdate(Integer... progresses) {
    }

    //接收线程任务执行结果、将执行结果返回给主线程显示到UI组件
    @Override
    protected void onPostExecute(Bitmap result) {
    }

    //将异步任务设置为:取消状态
    @Override
    protected void onCancelled() {
    }
}

//创建AsyncTask子类的实例对象,注:AsyncTask子类的实例必须在UI线程中创建
WorkTask workTask = new WorkTask();
//手动调用execute(Params... params)从而执行异步线程任务
//注:必须在UI线程中调用;同一个AsyncTask实例对象只能执行1次,若执行第2次将会抛出异常
workTask.execute(baseUrl);

       通过以上可以看到,在使用AsyncTask时,先定义一个类来继承AsyncTask,耗时任务在doInBackground()里面执行,执行完后的结果通过onPostExecute(result)来返回。
       使用时,先创建一个本地异步任务实例,然后执行execute(para)即可,注意:必须在UI线程中调用execute();同一个AsyncTask实例对象只能执行1次,若执行第2次将会抛出异常。

       AsyncTask源码分析
public abstract class AsyncTask<Params, Progress, Result> {

    public AsyncTask() {
        this((Looper) null);
    }

    public AsyncTask(@Nullable Looper callbackLooper) {
        mHandler = callbackLooper == null || callbackLooper == Looper.getMainLooper()
            ? getMainHandler()
            : new Handler(callbackLooper);

        mWorker = new WorkerRunnable<Params, Result>() {
            .......
        };

        mFuture = new FutureTask<Result>(mWorker) {
            .......
        };
    }

//抽象方法,子类必须实现
protected abstract Result doInBackground(Params... params);

       从上述可以看到,在AsyncTask的构造函数内,主要做了三件事:
       ①.获取主线程的Handler;Handler传入的Looper是主线程的Looper,所以该Handler工作在主线程;

private static Handler getMainHandler() {
    synchronized (AsyncTask.class) {
        if (sHandler == null) {
            sHandler = new InternalHandler(Looper.getMainLooper());
        }
        return sHandler;
    }
}

private static class InternalHandler extends Handler {
    public InternalHandler(Looper looper) {
        super(looper);
    }
    public void handleMessage(Message msg) {
        .......
    }
}

       ②.创建了一个WorkerRunnable实例;WorkerRunnable是一个抽象类,实现了Callable接口;
       ③.创建了一个FutureTask实例,将上面的WorkerRunnable实例作为参数传入,FutureTask是Runnable的子类,即后面在线程池中需要执行的runnable。
        以上就是在创建完本地实例后,做的三项工作,接下来执行execute(para),看一下内部实现:

private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
public static final Executor SERIAL_EXECUTOR = new SerialExecutor();

public final AsyncTask<Params, Progress, Result> execute(Params... params) {
    return executeOnExecutor(sDefaultExecutor, params);
}

public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,
            Params... params) {
    if (mStatus != Status.PENDING) {
        switch (mStatus) {
            case RUNNING:
                throw new IllegalStateException("Cannot execute task:"
                            + " the task is already running.");
            case FINISHED:
                throw new IllegalStateException("Cannot execute task:"
                            + " the task has already been executed "
                            + "(a task can be executed only once)");
        }
    }
    mStatus = Status.RUNNING;
    onPreExecute();
    mWorker.mParams = params;
    exec.execute(mFuture);
    return this;
}

        通过以上可以看到,在执行execute(paras)后,会调用到executeOnExecutor(),在executeOnExecutor()内部主要执行了三件事:
        ①.先判断当前的状态,如果是RUNNING,直接抛异常,这就是一个AsyncTask实例不能执行两次的原因;接下来设置状态为RUNNING;
        ②.执行onPreExecute(),准备工作,在主线程执行;
        ③.将execute()传入的参数赋值给WorkRunnable实例,后续会用到,接着通过Executor.execute(mFuture)[sDefaultExecutor]来执行逻辑;
       以上可以看到,最终是通过Executor.execute(mFuture)来执行逻辑,接下来看一下sDefaultExecutor是如何创建的:

private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
public static final Executor SERIAL_EXECUTOR = new SerialExecutor();

private static class SerialExecutor implements Executor {
    final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();
    Runnable mActive;

    public synchronized void execute(final Runnable r) {
        mTasks.offer(new Runnable() {
            public void run() {
                try {
                    r.run();
                } finally {
                    scheduleNext();
                }
            }
        });
        if (mActive == null) {
            scheduleNext();
        }
    }

    protected synchronized void scheduleNext() {
        if ((mActive = mTasks.poll()) != null) {
            THREAD_POOL_EXECUTOR.execute(mActive);
        }
    }
}

public static final Executor THREAD_POOL_EXECUTOR;

static {
    ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
                CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE_SECONDS, TimeUnit.SECONDS,
                new SynchronousQueue<Runnable>(), sThreadFactory);
    threadPoolExecutor.setRejectedExecutionHandler(sRunOnSerialPolicy);
    THREAD_POOL_EXECUTOR = threadPoolExecutor;
}

       创建逻辑如上,一目了然,当执行excute(mFuture)后,创建了一个Runnable实例,然后加入到mTasks的队列中,注意:此时还未执行;接下来mActive==null,执行scheduleNext(),在scheduleNext()内部取出mTasks里面的runnable,最终通过THREAD_POOL_EXECUTOR线程池来执行,注意该线程池是在静态代码块里面创建的,也就是说AsyncTask在整个Android系统中维护一个线程池,有可能被其他进程的任务抢占而降低效率。
       简单总结一下execute()的执行过程:在AsyncTask在执行execute()时,会创建两个线程池,一个是SERIAL_EXECUTOR,负责统一管理线程,将需要执行的runnable加入到mTasks队列里面,顺序执行;另外一个是THREAD_POOL_EXECUTOR,负责执行线程,将mTasks队列里面的runnable进行执行。
       看一下FutureTask内部的run()方法逻辑:

public void run() {
    ......
    try {
        Callable<V> c = callable;
        if (c != null && state == NEW) {
            V result;
            boolean ran;
            try {
                result = c.call();
                ran = true;
            } catch (Throwable ex) {
                result = null;
                ran = false;
                setException(ex);
            }
            if (ran)
                set(result);
        }
    } 
    ........
}

       在run()内部会执行到WorkRunnable实例mWorker的call()方法,在返回去看看mWorker内部call()的实现逻辑:

mWorker = new WorkerRunnable<Params, Result>() {
    public Result call() throws Exception {
        mTaskInvoked.set(true);
        Result result = null;
        try {
            Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
            result = doInBackground(mParams);
            Binder.flushPendingCommands();
        } catch (Throwable tr) {
            mCancelled.set(true);
            throw tr;
        } finally {
            postResult(result);
        }
        return result;
    }
};

private static abstract class WorkerRunnable<Params, Result> implements Callable<Result> {
    Params[] mParams;
}

       在call()内部,异步线程的优先级已经被默认设置成THREAD_PRIORITY_BACKGROUND,不会与 UI 线程抢占资源,然后执行doInBackground(),mParams是在构造函数内部传入的,最后执行postResult(result)。

protected final void publishProgress(Progress... values) {
    if (!isCancelled()) {
        getHandler().obtainMessage(MESSAGE_POST_PROGRESS,
                    new AsyncTaskResult<Progress>(this, values)).sendToTarget();
    }
}

private Result postResult(Result result) {
    Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,new AsyncTaskResult<Result>(this, result));
    message.sendToTarget();
    return result;
}

private static class InternalHandler extends Handler {
    public InternalHandler(Looper looper) {
        super(looper);
    }
    public void handleMessage(Message msg) {
        AsyncTaskResult<?> result = (AsyncTaskResult<?>) msg.obj;
        switch (msg.what) {
            case MESSAGE_POST_RESULT:
                result.mTask.finish(result.mData[0]);
                break;
            case MESSAGE_POST_PROGRESS:
                result.mTask.onProgressUpdate(result.mData);
                break;
        }
    }
}

private static class AsyncTaskResult<Data> {
    final AsyncTask mTask;
    final Data[] mData;

    AsyncTaskResult(AsyncTask task, Data... data) {
        mTask = task;
        mData = data;
    }
}

       在postResult()内部将result、AsyncTask对象封装成AsyncTaskResult作为object,通过handler进行处理,最终调用了AsyncTask的finish()方法:

private void finish(Result result) {
    if (isCancelled()) {
        onCancelled(result);
    } else {
        onPostExecute(result);
    }
    mStatus = Status.FINISHED;
}

       如果在执行execute()后返回结果前调用了cancle()方法,会调用onCancelled(),正常会调用onPostExecute(result),以上都是在主线程回调的。

六.总结

实现方式 优点 缺点
Thread+Handler 实现简单 1.代码规范性较差,不易维护
2.每次操作都会开启一个匿名线程,系统开销较大
HandlerThread 1.内部已经实现了普通线程的Looper消息循环
2.可以串行执行多个任务
3.内部拥有自己的消息队列,不会阻塞UI线程
1.没有结果返回接口,需要自行处理
2.消息过多时,容易造成阻塞
3.只有一个线程处理,效率较低
4.线程优先级默认优先级为 THREAD_PRIORITY_DEFAULT,容易和 UI 线程抢占资源
IntentService 1.只需要继承 IntentService,就可以在 onHandlerIntent 方法中异步处理Intent 类型任务
2.任务结束后 IntentService 会自行停止,无需手动调用 stopService
3.可以执行处理多个 Intent 请求,顺序执行多任务
4.IntentService 是继承自Service,具有后台Service的优先级
1.需要启动服务来执行异步任务,不适合简单任务处理
2.异步任务是由 HandlerThread 实现的,只能单线程、顺序处理任务
3.没有返回 UI 线程的接口
AsyncTask 1.结构清晰,使用简单,适合与后台任务的交互
2.异步线程的优先级已经被默认设置成THREAD_PRIORITY_BACKGROUND,不会与 UI 线程抢占资源
1.结构略复杂,代码较多
2.每个 AsyncTask 只能被执行一次,多次调用会发生异常
3.AsyncTask在整个Android系统中维护一个线程池,有可能被其他进程的任务抢占而降低效率

       以上就是Android系统中常用的异步任务的实现方式。

相关文章

网友评论

      本文标题:Android异步任务实现详解

      本文链接:https://www.haomeiwen.com/subject/hodtfltx.html