文档章节

Android 消息循环知识

会飞的章鱼
 会飞的章鱼
发布于 2015/06/25 11:01
字数 1811
阅读 26
收藏 0

            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源码




© 著作权归作者所有

共有 人打赏支持
会飞的章鱼
粉丝 3
博文 17
码字总数 12563
作品 0
深圳
程序员
深入解读Linux与Android的相互关系& Android消息处理系统的原理

深入解读Linux与Android的相互关系 大家都知道Android是基于Linux内核的操作系统,也曾经和Linux基金会因为内核问题产生过分歧,本文将开始对Android的内核进行剖析,主要介绍Android和Linux...

AlphaJay
2011/07/29
0
0
深入理解Android消息处理系统——Looper、Handler、Thread

熟悉Windows编程的朋友可能知道Windows程序是消息驱动的,并且有全局的消息循环系统。而Android应用程序也是消息驱动的,按道理来说也应该提供消息循环机制。实际上谷歌参考了Windows的消息循...

惊天
2011/07/26
0
0
android蓝牙手柄、仿QQ看房、仿慕课网、数据库二维码框架等源码

Android精选源码 可自定义图片指示器并支持自定义Tab宽度的TabLayout源码(http://www.apkbus.com/thread-599243-1-1.html) android蓝牙控制手柄操作源码(http://www.apkbus.com/thread-59928...

逆鳞龙
05/15
0
0
Android framework——Activity的启动和创建

本人制作的app:1号密码 欢迎试用   最近学习Android相关知识,感觉仅仅了解Activity几个生命周期函数基本上没有任何意义的; 于是想深入了解一下Activity到底是怎么一回事怎么运行来的;里...

jackxu2015
2015/08/14
0
0
android消息机制,异步和多线程

android消息机制,异步和多线程 前言 在xxxx1.92正式版的时候付费购买曾单独封装了一个流程PaymentFlow并继承于Handler,调用方只要依据这个流程创建流程实例并触发开始,实例即可依据设定的支付...

ZHL
2012/09/03
0
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

阿里云API网关使用教程

API 网关(API Gateway)提供高性能、高可用的 API 托管服务,帮助用户对外开放其部署在 ECS、容器服务等阿里云产品上的应用,提供完整的 API 发布、管理、维护生命周期管理。用户只需进行简...

mcy0425
32分钟前
4
0
解决远程登陆误按ctrl+s锁屏假死恢复

使用putty时,偶尔发生屏幕假死,不能输入等情况。 后来发现,只要数据ctrl+s,就会假死;输入ctrl+q就可以恢复过来。 很多刚从windows转移到linux上来工作的朋友,在用vi/vim编辑文件时,常常...

HJCui
35分钟前
0
0
@Transactional

事务管理是应用系统开发中必不可少的一部分。Spring 为事务管理提供了丰富的功能支持。Spring 事务管理分为编程式和声明式的两种方式。编程式事务指的是通过编码方式实现事务;声明式事务基于...

asdf08442a
39分钟前
2
0
widows下强制解除8080端口占用问题

使用win+R打开命令窗口 输入以下命令查看哪个任务占用了8080端口 netstat -ano |findstr "8080" 然后通过任务id强制关闭占用该端口的进程 tskill 10044 // 自己的试情况而定,这个ID是LISTE...

_Artisan
48分钟前
2
0
productFlavors简单实用

最近项目中,不同环境需要配置的参数越来越多,为了减少修改代码次数。研究了一下productFlavors的使用方式,总结如下 1. as3.0以上版本使用productFlavors时需要指定一个flavorDimensions,...

火云
51分钟前
1
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部