个人博客:https://blog.N0tExpectErr0r.cn

小专栏:https://xiaozhuanlan.com/N0tExpectErr0r

Handler完全解读——Handler的使用

Handler是什么

Handler是Android给我们提供用于更新UI的一套机制,也是一套消息处理机制。我们用它可以发送消息,也可以用它处理消息。在Android开发中有着非常重要的地位。

为什么要使用Handler

当一个应用程序运行时,它会创建一个进程。这个进程就是我们的主线程(UI线程&Activity Thread) 。在主线程中,会默认为我们在系统中默认创建一个Looper,这个Looper会与我们的Message Queue 和 主线程有一定联系。 在main线程中,主要是运行一个Message Queue,管理着顶级的应用程序(Activity,Boardcast Receiver…)这些顶级应用程序在默认情况下都会在主线程中创建。这就是为什么我们需要在主线程中更新UI。

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

非主线程更新UI的后果

我们可以尝试在一个新的线程中更新UI,会发现程序崩溃了。查看Logcat可以看到这样的一句提示

Only the original thread that created a view hierarchy can touch its views.

这告诉我们,在实际开发中,我们需要遵循Google为我们设定的这样的机制。

那么如何在其他线程达到更新UI的目的呢?使用Handler就是其中一种办法。

Handler的作用

根据Android Developer网站上的描述,Handler主要有两个用途

  1. 定时地去发送一个Message或Runnable对象
  2. 可以跳转到另一个线程中去执行一些操作

Handler使用方式

关于Message

参数

  • public int arg1(arg2):如果只需要存储几个整型数据,arg1 和 arg2是setData()的低成本替代品。
  • public Object obj: 发送给接收者的任意对象。当使用Message对象在线程间传递消息时,如果它包含一个Parcelable的结构类(不是由应用程序实现的类),此字段必须为非空(non-null)。其他的数据传输则使用setData(Bundle)方法。
  • public Messenger replyTo:用户自定义的消息代码,这样接受者可以了解这个消息的信息。每个handler各自包含自己的消息代码,所以不用担心自定义的消息跟其他handlers有冲突。

方法

另外 ,用Message来传递还可通过Message的**setData(Bundle)方法来传递。获取时调用getData()**方法。与sendData相似的还有peekData。

  • public void setData(*Bundle data):设置一个任意数据值的Bundle对象。如果可以,使用arg1和arg2域发送一些整型值以减少消耗。
  • public Bundle peekData():与getData()相似,但是并不延迟创建Bundle。如果Bundle对象不存在返回null。
  • public Bundle getData():获取附加在此事件上的任意数据的Bundle对象,需要时延迟创建。通过调用setData(Bundle)来设置Bundle的值。需要注意的是,如果通过Messenger对象在进程间传递数据时,需要调用Bundle类的Bundle.setClassLoader()方法来设置ClassLoader,这样当接收到消息时可以实例化Bundle里的对象。
  • public static Message obtain(): 从全局池中返回一个新的Message实例。在大多数情况下这样可以避免分配新的对象。

handleMessage方法

handleMessage方法用于接收Message对象并进行相应的处理,对应Handler的sendMessage方法。

使用时应在handler中重写此方法。当在其他线程调用sendMessage方法时,handleMessage方法便会被回调,并携带sendMessage方法调用者在Message中存入的信息。当我们想要在其他线程更新UI时,就可以用主线程中创建的Handler调用sendMessage方法,然后在该Handler重写的handleMessage方法中做相应的处理。

比如此处,我们在handleMessage方法中进行更新TextView的操作,并把Message的arg1作为文本的内容。

private Handler mHandler = new Handler(){public void handleMessage(Message msg){mTextView.setText(""+msg.arg1+"-"+msg.arg2);};
};new Thread(){@Overridepublic void run() {try {Thread.sleep(2000);Message message = new Message();message.arg1 = 88;mHandler.sendMessage(message);} catch (InterruptedException e) {e.printStackTrace();}}
}.start();

obtainMessage方法

