Android 消息循环知识
Android 消息循环知识
会飞的章鱼 发表于2年前
Android 消息循环知识
  • 发表于 2年前
  • 阅读 13
  • 收藏 0
  • 点赞 0
  • 评论 0
摘要: Android应用程序线程的消息循环模型 自己理解和归纳

            Android应用程序的主线程就是我们理解的界面线程,主要负责界面响应事件,所以需要避免其他不相干的事情在里面操作。如果主线程不能快速响应界面事件,将会得到一个很差的体验效果和ANR,那将是不能忍的一件事。如果有一个耗时操作或者其他和界面不相干的操作,建议是放在异步线程里面去做,对于一次性的操作,似乎可以写个线程,如果有不定期的后台事件需要处理,每次都去开启线程,销毁线程,将会是很大浪费资源。如果有一个消息循环的话,每当你有任务需要处理,你可以把这个任务丢给队列,发送一个消息给消息循环,就可以了,执行操作的线程根据消息去执行队列里面的任务,这样只需要一个线程的开销,当你最后不再使用的时候,销毁就可以了。

            在Android中主要有三种消息循环模型:1、应用程序主线程消息循环模型,主要是ActivityThread,2、与界面无关的应用程序子线程消息循环模型,主要是HandlerThread,3、是和界面相关的应用程序子线程消息循环模型,主要是AsyncTask。   

        ActivityThread:

                        Activity管理服务ActivityManagerService在启动一个应用程序组件,如果发现这个应用程序需要在新的应用程序中运行,就会调用Process类的静态成员函数start来启动一个新的应用程序,在start成员函数中会执行int pid = Process.start("android.app.ActivityThread",mSimpleProcessManagement?app.processName:null,uid,uid,gids,debugFlags,null);

        就会执行ActivityThread的静态成员函数Main的实现

//Android源码代码

 

public static void main(String[] args) {
        SamplingProfilerIntegration.start();
        // CloseGuard defaults to true and can be quite spammy.  We
        // disable it here, but selectively enable it later (via
        // StrictMode) on debug builds, but using DropBox, not logs.
        CloseGuard.setEnabled(false);
        Process.setArgV0("<pre-initialized>");
        Looper.prepareMainLooper();
        if (sMainThreadHandler == null) {
            sMainThreadHandler = new Handler();
        }
        ActivityThread thread = new ActivityThread();
        thread.attach(false);
        if (false) {
            Looper.myLooper().setMessageLogging(new
                    LogPrinter(Log.DEBUG, "ActivityThread"));
        }
        Looper.loop();
        throw new RuntimeException("Main thread loop unexpectedly exited");
    }

Looper.prepareMainLooper()的执行会在当前应用程序主线程中创建一个消息循环Looper对象,接着在Looper对象中创建一个MessageQueue对象,来描述一个应用程序主线程的消息队列。

Looper.loop()的执行使得当前应用程序主线程进入到当前所创建的一个消息循环中。

Looper类的静态成员函数preparemainLooper只能在应用程序的主线程中调用,并且只能被调用一次,由于Looper类的静态成员函数prepareMainLooper在应用程序主线程启动的时候调用了一次,因此,无论在主线程还是在子线程中,我们都不会在调用他,否则就会收到一个运行时异常,

如何解决子线程不能操作应用程序界面的问题呢? 这个也是Looper存在的一个理由,Looper对象除了会保存一个线程局部变量中之外,还会单独保存在Looper类的静态成员变量mMainLooper中,这样我们就可以在应用程序子线程中调用Looper的静态成员函数getMainLooper来获得主线程中的Looper对象,并且通过这个Looper对象像应用程序主线程的消息队列发送与界面操作相关的消息。这样就解决了 这个问题。


HandlerThread:

            在Java程序中我们可以通过实现抽线类Thread或者继承Runnable接口,实现里面的run方法,来创建一个线程,但是这个线程是没有消息循环的一个作用,也就做不到操控界面的效果。如果需要有消息循环的线程可以使用HandlerThread类实现。

使用方法:

//HadnlerThread  Android源码  这部分是粘贴Android源码

public class HandlerThread extends Thread {
    int mPriority;
    int mTid = -1;
    Looper mLooper; //这是消息循环的Looper
    public HandlerThread(String name) {
        super(name);
        mPriority = Process.THREAD_PRIORITY_DEFAULT;
    }
    
