Android中handler使用浅析

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类来实现,后面会做简单的实现介绍。

以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,同时也希望多多支持脚本之家!

时间: 2025-01-01 23:42:28

Android中handler使用浅析的相关文章

详解Android中Handler的内部实现原理_Android

本文主要是对Handler和消息循环的实现原理进行源码分析,如果不熟悉Handler可以参见博文<详解Android中Handler的使用方法>,里面对Android为何以引入Handler机制以及如何使用Handler做了讲解. 概括来说,Handler是Android中引入的一种让开发者参与处理线程中消息循环的机制.我们在使用Handler的时候与Message打交道最多,Message是Hanlder机制向开发人员暴露出来的相关类,可以通过Message类完成大部分操作Handler的功

详解Android中Handler的使用方法_Android

在Android开发中,我们经常会遇到这样一种情况:在UI界面上进行某项操作后要执行一段很耗时的代码,比如我们在界面上点击了一个"下载"按钮,那么我们需要执行网络请求,这是一个耗时操作,因为不知道什么时候才能完成.为了保证不影响UI线程,所以我们会创建一个新的线程去执行我们的耗时的代码.当我们的耗时操作完成时,我们需要更新UI界面以告知用户操作完成了.所以我们可能会写出如下的代码: package ispring.com.testhandler; import android.app.

Android中Handler Runnable与Thread的区别详解

转自: http://www.codeceo.com/article/android-handler-runnable-thread.html Android中Handler可以异步控制Runnable,那么这样做于Android中的Thread有什么区别呢?本文将通过多个角度来讲解这个问题,读完此文,相信你会对Android中的Handler Runnable与Thread有一个非常全面的了解. 在java中可有两种方式实现多线程,一种是继承Thread类,一种是实现Runnable接口:Th

Android中Handler机制问题

问题描述 Android中Handler机制问题 android中我们常通过handler来进行线程间的通信,里面通过Looper来进行消息管理.线程间的通信都可以自己来实现,Handler的优势在哪线程间通信 解决方案 Android中的Handler机制(一)android handler机制Android Handler机制 解决方案二: 最大优势是,Handler运行在主线程中(UI线程中),可以让子线程通过handler更新UI操作 解决方案三: handler主要优势就是线程通讯呀,

Android中Handler消息传递机制_Android

Handler 是用来干什么的? 1)执行计划任务,可以在预定的时间执行某些任务,可以模拟定时器 2)线程间通信.在Android的应用启动时,会创建一个主线程,主线程会创建一个消息队列来处理各种消息.当你创建子线程时,你可以在你的子线程中拿到父线程中创建的Handler 对象,就可以通过该对象向父线程的消息队列发送消息了.由于Android要求在UI线程中更新界面,因此,可以通过该方法在其它线程中更新界面. 出于性能优化考虑,Android的UI操作并不是线程安全的,这意味着如果有多个线程并发

android中handler和bundle有什么区别和联系

问题描述 android中handler和bundle有什么区别和联系 android中handler和bundle有什么区别和联系 都是用来传递消息吗都是信息的载体吗 解决方案 http://www.cnblogs.com/nuistlr/archive/2012/08/31/2665107.html handler提供了回调,接收返回,bundle打包发出信息.

Android中Handler消息传递机制

Handler 是用来干什么的? 1)执行计划任务,可以在预定的时间执行某些任务,可以模拟定时器 2)线程间通信.在Android的应用启动时,会创建一个主线程,主线程会创建一个消息队列来处理各种消息.当你创建子线程时,你可以在你的子线程中拿到父线程中创建的Handler 对象,就可以通过该对象向父线程的消息队列发送消息了.由于Android要求在UI线程中更新界面,因此,可以通过该方法在其它线程中更新界面. 出于性能优化考虑,Android的UI操作并不是线程安全的,这意味着如果有多个线程并发

Android中Handler常用方法

 package com.example.testhandler; import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.app.Activity; //Handler常用方法: //1 post() //2 postDelayed() //3 sendMessage() //4 handleMessage() public class MainActivity

Android 中Handler引起的内存泄露_Android

在Android常用编程中,Handler在进行异步操作并处理返回结果时经常被使用.通常我们的代码会这样实现. public class SampleActivity extends Activity { private final Handler mLeakyHandler = new Handler() { @Override public void handleMessage(Message msg) { // ... } } } 但是,其实上面的代码可能导致内存泄露,当你使用Androi