现在的位置: 首页 > 综合 > 正文

Android的线程—Handler和AsyncTask

2013年08月13日 ⁄ 综合 ⁄ 共 6170字 ⁄ 字号 评论关闭

1、Android进程模型

在安装Android应用程序的时候,Android会为每个程序分配一个Linux用户ID,并设置相应的权限,这样其它应用程序就不能访问此应用程序所拥有的数据和资源了。

在 Linux 中,一个用户ID 识别一个给定用户在 Android 上,一个用户ID 识别一个应用程序

应用程序在安装时被分配用户 ID,应用程序在设备上的存续期间内,用户ID 保持不变。

默认情况下,每个apk运行在它自己的Linux进程中。当需要执行应用程序中的代码时,Android会启动一个jvm,即一个新的进程来执行,因此不同的apk运行在相互隔离的环境中。

下图显示了:两个 Android 应用程序,各自在其自己的基本沙箱或进程上,他们是不同的Linux user ID。



开发者也可以给两个应用程序分配相同的linux用户id,这样他们就能访问对方所拥有的资源。

为了保留系统资源,拥有相同用户id的应用程序可以运行在同一个进程中,共享同一个jvm。

不同的应用程序可以运行在相同的进程中。要实现这个功能,首先必须使用相同的私钥签署这些应用程序,然后必须使用 manifest 文件给它们分配相同的 Linux 用户 ID,这通过用相同的值/名定义 manifest 属性 android:sharedUserId 来做到。

如下图,显示了两个 Android 应用程序,运行在同一进程上。


二、Android的单线程模型

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

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

如果在非UI线程中直接操作UI线程,会抛出异常,这与普通的java程序不同。

由于UI线程负责事件的监听和绘图,因此,必须保证UI线程能够随时响应用户的需求,UI线程里的操作应该像中断事件那样短小。

费时的操作(如网络连接)需要另开线程,否则,如果UI线程超过5s没有响应用户请求,会弹出对话框提醒用户终止应用程序。

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


Message Queue:

1/Message—消息,理解为线程间交流的信息。


2/Handler—处理者,是Message的主要处理者,负责Message的发送和处理。

主线程通过子类化Handler实现handlerMessage(msg)来处理数据,而子线程通过传入的handler对象的sendMessage()发送数据。

Handler有两种发送方式:sendMessage和post。sendMessage发送一个messgae对象交由handMessage()处理,而post()则发送一个runnable代码块插到主线程运行。

3/Message Queue—消息队列,用来存放通过Handler发布的消息,按照先进先出执行。

Android在第一次启动程序时会默认会为主线程UI Thread创建一个关联的消息队列MessageQueue(子线程不创建),用来管理程序的一些上层组件如activities,broadcastReceivers等等。

所以Looper.getMainLooper得到住线程Looper不为null,当前线程调用Looper.myLooper可能为null;

4/Looper—每个线程里的MessageQueue的管家。

Looper扮演着一个Handler和消息队列之间通讯桥梁的角色。

程序组件首先通过Handler把消息传递给Looper,Looper把消息放入队列。Looper也把消息队列里的消息广播给所有的Handler,Handler接受到消息后调用handleMessage进行处理。

总结:

1、主线程UI Thread 默认创建消息队列 Message Queue,队列拥有管家Looper;

2、在主线程中子类化Handler,并把对象传入子线程中;

3、子线程得到主线程的处理者Handler,通过Handler把消息传递给管家Looper;

4,管家Looper放到队列Message Queue中,让主线程处理数据(更新界面等),Over;

实例如下:

package com.jseven.messagehandler;

import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;

public class MessageAndHandlerActivity extends Activity {
	
	EditText input;
	Button bn;
	TextView show;
	
	MyHandler myHandler;
	
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        
        input = (EditText) findViewById(R.id.input);
        bn = (Button) findViewById(R.id.bn);
        show = (TextView) findViewById(R.id.show);
        
        // 1、主线程UI Thread 默认创建消息队列 Message Queue,队列拥有管家Looper;
        Looper looper = Looper.myLooper();
        
        // 2.1 、在主线程中子类化Handler,并把对象传入子线程中;
        myHandler = new MyHandler(Looper.myLooper()); 
        // 此处甚至可以不需要设置Looper,因为 Handler默认就使用当前线程的Looper
        // myHandler = new MyHandler();
        