    /**
     * Constructs a HandlerThread.
     * @param name
     * @param priority The priority to run the thread at. The value supplied must be from 
     * {@link android.os.Process} and not from java.lang.Thread.
     */
    public HandlerThread(String name, int priority) {
        super(name);
        mPriority = priority;
    }
    
    /**
     * Call back method that can be explicitly over ridden if needed to execute some
     * setup before Looper loops.
     */
    protected void onLooperPrepared() {
    }
    public void run() {
        mTid = Process.myTid();
        Looper.prepare();//在执行strat之后,就会执行run,也就会执行这行代码,这里会创建一个MessageQueue对象,用来描述一个应用程序主线程的消息队列
        synchronized (this) {
            mLooper = Looper.myLooper();
            notifyAll();
        }
        Process.setThreadPriority(mPriority);
        onLooperPrepared();
        Looper.loop();//进入线程创建的一个消息循环中
        mTid = -1;
    }
    
    /**
     * This method returns the Looper associated with this thread. If this thread not been started
     * or for any reason is isAlive() returns false, this method will return null. If this thread 
     * has been started, this method will block until the looper has been initialized.  
     * @return The looper.
     */
    public Looper getLooper() {
        if (!isAlive()) {
            return null;
        }
        
        // If the thread has been started, wait until the looper has been created.
        synchronized (this) {
            while (isAlive() && mLooper == null) {
                try {
                    wait();
                } catch (InterruptedException e) {
                }
            }
        }
        return mLooper;
    }
    
    /**
     * Ask the currently running looper to quit.  If the thread has not
     * been started or has finished (that is if {@link #getLooper} returns
     * null), then false is returned.  Otherwise the looper is asked to
     * quit and true is returned.
     */
    public boolean quit() {
        Looper looper = getLooper();
        if (looper != null) {
            looper.quit();
            return true;
        }
        return false;
    }
    
    /**
     * Returns the identifier of this thread. See Process.myTid().
     */
    public int getThreadId() {
        return mTid;
    }
//使用方式
     HandlerThread ht = new   HandlerThread("a");
     ht.start();
     public class ThreadTask implements Runnable{
        public void run(){
            
        }
    }
    Handler handler = new Handler(ht.getLooper());
    handler.post(new ThreadTask());
    ht.quit();//退出前面创建的子线程


AsyncTask:

        这个是Android提供的一个一部任务类,用来将一个涉及界面操作的任务放在一个子线程中执行,虽然异步任务子线程自己没有消息循环,但是它可以利用主线程的消息循环来执行界面相关的操作。

     

  AsyncTask<Integer, Integer, Integer> task = new AsyncTask<Integer, Integer, Integer>(){
        boolean stop;
    @Override
    protected Integer doInBackground(Integer... arg0) {
        // TODO Auto-generated method stub
        Integer initcounter = arg0[0];
        stop = false;
        while(!stop){
            publishProgress(initcounter);
            try{
                Thread.sleep(1000);
            }catch(Exception e){
                e.printStackTrace();
            }
        initcounter++;
        }
    return initcounter;
    }
    @Override
    protected void onPostExecute(Integer result) {
        // TODO Auto-generated method stub
        super.onPostExecute(result);
        callback.count(val);//回调
    }
    @Override
    protected void onProgressUpdate(Integer... values) {
        // TODO Auto-generated method stub
        super.onProgressUpdate(values);
        int val = values[0];
        callback.count(val);//回调
    }
        
  };
task.execute(1);

    这是一个简单的计数器逻辑,callback是回调接口,用于显示在界面上。异步任务task的执行是在doInbackground中执行的,并且是运行在一个子线程中,因此不会影响主线程处理界面事件。计数器+1之后调用成员函数publishProgress方法将这个计数值分发给onProgressUpdate来处理,后者就通过回调更新到界面上,那么就意味这个这个方法执行在主线程中,否则就不能更新到界面中,原因后面会分析,doInBackground方法返回之后,会将返回值分发给成员方法onPostExecute来处理,这个返回值就是异步任务task所描述的一个计数器的终止值,在这个方法里面也是通过回调更新到界面上,那么这个方法也是在主线程中执行的。接下来就接下为什么一个方法执行的异步线程中,另外两个方法执行在主线程中,AsyncTask是如何做到的。首先让我们看下Android源码:(由于字数限制,会单独开个文档copy源码




共有 人打赏支持
粉丝 4
博文 17
码字总数 12563
×
会飞的章鱼
如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!
* 金额(元)
¥1 ¥5 ¥10 ¥20 其他金额
打赏人
留言
* 支付类型
微信扫码支付
打赏金额:
已支付成功
打赏金额: