从源码角度理解Android线程

线程分为主线程和子线程,主线程主要是做与界面相关的事,而子线程往往用于做耗时操作。Android中扮演线程的角色有很多,如:AsyncTask、IntentService以及HandlerThread。AsyncTask的底层使用的是线程池,其他两种直接使用线程。

AsyncTask封装了线程池和Handler,在它的内部使用Handler去更新UI线程。HandlerThread是一个具有消息循环的线程。IntentService是一种服务,内部采用HandlerThread来执行任务,由于是一种服务,不是Service,所以不会轻易被系统杀死。

由于频繁的创建和销毁线程会消耗大量的系统资源,所以一般是用线程池来进行控制,Android的线程池源于Java。

一、主线程和子线程

主线程是进程拥有的线程,Java默认只有一个主线程,主线程主要处理界面交互的逻辑,必须有较高的响应度,不然会造成界面延缓卡顿,这就要求主线程不能执行耗时任务,耗时任务交由子线程,即工作线程。

Android沿用了Java的线程模式,其中主线程叫UI线程,作用是运行四大组件以及处理它们同用户交互,而子线程执行耗时的任务,否则会出现ANR现象。


二、Android的线程形态

除了传统的Thread以外,还包含AsyncTask、HandlerThread以及IntentService,这三者底层实现也是线程。

1.AsyncTask

AsyncTask是一个轻量级的异步任务类,他可以在线程池中执行后台任务,然后把任务的进度和最终的结果传递给主线程并在主线程中更新UI。AsyncTask封装了Thread和Handler,但是AsyncTask不适合执行特别耗时的任务,虽然说内部封装了线程池,但是该线程池只有一条线程。

AsyncTask是一个抽象的泛型类,它提供了Params、Progress、Result这三个泛型参数,其中Params表示参数的类型,Progress表示后台的执行任务的类型,Result表示后台任务的执行结果类型,如果不需要传入参数,可以使用void来代替。AsyncTask的声明如下:

  • Params 启动任务执行的输入参数,比如HTTP请求的URL。
  • Progress 后台任务执行的百分比。
  • Result 后台执行任务最终返回的结果,比如String。
public abstract class AsyncTask<Params,Progress,Result>

AsyncTask提供了4个核心方法,含义如下:

  • onPreExecute(): 在主线程中执行,在异步任务加载执行之前,此方法会调用,用于做一些任务准备工作。
  • doInBackground(Params... params):在线程池中执行,此方法用于执行异步任务,params表示异步任务的输入参数。在此方法中可以通过publishProgress来更新任务进度,publishProgress会调用omProgressUpdate方法。另外此方法需要返回结果给onPostExecute方法。
  • onProgressUpdate(Progress...values):在主线程中执行,当后台任务执行发生改变会调用此方法。
  • onPostExecute(Result result):在主线程中执行,在异步任务执行的后面,其中result参数是后台任务的返回值,即doInBackground的返回值。

上面的几个方法,onPreExecute先执行,接着是doInBackground,最后是onPostExecute。此外,AsyncTask还提供了onCancelled方法,它在主线程中用,当异步任务被取消,就会调用该方法,这个时候onPostExecute就不会被调用。

    private class DownloadFilesTask extends AsyncTask<URL, Integer, Long> {
        protected Long doInBackground(URL... urls) {
            int count = urls.length;
            long totalSize = 0;
            for (int i = 0; i < count; i++) {
                // totalSize += Downloader.downloadFile(urls[i]);
                publishProgress((int) ((i / (float) count) * 100));
                // Escape early if cancel() is called
                if (isCancelled())
                    break;
            }
            return totalSize;
        }

        protected void onProgressUpdate(Integer... progress) {
            // setProgressPercent(progress[0]);
        }

        protected void onPostExecute(Long result) {
            // showDialog("Downloaded " + result + " bytes");
        }
    }

该Demo主要模拟文件下载的过程,输入的参数类型是URL,后台任务的进程参数为Integer,而后台返回的结果是Long

new DownloadFilesTask().execute(new URL("http://www.baidu.com"),new URL("http://www.renyugang.cn"));

在DownloadFilesTask中,doInBackground用来执行具体的下载任务并通过publishProgress来更新下载的进度,同时要判断是否被外界取消。当下载完成后会调用onProgressUpdate来显示结果。doInBackground是在线程池中执行的,onProgressUpdate是在主线程中,当publishProgress被调用时,会顺带调用onProgressUpdate。当任务执行完毕后,也会调用onPostExecute,我们可以通过这个方法告知用户下载完毕。

AsyncTask在具体使用过程中也有一些限制,主要如下几点:

  • (1)AsyncTask必须在主线程中加载,Android4.1以上的系统已经自动完成了,在ActivityThread的main方法中会调用AsyncTask的init方法。
  • (2)AsyncTask的实例必须在主线程中创建
  • (3)execute方法必须在UI线程中调用
  • (4)不要在程序中手动调用onPreExecute、onPostExecute。doInBackground、onProgressUpdate方法。
  • (5)一个AsyncTask对象只能执行一次,只能调用一次execute方法。
  • (6)在Android 1.6之前,AsyncTask是串行执行任务,1.6后可以并行执行,但是3.0以后,为了避免并行带来的bug,又采用串行,虽然这样,但也可以通过AsyncTask的executeOnExecutor方法来并行执行任务。

2.AsyncTask的工作原理

