Handler和Application

Handler

handler是什么 ?

是android给我们提供用来更新UI的一套机制, 也是一套消息处理的机制, 我们可以发送消息, 也可以通过它处理消息

为什么要用handler

Android在设计的时候, 就封装了一套消息创建, 传递, 处理机制, 如果不遵循这样的机制就没有办法更新UI信息, 就会抛出一异常信息

handler怎么用 ?

文档表述:
A Handler allows you to send and process Message and Runnable objects associated with a thread’s MessageQueue. Each Handler instance is associated with a single thread and that thread’s message queue. When you create a new Handler, it is bound to the thread / message queue of the thread that is creating it – from that point on, it will deliver messages and runnables to that message queue and execute them as they come out of the message queue.

android为什么要设计只能通过Handler机制更新UI ?

最根本的原因是解决多线程并发问题.
假设如果在一个Activity当中, 有多个线程去更新UI, 并且都没有加锁机制, , 那么会产生什么样的现象呢 ?

更新界面错乱 !

如果对更新UI的操作都进行加锁处理的话又会产生什么样的问题呢

性能下降!!!!!

处于对以上目的问题的考虑, android给我们一套更新Ui的机制, 我们只需要遵循这样的机制就可以了,
根本不用关心多线程的问题, 所以更新UI的操作, 都是在主线程的消息队列当中去轮询处理的

Handler原理是什么 ?

面试重要!!!!!

Handler封装了消息的发送 (主要包括把消息发送给谁)

Android中对应 “生产者和消费者” 模型

Message : 产品
MessageQueue : 仓库 (永远用不到, Android已封装好)
Looper : 循环
Handler : 对仓库和循环的一个持有 (通过Handler放置产品) [类似物流]
Handler.Callback 回调接口, 必须自己去实现

Looper

1, 内部包含一个消息队列也就是MessageQueue, 所有的Handler发送的消息都走向这个消息队列

2, Looper.looper方法, 就是一个死循环, 不断的从MessageQueue去取消息, 如果有消息就处理消息, 没有消息就阻塞

3, 一个线程可以产生一个Looper对象,由它来管理此线程里的MessageQueue(消息队列)。

MessageQueue

就是一个消息队列, 可以添加消息, 并处理消息

Handler

内部会跟Looper进行关联, 也就是说Handler的内部可以找到Looper, 找到了Looper也就找到了, MessageQueue , 在Handler中发送消息, 其实就是向MessageQueue队列中发送消息

UIthread 通常就是main thread,而Android启动程序时会替它建立一个MessageQueue。

总结:

handler负责发送消息, Looper负责接收Handler发送的消息, 并直接把消息回传给handler自己, MessageQueue就是一个存储消息的容器

对Looper和Looper.loop的深入理解

Android中的Looper类,是用来封装消息循环和消息队列的一个类,用于在android线程中进行消息处理。handler其实可以看做是一个工具类,用来向消息队列中插入消息的。

(1) Looper类用来为一个线程开启一个消息循环。 默认情况下android中新诞生的线程是没有开启消息循环的。(主线程除外,主线程系统会自动为其创建Looper对象,开启消息循环。) Looper对象通过MessageQueue来存放消息和事件。一个线程只能有一个Looper,对应一个MessageQueue。

(2) 通常是通过Handler对象来与Looper进行交互的。Handler可看做是Looper的一个接口,用来向指定的Looper发送消息及定义处理 方法。 默认情况下Handler会与其被定义时所在线程的Looper绑定,比如,Handler在主线程中定义,那么它是与主线程的Looper绑定。 mainHandler = new Handler() 等价于new Handler(Looper.myLooper()). Looper.myLooper():获取当前进程的looper对象,类似的 Looper.getMainLooper() 用于获取主线程的Looper对象。

(3) 在非主线程中直接new Handler() 会报如下的错误: E/AndroidRuntime( 6173): Uncaught handler: thread Thread-8 exiting due to uncaught exception E/AndroidRuntime( 6173): java.lang.RuntimeException: Can’t create handler inside thread that has not called Looper.prepare() 原因是非主线程中默认没有创建Looper对象,需要先调用Looper.prepare()启用Looper。

