码迷,mamicode.com
首页 > 移动开发 > 详细

Android 基础总结:( 十四)Handler详解(上)

时间:2016-06-03 19:36:14      阅读:221      评论:0      收藏:0      [点我收藏+]

标签:

Handler的定义:

主要接受子线程发送的数据, 并用此数据配合主线程更新UI。

解释: 当应用程序启动时,Android首先会开启一个主线程 (也就是UI线程) , 主线程为管理界面中的UI控件,进行事件分发,比如说,你要是点击一个 Button ,Android会分发事件到Button上,来响应你的操作。

如果此时需要一个耗时的操作,例如:联网读取数据,或者读取本地较大的一个文件的时候,你不能把这些操作放在主线程中,如果你放在主线程中的话,界面会出现假死现象,如果5秒钟还没有完成的话,会收到Android系统的一个错误提示 "强制关闭"。 这个时候我们需要把这些耗时的操作,放在一个子线程中,因为子线程涉及到UI更新,Android主线程是线程不安全的,也就是说,更新UI只能在主线程中更新,子线程中操作是危险的。

这个时候,Handler就出现了。来解决这个复杂的问题,由于Handler运行在主线程中(UI线程中),它与子线程可以通过Message对象来传递数据,这个时候,Handler就承担着接受子线程传过来的(子线程用sedMessage()方法传弟)Message对象,(里面包含数据),把这些消息放入主线程队列中,配合主线程进行更新UI。

Handler 概念解释:

handler类允许你发送消息和处理线程消息队列中的消息及runnable对象。handler实例都是与一个线程和该线程的消息队列一起使用,一旦创建了一个新的handler实例,系统就把该实例与一个线程和该线程的消息队列捆绑起来,这将可以发送消息和runnable对象给该消息队列,并在消息队列出口处处理它们。

handler类有两种主要用途:1。按照时间计划,在未来某时刻,对处理一个消息或执行某个runnable实例。2。把一个对另外线程对象的操作请求放入消息队列中,从而避免线程间冲突。

时间类消息通过如下方法使用: post(Runnable), postAtTime(Runnable, long), postDelayed(Runnable, long), sendEmptyMessage(int), sendMessage(Message), sendMessageAtTime(Message, long), and sendMessageDelayed(Message, long)

methods.post之类函数可以传输一个runnable对象给消息队列,并在到达消息队列后被调用。sendmessage之类函数可以传送一个包含数据的message对象,该message对象可以被Handler类的handleMessage(Message) 方法所处理。

post之类函数和sendmessage之类的函数都可以指定消息的执行时机,是立即执行、稍后一段时间执行,还是在某个确定时刻执行。这可以用来实现超时、消息或其他时间相关的操作。

当一个进程启动时,主线程独立执行一个消息队列,该队列管理着应用顶层的对象(如:activities、broadcast receivers等等)和所有创建的窗口。你可以创建自己的一个线程,并通过handler来与主线程进行通信。这可以通过在新的线程中调用主线程的handler的post和sendmessage操作来实现。

HandlerThread/Looper & MessageQueue & Message的关系:

handler负责将需要传递的信息封装成Message,通过handler对象的sendMessage()来将消息传递给Looper,由Looper将Message放入MessageQueue中。当Looper对象看到MessageQueue中含有Message,就将其广播出去。该handler 对象收到该消息后,调用相应的handler对象的handleMessage()方法对其进行处理。

Handler一些特点

handler可以分发Message对象和Runnable对象到主线程中,每个Handler实例,都会绑定到创建他的线程中(一般是位于主线程),它有两个作用:

(1): 安排消息或Runnable 在某个主线程中某个地方执行。

(2)安排一个动作在不同的线程中执行。

Handler中分发消息的一些方法

post(Runnable)

postAtTime(Runnable,long)

postDelayed(Runnable long)

sendEmptyMessage(int)

sendMessage(Message)

sendMessageAtTime(Message,long)

sendMessageDelayed(Message,long)

以上post类方法允许你排列一个Runnable对象到主线程队列中,

sendMessage类方法, 允许你安排一个带数据的Message对象到队列中,等待更新.

Handler的使用


下面用两个实例讲解:


示例1:

public class HandlerTest extends Activity {
	/** Called when the activity is first created. */
	private Button startButton;
	private Button endButton;
	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.main);
		// 根据id获得控件对象
		startButton = (Button) findViewById(R.id.startButton);
		endButton = (Button) findViewById(R.id.endButton);
		// 为控件设置监听器
		startButton.setOnClickListener(new StartButtonListener());
		endButton.setOnClickListener(new EndButtonListener());
	}
	class StartButtonListener implements OnClickListener {
		public void onClick(View v) {
			// 调用Handler的post()方法,将要执行的线程对象放到队列当中
			handler.post(updateThread);
		}
	}
	class EndButtonListener implements OnClickListener {
		public void onClick(View v) {
			// 调用Handler的removeCallbacks()方法,删除队列当中未执行的线程对象
			handler.removeCallbacks(updateThread);
		}
	}
	// 创建Handler对象
	Handler handler = new Handler();
	// 新建一个线程对象
	Runnable updateThread = new Runnable() {
		// 将要执行的操作写在线程对象的run方法当中
		public void run() {
			System.out.println("updateThread");
			// 调用Handler的postDelayed()方法
			// 这个方法的作用是:将要执行的线程对象放入到队列当中,待时间结束后,运行制定的线程对象
			// 第一个参数是Runnable类型:将要执行的线程对象
			// 第二个参数是long类型:延迟的时间,以毫秒为单位
			handler.postDelayed(updateThread, 3000);
		}
	};
}