分析AsyncTask的工作原理,我们从它的execute方法爱是分析,execute又会调用executeOnExecutor方法,实现如下:

    @MainThread
    public final AsyncTask<Params, Progress, Result> execute(Params... params) {
        return executeOnExecutor(sDefaultExecutor, params);
    }
    
    
        @MainThread
    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;
    }

sDefaultExecutor实际上是一个串行的线程池,一个进程中所有的AsyncTask都在这个线程池中排队执行,在executeOnExecutor方法中,最先执行的是onPreExecute,然后线程池开始执行,下面试线程池的分析,如下所示:

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

从SerialExecutor的实现可以看出AsyncTask是排队执行的过程。首先会把AsyncTask的Params参数封转成FutureTask对象,FutureTask是并发类,在这里充当Runnable的作用,接着FutureTask交给SerialExecutor的execute方法执行,SerialExecutor的execute会通过mTask的offer将FutureTask对象插入到mTask中,如果这个时候没有正在活动的AsyncTask,或者当一个AsyncTask任务执行完,就会调用SerialExecutor的scheduleNext方法来执行下一个任务,直至任务被执行完毕。

AsyncTask中有两个线程池(SerialExecutor和THREAD_POOL_EXECUTOR)和一个Handler(InternalHandler),其中SerialExecutor用于任务的排队,THREAD_POOL_EXECUTOR用于真正的执行任务,InternalHandler用于将执行环境切换到主线程。AsyncTask的构造方法中有这么一段代码,由于FutureTask的run方法会调用mWorker的call方法,FutureTask会被提交到AsyncTask包含的线程池中执行,因此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);
                    //noinspection unchecked
                    result = doInBackground(mParams);
                    Binder.flushPendingCommands();
                } catch (Throwable tr) {
                    mCancelled.set(true);
                    throw tr;
                } finally {
                    postResult(result);
                }
                return result;
            }
        };

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;
    }
    
        private static Handler getHandler() {
        synchronized (AsyncTask.class) {
            if (sHandler == null) {
                sHandler = new InternalHandler();
            }
            return sHandler;
        }
    }

在上面代码中,会通过getHandler返回一个sHandler,通过sHandler返回一个MESSAGE_POST_RESULT的消息,这个sHandler的定义如下:


    private static InternalHandler sHandler;

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

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

sHandler是一个静态变量,为了能够将环境切换到主线程,必须要在主线程中创建sHandler,这就变相要求AsyncTask也需要在主线程中加载。sHandler收到MESSAGE_POST_RESULT这个消息后,会调用AsyncTask的finish的方法:

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

AdyncTask的finish方法逻辑比较简单,如果AsyncTask被执行了,那么onCancelled就被调用,否则就执行onPostExecute方法。

3.HandlerThread

HandlerThread继承了Thread,本质上是一个Thread,只不过内部建立了Looper,可以使用Handler,主要是在run方法中通过Looper.prepare()来创建消息队列,然后通过Lopper.loop()来开启消息循环,在实际应用中就允许在HandlerThread中创建Handler了。HandlerThread的run方法如下:

    @Override
    public void run() {
        mTid = Process.myTid();
        Looper.prepare();
        synchronized (this) {
            mLooper = Looper.myLooper();
            notifyAll();
        }
        Process.setThreadPriority(mPriority);
        onLooperPrepared();
        Looper.loop();
        mTid = -1;
    }

从HandlerThread的实现来看,HandlerThread内不能创建了消息队列,这和普通的Thread不一样,要通过Handler的消息方式通知HandlerThread去执行任务。在使用完HandlerThread后,可以通过quit或者quitSafety来退出。

4.IntentService

IntentService是一种特殊的Service,它继承Service并且是个抽象类,所以要用它的子类。IntentService是一种服务,所以优先级比单纯的线程高,里面封装了HandlerThread和Handler,这一点从他的onCreate方法可以看出。

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

当IntentService第一次被启动的时候,它的onCreate会被调用,然后创建一个HandlerThread对象,用它的Looper来构建一个Handler对象,这样每次发送handler发送消息的时候都会在HandlerThread线程中执行。

每次启动IntentHandler,都会调用onStartCommand方法,IntentService在onStartCommand中处理每个后台任务的Intent,同时调用onStart方法:

    @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 onStart(@Nullable Intent intent, int startId) {
        Message msg = mServiceHandler.obtainMessage();
        msg.arg1 = startId;
        msg.obj = intent;
        mServiceHandler.sendMessage(msg);
    }

可以看出,IntentService仅仅通过mServiceHandler发送一个消息,这个消息会在HandlerThread中处理,mServiceHandler收到消息后,会将Intent对象传递到onHandlerIntent中去处理,这个Intent和一开始传进来的Intent是一样的。当onHandlerIntent方法执行结束后,IntentService会调用stopSelf(int startId)来停止服务,该方法会等消息队列没有消息后才停止,而stopSelf()会立即就停止。

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

InstentService的onHandleIntent方法是一个抽象方法,我们需要在子类中实现,它的作用是通过Intent参数中区分具体的任务并执行任务,执行完后通过stopSelf(int startId)来停止服务。另外,每次执行一个后台任务必须启动一次IntentHandler,而内部通过消息的方式向HandlerThread请求执行任务,HandlerThread的Looper是顺序执行任务,这就意味着IntentService也是顺序执行任务。

csdn博客:http://blog.csdn.net/AAAAA_Sean_M/article/details/74139754

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,560评论 4 361
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,104评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,297评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,869评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,275评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,563评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,833评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,543评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,245评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,512评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,011评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,359评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,006评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,062评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,825评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,590评论 2 273
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,501评论 2 268

推荐阅读更多精彩内容