Android中Handler使用浅析,androidhandler
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是在主线程中创建的,所以此时handleMe