当前位置: 移动技术网 > IT编程>移动开发>Android > 从源码角度理解Android线程

从源码角度理解Android线程

2018年11月03日  | 移动技术网IT编程  | 我要评论

动物总动员下载,北京pm2.5,如果没有你 曾昱嘉

线程分为主线程和子线程,主线程主要是做与界面相关的事,而子线程往往用于做耗时操作。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

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也是顺序执行任务。

如对本文有疑问,请在下面进行留言讨论,广大热心网友会与你互动!! 点击进行留言回复

相关文章:

验证码:
移动技术网