当前位置:   article > 正文

Android进阶知识(二十三):Android的线程_android 什么任务必须放在主线程

android 什么任务必须放在主线程

Android进阶知识(二十三):Android的线程

  线程是Android的一个重要概念,从用途来说,线程分为主线程和子线程。

线程作用
主线程进程所拥有的线程,在Java中默认情况下一个进程只有一个线程,即为主线程。主线程主要处理界面交互相关的逻辑,在任何时候都必须有较高的响应速度,因此主线程中不能执行耗时任务
子线程也叫工作线程,用于执行耗时操作,除了主线程之外的线程都是子线程

  Android沿用了Java的线程模型,其中的线程也分为主线程和子线程,其中主线程也叫UI线程。主线程作用是运行四大组件以及处理它们和用户的交互,子线程作用则是执行耗时任务,例如网络请求、I/O操作等。
  Android中的线程形态除了传统的Thread之外,还包含了AsyncTask、HandlerThread以及IntentService,这三者的底层实现也是线程

线程形态描述
AsyncTask封装了线程池和Handler,主要是为了方便开发者在子线程中更新UI
HandlerThread具有消息循环的线程,内部可以使用Handler
IntentService内部采用HandlerThread执行任务,任务执行完毕自动退出

一、AsyncTask

  AsyncTask的描述可以总结为:

  • 一种轻量级的异步任务类,可以在线程池中执行后台任务,然后把执行的进度和结果传递给主线程并在主线程更新UI。
  • 封装了Thread和Handler。
  • 不适合进行特别耗时的后台任务,对于特别耗时的任务,建议使用线程池。

  AsyncTask的5个核心方法如下表所示。具体是使用方法可以参考笔者的笔记:Android基础知识(十一):AsyncTask原理与使用简介

方法含义
onPreExecute()主线程中执行,在异步任务执行之前被调用,一般可以用于做一些准备工作
doInBackground(Params… params)线程池中执行,用于执行异步任务,params表示异步任务的输入参数。可通过调用publishProgress方法更新任务进度,publishProgress会调用onProgressUpdate方法。此外,该方法返回结果给onPostExecute方法。
onProgressUpdate(Progress… values)在主线程中执行,当后台任务的执行进度发生改变时此方法会被调用
onPostExecute(Result result)主线程中执行,异步任务执行之后被调用,result为后台任务doInBackground返回值
onCancelled()主线程中执行,异步任务被取消时调用,此时onPostExecute不会被调用

  AsyncTask的具体使用过程有一些条件限制:
  ① AsyncTask的类必须在主线程中加载,这意味着AsyncTask第一次访问必须发生在主线程中。在Android 4.1及以上版本系统已自动完成——ActivityThread的main方法。
  ② AsyncTask的对象必须在主线程中创建
  ③ execute方法必须在UI线程中调用
  ④ 不要在程序中直接调用onPreExecute()、onPostExecute()、doInBackground()和onProgressUpdate()方法。
  ⑤ 一个AsyncTask对象只能执行一次,即只能调用一次excute方法,否则会报运行时异常。
  ⑥ 在Android 1.6之前,AsyncTask是串行执行任务的,Android 1.6的时候开始采用线程池处理并行任务。但是从Android 3.0开始,为了避免AsyncTask所带来的并发错误,又采用一个线程来串行执行任务,但是可以通过excuteOnExecutor方法来并行执行方法。
在这里插入图片描述
  接下来分析AsyncTask的工作原理,从execute方法开始分析,execute方法又会调用executeOnExecutor方法。

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;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

  其中sDefaultExecutor是一个串行的线程池,一个进程中的所有的AsyncTask在该线程池中排队执行。从上面代码可以看到,AsyncTask先执行onPreExecute方法,然后线程池开始执行。

@UnsupportedAppUsage
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);
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29

  从SerialExecutor的实现分析AsyncTask的排队执行过程。首先系统会把AsyncTask的Params参数封装成FutureTask(并发类,充当Runnable作用)对象mFuture。接着mFuture交给SerialExecutor的excute方法处理,该方法把mFuture插入到任务队列mTasks中,如果这个时候没有正在活动的AsyncTask任务,那么会调用scheduleNext方法执行下一个任务。当一个任务执行完,AsyncTask会继续执行其他任务直到所有任务被执行为止,这也就是其串行执行的原因。
  AsyncTask中有两个线程池和一个Handler,如下表所示。

组成描述
SerialExecutor用于任务的排队
THREAD_POOL_EXECUTOR用于真正地执行任务
InternalHandler用于将执行环境从线程池切换到主线程

  由于FutureTask的run方法会调用mWorker的call方法,因此call方法会在线程池执行。