        // 2.2 、 在主线程中子类化Handler,并把对象传入子线程中;
        bn.setOnClickListener(new OnClickListener() {
			public void onClick(View v) {
				// 这里子线程是内部类,可以直接访问handler。但有时候可能是外部类,所以还是以参数传递
				new Thread(new MySubThread(myHandler)).start();
			}
		});
        
    }
    
    // 3、子线程得到主线程的处理者Handler,通过Handler把消息传递给管家Looper;
    class MySubThread implements Runnable {
    	Handler h;
    	
		public MySubThread(Handler handler) {
			super();
			this.h = handler;
		}

		public void run() {
			String str = "啥都处理只是露个面------" + input.getText().toString();
			Message msg = new Message();
			msg.what = 0x123;	// 0x123表示符,让handlerMessage知道而已
			msg.obj = str;
			h.sendMessage(msg);
		}
    	
    }
    
    // 4,管家Looper放到队列Message Queue中,让主线程处理数据(更新界面等),Over;
    class MyHandler extends Handler {
    	
    	public MyHandler() {
			super();
		}
		public MyHandler(Looper looper) {
			super(looper);
		}

		// 发送消息给主线程
		public void handleMessage(Message msg) {
			if(msg.what == 0x123) {
				show.setText(msg.obj.toString());
			}
			
		}
    	
    }
}


对于这个实例,当这个Activity执行完onCreate,onStart,onResume后,就监听UI的各种事件(如按钮点击事件)和消息(如Handler Message)。

当点击一个按钮后启动线程,线程执行结束后通过handler发送一个消息,由于这个handler属于UI线程,因此这个消息也发送给UI线程,然后UI线程又把这个消息给handler处理,而这个handler是UI线程创造的,它可以访问UI组件,因此就更新了页面。

由于通过handler需要自己管理线程类,如果业务稍微复杂,代码看起来就比较混乱,因此android提供了AsyncTask类来解决此问题。

三、AsyncTask类


线程更新是可用,但是这种匿名线程的方式是存在缺陷的:

第一,线程的开销较大,如果每个任务都要创建一个线程,那么应用程序的效率要低很多;

第二,线程无法管理,匿名线程创建并启动后就不受程序的控制了,如果有很多个请求发送,那么就会启动非常多的线程,系统将不堪重负。

另外,前面已经看到,在新线程中更新UI还必须要引入handler,这让代码看上去非常臃肿。

  AsyncTask很好地解决这一问题。AsyncTask的特点是任务在主线程之外运行,而回调方法是在主线程中执行,这就有效地避免了使用Handler带来的麻烦。

阅读AsyncTask的源码可知,AsyncTask是使用java.util.concurrent 框架来管理线程以及任务的执行的,concurrent框架是一个非常成熟,高效的框架,经过了严格的测试。这说明AsyncTask的设计很好的解决了匿名线程存在的问题。

首先继承一下此类,实现以下若干方法:

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

2) doInBackground(Params...)将在onPreExecute 方法执行后马上执行,该方法运行在后台线程中。这里将主要负责执行那些很耗时的后台计算工作。

可以调用publishProgress方法来更新实时的任务进度。该方法是抽象方法,子类必须实现。

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

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

使用时需要遵循以下规则:

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

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

3、不要手动的调用这些方法,只调用execute即可

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

实例如下:
package com.jseven.messagehandler;

import android.app.Activity;
import android.os.AsyncTask;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;

public class AsyncTaskTest extends Activity {
	
	EditText input;
	Button bn;
	TextView show;
	
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        
        input = (EditText) findViewById(R.id.input);
        bn = (Button) findViewById(R.id.bn);
        show = (TextView) findViewById(R.id.show);
        
        bn.setOnClickListener(new OnClickListener() {
			
			public void onClick(View v) {
				// 1、Task的实例必须在UI thread中创建
				// 2、execute方法必须在UI thread中调用
				// 3、不要手动的调用这些方法,只调用execute即可
				// 4、该task只能被执行一次,否则多次调用时将会出现异常
				new MyTask().execute(input.getText().toString()); 
			}
		});
        
    }
    
    
    class MyTask extends AsyncTask<String, Integer, String> {

    	// 将在onPreExecute 方法执行后马上执行,该方法运行在后台线程中。
    	// 这里将主要负责执行那些很耗时的后台计算工作。
		protected String doInBackground(String... params) {
			return params[0] + "----在后面露个面"; // 处理
		}

		// 在doInBackground 执行完成后,onPostExecute 方法将被UI thread调用,
		// 后台的计算结果将通过该方法传递到UI thread.
		protected void onPostExecute(String result) {
			show.setText(result.toString());	// 更新
		}
		
    }

}


参考文章:(感谢一下博主的分享)

http://blog.csdn.net/sunboy_2050/article/details/8027442

http://blog.csdn.net/super005/article/details/5827298

相关收藏:

Android消息处理机制http://www.cnblogs.com/qingblog/archive/2012/06/27/2566021.html

Android的异步(Thread,Handler,AsyncTask)http://blog.csdn.net/jave_/article/details/6879978

抱歉!评论已关闭.