Android AsyncTask与handler

系统 1756 0

本文主要讲解下AsyncTask的使用以及Handler的应用

首先,我们得明确下一个概念,什么是UI线程。顾名思义,ui线程就是管理着用户界面的那个线程!

android的ui线程操作并不是安全的,并且和用户直接进行界面交互的操作都必须在ui线程中进行才可以。这种模式叫做单线程模式。

我们在单线程模式下编程一定要注意: 不要阻塞ui线程、确保只在ui线程中访问ui组件

当我们要执行一个复杂耗时的算法并且最终要将计算结果反映到ui上时,我们会发现,我们根本没办法同时保证上面的两点要求;我们肯定会想到开启一个新的线程,让这个复杂耗时的任务到后台去执行,但是执行完毕了呢?我们发现,我们无法再与ui进行交互了。

为了解决这种情况,android为我们提供了很多办法。

1)、handler和message机制:通过显示的抛出、捕获消息与ui进行交互;

2)、Activity.runOnUiThread(Runnable):如果当前线程为ui线程,则立即执行;否则,将参数中的线程操作放入到ui线程的事件队列中,等待执行。

3)、View.post(Runnable):将操作放入到message队列中,如果放入成功,该操作将会在ui线程中执行,并返回true,否则返回false

4)、View.postDelayed(Runnable, long)跟第三条基本一样,只不过添加了一个延迟时间。

5)、android1.5以后为我们提供了一个工具类来搞定这个问题AsyncTask.


AsyncTask是抽象类 ,定义了三种泛型类型 Params,Progress,Result。

Params 启动任务执行的输入参数,比如HTTP请求的URL

Progress 后台任务执行的百分比。

Result 后台执行任务最终返回的结果,比如String

用程序调用,开发者需要做的就是实现这些方法。

1) 子类化AsyncTask

2) 实现AsyncTask中定义的下面一个或几个方法

onPreExecute() ,该方法将在执行实际的后台操作前被UI thread调用。可以在该方法中做一些准备工作,如在界面上显示一个进度条。

doInBackground(Params…) ,将在onPreExecute 方法执行后马上执行,该方法运行在后台线程中。这里将主要负责执行那些很耗时的后台计算工作。可以调用 publishProgress方法来更新实时的任务进度。该方法是抽象方法,子类必须实现。

onProgressUpdate(Progress…) ,在publishProgress方法被调用后,UI thread将调用这个方法从而在界面上展示任务的进展情况,例如通过一个进度条进行展示。

onPostExecute(Result) ,在doInBackground 执行完成后,onPostExecute 方法将被UI thread调用,后台的计算结果将通过该方法传递到UI thread.

为了正确的使用AsyncTask类,以下是几条必须遵守的准则:

1) Task的实例必须在UI thread中创建

2) execute方法必须在UI thread中调用

3) 不要手动的调用onPreExecute(), onPostExecute(Result),doInBackground(Params…), onProgressUpdate(Progress…)这几个方法

4) 该task只能被执行一次,否则多次调用时将会出现异常

    package cn.com.chenzheng_java;
 
import android.os.AsyncTask;
/**
 * 
 * @author chenzheng_java
 * @description 异步任务AcyncTask示例
 *    
 */
public class MyAsyncTask extends AsyncTask<String, Integer, Object> {
 
 /**
 * 该方法由ui线程进行调用,用户可以在这里尽情的访问ui组件。
 * 很多时候,我们会在这里显示一个进度条啥的,以示后台正在
 * 执行某项功能。
 */
 @Override
 protected void onPreExecute() {
 super.onPreExecute();
 }
 
 /**
 * 该方法由后台进程进行调用,进行主要的耗时的那些计算。
 * 该方法在onPreExecute方法之后进行调用。当然在执行过程中
 * 我们可以每隔多少秒就调用一次publishProgress方法,更新
 * 进度信息
 */
 @Override
 protected Object doInBackground(String... params) {
 return null;
 }
 
 
 /**
 * doInBackground中调用了publishProgress之后,ui线程就会
 * 调用该方法。你可以在这里动态的改变进度条的进度,让用户知道
 * 当前的进度。
 */
 @Override
 protected void onProgressUpdate(Integer... values) {
 super.onProgressUpdate(values);
 }
 