有的时候,我们不需要创建一个新的Message对象,可以去复用系统的Message对象。这时可以调用obtainMessage()方法,就会为我们返回一个Message对象。然后就可以直接用其来发送消息

private TextView mTextView;
private Handler mHandler = new Handler() {@Overridepublic void handleMessage(Message msg) {Person person = (Person)msg.obj;mTextView.setText("name:" + person.getName()+" age:"+person.getAge());}
};
private int index = 0;@Override
protected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);mTextView = findViewById(R.id.tv_text);new Thread() {@Overridepublic void run() {try {Thread.sleep(2000);Message message = handler.obtainMessage();Person person = new Person();person.setName("梁文俊");person.setAge(18);message.obj = person;mHandler.sendMessage(message);} catch (InterruptedException e) {e.printStackTrace();}}}.start();
}

那么在obtainMessage方法中,究竟是做了哪些操作呢?

我们进入obtainMessage的源码,会发现它实际上是调用了Message的obtain方法,将调用的Handler作为了参数。

public final Message obtainMessage()
{return Message.obtain(this);
}

这里实际上是obtain()方法的一个重载方法,与之不同的是设置了target的值。

我们可以进入obtain方法的源码中研究一下。

可以看到,它其实就是用obtain()方法来创建Message,然后为其设置一个target。

public static Message obtain(Handler h) {Message m = obtain();m.target = h;return m;
}

那么obtain()方法又是如何创建Message的呢?

进入它的源码,可以看到,它其实就是先从系统中取出Message对象。如果没有多余的Message对象,则创建一个新的Message并返回,然后将sPool指向该Message的下一个Message。

/*** Return a new Message instance from the global pool. Allows us to* avoid allocating new objects in many cases.*/
public static Message obtain() {synchronized (sPoolSync) {if (sPool != null) {Message m = sPool;sPool = m.next;m.next = null;m.flags = 0; // clear in-use flagsPoolSize--;return m;}}return new Message();
}

一些小猜测

这里发现Message居然有next参数,这里感觉有点像链表了…因此跳到next定义处,发现它也是一个Message类型。结合前面的注释。在这里猜测,从系统中获取的Message都是以链表的形式来连接的,然后按顺序取出。

emmm…跑题了…不再往内研究了…不知道这个猜测对不对…

// sometimes we store linked lists of these things
/*package*/ Message next;

当我们为Message制定了target后,就可以不再调用handler的sendMessage发送消息,直接通过该Message的sendToTarget方法即可。

那么它具体是如何实现的呢,我们可以查看一下源码。

这里其实很简单。它实际上就是调用了target的sendMessage方法,将自己作为参数而实现的而已。本质上仍然是调用Handler的sendMessage方法

/*** Sends this Message to the Handler specified by {@link #getTarget}.* Throws a null pointer exception if this field has not been set.*/
public void sendToTarget() {target.sendMessage(this);
}

post方法

刚刚的异常我们已经看到,那如何才能使用Handler在这个新建的线程更新UI呢?

我们可以这样做:在主线程中创建一个Handler。然后在子线程中,我们可以调用Handler的post方法,并向其中传递一个Runnable为参数,在Runnable中更新UI即可。

private TextView mTextView;
private Handler mHandler = new Handler();mTextView = findViewById(R.id.tv_text);
new Thread(new Runnable() {@Overridepublic void run() {try {Thread.sleep(1000);mHandler.post(new Runnable() {@Overridepublic void run() {mTextView.setText("update");}});} catch (InterruptedException e) {e.printStackTrace();}}
}).start();

postDelayed方法

postDelayed与post方法非常接近,仅仅是参数多了一个long类型的参数delayMills。它与post的区别就是它会在delayMills这段时间之后再去执行Runnable的方法,也就是延迟执行。

有时候我们需要定时的完成一些事情(比如定时更换TextView的文字)时,就可以利用它延迟执行的这一特点来实现。做法是分别在主函数中以及它所执行的Runnable中postDelayed一段时间。这样就可以达到定时地完成某件任务的工作。

比如下例就简单地实现了每隔一秒切换一次TextView文字的作用。

private TextView mTextView;
private Handler mHandler = new Handler();
private String[] mTexts = {"苟利国家生死以","岂因祸福避趋之"};
private ChangeRunnable mRunnable = new ChangeRunnable();
private int index=0;
@Override
protected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);mTextView = findViewById(R.id.tv_text);mHandler.postDelayed(mRunnable,1000);
}
class ChangeRunnable implements Runnable{@Overridepublic void run() {index = index%2;mTextView.setText(mTexts[index++]);mHandler.postDelayed(mRunnable,1000);}
}