mWorker = new WorkerRunnable<Params, Result>() {
    public Result call() throws Exception {
        mTaskInvoked.set(true);
        Result result = null;
        try {
            Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
            //noinspection unchecked
            result = doInBackground(mParams);
            Binder.flushPendingCommands();
        } catch (Throwable tr) {
            mCancelled.set(true);
            throw tr;
        } finally {
            postResult(result);
        }
        return result;
    }
};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

  在mWorker的call方法中,mTaskInvoked设置为true,表示当前任务已经被调用,然后执行AsyncTask的doInBackground方法,将其返回值传递给postResult方法。

private Result postResult(Result result) {
    @SuppressWarnings("unchecked")
    Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
            new AsyncTaskResult<Result>(this, result));
    message.sendToTarget();
    return result;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

  在上面代码中,postResult方法会通过sHandler发送一个MESSAGE_POST_RESULT的消息。

private static class InternalHandler extends Handler {
    public InternalHandler(Looper looper) {
        super(looper);
    }

    @SuppressWarnings({"unchecked", "RawUseOfParameterizedType"})
    @Override
    public void handleMessage(Message msg) {
        AsyncTaskResult<?> result = (AsyncTaskResult<?>) msg.obj;
        switch (msg.what) {
            case MESSAGE_POST_RESULT:
                // There is only one result
                result.mTask.finish(result.mData[0]);
                break;
            case MESSAGE_POST_PROGRESS:
                result.mTask.onProgressUpdate(result.mData);
                break;
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

  sHandler是一个静态的Handler对象,为了能够将执行环境切换到主线程,这要求sHandler这个对象必须在主线程中创建。由于静态成员会在加载类的时候进行初始化,这变相要求在主线程中加载
在这里插入图片描述
  sHandler收到MESSAGE_POST_RESULT消息之后,调用finish方法。

private void finish(Result result) {
    if (isCancelled()) {
        onCancelled(result);
    } else {
        onPostExecute(result);
    }
    mStatus = Status.FINISHED;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

  从上面代码也可以看到,如果AsyncTask被取消执行,会调用onCancelled方法,否则调用onPostExecute方法,并且会传递doInBackground的返回结果
  以上就是AsyncTask的整个工作原理过程。
在这里插入图片描述

二、HandlerThread

  HandlerThread继承了Thread,是一种可以使用Handler的Thread。其实现比较简单易理解,在run方法中通过Looper.prepare()创建消息队列,并通过Looper.loop()开启消息循环。

@Override
public void run() {
    mTid = Process.myTid();
    Looper.prepare();
    synchronized (this) {
        mLooper = Looper.myLooper();
        notifyAll();
    }
    Process.setThreadPriority(mPriority);
    onLooperPrepared();
    Looper.loop();
    mTid = -1;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

  与普通Thread不同之处在于,HandlerThread在内部创建了消息队列,外界需要通过Handler的消息方式通知HandlerThread执行任务。由于HandlerThread的run方法是无限循环,因此使用的时候需要通过quit或者quitSafely方法终止线程
在这里插入图片描述

三、IntentService

  IntentService的特点可以总结为如下几点:

  • 特殊的Service,继承了Service。
  • 用于执行后台耗时任务,当任务执行结束后自动停止
  • 由于是服务,优先级比单纯的线程高很多,不容易被杀死,适合执行高优先级的后台任务。

  在实现上,从onCreate方法可以看出,IntentService封装了HandlerThread和Handler。

@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);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

  在onCreate中,创建了HandlerThread,并其Looper构造Handler,这样通过mServiceHandler发送的消息会在HandlerThread中执行,即后台执行。
  每次启动IntentService其onStartCommand方法被调用,从而处理每个后台任务的Intent,而该方法调用了onStart方法。

@Override
public void onStart(@Nullable Intent intent, int startId) {
    Message msg = mServiceHandler.obtainMessage();
    msg.arg1 = startId;
    msg.obj = intent;
    mServiceHandler.sendMessage(msg);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

  可以看出,IntentService只是通过mServiceHandler发送消息,这个消息会在HandlerThread线程中处理,处理方式具体看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);
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

  handleMessage方法先调用onHandleIntent方法处理后台任务,之后通过stopSelf(startId)尝试停止服务,这就是其能够自动停止的原因。而Service可以使用stopSelf()停止服务,为何不用该方法呢?
在这里插入图片描述
  这两个方法的区别具体看下表。

方法描述
stopSelf()立刻停止服务,这个时候可能还有其他消息未处理
stopSelf(startId)等待所有消息都处理完毕后才终止服务。在尝试停止服务之前会判断最近启动服务的次数是否和startId相等,相等则停止,否则不停止

  因此,从逻辑上来说,使用stopSelf(startId)才能处理完所有消息。另外,onHandleIntent方法是抽象方法,需要子类实现。
  由于每执行一次后台任务就必须启动一次IntentService,而Looper是顺序处理消息的,这就意外着IntentService也是顺序执行后台任务的
  关于IntenService的使用可以参见笔记:Android基础知识(十三):Service生命周期及更多技巧
在这里插入图片描述

参考资料:《Android艺术开发探索》

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/你好赵伟/article/detail/251059
推荐阅读
相关标签
  

闽ICP备14008679号