 /**
 * 当doInBackground执行完毕之后,由ui线程调用。可以在这里
 * 返回我们计算的最终结果给用户。
 */
 @Override
 protected void onPostExecute(Object result) {
 super.onPostExecute(result);
 }
}
  


下面介绍最本质的多线程:hanlder和message机制:

为何需要多线程:

在日常应用中,我们通常需要处理一些“后台,用户不可见”的操作,例如说,我们需要下载一个音乐,要是你的应用必须等用户下载完成之后才可以进行别的操作,那肯定让用户非常的不爽。这时候,我们通常的做法是,让这些操作去后台执行,然后等后台执行完毕之后,再给用户弹出相应的提示信息。这时候,我们就需要使用多线程机制,然后通过创建一个新的线程来执行这些操作。

明白了,实现需求,我们就准备着手实现了。但是,经过进一步的了解,我们悲剧的发现,android中的线程机制是,只能在UI线程中和用户进行交互。当我们创建了一个新线程,执行了一些后台操作,执行完成之后,我们想要给用户弹出对话框以确认,但是却悲剧的发现,我们根本无法返回UI主线程了。 (UI线程就是你当前看到的这些交互界面所属的线程)。

这时候,我们如果想要实现这些功能,我们就需要一个android为我们提供的handler和message机制。

先讲解下编程机制:

我们通常 在UI线程中创建一个handler,handler相当于一个处理器 ,它主要负责处理和绑定到该handler的线程中的message。每一个handler都必须关联一个looper,并且两者是一一对应的,注意,这点很重要哦!此外,looper负责从其内部的messageQueue中拿出一个个的message给handler进行处理。因为我们这里handler是在UI线程中实现的,所以经过这么一个handler、message机制,我们就可以回到UI线程中了。

handler: 处理后台进程返回数据的工作人员。

message: 后台进程返回的数据,里面可以存储bundle等数据格式

messageQueue: 是线程对应looper的一部分,负责存储从后台进程中抛回的和当前handler绑定的message,是一个队列。

looper: looper相当于一个messageQueue的管理人员,它会不停的循环的遍历队列,然后将符合条件的message一个个的拿出来交给handler进行处理。

注意,handler是在UI线程中声明的,如果我们直接用类似代码执行一个线程的话,实际上并没有创建一个新的线程,因为handler已经跟默认的UI线程中的looper绑定了。

如果有兴趣的话,可以去看下Handler的默认空构造函数便知道原因了,里面直接绑定了当前UI线程的looper。

