Android的消息机制,用Android线程间通信的Message机制,Android中Handler的用法——在子线程中更新界面,handler机制

Android的消息机制(一)

android 有一种叫消息队列的说法,这里我们能够这样理解:假如一个隧道就是一个消息队列,那么里面的每一部汽车就是一个一个消息,这里我们先忽略掉超车等种种因素,仅仅那么先进隧道的车将会先出,这个机制跟我们android 的消息机制是一样的。

一、 角色描写叙述

1.Looper:(相当于隧道) 一个线程能够产生一个Looper 对象,由它来管理此线程里的Message Queue( 车队,消息隧道)

2.Handler: 你能够构造Handler 对象来与Looper 沟通,以便push 新消息到Message Queue 里;或者接收Looper(Message Queue 取出) 所送来的消息。

3. Message Queue( 消息队列): 用来存放线程放入的消息。

4 .线程:UI thread 通常就是main thread ,而Android 启动程序时会替它建立一个Message Queue

每个线程里可含有一个Looper 对象以及一个MessageQueue 数据结构。在你的应用程序里,能够定义Handler 的子类别来接收Looper 所送出的消息。

在你的Android 程序里,新诞生一个线程,或运行 (Thread) 时,并不会自己主动建立其Message Loop

Android 里并没有GlobalMessage Queue 数据结构,比如,不同APK 里的对象不能透过Massage Queue 来交换讯息(Message)

比如:线程AHandler 对象能够传递消息给别的线程,让别的线程BC 等能送消息来给线程A( 存于AMessage Queue)

线程AMessage Queue 里的讯息,仅仅有线程A 所属的对象能够处理。

使用Looper.myLooper 能够取得当前线程的Looper 对象。

使用mHandler = new EevntHandler(Looper.myLooper()); 可用来构造当前线程的Handler 对象;当中,EevntHandler 是自已实现的Handler 的子类别。

使用mHandler = new EevntHandler(Looper.getMainLooper()); 可诞生用来处理main 线程的Handler 对象;当中,EevntHandler 是自已实现的Handler 的子类别。

这样描写叙述可能太抽像,以下举几个实际的样例来说明:

二、 举例

1. 同线程内不同组件间的消息传递

Looper 类用来管理特定线程内对象之间的消息交换(Message Exchange) 。你的应用程序能够产生很多个线程。而一个线程能够有很多个组件,这些组件之间经常须要互相交换讯息。假设有这样的须要,您能够替线程构造一个Looper 对象,来担任讯息交换的管理工作。Looper 对象会建立一个MessageQueue 数据结构来存放各对象传来的消息( 包含UI 事件或System 事件等) 。例如以下图:

每个线程里可含有一个Looper 对象以及一个MessageQueue 数据结构。在你的应用程序里,能够定义Handler 的子类别来接收Looper 所送出的消息。

同线程不同组件之间的消息传递:

public class Activity1 extends Activity implements OnClickListener{

Button button = null ;

TextView text = null ;

@Override

protected void onCreate(Bundle savedInstanceState) {

super .onCreate(savedInstanceState);

setContentView(R.layout.activity1 );

button = (Button)findViewById(R.id.btn );

button.setOnClickListener(this );

text = (TextView)findViewById(R.id.content );

}

public void onClick(View v) {

switch (v.getId()) {

case R.id.btn :

Looper looper = Looper.myLooper();//取得当前线程里的looper

MyHandler mHandler = new MyHandler(looper);//构造一个handler使之可与looper通信

//buton等组件能够由mHandler将消息传给looper,再放入messageQueue,同一时候mHandler也能够接受来自looper消息

mHandler.removeMessages(0);

String msgStr = "主线程不同组件通信:消息来自button";

Message m = mHandler.obtainMessage(1, 1, 1, msgStr);//构造要传递的消息

mHandler.sendMessage(m);//发送消息:系统会自己主动调用handleMessage方法来处理消息

break ;

}

}

private class MyHandler extends Handler{

public MyHandler(Looper looper){

super (looper);

}

@Override

public void handleMessage(Message msg) {//处理消息

text.setText(msg.obj.toString());

}

}

}

说明:

此程序启动时,当前线程( 即主线程, main thread) 已诞生了一个Looper 对象,而且有了一个MessageQueue 数据结构。

looper = Looper.myLooper ();

调用Looper 类别的静态myLooper() 函数,以取得眼下线程里的Looper 对象.

mHandler = new MyHandler (looper);

构造一个MyHandler 对象来与Looper 沟通。Activity 等对象能够藉由MyHandler 对象来将消息传给Looper ,然后放入MessageQueue 里;MyHandler 对象也扮演Listener 的角色,可接收Looper 对象所送来的消息。