(4) Looper.loop(); 让Looper开始工作,从消息队列里取消息,处理消息。

注意:写在Looper.loop()之后的代码不会被执行,这个函数内部应该是一个循环,当调用mHandler.getLooper().quit()后,loop才会中止,其后的代码才能得以运行。

感谢原作者

Handler机制运行过程

以上是对Handler整个运行机制内部的详细描述, 有兴趣的同学可以查看一下源码

Handler创建消息

每一个消息都需要被指定的Handler处理,通过Handler创建消息便可以完成此功能。Android消息机制中引入了消息池。Handler创建消息时首先查询消息池中是否有消息存在,如果有直接从消息池中取得,如果没有则重新初始化一个消息实例。使用消息池的好处是:消息不被使用时,并不作为垃圾回收,而是放入消息池,可供下次Handler创建消息时使用。消息池提高了消息对象的复用,减少系统垃圾回收的次数。消息的创建流程如图所示。

这里写图片描述

//推荐使用, Message中有一个消息池, 会循环使用内部的message
Message message = Message.obtain();
Message obtain = Message.obtain(handler, 0, String.valueOf(i + 1))
Message.obtain(handler);
handler.obtainMessage()
Handler发送消息

UI主线程初始化第一个Handler时会通过ThreadLocal创建一个Looper,该Looper与UI主线程一一对应。使用ThreadLocal的目的是保证每一个线程只创建唯一一个Looper。之后其他Handler初始化的时候直接获取第一个Handler创建的Looper。Looper初始化的时候会创建一个消息队列MessageQueue。至此,主线程、消息循环、消息队列之间的关系是1:1:1。

Handler、Looper、MessageQueue的初始化流程如图所示:
这里写图片描述

Hander持有对UI主线程消息队列MessageQueue和消息循环Looper的引用,子线程可以通过Handler将消息发送到UI线程的消息队列MessageQueue中

handler.sendMessage(message);
//在有Handler对象的前提下调用
message.sendToTarget();
处理消息

UI主线程通过Looper循环查询消息队列UI_MQ,当发现有消息存在时会将消息从消息队列中取出。首先分析消息,通过消息的参数判断该消息对应的Handler,然后将消息分发到指定的Handler进行处理。

子线程通过Handler、Looper与UI主线程通信的流程如图所示。

这里写图片描述

// 临时解决方案, 如果代码规范的话不会出现下面的情况
 Message obtain = Message.obtain(handler, new Runnable() {
     @Override
     public void run() {
         //可以在这个方法中执行UI操作
     }
 });
 //不会调用handler中的handleMessage, 而是调用上面的run方法
 handler.sendMessage(obtain);
 handler.post(new Runnable() {
     @Override
     public void run() {

     }
 });
 runOnUiThread(new Runnable() {
     @Override
     public void run() {

     }
 });

感谢原作者

Application

只要在一个应用中, 所有的Activity共用一个Application, 保存一些全局的变量

注意: 所有的应用被销毁的时候, Application不一定被销毁无论怎么用, Application只会创建一次

如何创建一个自己的Application

BaseApplication.java

public class BaseApplication extends Application {
    private static final String TAG = BaseApplication.class.getSimpleName();
    private String text;

    /**
     * 启动时, 只执行一次, 做应用的初始化
     */
    @Override
    public void onCreate() {
        super.onCreate();
        Log.d(TAG, "onCreate: ");
        text = "BaseApplication";

    }


    public String getText() {
        return text;
    }

    public void setText(String text) {
        this.text = text;
    }
}

在清单文件中添加

<application
     android:name=".BaseApplication"
        ...
</application>

在MainActivity中获取创建的Application

private static final String TAG = MainActivity.class.getSimpleName();

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    Log.d(TAG, "onCreate: ");
    setContentView(R.layout.activity_main);
    BaseApplication application = (BaseApplication) getApplication();
    String text = application.getText();
    Toast.makeText(MainActivity.this, text, Toast.LENGTH_SHORT).show();
    application.setText("启动一次后");
}
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值