示例2:

一个应用程序中有一个进度条和一个按钮,当点击按钮后,每隔一秒钟进度条前进一部分。

下图为应用程序的运行效果图:

技术分享


public class ProgressBarHandlerTest extends Activity {
	/** Called when the activity is first created. */
	private ProgressBar progressBar;
	private Button startButton;
	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.main);
		progressBar = (ProgressBar) findViewById(R.id.progressbar);
		startButton = (Button) findViewById(R.id.startButton);
		startButton.setOnClickListener(new ProgressBarOnClickListener());
	}
	class ProgressBarOnClickListener implements OnClickListener {
		public void onClick(View v) {
			// 设置进度条为可见状态
			progressBar.setVisibility(View.VISIBLE);
			updateBarHandler.post(updateThread);
		}
	}
	// 使用匿名内部类来复写Handler当中的handlerMessage()方法
	Handler updateBarHandler = new Handler() {
		@Override
		public void handleMessage(Message msg) {
			progressBar.setProgress(msg.arg1);
			updateBarHandler.post(updateThread); // 将要执行的线程放入到队列当中
		}
	};
	// 线程类,该类使用匿名内部类的方式进行声明
	Runnable updateThread = new Runnable() {
		int i = 0;
		public void run() {
			// TODO Auto-generated method stub
			System.out.println("Begin Thread");
			i += 10;
			// 得到一个消息对象,Message类是android系统提供的
			Message msg = updateBarHandler.obtainMessage();
			// 将Message对象的arg1参数的值设置为i
			msg.arg1 = i; // 用arg1、arg2这两个成员变量传递消息,优点是系统性能消耗较少
			try {
				Thread.sleep(1000); // 让当前线程休眠1000毫秒
			} catch (InterruptedException ex) {
				ex.printStackTrace();
			}
			// 将Message对象加入到消息队列当中
			updateBarHandler.sendMessage(msg);
			// 如果i的值等于100
			if (i == 100) {
				// 将线程对象从队列中移除
				updateBarHandler.removeCallbacks(updateThread);
			}
		}
	};
}

Handler与线程的关系

Handler在默认情况下,实际上它和调用它的Activity是处于同一个线程的。

例如在Handler的使用(一)的示例1中,虽然声明了线程对象,但是在实际调用当中它并没有调用线程的start()方法,而是直接调用当前线程的run()方法。

通过一个例子来证实一下

一个Android应用程序,在Activity中创建Handler和线程对象,并且在Activity的onCreate()方法中输出当前线程的id和名字,然后在线程对象的run方法中也打印输出下当前线程的id和名字。如果说,Activity输出的结果与线程对象输出的结果是一样的,那么就表示它们使用的是同一个线程。

下面是Activity代码:

public class HandlerTwo extends Activity {
	/** Called when the activity is first created. */
	Handler handler = new Handler();
	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		// 在设置布局文件之前先调用post方法,
		// 表示在执行完线程之后才会显示布局文件中的内容,而线程中又设置了休眠10秒钟,
		// 所以最终效果为,先显示应用程序主界面,等待10秒钟之后才显示布局文件中的内容
		handler.post(r);
		setContentView(R.layout.main);
		System.out.println("activity id--->" + Thread.currentThread().getId());
		System.out.println("activity name--->
				+ Thread.currentThread().getName());
	}
	Runnable r = new Runnable() {
		public void run() {
			// 输出当前线程的id和name
			// 如果这里输出的线程id、name与上面onCreate()方法中输出的线程id、name相同的话,
			// 那么则表示,他们使用的是同一个线程
			System.out.println("runnable_id--->
					+ Thread.currentThread().getId());
			System.out.println("runnable_name--->
					+ Thread.currentThread().getName());
			try {
				Thread.sleep(10000); // 让线程休眠10秒
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	};
}

根据结果可以看出,两个输出的id和name都相同,它们使用的是同一个线程。

现在将Activity中的代码修改一下,新建一个线程Thread,然后调用线程的start()方法,再观察一下控制台的输出结果。

这里只要将上面的代码稍微修改一下就可以了。

1、先将handler.post(r)注释掉。

2、再添加下面两句代码就OK了。

//handler.post(r); 
Thread t = new Thread(r); 
t.start();

从这个输出结果中可以看出,这次线程对象的id、name与activity里的线程id、name完全不一样了,由此可见,它们现在使用的不是同一个线程。

这个例子中还掩饰了一个效果,就是平时我们是将Handler的post()方法放在setContentView(R.layout.main)这个方法之后调用,将设置完布局之后再执行其他的操作,而在这个例子中,是将Handler的post()方法放在setContent()方法之前调用,而post里传递的线程对象的run()方法呢,又执行了休眠线程10秒钟,所以运行实现的效果会是,当程序运行后,首先Activity上没有任何内容,过来10秒之后,才会显示Activity里的内容。

欢迎阅读下篇:

Android 基础总结:( 十五)Handler详解(下)




Android 基础总结:( 十四)Handler详解(上)

标签:

原文地址:http://blog.csdn.net/wenwins/article/details/51570555

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!