Android中handler使用浅析
发布时间 - 2026-01-10 23:24:29 点击率:次1. Handler使用引出

现在作为客户,有这样一个需求,当打开Activity界面时,开始倒计时,倒计时结束后跳转新的界面(思维活跃的朋友可能立马想到如果打开后自动倒计时,就类似于各个APP的欢迎闪屏页面),如下图:
作为初学者,可能觉得直接开启一个包含倒序循环的子线程就ok了,具体实现如下:
1.1 Layout界面代码如下:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/activity_main2" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" tools:context="com.mly.panhouye.handlerdemo.Main2Activity"> <TextView android:gravity="center" android:textSize="30sp" android:layout_width="match_parent" android:layout_height="match_parent" android:text="NO DATA" android:id="@+id/tv"/> </LinearLayout>
1.2 java实现代码如下:
public class Main2Activity extends AppCompatActivity {
TextView tv;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main2);
tv = (TextView) findViewById(R.id.tv);
new Thread(new Runnable() {
@Override
public void run() {
for (int i=5;i>0;i--){
tv.setText(String.valueOf(i));
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//计时结束后跳转到其他界面
startActivity(new Intent(Main2Activity.this,Main3Activity.class));
//添加finish方法在任务栈中销毁倒计时界面,使新开界面在回退时直接退出而不是再次返回该界面
finish();
}
}).start();
}
逻辑很简单,但当点进入界面时,会发现程序奔溃了,logcat中错误日志如下(只有UI线程可以更改UI界面):
由此我们发现在安卓开发中,例如上面的示例,我们常常通过一个线程来完成某些操作,然后同步显示对应的视图控件UI上,通过上面的例子我们也知道了安卓中无法直接通过子线程来进行UI更新操作,对于这种情况,Android提供了一套异步消息处理机制Handler。
2. Handler实现方法
使用handler实现,修改java代码Main2Activity.java如下:
package com.mly.panhouye.handlerdemo;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.widget.TextView;
/**
* Handler:
* 1 处理的消息对象就是Message,理解为要传递的消息数据的封装对象
* Message what : 标记,用来区分多个消息
* Message arg1,arg2 : 用来传递int类型的数据
* Message obj : 可以传递任何类型的对象(Object)
*/
public class Main2Activity extends AppCompatActivity {
public static final int UPDATE = 0x1;
TextView tv;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main2);
tv = (TextView) findViewById(R.id.tv);
begin();//开启倒计时并跳转页面的方法
}
//消息处理者,创建一个Handler的子类对象,目的是重写Handler的处理消息的方法(handleMessage())
private Handler handler = new Handler(){
@Override
public void handleMessage(Message msg) {
switch (msg.what){
case UPDATE:
tv.setText(String.valueOf(msg.arg1));
break;
}
}
};
public void begin(){
new Thread(new Runnable() {
@Override
public void run() {
for (int i=5;i>0;i--){
Message msg = new Message();
msg.what = UPDATE;
msg.arg1 = i;
handler.sendMessage(msg);
try {
Thread.sleep(1000);//休眠1秒
} catch (InterruptedException e) {
e.printStackTrace();
}
//打印log
Log.i("tag",Main2Activity.this+"-"+ i);
}
//计时结束后跳转到其他界面
startActivity(new Intent(Main2Activity.this,Main3Activity.class));
//添加finish方法在任务栈中销毁倒计时界面,使新开界面在回退时直接退出而不是再次返回该界面
finish();
}
}).start();
}
@Override
protected void onDestroy() {
super.onDestroy();
//log打印用于测试activity销毁
Log.i("tag","destory");
}
}
3. Handler实现原理
使用Handler方式进行异步消息处理主要由Message,Handler,MessageQueue,Looper四部分组成:
(1)Message,线程之间传递的消息,用于不同线程之间的数据交互。Message中的what字段用来标记区分多个消息,arg1、arg2 字段用来传递int类型的数据,obj可以传递任意类型的字段。
(2)Handler,用于发送和处理消息。其中的sendMessage()用来发送消息,handleMessage()用于消息处理,进行相应的UI操作。
(3)MessageQueue,消息队列(先进先出),用于存放Handler发送的消息,一个线程只有一个消息队列。
(4)Looper,可以理解为消息队列的管理者,当发现MessageQueue中存在消息,Looper就会将消息传递到handleMessage()方法中,同样,一个线程只有一个Looper。
Handler实现原理如下图:
结合上文的的代码示例以及上图的实现流程,要使用Handler实现异步消息处理,首先我们需要在主线程中创建Handler对象并重写handleMessage()方法,然后当子线程中需要进行UI操作时,就创建一个Message对象,并通过Handlerr将这条消息发送出去。之后这条消息会被添加到MessageQueue的队列中等待被处理,而Looper则会一直尝试从MessageQueue中取出待处理消息,最后分发回Handler的handleMessage()方法中。由于Halldler是在主线程中创建的,所以此时handleMessage()方法中的代码也会在主线程中运行,从而实现子线程通过Handler机制实现UI线程操作的目的。
4. Handler内存泄漏分析
4.1 Handler内存泄漏问题的引出:
上面的Handler实现代码中,其实在Android Studio中会提示以下问题:
大致意思就是应该让Handler类为静态的,不然就会产生内存泄漏。 原因也说的很清楚,Handler被声明为一个非静态内部类或者匿名类可能会阻止外部类的垃圾回收(大家可以了解下Android的gc回收机制)。过多的内存泄漏使程序占用的内存超出系统限制,导致OOM(内存溢出),程序出错。
4.2 防止Handler引起内存泄漏:
方法一:通过程序逻辑进行保护:
(1)在关闭Activity时停掉对应的后台线程。线程停止就相当于切断了Handle和外部链接的线,Activity自然会在合适的时候被回收。
(2)如果Handler是被delay的Message持有了引用,那就使用Handler的removeCallbacks()方法将消息对象从消息队列移除即可。
方法二:将Handler声明为静态类,静态类不持有外部类的对象,所以Activity可以被随意回收。此处使用了弱引用WeakReference,也就是说当在内存不足时,系统会销毁弱/回收引用引用的对象,从而达到优化内存的目的。优化后代码如下:
package com.mly.panhouye.handlerdemo;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.widget.TextView;
import java.lang.ref.WeakReference;
public class Main4Activity extends AppCompatActivity {
public static final int UPDATE = 0x1;
TextView tv;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main2);
tv = (TextView) findViewById(R.id.tv);
begin();//开启倒计时并跳转页面的方法
}
//Handler静态内部类
private static class MyHandler extends Handler {
//弱引用
WeakReference<Main4Activity> weakReference;
public MyHandler(Main4Activity activity) {
weakReference = new WeakReference<Main4Activity>(activity);
}
@Override
public void handleMessage(Message msg) {
Main4Activity activity = weakReference.get();
if (activity != null) {
activity.tv.setText(String.valueOf(msg.arg1));
}
}
}
private MyHandler handler = new MyHandler(this);
public void begin() {
new Thread(new Runnable() {
@Override
public void run() {
for (int i = 5; i > 0; i--) {
Message msg = new Message();
msg.what = UPDATE;
msg.arg1 = i;
handler.sendMessage(msg);
try {
Thread.sleep(1000);//休眠1秒
} catch (InterruptedException e) {
e.printStackTrace();
}
Log.i("tag", Main4Activity.this + "-" + i);
}
//计时结束后跳转到其他界面
startActivity(new Intent(Main4Activity.this, Main3Activity.class));
//添加finish方法在任务栈中销毁倒计时界面,使新开界面在回退时直接退出而不是再次返回该界面
finish();
}
}).start();
}
@Override
protected void onDestroy() {
super.onDestroy();
handler.removeCallbacksAndMessages(null);
Log.i("tag", "destory");
}
}
5.小结
本次使用handler实现倒计时页面跳转的效果实现,只是向大家简单介绍handler的使用方法以及注意事项,但依然存在bug,如果倒计时未完成时退出activity,子线程依然会在后台运行直至完成跳转,效果以及log日志如下:
针对这种情况,我处理起来比较麻烦,需要在销毁倒计时activity时,同时终止线程,我试了很多方法,未能实现。
其实要实现倒计时闪屏效果,可以使用Android中有个countDownTimer类来实现,后面会做简单的实现介绍。
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,同时也希望多多支持!
# android
# handler使用
# handler
# handler用法
# android开发教程之android的handler使用方法
# Android多线程处理机制中的Handler使用介绍
# Android开发笔记 Handler使用总结
# Android使用Handler和Message更新UI
# Android开发使用UncaughtExceptionHandler捕获全局异常
# 实例讲解Android多线程应用开发中Handler的使用
# 详解Android中Handler的使用方法
# android使用handler ui线程和子线程通讯更新ui示例
# android使用handlerthread创建线程示例
# android Handler详细使用方法实例
# 倒计时
# 跳转
# 会在
# 新开
# 结束后
# 跳转到
# 多个
# 而不是
# 这条
# 这种情况
# 只有一个
# 重写
# 创建一个
# 如下图
# 就会
# 是在
# 有个
# 那就
# 子类
# 很简单
相关栏目:
【
网站优化151355 】
【
网络推广146373 】
【
网络技术251813 】
【
AI营销90571 】
相关推荐:
长沙做网站要多少钱,长沙国安网络怎么样?
Linux安全能力提升路径_长期防护思维说明【指导】
通义万相免费版怎么用_通义万相免费版使用方法详细指南【教程】
php485函数参数是什么意思_php485各参数详细说明【介绍】
Python自然语言搜索引擎项目教程_倒排索引查询优化案例
Laravel怎么写单元测试_PHPUnit在Laravel项目中的基础测试入门
如何在宝塔面板创建新站点?
JS经典正则表达式笔试题汇总
Laravel如何使用Contracts(契约)进行编程_Laravel契约接口与依赖反转
Internet Explorer官网直接进入 IE浏览器在线体验版网址
PHP怎么接收前端传的文件路径_处理文件路径参数接收方法【汇总】
ChatGPT怎么生成Excel公式_ChatGPT公式生成方法【指南】
为什么php本地部署后css不生效_静态资源加载失败修复技巧【技巧】
Laravel如何使用Service Provider注册服务_Laravel服务提供者配置与加载
如何在Ubuntu系统下快速搭建WordPress个人网站?
Linux虚拟化技术教程_KVMQEMU虚拟机安装与调优
详解Nginx + Tomcat 反向代理 负载均衡 集群 部署指南
实例解析angularjs的filter过滤器
网站优化排名时,需要考虑哪些问题呢?
图册素材网站设计制作软件,图册的导出方式有几种?
用v-html解决Vue.js渲染中html标签不被解析的问题
谷歌Google入口永久地址_Google搜索引擎官网首页永久入口
详解Android——蓝牙技术 带你实现终端间数据传输
Laravel如何实现API资源集合?(Resource Collection教程)
Laravel路由Route怎么设置_Laravel基础路由定义与参数传递规则【详解】
Laravel如何使用查询构建器?(Query Builder高级用法)
php做exe能调用系统命令吗_执行cmd指令实现方式【详解】
Laravel如何发送系统通知_Laravel Notifications实现多渠道消息通知
如何快速搭建高效可靠的建站解决方案?
如何在Windows虚拟主机上快速搭建网站?
HTML5段落标签p和br怎么选_文本排版常用标签对比【解答】
详解一款开源免费的.NET文档操作组件DocX(.NET组件介绍之一)
Laravel Telescope怎么调试_使用Laravel Telescope进行应用监控与调试
Laravel如何与Pusher实现实时通信?(WebSocket示例)
网站建设整体流程解析,建站其实很容易!
详解Oracle修改字段类型方法总结
laravel怎么用DB facade执行原生SQL查询_laravel DB facade原生SQL执行方法
Laravel怎么实现搜索功能_Laravel使用Eloquent实现模糊查询与多条件搜索【实例】
如何在橙子建站上传落地页?操作指南详解
如何用低价快速搭建高质量网站?
Laravel怎么发送邮件_Laravel Mail类SMTP配置教程
Laravel怎么进行浏览器测试_Laravel Dusk自动化浏览器测试入门
Laravel任务队列怎么用_Laravel Queues异步处理任务提升应用性能
如何自己制作一个网站链接,如何制作一个企业网站,建设网站的基本步骤有哪些?
HTML5打空格有哪些误区_新手常犯的空格使用错误【技巧】
🚀拖拽式CMS建站能否实现高效与个性化并存?
个人摄影网站制作流程,摄影爱好者都去什么网站?
利用python获取某年中每个月的第一天和最后一天
Laravel怎么实现观察者模式Observer_Laravel模型事件监听与解耦开发【指南】
Laravel怎么集成Log日志记录_Laravel单文件与每日日志配置及自定义通道【详解】

