多线程机制实例

概述

目前我们了解到在一个Android 程序开始运行的时候,会单独启动一个Process。默认的情况下,所有这个程序中的Activity或者Service(Service和 Activity只是Android提供的Components中的两种,除此之外还有Content Provider和Broadcast Receiver)都会跑在这个Process。

一个Android 程序默认情况下也只有一个Process,但一个Process下却可以有许多个Thread。

在这么多Thread当中,有一个Thread,我们称之为UI Thread。UI Thread在Android程序运行的时候就被创建,是一个Process当中的主线程Main Thread,主要是负责控制UI界面的显示、更新和控件交互。在Android程序创建之初,一个Process呈现的是单线程模型,所有的任务都在一 个线程中运行。因此,我们认为,UI Thread所执行的每一个函数,所花费的时间都应该是越短越好。而其他比较费时的工作(访问网络,下载数据,查询数据库等),都应该交由子线程去执行, 以免阻塞主线程。

那么,UI Thread如何和其他Thread一起工作呢?常用方法是:

诞生一个主线程的Handler物件,当做Listener去让子线程能将讯息Push到主线程的Message Quene里,以便触发主线程的handlerMessage()函数,让主线程知道子线程的状态,并在主线程更新UI。

框架实例学习

UI线程和Lua线程指的是什么

目前我们框架中存在两个线程:UI线程和Lua线程。
每当客户端获得一断新的报文后,都会解析这段报文,解析的动作包括构建DOM树、创建控件、存储并装配样式和属性、执行Lua脚本等。这么多的工作不可能全部放到UI线程中,因此需要有另外一个线程来做这些事情。
我们把这“另外一个线程”称为Lua线程,因为这个线程中进行的动作都是由Lua脚本主动触发的。
和Lua线程对应的,系统自带的主线程即UI线程。

UI线程和Lua线程所负责的工作

  1. UI线程本身只做和页面刷新有关的工作,如addView、设置控件的LayoutParams、setContentView等。
  2. Lua线程本身做一些后台的工作,如上文提到的解析报文和样式、执行Lua脚本等工作。其中Lua线程管理了一个任务队列,并且管理控件的属性库和样式库资源。控件的属性库/样式库被Lua线程上任务队列中的任务所使用。

两条线程之间如何进行交互

  • UI->Lua:如果需要在UI线程中操作Lua线程所持有的控件属性库/样式库,则需要生成一个任务,并将这个任务放到Lua线程的任务队列上,等待Lua线程从任务队列上拿出并执行。
  • Lua->UI:非UI线程的动作想要操作UI线程上的控件,需要使用handler机制,发送message,这是一个Android系统的机制。

代码实例

首先在程序启动时会:

// 构建基础模块和底层类
mAndroidEMPBuilder = onCreateAndroidEMPBuilder();
mEMPRender = new EMPRender(mAndroidEMPBuilder);

之后会在Render中初始化任务环境:

// 构建任务环境
mEMPTaskManager = new EMPTaskManager();
mEMPTaskManager.setEMPRender(this);

实例化EMPTaskManager()任务管理类:

public EMPTaskManager() {
    mQueueTask = new QueueTask();//初始化一个任务队列
    new MainThread().start();//框架主线程Lua线程启动
    mEMPThreadPool = new EMPThreadPool();//初始化一个线程池
}

初始化的任务队列:

class QueueTask {

    public boolean mQuiting = false;
    private Queue<InstructTask<?, ?>> mQueue = null;
    //队列中存放(抽象类)InstructTask<E,F>实例
    protected QueueTask() {
        mQuiting = false;
        mQueue = new LinkedList<InstructTask<?, ?>>();//链式列表,插入和删除性能较高,Arraylist线性列表,读取查找性能较高。
    }

    protected void put(InstructTask<?, ?> task) {
        if (task == null) {
            return;
        }
        synchronized (this) {
            if (mQuiting) {
                return;
            } 
            mQueue.add(task);
            notifyAll();
        }
    }

    protected InstructTask<?, ?> next() {
        synchronized (this) {
            if (mQueue.isEmpty()) {
                return null;
            }
            return mQueue.poll();
        }
    }

