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

Android GWES之Android消息系统

Looper,Handler,View

我们要理解Android的消息系统,Looper,Handle,View等概念还是需要从消息系统的基本原理及其构造这个源头开始。从这个源头,我们才能很清楚的看到Android设计者设计消息系统之意图及其设计的技术路线。

1.消息系统的基本原理

从一般的系统设计来讲,一个消息循环系统的建立需要有以下几个要素:

消息队列

发送消息

消息读取

消息分发

消息循环线程

首先来研究一下消息驱动的基本模型,我使用如下的图形来表示一个消息系统最基本构成:

上面的模型代表应用程序一直查询自己的消息队列,如果有有消息进来,应用消息处理函数中根据消息类型及其参数来作相应的处理。消息系统要运作起来,必定有消息的产生和消费。我们可以从下图看到消息生产和消费的一个基本的链条,这是一个最基本的,最简单的消息系统。

生产线程将消息发送到消息队列,消息消费者线程从消息队列取出消息进行相应的处理。但是这样简单的模型对实际运行的系统来说是不够的,例如对系统资源的消耗等不能很好的处理,我们就需要一个有旗语的消息系统模型,在上面的消息系统模型中加入了一个旗语,让消息消费者线程在没有消息队列为空时,等待旗语,进入到挂起状态,而有消息到达时,才被唤醒继续运行。当然生产者同时也可以是消费者。

2. Android的消息模型

Android要建立一个消息系统使用了Looper,MessageQueue,Handler等概念,从上节的原理我们可以知道这些都是概念包装,本质的东西就是消息队列中消息的分发路径的和消息分发处理方式的设计。Android巧妙的利用了对象抽象技术抽象出了Looper和Handler的概念。在Looper和Handler两个概念的基础上,通过View的处理函数框架,Android十分完美的达到消息分发的目的。 参照基本消息系统描述模型,我给出了Android消息系统整体框架,表示如下:

Android消息系统消息分发框架

3 Looper,Handler详解

Looper只是产生一个消息循环框架,首先Looper创建了消息队列并把它挂接在Linux的线程上下文中,进入到取消息,并分发消息的循环当中。Handler对象在同一个线程上下文中取得消息队列,对消息队列进行封装操作,最主要的就是SendMessage和担当起dispatchMessage这个实际工作。外部系统需要向某个Android线程发送消息,必须通过属于该AndroidThread的Handler这个对象进行。

Handler属于某个线程,取决Handlerd对象在哪个线程中建立。Handler在构建时做了如下的默认动作:

1、从线程上下文取得Looper。

2、通过Looper获取到消息队列并记录在自己的成员mQueue变量中,Handler使用消息队列进行对象封装,提供如下的成员函数:

3、通过post(Runnable r)发送。Runnable是消息处理的回调函数,通过该消息的发送,引起Runable的回调运行,post消息放置消息队列的前面。Message.callback=Runable。

/**
 * 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.
 *
 * @param r
 *            The Runnable that will be executed.
 *
 * @return Returns true if the Runnable was successfully placed in to the
 *         message queue. Returns false on failure, usually because the
 *         looper processing the message queue is exiting.
 */
public final boolean post(Runnable r) {
	return sendMessageDelayed(getPostMessage(r), 0);
}

通过 sendMessage发送。放置在所有的Post消息之后,sendMessage发送消息。(具体源码:)

/**
 * Pushes a message onto the end of the message queue after all pending
 * messages before the current time. It will be received in
 * {@link handleMessage}, in the thread attached to this handler.
 *
 * @return Returns true if the message was successfully placed in to the
 *         message queue. Returns false on failure, usually because the
 *         looper processing the message queue is exiting.
 */
public final boolean sendMessage(Message msg) {
	return sendMessageDelayed(msg, 0);
}

dispatchMessage分发消息。消息带有回调函数,则执行消息回调函数,如何没有则使用默认处理函数:handleMessage。而handleMessage往往被重载成某个继承Handler对象的新的特定的handleMessage。几乎所有的Message发送时,都指定了target。Message.target=(this).

/*package*/ Handler target;
/*package*/ Runnable callback;
....
....
.....
.....
public void setTarget(Handler target) {
		this.target = target;
}

Looper运行在Activity何处?我们现在可以从代码堆栈中纵观一下Looper的位置。