Message m = mHandler.obtainMessage(1, 1, 1, obj);

先构造一个Message 对象,并将数据存入对象里。

mHandler.sendMessage(m);

就透过mHandler 对象而将消息m 传给Looper ,然后放入MessageQueue 里。

此时,Looper 对象看到MessageQueue 里有消息m ,就将它广播出去,mHandler 对象接到此讯息时,会呼叫其handleMessage() 函数来处理,于是输出"This my message!" 于画面上,

Android消息处理机制(二)

角色综述(回想):

(1)UI thread 通常就是main thread ,而Android 启动程序时会替它建立一个MessageQueue

(2) 当然须要一个Looper 对象,来管理该MessageQueue

(3) 我们能够构造Handler 对象来push 新消息到Message Queue 里;或者接收Looper(Message Queue 取出) 所送来的消息。

(4) 线程AHandler 对象能够传递给别的线程,让别的线程BC 等能送讯息来给线程A( 存于AMessage Queue)

(5) 线程AMessage Queue 里的消息,仅仅有线程A 所属的对象能够处理。

子线程传递消息给主线程

public class Activity2 extends Activity implements OnClickListener{

Button button = null ;

TextView text = null ;

MyHandler mHandler = null ;

Thread thread ;

@Override

protected void onCreate(Bundle savedInstanceState) {

super .onCreate(savedInstanceState);

setContentView(R.layout.activity1 );

button = (Button)findViewById(R.id.btn );

button.setOnClickListener(this );

text = (TextView)findViewById(R.id.content );

}

public void onClick(View v) {

switch (v.getId()) {

case R.id.btn :

thread = new MyThread();

thread.start();

break ;

}

}

private class MyHandler extends Handler{

public MyHandler(Looper looper){

super (looper);

}

@Override

public void handleMessage(Message msg) {//处理消息

text.setText(msg.obj.toString());

}

}

private class MyThread extends Thread{

@Override

public void run() {

Looper curLooper = Looper.myLooper();

Looper mainLooper = Looper.getMainLooper();

String msg ;

if (curLooper==null ){

mHandler = new MyHandler(mainLooper);

msg = "curLooper is null";

}else {

mHandler = new MyHandler(curLooper);

msg = "This is curLooper";

}

mHandler.removeMessages(0);

Message m = mHandler.obtainMessage(1, 1, 1, msg);

mHandler.sendMessage(m);

}

}

}

说明:

Android 会自己主动替主线程建立Message Queue 。在这个子线程里并没有建立Message Queue 。所以,myLooper 值为null ,而mainLooper 则指向主线程里的Looper 。于是,运行到:

mHandler = new MyHandler (mainLooper);

mHandler 属于主线程。

mHandler.sendMessage(m);

就将m 消息存入到主线程的Message Queue 里。mainLooper 看到Message Queue 里有讯息,就会作出处理,于是由主线程运行到mHandlerhandleMessage() 来处理消息。

用Android线程间通信的Message机制

