Android实现滑动图片(ViewPager)学习之二:Handler(一)


说实话,之前在java开发的时候,很少涉及多线程的东西。由于开发的项目体量比较小,技术也比较差,所以更多的考虑的是功能,很少对并发做优化

 

如今借着学习Android的机会,希望可以对多线程的知识有一个更好更全面地认识。哎,感觉自己技术基础还是太差,好好加油吧

 

首先,安卓使用的时单线程模型:

    当一个程序第一次启动时,Android会同时启动一个对应的主线程(Main Thread),主线程主要负责处理与UI相关的事件,如:用户的按键事件,用户接触屏幕的事件以及屏幕绘图事件,并把相关的事件分发到对应的组件进行处理。所以主线程通常又被叫做UI线程。

    在开发Android 应用时必须遵守单线程模型的原则: Android UI操作并不是线程安全的并且这些操作必须在UI线程中执行。

           如果在非UI线程中直接操作UI线程,会抛出android.view.ViewRoot$CalledFromWrongThreadException:Only the original thread that created a view hierarchy can touch itsviews,这与普通的java程序不同。

    由于UI线程负责事件的监听和绘图,因此,必须保证UI线程能够随时响应用户的需求,UI线程里的操作应该向中断事件那样短小,费时的操作(如网络连接)需要另开线程,否则,如果UI线程超过5s没有响应用户请求,会弹出对话框提醒用户终止应用程序。

    如果在新开的线程中需要对UI进行设定,就可能违反单线程模型,因此android采用一种复杂的Message Queue机制保证线程间通信。

    以上内容摘自http://www.cnblogs.com/nio-nio/archive/2012/07/23/2604900.html,是我在网上看到的相对比较容易理解的说明。读完以上这几段话之后,我对安卓单线程模式的理解:如果你要对UI进行操作那这些操作应该都放到主线程中;如果你要处理一些业务逻辑或者其它的比较费时的操作,那就把这些操作放到子线程中。那因为大多数的UI更新都是基于业务逻辑的判断,如何让处理逻辑的子线程和处理UI的主线程通信呢,这就需要用到Handler


Handler是干什么的: 


        Handler可以分发Message对象和Runnable对象到主线程中, 每个Handler实例,都会绑定到创建他的线程中(一般是位于主线程),它有两个作用: (1): 安排消息或Runnable 在某个主线程中某个地方执行, (2)安排一个动作在不同的线程中执行。

关于Handler的使用,我们首先来看一个简单例子:

         我们假设有这么一个需求:我们需要在程序中点击一个开始按钮,然后我们的程序会在后台启动一个定时任务(每隔一秒打印一个语句)。另外,我们还需要一个取消按钮,点击它终止后台的定时任务.

        我们先在界面中加入两个按钮,一个开始,一个取消。接下来是activity


package com.example.hander1;

import java.util.Date;
import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;

public class MainActivity extends Activity {

	Button startButton = null;
	Button stopButton = null;
	Handler handler = new Handler();
	
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		
		startButton = (Button) findViewById(R.id.startButton);
		stopButton = (Button) findViewById(R.id.stopButton);
		
		//为button绑定onclicklistener
		startButton.setOnClickListener(new ButtonOnclikListener());
		stopButton.setOnClickListener(new ButtonOnclikListener());
	}
	
	Runnable going = new Runnable() {	
		@Override
		public void run() {
			//线程每次执行,打印日志
			Log.i("run", "going  "+new Date().getSeconds());
			//延时一秒钟后,再次将线程加入队列中
			handler.postDelayed(going, 1000);
		}
	};

	class ButtonOnclikListener implements OnClickListener{

		@Override
		public void onClick(View v) {	
			switch(v.getId()){
			case R.id.startButton:
				begin();
				break;
			case R.id.stopButton:
				stop();
				break;
			default:
				break;
			}
		}
		
	}
	
	//该方法将going添加到message queue中 
	void begin(){
		handler.post(going);
	}
	
	//从message queue中移除going
	void stop(){
		handler.removeCallbacks(going);
	}

}

下面是运行结果,程序启动后是这样



      当我点击启动按钮后,后台每秒中打印一条语句


     当我点击取消按钮后,后台不再打印语句


对于这个程序的运行过程我是这样理解的:

      1、当我点击启动按钮时:调用handler.post(going); 

handler的post方法说明是这样的:Causes the Runnable r to be added to the message queue. The runnable will be run on the thread to which this handler is attached.

        也就是说,当我调用了handler.post(going);这个方法之后,handler会将我实现的Runnable对象加入到message queue中,系统会根据message queue来执行。后面一句很重要,runnable会在当前handler所属的那个线程中执行,也就是说我的going会在程序的主线程中执行,而不是新开启的其它线程。


·      2、在going的run方法中,我调用了handler.postDelayed(going, 1000);

     postDelayed这个方法和前面的post方法类似,只不过第二个参数的作用是让我的going延迟一秒钟执行。

     这样其实就形成了一个死循环(我没有规定跳出的条件),程序在我点击启动之后,每隔一秒钟会在message queue中加入执行going的内容。


3、当我点击取消按钮时:调用handler.removeCallbacks(going);

      虽在我在上面的写法中形成了一个死循环,但是我可以通过handler.removeCallbacks(going)这个方法,将message queue这个队列中的going去掉,这样就不再执行going了。而且根据这个方法源码中的注释:Remove any pending posts of Runnable r that are in the message queue. 应该是会把message queue中所有未执行going全部去掉。



这个程序虽然很简单,但是我在一次看到的时候,仍然陷入了误区。由于没有仔细阅读API,我刚开始以为handler.post()这个方法会新开启一个线程。后来看到API的时候才明白原来这个程序,只有一个主线程,只不过是通过handler将实现的Runnable 对象加入到message queue中执行的。


Android实现滑动图片(ViewPager)学习之二:Handler(一),,5-wow.com

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。