NaiveStart.main()
ZygoteInit.main
ZygoteInit$MethodAndArgsCall.run
Method.Invoke
method.invokeNative
ActivityThread.main()
Looper.loop()
ViewRoot$RootHandler().dispatch()
handleMessage ....

这样我们就更清楚的了解到Looper的运行位置。

用Handler更新进度条ProgressBar

通过对Handler的学习,使我们理解了线程的运行,当我们使用多个线程的时候,会感觉很乱,如果我们应用Handler,我们可以更加清晰的去进行更新。下面是利用线程Handler更新进度条的示例:

public class Main extends Activity {
	/** Called when the activity is first created. */
	ProgressBar pb1;
	Handler handle = new Handler();
	// 新建一个Handler对象
	Button b1;
	Button b2;
	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.main);
		pb1 = (ProgressBar) findViewById(R.id.pb1);
		pb1.setProgress(0);
		b1 = (Button) findViewById(R.id.b1);
		b1.setOnClickListener(b1Lis);
		b2 = (Button) findViewById(R.id.b2);
		b2.setOnClickListener(b2Lis);
	}
	private OnClickListener b1Lis = new OnClickListener() {
		@Override
		public void onClick(View v) {
			// TODO Auto-generated method stub
			handle.post(add);
			// 开始执行add
		}
	};
	private OnClickListener b2Lis = new OnClickListener() {
		@Override
		public void onClick(View v) {
			// TODO Auto-generated method stub
			handle.removeCallbacks(add);
			// 停止执行
			pb1.setProgress(0);
		}
	};
	int pro = 0;
	Runnable add = new Runnable() {
		// 定义add
		@Override
		public void run() {
			// TODO Auto-generated method stub
			pro = pb1.getProgress() + 1;
			pb1.setProgress(pro);
			setTitle(String.valueOf(pro));
			if (pro < 100) {
				handle.postDelayed(add, 500);
				// 如果进度小于100,,则延迟500毫秒后重复执行add
			}
		}
	};
}

在Android的Notification中显示进度条

最近研究了Notification,参考了一些文档,写了一些心得。在官方文档中得知在Android的Notification中可以显示进度条,就想做个例子试一下。在网上查了下,没有找到。决定自己写下,费了九牛二虎之力搞定了,现在拿出与大家分享下:废话少说,上代码:

先自定义一个view

<?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:background="880490FF
    android:orientation="horizontal
    android:padding="10dp" >
    <ImageView
        android:id="@+id/image
        android:layout_width="wrap_content
        android:layout_height="fill_parent" />
    <ProgressBar
        android:id="@+id/pb
        style="?android:attr/progressBarStyleHorizontal
        android:layout_width="180dip
        android:layout_height="wrap_content
        android:layout_gravity="center_vertical" />
    <TextView
        android:id="@+id/tv
        android:layout_width="wrap_content
        android:layout_height="fill_parent
        android:textColor="FF0000
        android:textSize="16px" />
</LinearLayout>

接着在Activity中写了逻辑代码:

public class MainActivity extends Activity {
	// 当前进度条里的进度值
	private int progress = 0;
	private RemoteViews view = null;
	private Notification notification = new Notification();
	private NotificationManager manager = null;
	private Intent intent = null;
	private PendingIntent pIntent = null;// 更新显示
	private Handler handler = new Handler() {
		@Override
		public void handleMessage(Message msg) {
			view.setProgressBar(R.id.pb, 100, progress, false);
			view.setTextViewText(R.id.tv, "下载" + progress + "%");// 关键部分,如果你不重新更新通知,进度条是不会更新的
			notification.contentView = view;
			notification.contentIntent = pIntent;
			manager.notify(0, notification);
			super.handleMessage(msg);
		}
	};
	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.main);
		manager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
		view = new RemoteViews(getPackageName(), R.layout.custom_dialog);
		intent = new Intent(MainActivity.this, NotificationService.class);
		pIntent = PendingIntent.getService(MainActivity.this, 0, intent, 0);
		Button button = (Button) findViewById(R.id.bt);
		button.setOnClickListener(new Button.OnClickListener() {
			@Override
			public void onClick(View v) {
				// 通知的图标必须设置(其他属性为可选设置),否则通知无法显示
				notification.icon = R.drawable.icon;
				view.setImageViewResource(R.id.image, R.drawable.icon);// 起一个线程用来更新progress
				new Thread(new Runnable() {
					@Override
					public void run() {
						for (int i = 0; i < 20; i++) {
							progress = (i + 1) * 5;
							try {
								if (i < 19) {
									Thread.sleep(1000);
								} else {
									Thread.currentThread().interrupt();
								}
							} catch (InterruptedException e) {
								e.printStackTrace();
							}
							Message msg = new Message();
							handler.sendMessage(msg);
						}
					}
				}).start();
			}
		});
	}
}