在Android 以下也有多线程 的概念,在C/C++中,子线程能够是一个函数 ,一般都是一个带有循环的函数,来处理某些数据 ,优先线程仅仅是一个复杂的运算过程,所以可能不须要while循环,运算完毕,函数结束,线程就销毁。对于那些须要控制的线程,一般我们都是和相互排斥锁相互关联,从而来控制线程的进度,一般我们创建子线程,一种线程是非经常见的,那就是带有消息循环的线程。
消息循环是一个非常实用的线程方式,以前自己用C在Linux以下实现一个消息循环的机制 ,往消息队列里加入数据,然后异步的等待消息的返回。当消息队列为空的时候就会挂起线程,等待新的消息的加入。这是一个非常通用的机制。
在 Android,这里的线程分为有消息循环的线程和没有消息循环的线程,有消息循环的线程一般都会有一个Looper,这个事android的新概念。我 们的主线程(UI线程)就是一个消息循环的线程。针对这样的消息循环的机制,我们引入一个新的机制Handle,我们有消息循环,就要往消息循环里面发送相 应的消息,自己定义 消息一般都会有自己相应的处理,消息的发送和清除,消息的的处理,把这些都封装在Handle里面,注意Handle仅仅是针对那些有Looper的线程,无论是UI线程还是子线程,仅仅要你有Looper,我就能够往你的消息队列里面加入东西,并做相应的处理。
可是这里另一点,就是仅仅要是关于UI相关的东西,就不能放在子线程中,由于子线程是不能操作UI的,仅仅能进行数据、系统 等其它非UI的操作。
那么什么情况以下我们的子线程才干看做是一个有Looper的线程呢?我们怎样得到它Looper的句柄呢?
Looper.myLooper();获得当前的Looper
Looper.getMainLooper () 获得UI线程的Lopper
我们看看Handle的初始化函数,假设没有參数,那么他就默认使用的是当前的Looper,假设有Looper參数,就是用相应的线程的Looper。
如 果一个线程中调用Looper.prepare(),那么系统就会自己主动的为该线程建立一个消息队列,然后调用 Looper.loop();之后就进入了消息循环,这个之后就能够发消息、取消息、和处理消息。这个怎样发送消息和怎样处理消息能够再其它的线程中通过 Handle来做,但前提是我们的Hanle知道这个子线程的Looper,可是你假设不是在子线程执行 Looper.myLooper(),通常是得不到子线程的looper的。
public void run() {
synchronized (mLock) {
Looper.prepare();
//do something
}
Looper.loop();
}
所以非常多人都是这样做的:我直接在子线程中新建handle,然后在子线程中发送消息,这种话就失去了我们多线程的意义了。
class myThread extends Thread{
private EHandler mHandler ;
public void run() {
Looper myLooper, mainLooper;
myLooper = Looper.myLooper ();
mainLooper = Looper.getMainLooper ();
String obj;
if (myLooper == null ){
mHandler = new EHandler(mainLooper);
obj = "current thread has no looper!" ;
}
else {
mHandler = new EHandler(myLooper);
obj = "This is from current thread." ;
}
mHandler .removeMessages(0);
Message m = mHandler .obtainMessage(1, 1, 1, obj);
mHandler .sendMessage(m);
}
}
能够让其它的线程来控制我们的handle,能够把 private EHandler mHandler ;放在外面,这样我们的发消息和处理消息都能够在外面来定义,这样添加程序 代码 的美观,结构更加清晰。
对如不论什么的Handle,里面必需要重载一个函数
public void handleMessage(Message msg)
这个函数就是我们的消息处理,怎样处理,这里全然取决于你,然后通过 obtainMessage和 sendMessage等来生成和发送消息, removeMessages(0)来清除消息队列。Google 真是太智慧了,这样的框架 的产生,我们写代码更加轻松了。
有的时候,我们的子线程想去改变UI了,这个时候千万不要再子线程中去改动,获得UI线程的Looper,然后发送消息就可以。
我们看看Goole Music App的源码 。
在MediaPlaybackActivity.java 中,我们能够看一下再OnCreate中的有这种两句:
mAlbumArtWorker = new Worker("album art worker");
mAlbumArtHandler = new AlbumArtHandler(mAlbumArtWorker.getLooper());
非常 明显这两句,是构建了一个子线程。而且这个子线程还是Looper的子线程,这里非常牛逼的使用了 mAlbumArtWorker.getLooper()这个函数,由于我们知道,我们可以得到子线程的Looper的途径仅仅有一个:就是在子线程中调用 Looper.myLooper (),而且这个函数还要在我们perpare之后调用才干得到正确的Looper,可是他这里用了一个这种什么东东 getLooper,不知道它是怎样实现的?
这里有一个大概的思路,我们在子线程的的prepare之后调用 myLooper ()这种方法,然后保存在一个成员变量中,这个getLooper就返回这个东西,可是这里会碰到多线程的一个非常突出的问题,同步。我们在父线程中调用 mAlbumArtWorker.getLooper(),可是想要这个返回正确的looper就必需要求我们的子线程执行了prepare,可是这个东 西实在子线程执行的,我们怎样保证呢?
我们看Google是怎样实现的?
private class Worker implements Runnable {
private final Object mLock = new Object();
private Looper mLooper;

/**
* Creates a worker thread with the given name. The thread
* then runs a [email=%7B@link]{@link [/email] android.os.Looper}.
* @param name A name for the new thread
*/
Worker(String name) {
Thread t = new Thread(null, this, name);
t.setPriority(Thread.MIN_PRIORITY);
t.start();
synchronized (mLock) {
while (mLooper == null) {
try {
mLock.wait();
} catch (InterruptedException ex) {
}
}
}
}

public Looper getLooper() {
return mLooper;
}

public void run() {
synchronized (mLock) {
Looper.prepare();
mLooper = Looper.myLooper();
mLock.notifyAll();
}
Looper.loop();
}

public void quit() {
mLooper.quit();
}
}
我 们知道,一个线程类的构造函数是在主线程中完毕的,所以在我们的 Worker的构造函数中我们创佳一个线程,然后让这个线程执行,这一这个线程的创建是指定一个 Runnabl,这里就是我们的Worker本身,在主线程调用 t.start();,这后,我们子线程已经创建,而且開始执行work的run方法。然后以下的代码非常艺术:
synchronized (mLock) {
while (mLooper == null) {
try {
mLock.wait();
} catch (InterruptedException ex) {
}
}
}
我们開始等待我们的子线程给mLooper赋值,假设不赋值我们就继续等,然后我们的子线程在执行run方法之后,在给 mLooper赋值之后,通知worker够着函数中的wait,然后我们的构造函数才干完毕,所以我们说:
mAlbumArtWorker = new Worker("album art worker");
这句本身就是堵塞的,它创建了一个子线程,开启了子线程,而且等待子线程给mLooper赋值,赋值完毕之后,这个函数才返回,这样才干保证我们的子线程的Looper的获取 绝对是正确的,这个构思非常有创意。值得借鉴

