引言:

出于性能优化考虑,Android的UI操作并不是线程安全的,这意味着如果有多个线程并发操作UI组件,可能导致线程安全问题。

为了解决这个问题,Android制定了一条简单的规则:只允许UI线程修改Acitivity里的组件,这样就会导致新启动的线程无法动态改变组建的属性值。

但在实际的应用开发中,需要让新启动的线程改变界面组件的属性值,这就需要借助与Handler的消息传递机制来实现了。

一、Handler的使用

Handler类的主要作用有两个:

》在新启动的线程中发送消息

》在主线程中获取,处理消息

为了让主线程能“适时”的处理新启动的线程发送的消息,只能通过回调的方式来实现。

重写Handler类中处理消息的方法;新启动的线程发送消息时消息会发送到与之关联的MessageQueue;而Handler会不断地从MessageQueue种获取并处理消息;这将导致handler类中处理消息的方法被回调。

new Thread(new Runnable() {
    @Override
    public void run() {
    try {
        Thread.sleep(2000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    Message msg = handler.obtainMessage();
    msg.what = Constant.USER_LOGIN;
    msg.obj = "您尚未注册,是否快捷登录";
    handler.sendMessage(msg);
    }
}).start();
Handler mHandler = new Handler() {

    public void handleMessage(android.os.Message msg) {
        super.handleMessage(msg);
        if (activity.isFinishing()) {
          return;
        }
        if (msg.what == Constant.USER_LOGIN) {
          ToastUtils.showShort(activity, msg.obj.toString());
        }
    }
};

二、与Handler一起工作的几个组件,Looper、MessageQueue

》Looper:每个线程只有一个Looper,负责管理MessagQueue,会不断地总MessageQueue中取出消息,并将消息分给对应的Handler处理。

》MessageQueue:由Looper负责管理。采用先进先出的方式来管理Message。

》Handler:把消息发送给Looper管理的MessageQueue,并负责处理Looper分给它的消息。

三、使用Handler源码分析:

在线程中使用Handler的步骤如下。

private class LooperThread extends Thread {
    public Handler mHandler;

    @Override
    public void run() {
    Looper.prepare();
    mHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
        Log.i(TAG, msg.toString());
        }
    };
    Looper.loop();
    }
}

-调用Looper的prepare()方法为当前线程创建Looper对象,创建Looper对象时,构造器会创建与之配套的MessageQueue。

public final class Looper {
    private static final String TAG = "Looper";  

    // sThreadLocal.get() will return null unless you've called prepare().
    static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
    private static Looper sMainLooper;  // guarded by Looper.class  

    final MessageQueue mQueue;
    final Thread mThread;  

    private Printer mLogging;
}  
public static void prepare() {
    prepare(true);
}
private static void prepare(boolean quitAllowed) {
    if (sThreadLocal.get() != null) {
        throw new RuntimeException("Only one Looper may be created per thread");
    }
    //这里new一个Looper对象
    sThreadLocal.set(new Looper(quitAllowed));  }
}  

-有了Looper之后,创建Handler子类的实例,重写handleMessage()方法,该方法负责处理来自于其他线程的消息。

//构造方法
public Handler() {this(null, false);}  

public Handler(Callback callback, boolean async) {
    if (FIND_POTENTIAL_LEAKS) {
        final Class<? extends Handler> klass = getClass();
        if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
                (klass.getModifiers() & Modifier.STATIC) == 0) {
            Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
                klass.getCanonicalName());
        }
    }  

    mLooper = Looper.myLooper();
    if (mLooper == null) {
        throw new RuntimeException(
            "Can't create handler inside thread that has not called Looper.prepare()");
    }
    mQueue = mLooper.mQueue;
    mCallback = callback;
    mAsynchronous = async;
}  

-调用Looper的loop()方法启动Looper。

public static void loop() {
    final Looper me = myLooper();
    if (me == null) {
        throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
    }
    final MessageQueue queue = me.mQueue;

    Binder.clearCallingIdentity();
    final long ident = Binder.clearCallingIdentity();

    for (;;) {
        Message msg = queue.next(); //获取消息队列的笑一个消息,没有消息将会阻塞
        if (msg == null) {
            // 如果消息为空,表明消息队列正在退出
            return;
        }

        // This must be in a local variable, in case a UI event sets the logger
        Printer logging = me.mLogging;
        if (logging != null) {
            logging.println(">>>>> Dispatching to " + msg.target + " " +
                    msg.callback + ": " + msg.what);
        }

        msg.target.dispatchMessage(msg);

        if (logging != null) {
            logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
        }

        // 使用final修饰标识符,保证在分发消息的过程中线程标识符不会被更改
        final long newIdent = Binder.clearCallingIdentity();
        if (ident != newIdent) {
            Log.wtf(TAG, "Thread identity changed from 0x"
                    + Long.toHexString(ident) + " to 0x"
                    + Long.toHexString(newIdent) + " while dispatching to "
                    + msg.target.getClass().getName() + " "
                    + msg.callback + " what=" + msg.what);
        }

        msg.recycle();
    }
}