运行效果如图所示:

通过线程改变界面button的内容

代码如下:

public class MyHandlerActivity extends Activity {
	Button button;
	MyHandler myHandler;
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.handlertest);
		button = (Button) findViewById(R.id.button);
		myHandler = new MyHandler();
		// 当创建一个新的Handler实例时, 它会绑定到当前线程和消息的队列中,开始分发数据
		// Handler有两个作用, (1) : 定时执行Message和Runnalbe 对象
		// (2): 让一个动作,在不同的线程中执行.
		// 它安排消息,用以下方法
		// post(Runnable)
		// postAtTime(Runnable,long)
		// postDelayed(Runnable,long)
		// sendEmptyMessage(int)
		// sendMessage(Message);
		// sendMessageAtTime(Message,long)
		// sendMessageDelayed(Message,long)
		// 以上方法以 post开头的允许你处理Runnable对象
		// sendMessage()允许你处理Message对象(Message里可以包含数据,)
		MyThread m = new MyThread();
		new Thread(m).start();
	}
	/**
	 * 接受消息,处理消息 ,此Handler会与当前主线程一块运行
	 * */
	class MyHandler extends Handler {
		public MyHandler() {
		}
		public MyHandler(Looper L) {
			super(L);
		}
		// 子类必须重写此方法,接受数据
		@Override
		public void handleMessage(Message msg) {
			// TODO Auto-generated method stub
			Log.d("MyHandler", "handleMessage......");
			super.handleMessage(msg);
			// 此处可以更新UI
			Bundle b = msg.getData();
			String color = b.getString("color");
			MyHandlerActivity.this.button.append(color);
		}
	}
	class MyThread implements Runnable {
		public void run() {
			try {
				Thread.sleep(10000);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			Log.d("thread.......", "mThread........");
			Message msg = new Message();
			Bundle b = new Bundle();// 存放数据
			b.putString("color", "我的");
			msg.setData(b);
			MyHandlerActivity.this.myHandler.sendMessage(msg); // 向Handler发送消息,更新UI
		}
	}
}

5秒钟自动更新Title

通过对Handler的学习,使我们理解了线程的运行,当我们使用多个线程的时候,会感觉很乱,如果我们应用Handler,我们可以更加清晰的去进行更新。下面是一个规定时间自动更新Title的示例:

public class HandlerDemo extends Activity {
	// title为setTitle方法提供变量,这里为了方便我设置成了int型
	private int title = 0;
	private Handler mHandler = new Handler() {
		public void handleMessage(Message msg) {
			switch (msg.what) {
			case 1:
				updateTitle();
				break;
			}
		};
	};
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.main);
		Timer timer = new Timer();
		timer.scheduleAtFixedRate(new MyTask(), 1, 5000);
	}
	private class MyTask extends TimerTask {
		@Override
		public void run() {
			Message message = new Message();
			message.what = 1;
			mHandler.sendMessage(message);
		}
	}
	public void updateTitle() {
		setTitle("Welcome to Mr Wei's blog " + title);
		title++;
	}
}

实例说完是否对Handler有了更多的了解,还可以深入源码了解:

Handler源码解析:android异步消息机制,源码层面彻底解析(一)

时间: 2024-12-25 20:02:40

Android 基础总结:( 十五)Handler详解(下)的相关文章

Android 中Message,MessageQueue,Looper,Handler详解+实例

一.几个关键概念 1.MessageQueue:是一种数据结构,见名知义,就是一个消息队列,存放消息的地方.每一个线程最多只可以拥有一个MessageQueue数据结构. 创建一个线程的时候,并不会自动创建其MessageQueue.通常使用一个Looper对象对该线程的MessageQueue进行管理.主线程创建时,会创建一 个默认的Looper对象,而Looper对象的创建,将自动创建一个Message Queue.其他非主线程,不会自动创建Looper,要需要的时候,通过调 用prepar