Android中Handler的用法——在子线程中更新界面

本文主要介绍Android的Handler的用法。Handler能够发送Messsage和Runnable对象到与其相关联的线程的消息队列。每一个Handler对象与创建它的线程相关联,而且每一个Handler对象仅仅能与一个线程相关联。

1. Handler一般有两种用途:1)运行计划任务,你能够再预定的实现运行某些任务,能够模拟定时器。2)线程间通信。在Android的应用启动时,会 创建一个主线程,主线程会创建一个消息队列来处理各种消息。当你创建子线程时,你能够再你的子线程中拿到父线程中创建的Handler对象,就能够通过该 对象向父线程的消息队列发送消息了。因为Android要求在UI线程中更新界面,因此,能够通过该方法在其他线程中更新界面。
◆ 通过Runnable在子线程中更新界面的样例

1.○ 在onCreate中创建Handler
public class HandlerTestApp extends Activity {
Handler mHandler;
TextView mText;
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
mHandler = new Handler();//创建Handler
mText = (TextView) findViewById(R.id.text0);//一个TextView

}
○ 构建Runnable对象,在runnable中更新界面,此处,我们改动了TextView的文字.此处须要说明的是,Runnable对象能够再主线程中创建,也能够再子线程中创建。我们此处是在子线程中创建的。
Runnable mRunnable0 = new Runnable()
{
@Override
public void run() {
mText.setText("This is Update from ohter thread, Mouse DOWN");
}
};
? ○ 创建子线程,在线程的run函数中,我们向主线程的消息队列发送了一个runnable来更新界面。

private void updateUIByRunnable(){
new Thread()
{
//Message msg = mHandler.obtainMessage();
public void run()
{

//mText.setText("This is Update from ohter thread, Mouse DOWN");//这句将抛出异常
mHandler.post(mRunnable0);
}

}.start();

}

◆ 用Message在子线程中来更新界面

1. 用Message更新界面与Runnable更新界面类似,仅仅是须要改动几个地方。
○ 实现自己的Handler,对消息进行处理

private class MyHandler extends Handler
{

@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch(msg.what)
{
case UPDATE ://在收到消息时,对界面进行更新
mText.setText("This update by message");
break;
}
}
}

○ 在新的线程中发送消息
private void updateByMessage()
{
//匿名对象
new Thread()
{
public void run()
{
//mText.setText("This is Update from ohter thread, Mouse DOWN");

//UPDATE是一个自定义的整数,代表了消息ID
Message msg = mHandler.obtainMessage(UPDATE);
mHandler.sendMessage(msg);

}
}.start();
}

AsyncTask与handler

AsyncTask实际上就是一个线程池,AsyncTask在代码上比handler要轻量级别,而实际上要比handler更耗资源,由于AsyncTask底层是一个线程池!而Handler只就是发送了一个消息队列,连线程都没有开。
可是,假设异步任务的数据特别庞大,AsyncTask这样的线程池结构的优势就体现出来了


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仅仅能被运行一次,否则多次调用时将会出现异常

?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 <span style= "font-size: 14px;" > 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); } } </span>

更多相关文章

  1. 【Android自助餐】Handler消息机制完全解析(二)MessageQueue的队列
  2. android launcher 分析
  3. Android数据推送实现方案
  4. Android消息通信之无所不能的第三方开源项目EventBus
  5. Android(安卓)Handler 用法
  6. android XMl 解析神奇xstream 四: 将复杂的xml文件解析为对象
  7. Android应用程序与SurfaceFlinger服务的连接过程分析
  8. android:gravity与android:layout_gravity
  9. Android应用程序键盘(Keyboard)消息处理机制分析

随机推荐

  1. android 动态添加组件(RadioGroup 添加Rad
  2. Android事件机制(三)
  3. Unity5.0与Android交互
  4. android donut 飞行模式分析
  5. Android(安卓)ViewFlipper触摸动画
  6. Android架构组件(Architecture Component
  7. Android Studio 中org.apache.http.legac
  8. IPC——android进程间通信
  9. Android应用最上层悬浮窗实现不依赖于Act
  10. Win7 64位 Android SDK下载和更新失败的