removeCallbacks方法

比如我们这里有个定时更新TextView的文本的代码,如果想要按下按钮,停止定时更换文本,就可以通过removeCallbacks方法,传入该Runnable来中止消息。

private Button mBtnRemove;
private ChangeRunnable mRunnable = new ChangeRunnable();class ChangeRunnable implements Runnable{@Overridepublic void run() {mIndex = mIndex%3;mTextView.setText(mTexts[mIndex++]);mHandler.postDelayed(mRunnable,1000);}
}
...
mBtnRemove.setOnClickListener(new OnClickListener() {@Overridepublic void onClick(View v) {mHandler.removeCallbacks(mRunnable);}
});

Handler的另一种构造方法

Handler(Callback)

Handler的构造方法中,有一个这样的形式的构造方法。在我们创建Handler时,可以指定一个Callback。

public Handler(Callback callback);

我们可以试着在创建Handler过程中传入一个new出的Callback。会发现它有一个handleMessage方法。这个handleMessage方法和我们之前用到的有一个不同点:它有一个boolean类型的返回值。

我们分别创建两个handleMessage方法,通过Log可以发现是Callback中的handleMessage方法先被调用。这时,如果Callback的handleMessage返回false,则后面的可以正常执行。但当返回true时,Message就被截获了,后面的handleMessage将不会被执行。

我们可以通过Callback来截获Message。

Handler(Looper)

使用Handler时,还可以指定与之关联的Looper。这样handler发送消息时,消息就会进入指定的Looper的消息队列中。

比如我们可以尝试这样新建一个Handler:

Handler handler = new Handler(Looper.getMainLooper());

HandlerThread的使用

HandlerThread可以解决一些多线程并发的问题,达到线程的同步的效果。我们可以用它的Looper来新建一个Handler,在Handler的handleMessage中再做操作。这时的handleMessage是在这个HandlerThread中执行的

@Override
protected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);HandlerThread thread = new HandlerThread("handler thread");Handler handler = new Handler(thread.getLooper()){@Overridepublic void handleMessage(Message msg) {Log.d("test",""+msg.arg1);}};Message message = handler.obtainMessage();message.arg1 = 123;handler.sendMessage(message);
}

Handler导致的内存泄漏(学习自民神博客)

问题描述

Handler使用过程中,我们需要特别注意一个问题,那就是Handler可能会导致内存泄漏。

具体原因如下:

  • Handler的生命周期与Activity不同,Handler会关联Looper来管理Message Queue。这个队列在整个Application的生命周期中存在,因此Handler不会因Activity的finish()方法而被销毁。
  • 非静态(匿名)内部类会持有外部对象,当我们这样重写Handler时它就成为了一个匿名内部类,这样如果调用finish方法时Handler有Message未处理的话,就会导致Activity不能被销毁。

解决方法

  1. 可以在外部新建一个类,这样便可解决这个问题。但这样无疑过于麻烦了,内部类更方便些。
  2. 可以同时使用静态内部类和弱引用,当一个对象只被弱引用依赖时它便可以被GC回收。

**注意,要static和弱引用要同时使用,否则由于非静态内部类隐式持有了外部类Activity的引用,而导致Activity无法被释放 **