    protected void clear() {
        synchronized (this) {
            mQueue.clear();
        }
    }

    protected final void quit() {
        synchronized (this) {
            if (mQuiting) {
                return;
            }
            mQuiting = true;
            notifyAll();
        }
    }
}

(抽象类)InstructTask(E,F),本次任务的输出,作为下次任务的输入,这种模式下,可以把一系列的任务顺序执行。

/**
 * 任务执行
 * 
 * @param <E>
 *            入口数据对象
 * @param <F>
 *            出口数据对象
 */
public abstract class InstructTask<E, F> implements Runnable {

    private E mE = null;
    private InstructTask<F, ?> mNextTask = null;

    public InstructTask(E e) {
        mE = e;
    }

    private void setE(E e) {
        mE = e;
    }

    /**
     * 添加下一任务
     * 
     * @param nextTask
     */
    public final void addNextTask(InstructTask<F, ?> nextTask) {
        mNextTask = nextTask;
    }

    @Override
    public final void run() {
        F f = doRun(mE);//执行本次次任务,并把结果作为下个任务的入口数据
        if (mNextTask != null) {
            if (f != null) {
                mNextTask.setE(f);
            }
            mNextTask.run();
        }
        doFinish();//如果没有next任务,则走finsh结束任务
    }

    /**
     * 任务处理
     * 
     * @param e
     * @return
     */
    public abstract F doRun(E e);

    /**
     * 当前任务完成
     * 
     */
    public void doFinish() {
    }
}

框架主线程Lua线程,构造一个消息循环机制,MaintThread开启后,一直等待mQueueTask任务队列中poll出一个任务,然后执行,如果没有任务就一直等。

class MainThread extends Thread {

        @Override
        public void run() {
            if (Looper.myLooper() == null) {
                Looper.prepare();
            }
            try {
                for (;;) {
                    if (mQueueTask.mQuiting) {
                        mQueueTask.clear();
                        mQueueTask = null;
                        return;
                    }
                    InstructTask<?, ?> task = mQueueTask.next();
                    if (task == null) {
                        synchronized (mQueueTask) {
                            try {
                                mQueueTask.wait();
                            } catch (InterruptedException e) {
                                Utils.printException(e);
                            }
                        }
                        if (task == null) {
                            continue;
                        }
                    }
                    task.run();
                }
            } catch (Exception e) {
                Utils.printException(e);
            } finally {
                if (mQueueTask != null && !mQueueTask.mQuiting) {
                    new MainThread().start();
                }
            }
        }
    }

具体在框架中应用,任务链:

  • 当程序启动时首先创建一个页面加载任务,下面涉及到的任务全是InstructTask抽象类的实例
InstructTask<String, String> loadTask = mResources.createLoadTask(path, isAddHistory);
//读取报文,并把报文保存在Stack页面保存栈里面
  • InstructTask<String, ?> parserTask = getParserTask(null, animationType, listener, sltParam);//创建解析任务
  • loadTask.addNextTask(parserTask);//把解析任务作为加载报文任务的下一个任务mQueueTask.put(loadTask);//把任务链的第一个任务加到任务队列里
  • 解析任务中所包含的任务:
// 第一步,slt解析
        SLTParserTask sltParserTask = new SLTParserTask(mEMPRender, content, sltParams);
        // 第二步,xml解析
        ParserTask parserTask = new ParserTask(mEMPRender, content);
        //slt解析任务之后执行xml解析任务
        sltParserTask.addNextTask(parserTask);
        // 第三步,布局
        Compose composer = new Compose(mEMPRender);
        //把布局任务放到解析任务之后
        parserTask.addNextTask(composer);
        // 第四步,执行lua
        composer.addNextTask(getExecuteTask(null));
/**
     * 创建任务执行lua脚本
     * 
     * @param script
     * @return
     */
    public InstructTask<Object, String> getExecuteTask(Object script) {
        return new InstructTask<Object, String>(script) {
            @Override
            public String doRun(Object e) {
                return mEMPLua.loadBuffer(e);
            }
        };
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值