下面给出一个比较简单,并且实用的实例。

    public class MainActivity extends Activity implements OnClickListener {
	private Button btnTXT;
	private TextView tvTXT;
	
	private StringBuffer returnMsg;
	
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        
        btnTXT = (Button)findViewById(R.id.btnTXT);
        tvTXT = (TextView)findViewById(R.id.tvTXT);
        
        btnTXT.setOnClickListener(this);		
    }

	@Override
	public void onClick(View v) {
		returnMsg = new StringBuffer();
		
		// 创建一个包含Looper的线程,这里如果没有HandlerThread的调用,会直接将后边的MyRunnable放到UI线程队列(myHandler.post(new MyRunnable()))
		HandlerThread handlerThread = new HandlerThread("handler_thread");
		handlerThread.start();		// 启动自定义处理线程
		
		myHandler = new MyHandler(handlerThread.getLooper());		// 将handler绑定到新线程  
		myHandler.post(new MyRunnable());		// 在新线程中执行任务 
	}
    
	/** 主线程Handler,可以与UI控件交互 */
	Handler mainHanlder = new Handler(){
		@Override
		public void handleMessage(Message msg) {
			if(msg.what == 0) {
				tvTXT.setText(returnMsg.toString());	// 与主线程控件打交道(直接访问)
			}
		}
	};

	
	/** 构造Hanlder,不可与UI控件直接交互 */
	private MyHandler myHandler = null;
	private class MyHandler extends Handler{
		/** 
		 * 使用默认的构造函数,会将handler绑定当前UI线程的looper。 
		 * 如果想使用多线程这里是不能使用默认的构造方法的。 
		 */  
		public MyHandler(){
			super();
		}
		
		/** 构造函数,自定义looper */
		public MyHandler(Looper looper) {
			super(looper);
		}
		
		// 处理具体的message消息,继承自父类的方法
		@Override
		public void handleMessage(Message msg) {
			int what = msg.what;	
			Bundle bundle = (Bundle)msg.obj;			// 提取bundle中的信息
			String name = bundle.getString("name");
			String sex = bundle.getString("sex");
			boolean marry = bundle.getBoolean("marray");
			int age = bundle.getInt("age");
			
			StringBuffer strBuf = new StringBuffer();		// 拼接bundle信息
			strBuf.append("what = ").append(what).append("\n\n");
			strBuf.append("name = ").append(name).append("\n");
			strBuf.append("sex = ").append(sex).append("\n");
			strBuf.append("marry = ").append(marry).append("\n");
			strBuf.append("age = ").append(age).append("\n\n");
			strBuf.append("http://blog.csdn.net/sunboy_2050");
			
			returnMsg = returnMsg.append(strBuf);	// 保存要显示的结果
			mainHanlder.sendEmptyMessage(0);		// 向主线程mainHanlder发送消息,与UI控件交互显示结果
			
			super.handleMessage(msg);
		}
	}
    
	
	// 构造Runnable,处理后台业务逻辑,如下载
	private class MyRunnable implements Runnable{
		@Override
		public void run() {
			try {
				Message msg = Message.obtain(myHandler);	// 捕获myHandler消息
				
				msg.what = 10;
				Bundle bundle = new Bundle();				// 封装bundle信息
				bundle.putString("name", "yanggang");
				bundle.putString("sex", "pure boy");
				bundle.putBoolean("marry", false);
				bundle.putInt("age", 18);
				msg.obj = bundle;
				
				long thID = Thread.currentThread().getId();
				returnMsg.append(thID).append(" : send msg start...").append("\n");
				msg.sendToTarget();		// 向myHandler发送消息

				Thread.sleep(3000);
			} catch (Exception e) {
				Log.i("", "Runnable send msg error...");
				e.printStackTrace();
			}
		}
	}
}
  

运行结果:

Android AsyncTask与handler


示例代码下载



转载声明:

Android之多线程工作-AsyncTask与handler

Android自用-----AsyncTask实现异步处理任务

android线程 Handler Message Queue AsyncTask


参考推荐:

java synchronized详解

java中synchronized用法

Android实现计时与倒计时的几种方法

java同步synchronized关键字用法示例

Android AsyncTask与handler


更多文章、技术交流、商务合作、联系博主

微信扫码或搜索:z360901061

微信扫一扫加我为好友

QQ号联系: 360901061

您的支持是博主写作最大的动力,如果您喜欢我的文章,感觉我的文章对您有帮助,请用微信扫描下面二维码支持博主2元、5元、10元、20元等您想捐的金额吧,狠狠点击下面给点支持吧,站长非常感激您!手机微信长按不能支付解决办法:请将微信支付二维码保存到相册,切换到微信,然后点击微信右上角扫一扫功能,选择支付二维码完成支付。

【本文对您有帮助就好】

您的支持是博主写作最大的动力,如果您喜欢我的文章,感觉我的文章对您有帮助,请用微信扫描上面二维码支持博主2元、5元、10元、自定义金额等您想捐的金额吧,站长会非常 感谢您的哦!!!

发表我的评论
最新评论 总共0条评论