【Android基础】-Service组件使用详解

Service是Android四大组件之一,它与Activity的区别是:它一直在后台运行,没有前台界面.一旦Service被启动起来后,他就跟Activity一样,完全具有自己的生命周期. 一.创建Service,定义一个继承Service的子类 Service中定义了一系列生命周期方法,如下: IBinder onBind(Intent intent):该方法是Service必须实现的方法.该方法返回一个IBinder对象,应用程序可以通过该对象与Service组件通信. void onCr

Android之Message,MessageQueue,Looper,Handler详解(带示例)

一.几个关键概念 1.MessageQueue:是一种数据结构,见名知义,就是一个消息队列,存放消息的地方.每一个线程最多只可以拥有一个MessageQueue数据结构. 创建一个线程的时候,并不会自动创建其MessageQueue.通常使用一个Looper对象对该线程的MessageQueue进行管理.主线程创建时,会创建一 个默认的Looper对象,而Looper对象的创建,将自动创建一个Message Queue.其他非主线程,不会自动创建Looper,要需要的时候,通过调 用prepar

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

Handler的定义: 主要接受子线程发送的数据, 并用此数据配合主线程更新UI. 解释: 当应用程序启动时,Android首先会开启一个主线程 (也就是UI线程) , 主线程为管理界面中的UI控件,进行事件分发,比如说,你要是点击一个 Button ,Android会分发事件到Button上,来响应你的操作. 如果此时需要一个耗时的操作,例如:联网读取数据,或者读取本地较大的一个文件的时候,你不能把这些操作放在主线程中,如果你放在主线程中的话,界面会出现假死现象,如果5秒钟还没有完成的话,会收

Android基础之十四数据存储 之 SQLite数据库详解

Android基础之十四数据存储 之 SQLite数据库详解 SQLite 是一款 轻量级的关系型数据库,它的运算速度非常快,占用资源很少,通常只需要几百 K 的内存就足够了,因而特别适合在移动设备上使用. SQLite 不仅支持标准的 SQL 语法,还遵循了数据库的 ACID( 原子性(Atomicity) .一致性(Consistency) . 隔离性(Isolation) . 持久性(Durability))事务,所以只要你以前使用过其他的关系型数据库,就可以很快地上手 SQLite.而

android四大基础组件--Service生命周期详解

android四大基础组件--ServiceService 生命周期详解 1.Service的生命周期: I> 在非绑定Service情况下,只有oncreate(),onStartCommand(),onDestory()方法情况下:  操作方法对应生命周期一: a.[执行startService(Intent)] 执行生命周期方法:oncreate()--->onStartCommand(): b.[执行stopService(Intent)] 执行生命周期方法:onDestory();

[Android]Message,MessageQueue,Looper,Handler详解+实例

转http://www.eoeandroid.com/forum-viewthread-tid-49595-highlight-looper.html 一.几个关键概念 1.MessageQueue:是一种数据结构,见名知义,就是一个消息队列,存放消息的地方.每一个线程最多只可以拥有一个MessageQueue数据结构. 创建一个线程的时候,并不会自动创建其MessageQueue.通常使用一个Looper对象对该线程的MessageQueue进行管理.主线程创建时,会创建一 个默认的Loope

Android ViewGroup触摸屏事件派发机制详解与源码分析

PS一句:最终还是选择CSDN来整理发表这几年的知识点,该文章平行迁移到CSDN.因为CSDN也支持MarkDown语法了,牛逼啊! [工匠若水 http://blog.csdn.net/yanbober] 该篇承接上一篇<Android View触摸屏事件派发机制详解与源码分析>,阅读本篇之前建议先阅读. 1 背景 还记得前一篇<Android View触摸屏事件派发机制详解与源码分析>中关于透过源码继续进阶实例验证模块中存在的点击Button却触发了LinearLayout的事

Android 4.4 KitKat NotificationManagerService使用详解与原理分析(二)__原理分析

前置文章: <Android 4.4 KitKat NotificationManagerService使用详解与原理分析(一)__使用详解> 转载请务必注明出处:http://blog.csdn.net/yihongyuelan 概况 在上一篇文章<Android 4.4 KitKat NotificationManagerService使用详解与原理分析(一)__使用详解>中详细介绍了NotificationListenerService的使用方法,以及在使用过程中遇到的问题和