Handler的基本概念
当有一段处理耗时比较漫长的时候,我们就需要用线程来处理。Android中是用Handler类来处理线程的。
与Handler绑定的有两个队列,一个为消息队列,另一个为线程队列。Handler可以通过这两个队列来分别:
- 【消息队列】发送、接受、处理消息
- 【线程队列】启动、结束、休眠线程
Android OS中,一个进程(即一个App)被创建之后,主线程(可理解为当前Activity)创建一个消息队列,这个消息队列维护所有顶层应用对象(Activities, Broadcast receivers等)以及主线程创建的窗口。
你可以在主线程中创建新的线程,这些新的线程都通过Handler与主线程进行通信。通信通过新线程调用 Handler的post()方法和sendMessage()方法实现,分别对应功能:
- post() 将一个线程加入线程队列(当然,post()方法还有一些变体,比如postDelayed()、postAtTime()分别用来延迟发送、定时发送)
- sendMessage() 发送一个消息对象到消息队列
消息的处理,在主线程的Handler对象中进行。具体处理过程,需要在new Handler对象时使用匿名内部类重写Handler的handleMessage(Message msg)方法。
线程加入线程队列可以在主线程中也可以在子线程中进行,但都要通过主线程的Handler对象调用post()。
Handler的基本使用方法
我们用一个简单的例子(隔3000毫秒就打印一行“UpdateThread”的Log)来说明Handler的基本使用方法,一些说明就直接写在注释里头了。
main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
<Button
android:id="@+id/startButton"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="start" />
<Button
android:id="@+id/endButton"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="end" />
</LinearLayout>
Handler_01Activity.java
package com.tianjf;
import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
public class Handler_01Activity extends Activity {
private Button startButton = null;
private Button endButton = null;
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
startButton = (Button) findViewById(R.id.startButton);
endButton = (Button) findViewById(R.id.endButton);
startButton.setOnClickListener(new StartButtonListener());
endButton.setOnClickListener(new EndButtonListener());
}
class StartButtonListener implements OnClickListener {
@Override
public void onClick(View v) {
// 调用Handler的post方法,将要执行的线程对象添加到队列当中
handler.post(updateThread);
}
}
class EndButtonListener implements OnClickListener {
@Override
public void onClick(View v) {
//调用Handler的removeCallbacks()方法,删除队列当中未执行的线程对象
handler.removeCallbacks(updateThread);
}
}
// 创建一个Handler对象
Handler handler = new Handler();
// Java实现线程有两种方法:①继承Thread类 ②实现Runnable接口
/*
* 以下是用匿名内部类的方式实现Runnable接口
* 接口 XXXX = new 接口() {
* @Override
* 实现接口的方法
* }
* 这种new一个接口的情况在Android中还是很常见的(省去了先创建一个类继承接口,再new这个创建的类这一步骤)
*/
// 将要执行的操作写在线程对象的run方法当中
Runnable updateThread = new Runnable() {
@Override
public void run() {
System.out.println("UpdateThread");
//在run方法内部,执行postDelayed或者是post方法
//postDelayed方法的作用是:将要执行的线程对象放入到队列当中,待时间结束后,运行制定的线程对象
//第一个参数是Runnable类型:将要执行的线程对象
//第二个参数是long类型:延迟的时间,以毫秒为单位
handler.postDelayed(updateThread, 3000);
}
};
}
本例子涉及到的知识点:
- 线程接口Runnable
- 线程接口Runnable中的run()方法
- 管理Runnable接口的Handler类
- Handler类中的post()方法和removeCallbacks()方法
使用Handler更新ProgressBar
同样用一个简单的例子来说明怎么使用Handler来更新ProgressBar。
main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
<ProgressBar
android:id="@+id/bar"
style="?android:attr/progressBarStyleHorizontal"
android:layout_width="200dp"
android:layout_height="wrap_content"
android:visibility="gone" />
<Button
android:id="@+id/startButton"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="start" />
</LinearLayout>
Handler_02Activity.java
package com.tianjf;
import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.ProgressBar;
public class Handler_02Activity extends Activity {
ProgressBar progressBar = null;
Button startButton = null;
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
progressBar = (ProgressBar) findViewById(R.id.bar);
startButton = (Button) findViewById(R.id.startButton);
startButton.setOnClickListener(new ButtonListener());
}
class ButtonListener implements OnClickListener {
@Override
public void onClick(View v) {
progressBar.setVisibility(View.VISIBLE);
handler.post(updateThread);
}
}
// 使用匿名内部类来复写Handler当中的handleMessage方法
Handler handler = new Handler() {
// handleMessage方法会从消息队列中依次取消息,取出来之后做一系列处理
@Override
public void handleMessage(Message msg) {
progressBar.setProgress(msg.arg1);
handler.post(updateThread);
}
};
// 线程类,该类使用匿名内部类的方式进行声明
Runnable updateThread = new Runnable() {
int i = 0;
@Override
public void run() {
System.out.println("Begin Thread");
i = i + 10;
// 得到一个消息对象,Message类是有Android操作系统提供
Message msg = handler.obtainMessage();
// 将msg对象的arg1参数的值设置为i,用arg1和arg2这两个成员变量传递消息,优点是系统性能消耗较少
// 还可以对msg对象的obj参数设值,例如msg.obj = "abc"
// 另外还可以用msg对象的setData()方法设值,这个要涉及到Bundle类,请看例子Handler_03
msg.arg1 = i;
try {
// 设置当前显示睡眠1秒
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 将msg对象加入到消息队列当中,消息能够被handleMessage(Message msg)方法接收到
handler.sendMessage(msg);
if (i == 100) {
// 如果当i的值为100时,就将线程对象从handler当中移除
handler.removeCallbacks(updateThread);
}
}
};
}
本例子涉及到的知识点:
- 消息类:Message
- handler.sendMessage(msg);将一个消息对象加入到消息队列中
- 在new Handler对象时使用匿名内部类重写Handler的handleMessage(Message msg)方法,来依次取出消息队列中的消息,然后对这些消息做处理
意想不到的事情:上面例子中的创建出来的线程居然还是在主线程运行的
你可能会说:不可能吧。但是我要说:看下面的例子,我们来见证奇迹。
import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
public class HandlerTest extends Activity {
private Handler handler = new Handler();
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
handler.post(runnable);
setContentView(R.layout.main);
System.out.println("activity--->" + Thread.currentThread().getId());
System.out.println("activityname--->" + Thread.currentThread().getName());
}
Runnable runnable = new Runnable(){
@Override
public void run() {
System.out.println("handler--->" + Thread.currentThread().getId());
System.out.println("handlername--->" + Thread.currentThread().getName());
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
}
我们将setContentView(R.layout.main);放在handler.post(runnable);之后是为什么呢?
我们知道setContentView(R.layout.main);执行完了之后,画面的控件才会显示出来,而我们又在Runnable里面设置了Thread.sleep(10000);让程序睡10秒,如果创建出来的线程是在主线程运行的话,那么程序启动后会过10秒才看到画面,如果是两个线程的话,那么画面会立即显示。
除了通过UI来确认,我们还可以通过程序中的Log来确认。
下面是见证奇迹的时刻:
Log很明确的显示,都是在主线程执行的。如果你还是不相信,那么请看看UI,看是不是过了10秒画面才显示出来。
因为handler.post(runnable);是直接执行runnable的run()方法,没有另外起一个线程,所以还是在主线程执行。
我们把handler.post(runnable);注释掉,换成Java的标准启动子线程的方法
Thread thread = new Thread(runnable);
thread.start();
运行之后会发现
已经另起了一个线程了,而且画面也是立即显示。
上面的程序仅仅是另外启动了一个子线程,但是还没有讲到怎么传递消息。
如果要另起一线程并想传递消息并处理消息,那么就要用到Looper类,这个是用来封装消息循环和消息队列的一个类,也就是可以循环从消息队列中取消息。Looper会一直从消息队列中取消息并处理消息,如果没有消息了,线程就处于休眠状态。
在Android中,我们很少创建一个Looper对象,因为Android框架中的HandlerThread类本身就具备了循环处理消息的功能,我们只要调用HandlerThread类的对象的getLooper方法就可以了。
具体看下面代码
import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;
import android.os.Message;
public class HandlerTest2 extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
System.out.println("Activity-->" + Thread.currentThread().getId());
HandlerThread handlerThread = new HandlerThread("handler_thread");
handlerThread.start();
MyHandler myHandler = new MyHandler(handlerThread.getLooper());
Message msg = myHandler.obtainMessage();
Bundle b = new Bundle();
b.putInt("age", 20);
b.putString("name", "Jhon");
msg.setData(b);
msg.sendToTarget();
}
class MyHandler extends Handler{
public MyHandler(){
}
public MyHandler(Looper looper){
super(looper);
}
@Override
public void handleMessage(Message msg) {
Bundle b = msg.getData();
int age = b.getInt("age");
String name = b.getString("name");
System.out.println("age is " + age + ", name is" + name);
System.out.println("Handler--->" + Thread.currentThread().getId());
System.out.println("handlerMessage");
}
}
}
需要了解的知识点如下
Handler其实可以看做是一个工具类,用于向消息队列中插入消息的。如果想让子线程可以循环处理消息队列中的消息,那么必须要用到一个类:Looper
Looper是封装消息循环和消息队列的一个类,,Looper用于。
也就是说Looper可以循环从消息队列中取消息。Looper会一直从消息队列中取消息并处理消息,如果没有消息了,线程就处于休眠状态。
在Android中,我们很少创建一个Looper对象,因为Android框架中的HandlerThread类本身就具备了循环处理消息的功能。我们只要调用HandlerThread类的对象的getLooper方法就可以了。需要注意的是,只有在handlerThread.start();之后才能调用getLooper方法,否在取出来的Looper对象为null。
这个构造函数目的就是接收一个Looper对象作为参数,那么这个Looper对象就绑定到了这个myHandler上面。我们在程序里面有这么一句话MyHandler myHandler = new MyHandler(handlerThread.getLooper());,传进去的looper是handlerThread里面的looper,既然这个looper绑定到了myHandler上面,那么也就相当于myHandler绑定到了handlerThread这个线程上面,那么也就相当于myHandler的一切操作就在handlerThread这个子线程上执行,也就实现了多线程。
前面我们介绍了两种传递消息的方法
- msg.arg1 = i;
- msg.obj = "abc";
第一种是传递int类型的方法,用arg1和arg2这两个成员变量传递消息,优点是系统性能消耗较少
第二种是传递object类型的方法
此例中用了第三种方法
Bundle b = new Bundle();
b.putInt("age", 20);
b.putString("name", "Jhon");
msg.setData(b);
myHandler.sendMessage(msg);
msg.sendToTarget();
此方法用于传递大量的数据
msg.setData(b);方法传递的是Bundle对象。其实Bundle就是一个数据存储的工具,我们可以将数据存储到Bundle里面然后传递到另外一个地方。
Bundle和HashMap类似,不同点如下
- Bundle的key都是String类型的
- Bundel的value是基本类型以及基本类型的数组
好了,over。
分享到:
相关推荐
NULL 博文链接:https://txlong-onz.iteye.com/blog/934957
Android的Handler使用方法总结,不错的文档,跟大家分享分享
Android Handler传值的,简单的介绍了Handler的用法
Android Handler类详解 Android Handler类详解 Android Handler类详解 Android Handler类详解
android Handler的使用,我也刚开始学习,从别处下载了给大家分享
NULL 博文链接:https://dingran.iteye.com/blog/1930178
android demo,使用Handler的postDelay,Runnable run实现延时3秒的splash。
Android Handler Looper
android handler runnable使用实例(关键是内部run中停止)
android的多线程消息处理机制核心成员handler,基本用法很简单,相关资料也很多。本例子给大家带来handler的奇葩用法,与大家一起分享。
android handler的一些测试,套用他人的代码做的一些测试,多个线程sendmessage,该由那个handler处理?
Android中handler的使用,处理多线程的使用
比较简单的handler例子,通过接受消息改变图片展示
本资源是自己文章的demo的代码,以及android 系统部分的源代码
在Android开发中,我们经常会遇到这样一种情况:在UI界面上进行某项操作后要执行一段很耗时的代码,比如我们在界面上点击了一个”下载“按钮,那么我们需要执行网络请求,这是一个耗时操作,因为不知道什么时候才能...
Android Handler详细解析,讲解Handler之间的通讯,叫你如果用Handler完成异步线程对 UI的更新
android 中Handler 的几种写法,很简单的demo,大神简单修改下,用的是Handler.Callback,的方法
Android review handler的使用
Android Handler AsyncTask 异步加载
Android Handler使用方法,Button事件响应处理,Activity切换