Handler完全解读——Handler的使用相关推荐

  1. gen_event中的handler和supervised handler

    呃,在gen_event中有两个添加handler的方法 gen_event:add_handler/3 gen_event:add_sup_handler/3 一开始总是有些迷惑两者的区别,今天查看 ...

  2. 【Android 异步操作】手写 Handler ( Handler 发送与处理消息 | Handler 初始化 | 完整 Handler 代码 )

    文章目录 一.Handler 发送与处理消息 ( 两大功能 ) 二.Handler 初始化 三.完整 Handler 代码 一.Handler 发送与处理消息 ( 两大功能 ) Handler 有两个 ...

  3. 【Android 异步操作】Handler ( 主线程中的 Handler 与 Looper | Handler 原理简介 )

    文章目录 一.主线程中的 Handler 与 Looper 二.Handler 原理简介 一.主线程中的 Handler 与 Looper Android 系统中 , 点击图标启动一个应用进程 , 就 ...

  4. android handler内存,Android handler之内存泄露原因揭示

    关于handler机制大家可以看前面专题Android面试精选--再聊android Handler机制.今天我们要说的重点是 handler为什么会发生内存泄露? 我们先从源头说起,应用刚启动时,第 ...

  5. 85、android handler的警告Handler Class Should be Static or Leaks Occur

    转载:http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2014/1106/1922.html 在使用Handler更新UI的时候,我是这样写 ...

  6. 安卓handler的用法,handler举例

    原帖跳转: link 一.Handler的定义: Handler主要接收子线程发送的数据, 并用此数据配合主线程更新UI,用来跟UI主线程交互用.比如可以用handler发送一个message,然后在 ...

  7. handler.post和handler.sendMessage的区别和联系

    现在大部分人都很熟悉handler这个东西了,我们常用handler的场景无非有两个: 1. 异步更新UI 2. 延时任务 但是我一直有一个困惑,就是handler.post(r)这个方法有什么独特的 ...

  8. 继承Handler还是实现Handler.Callback?

    Android的Handler提供了多种创建方式: package com.chanryma.demo;import android.os.Handler; import android.os.Mes ...

  9. java handler类_java——Handler类

    一直以来,分不清楚java的Handler和android的handler,今天得空,看了下api,和源码,就胡乱写一下. java中的handler类直接继承自Object类,jdk 1.6 api ...

最新文章

  1. ubuntu clion 创建桌面快捷方式
  2. wrs-arcface虹软人脸识别
  3. 分解原理_基于矩阵分解原理的推荐系统
  4. RHEL5一个网卡绑定多个IP
  5. 安卓之父的手机创业项目卖了!这个华人接手,谷歌风投还投了钱
  6. php-redis扩展模块安装记录
  7. iOS app 企业内部发布及HTTPS服务器配置
  8. VXWORKS 几种定时机制
  9. 信息学奥赛一本通(1141:删除单词后缀)
  10. 三大运营商2月份运营数据发布:超过一半的中国人都在用移动
  11. 80-040-000-原理-MySQL的 ICP
  12. java四种修饰符_java中的四种修饰符
  13. VTK(二)vs2010第一个VTK程序。
  14. ​京东云:原来落地 AI 应用是这么回事儿!
  15. 修改fstab导致UBUNTU无法启动的解决办法
  16. 计算机硬件英语单词有哪些,计算机硬件英语词汇
  17. map转json字符串字段排序
  18. apache ab压测与参数传递
  19. 句子迷,语录,俞凌雄
  20. find命令 、 文件名后缀

热门文章

  1. 微信是怎样一步步控制你手机
  2. 计算机三级网络技术最全知识点总结【11】
  3. 玩转springcloud(一):什么是Springcloud ,有什么优缺点? 学习顺序是什么?
  4. fiddler学习笔记(一)
  5. 牛客挑战赛32 B 114514
  6. win7 系统远程默认共享共享失败,拒绝访问的解决
  7. 绝地求生开箱html素材,绝地求生开箱模拟器
  8. 远古历史研究的魅力与挑战
  9. 王权富贵:树莓派ssh安装和启动
  10. 网络工程毕业设计 SSM中药店商城系统(源码+论文)