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异步消息机制,源码层面彻底解析(一)