Handler消息传递机制的更多相关文章

  1. 安卓开发_深入理解Handler消息传递机制

    一.概述 因为子线程的run()方法无法修改UI线程(主线程)的UI界面,所以Android引入了Handler消息传递机制,实现在新创建的线程中操作UI界面 二.消息类(Message) 消息类是存 ...

  2. Android学习笔记-事件处理之Handler消息传递机制

    内容摘要:Android Handler消息传递机制的学习总结.问题记录 Handler消息传递机制的目的: 1.实现线程间通信(如:Android平台只允许主线程(UI线程)修改Activity里的 ...

  3. 事件处理机制与Handler消息传递机制

    一.基于监听的事件处理机制 基于监听的时间处理机制模型: 事件监听机制中由事件源,事件,事件监听器三类对象组成 处理流程如下: Step 1:为某个事件源(组件)设置一个监听器,用于监听用户操作 St ...

  4. Android学习之Handler消息传递机制

    Android只允许UI线程修改Activity里的UI组件.当Android程序第一次启动时,Android会同时启动一条主线程(Main Thread),主线程主要负责处理与UI相关的事件,如用户 ...

  5. Handler消息传递机制——Handler类简洁

    Handler类的主要作用有两个: 在新启动的线程中发送消息. 在主线程中获取.处理消息. 上面的说法很简单,只要分成两步即可:在新启动的线程中发送消息:然后在主线程上获取.并处理消息.但这个过程涉及 ...

  6. Android Handler消息传递机制

    在Android系统中,类Handler主要有如下两个作用. 在新启动的线程中发送消息. 在主线程中获取.处理消息. 类Handler在实现上述作用时,首先在新启动的线程中发送消息,然后在主线程中获取 ...

  7. Handler 消息传递机制

    1,Handler 的概念Handler 是用来干什么的?1)执行计划任务,可以在预定的时间执行某些任务,可以模拟定时器 2)线程间通信.在Android的应用启动时,会创建一个主线程,主线程会创建一 ...

  8. Android中的消息机制:Handler消息传递机制

    参考<疯狂android讲义>第2版3.5 P214 一.背景 出于性能优化考虑,Android的UI操作并不是线程安全的,这意味着如果有多个线程并发操作UI组件,可能导致线程安全问题.为 ...

  9. Handler消息传递机制——Handler、Loop、MessageQueue的工作原理

    为了更好地理解Handler的工作原理,先介绍一下与Handler一起工作的几个组件. Message:Handler接收和处理的消息对象. Looper:每个线程只能拥有一个Looper.它的loo ...

随机推荐

  1. 智能车学习(十八)——电机学习

    一.C车电机选择 1.摘要:      因为C车模在四轮车的优势是有两个电机,可以进行主动差速,劣势是电机太弱了....所以如何选择电机,就是个钱的问题了,电机多一点,就比较好选,但是C车电机跑多了就 ...

  2. 已解决:Strict Standards: Non-static method cls_image::gd_version() should not be called statically in...

    在安装Ecshop的时候,遇到两个⚠️问题: Strict Standards: Non-static method cls_image::gd_version() should not be cal ...

  3. 使用yum来安装或卸载CentOS图形界面包

    命令行模式安装图形界面 yum grouplist 检查已安装的组 yum groupinstall "X Window System" yum groupinstall &quo ...

  4. 软件开发中的完整测试所包括的环节UT、IT、ST、UAT

    软件开发中的完成测试环境所包括的环节包括:UT.IT.ST.UAT UT = Unit Test 单元测试 IT = System Integration Test 集成测试ST = System T ...

  5. HTML轉PDF - 使用Pechkin套件

    剛好跟人討論到HTML轉PDF需求,便對工具進行簡單評估以備不時之需. 網路上比較多人推的是WkHtmlToPdf,如果是用.NET開發,已經有人包成NuGet套件,直接搜尋pechkin就可找到,它 ...

  6. CocoaPods安装和使用教程

    Code4App 原创文章.转载请注明出处:http://code4app.com/article/cocoapods-install-usage 目录 CocoaPods是什么? 如何下载和安装Co ...

  7. js公有、私有、静态属性和方法的区别

          现下,javascript大行其道,对于网站开发人员来说,javascript是必需掌据的一门语言,但随着jquery等框架的流行和使用,许多人对于原生javascript缺乏深入的理解, ...

  8. outlook——还原“未读邮件”文件夹

    参考链接:http://office.microsoft.com/zh-cn/outlook-help/HA010283248.aspx 摘抄: 还原“未读邮件”文件夹 全部显示 全部隐藏 “未读邮件 ...

  9. XIII Open Cup named after E.V. Pankratiev. GP of SPb

    A. Graph Coloring 答案为$1$很好判,为$2$只需要二分图染色,对于$3$,首先爆搜哪些边要染成第$3$种颜色,然后二分图染色判定即可. B. Decimal Fraction 枚举 ...

  10. Haskell 笔记 ③

    ①循环?NO!请递归思考问题! 手艹一个求列表中最大值代码,C语言中习惯性for扫一下比较出最大值.但是可以用递归! maximum'::(Ord a)=>[a]->a maximum' ...