欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

从源码角度理解Android线程

程序员文章站 2022-06-01 20:05:50
线程分为主线程和子线程,主线程主要是做与界面相关的事,而子线程往往用于做耗时操作。android中扮演线程的角色有很多,如:asynctask、intentservice以及handlerthrea...

线程分为主线程和子线程,主线程主要是做与界面相关的事,而子线程往往用于做耗时操作。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,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 {
        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("https://www.baidu.com"),new url("https://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 execute(params... params) {
        return executeonexecutor(sdefaultexecutor, params);
    }


        @mainthread
    public final asynctask 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 mtasks = new arraydeque();
        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() {
            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(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也是顺序执行任务。