我们先来看一些术语:
1.Parcel:其实就是一个容器,我们来看官方描述:Container for a message (data and object references) that can be sent through an IBinder。很简单,就是一个消息的集合,而这样的集合是可以通过IBinder接口发送的。
2.Message:Defines a message containing a description and arbitrary data object that can be sent to aHandler
. :消息就是一种包含描述和任意数据而且能够发送给Handler的数据结构。
3.Handler:A Handler allows you to send and process Message
and Runnable objects associated with a thread'sMessageQueue
. Each Handler instance is associated with a single thread and that thread's message queue.:Handler 允许你发送以及处理与某个线程的消息队列关联的消息和Runnable对象。每个Handler实例是和单个线程以及它的消息队列是关联起来的。
4.Looper:Class used to run a message loop for a thread. Threads by default do not have a message loop associated with them; to create one, callprepare()
in the thread that is to run the loop, and thenloop()
to have it process messages until the loop is stopped.
Most interaction with a message loop is through the Handler
class.:一个类,什么样的类呢?用于运行某个线程的消息循环的类。通常情况下,一个线程是不含一个与自身关联的消息loop的。如果new一个thread,需要在你的线程中调用prepare()函数来启动loop,然后loop()在终止之前会自己处理消息。对于一般的主线程来说,会自动创建一个mainloop与主线程绑定。可以使用getMainLooper()来获取主线程的loop函数。
5.MessageQueue:Low-level class holding the list of messages to be dispatched by aLooper
. Messages are not added directly to a MessageQueue, but rather throughMessageQueue.IdleHandler
objects associated with the Looper.You can retrieve the MessageQueue for the current thread withLooper.myQueue()
.:也是一个类,什么样的类呢?保存了N个用于被Looper分发的Message的类。因为我们的Message不是直接add到MessageQueue里面的,而是通过一个MessageQueue.IdleHandler
的对象来和Looper关联起来的,我们可以通过Looper.myQueue()
来获取到当前线程的消息队列。
好了,上面的术语基本上都是Android消息机制相关的术语,也做了一些简单翻译,讨厌看的童鞋可以看看英文原始注释。俺们一起来理下思路:
· N个消息放一起,堆在一个容器里面,该容器里面的东东而且能够通过IBinder形式发送,这就形成了Parcel。(Parcel的用法暂时不单独提了,后面相关技术文档会涉及到了就说,这个在Android Framework里面用的比较多。)
·通常我们的Message不是发一条就处理一条的,它首先是放在消息队列里面的进行逐条处理,怎么放到消息队列里面的呢?不是直接的add进去,而是和Looper关联的一个MessageQueue.IdleHandler对象来add进去的,这里就看到了,looper起到了Message和MessageQueue的一个桥梁作用(记住:消息队列中的消息也是由Looper给整出去的。),这个还不是最主要的。looper最主要的桥梁作用是Message和Handler,Looper它可以分发消息,分发的消息是给Hander进行发送和处理的。Hanlder通过looper从MessageQueue里面获取Message进行处理。它也可以get 消息,然后add到消息队列里面去。
6.AsyncTask :这是个异步任务机制,也是来捣鼓线程间通信的,该机制将相应操作在线程池中取出一个线程来执行,然后将结果传递给主线程,主线程拿到该结果后即可以进行更新界面等相应操作了。
AsyncTask的使用需要我们去创建一个继承自AsyncTask的类AsyncTask<Params
, Progress
, Result
>,
参数说明如下:
Params
, the type of the parameters sent to the task upon execution.:发送给异步任务去执行的参数类型。Progress
, the type of the progress units published during the background computation.:在后台执行程序的时候返回给调用线程进度的参数类型。Result
, the type of the result of the background computation.:后台执行程序计算好的结果类型。
doInBackground(String... params) :后台执行程序,参数params是由调用线程传递过来。然后将返回的结果送给onPostExecute, onPostExecute可以依据放回的结果来更新UI的操作。在子线程中操作。
onCancelled():可以在处理的过程中取消异步线程处理。如果执行已经结束的话该方式执行失败。在主线程中操作。
onPostExecute(String result):从doInBackground获取执行后的结果,然后可以对UI的主界面进行更新操作。该操作的执行是在主线程中。
onPreExecute() :执行子线程前进行的一系列的准备工作。该操作在主线程中操作。
onProgressUpdate(String... values):主线程中用于显示进度的函数,该函数由doInBackground()调用publishProgress()函数将结果发送过来。在主线程中操作。
以上几个函数都是回调函数,系统会自己调用,切忌不要手动去调用它。
通常情况下我们使用AsyncTask的情况会比较多,因为它比handle机制简单,而且使用起来也比较方便,在后面的例子中将会将两种情况一起使用。
实例:
介绍,该例子主要是利用Handle的消息机制实现一个音乐播放器;用AsyncTask形式实现点击按钮就更改UI界面的文字信息。两种方式都是多线程的通信方式。具体注释贴代码如下:
HandleMessageActivity.java
import java.security.PublicKey;
import android.app.Activity;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;
import android.os.Looper;
import android.content.Intent;
public class HandleMessageActivity extends Activity {
/** Called when the activity is first created. */
private Button mBtnPlayMusic = null;
private Button mBtnStopMusic = null;
private Button mBtnChangeWelInfo = null;
private TextView mTextShowMusicName = null;
private TextView mTextWelcomeInfo = null;
Looper loop = Looper.myLooper();//获取主线程的looper
MyHandle myHandle = new MyHandle();//new一个Handle,来处理主线程的消息信息
private String mArgs = null;//这个用于AsyncTask中的参数传递。
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
mBtnPlayMusic = (Button)findViewById(R.id.play_music);
mBtnStopMusic = (Button)findViewById(R.id.stop_music);
mBtnChangeWelInfo = (Button)findViewById(R.id.changeText);
mTextShowMusicName = (TextView)findViewById(R.id.music_name);
mTextWelcomeInfo = (TextView)findViewById(R.id.welcomeinfo);
/**
* 实现播放音乐的功能
*/
mBtnPlayMusic.setOnClickListener(new Button.OnClickListener(){
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
new Thread(){
public void run(){
//实现细节
System.out.println(" star music:Current Thread ID is:"+Thread.currentThread().getId());
System.out.println("star music Current Thread Name is:"+Thread.currentThread().getName());
//获取包内的资源:取得music的全路径名称
String musicName = getResources().getString(R.raw.dongni);
System.out.println("the full of music name and path is:"+musicName);// res/raw/dongni.mp3
System.out.println("the full of resource name and path is:"+getResources().getResourceName(R.raw.dongni));//这里返回的包含了包得路径
//获取路径,不需要后缀名
int b = musicName.lastIndexOf("/");
int e = musicName.lastIndexOf(".");
String musicTrueName = musicName.substring(b+1, e);
/**
* 定义消息,并和Handle关联起来,这里注意的是,该消息只会和所定义的Handle关联起来,
* 而我们这里的Handle是在UI主线程里面创建的,所以此处的消息是会发送给UI的消息队列的。
*/
Message message = myHandle.obtainMessage();
message.arg1 = 0;//用于区分在哪个线程里面发过来的Handle消息。
message.obj = musicTrueName;//所发送的对象
message.sendToTarget();//将消息发送给UI线程。
Intent intent = new Intent(HandleMessageActivity.this,MusicServer.class);
startService(intent);//启动service
}
}.start();
}
});
/**
* 实现停止音乐播放的功能
*/
mBtnStopMusic.setOnClickListener(new Button.OnClickListener(){
public void onClick(View v){
new Thread() {
@Override
public void run() {
// TODO Auto-generated method stub
System.out.println("stop music Current Thread ID is:"+Thread.currentThread().getId());
System.out.println("stop music Current Thread Name is:"+Thread.currentThread().getName());
Message stopMsg = myHandle.obtainMessage();//获取消息对象,表示要发送给myHandle的消息
stopMsg.arg1 = 1;//给消息赋值
stopMsg.sendToTarget();//发给myHandle
Intent intent = new Intent(HandleMessageActivity.this,MusicServer.class);
stopService(intent);//停止service
}
}.start();
}
});
/**
* 改变欢迎信息,这里使用AsynTask的方式来执行
*/
mBtnChangeWelInfo.setOnClickListener(new Button.OnClickListener(){
public void onClick(View v){
System.out.println("in mBtnChangeWelInfo ,Current Thread ID is:"+Thread.currentThread().getId());
System.out.println("in mBtnChangeWelInfo ,Current Thread Name is:"+Thread.currentThread().getName());
mArgs = "100";
new AsynTask().execute(mArgs);
/*
new Runnable() {
@Override
public void run() {
System.out.println("Current Thread ID is:"+Thread.currentThread().getId());
System.out.println("Current Thread Name is:"+Thread.currentThread().getName());
Message changeTextMsg = myHandle.obtainMessage();
changeTextMsg.arg1 = 20;
changeTextMsg.obj = "改变欢迎信息";
changeTextMsg.sendToTarget();
}
}.run();
*/
}
});
}
/**
*
* @author bluesky
* 定义主线程的处理程序,这里我们复写handleMessage来处理发送给UI线程的消息。
*/
class MyHandle extends Handler{
public MyHandle(){
}
public MyHandle(Looper loop){
super(loop);
}
@Override
public void dispatchMessage(Message msg) {
// TODO Auto-generated method stub
super.dispatchMessage(msg);
}
@Override
public void handleMessage(Message msg) {
// TODO Auto-generated method stub
super.handleMessage(msg);
switch(msg.arg1){
case 0:
CharSequence getMusicName = (CharSequence)msg.obj;
mTextShowMusicName.setText(getMusicName);
break;
case 1:
CharSequence change = "no music played";
mTextShowMusicName.setText(change);
break;
// case 20:
//mTextWelcomeInfo.setText(msg.obj.toString());
//break;
default:
break;
}
}
@Override
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
// TODO Auto-generated method stub
return super.sendMessageAtTime(msg, uptimeMillis);
}
@Override
public String toString() {
// TODO Auto-generated method stub
return super.toString();
}
}
/**
*
* @author bluesky
* 异步消息处理类,用于处理异步操作。
*/
class AsynTask extends AsyncTask<String, String, String>{
/**
* 后台进行一些处理操作,然后将返回的结果送给onPostExecute,
* onPostExecute可以依据放回的结果来更新UI的操作。
* 在线程池中操作
*/
@Override
protected String doInBackground(String... params) {
// TODO Auto-generated method stub
System.out.println("do InBackground,Current Thread ID is:"+Thread.currentThread().getId());
System.out.println("do InBackground,Current Thread Name is:"+Thread.currentThread().getName());
String getParam = null;
getParam = params[0];
String progress = "测试更新进度";
publishProgress(progress);
return getParam;
}
/**
* 可以再处理的过程中取消异步线程处理。
*/
@Override
protected void onCancelled() {
// TODO Auto-generated method stub
System.out.println("do onCancelled,Current Thread ID is:"+Thread.currentThread().getId());
System.out.println("do onCancelled,Current Thread Name is:"+Thread.currentThread().getName());
super.onCancelled();
}
/**
* 从doInBackground获取执行后的结果,然后可以对UI的主界面进行更新操作。
* 在主线程中操作
*/
@Override
protected void onPostExecute(String result) {
// TODO Auto-generated method stub
System.out.println("do onPostExecute,Current Thread ID is:"+Thread.currentThread().getId());
System.out.println("do onPostExecute,Current Thread Name is:"+Thread.currentThread().getName());
if(result == "100"){
mTextWelcomeInfo.setText("AsynTask方式改变UI显示!");
}
}
/**
* 在主线程中进行一系列的准备工作。
*
*/
@Override
protected void onPreExecute() {
// TODO Auto-generated method stub
System.out.println("do onPreExecute,Current Thread ID is:"+Thread.currentThread().getId());
System.out.println("do onPreExecute,Current Thread Name is:"+Thread.currentThread().getName());
Toast.makeText(HandleMessageActivity.this, "开始使用AsynTask来进行进程中通信", Toast.LENGTH_LONG).show();
super.onPreExecute();
}
/**
* 主线程中用于显示进度的函数,该函数由doInBackground()调用publishProgress()函数将结果发送过来。
*/
@Override
protected void onProgressUpdate(String... values) {
// TODO Auto-generated method stub
String getProgress = values[0];
System.out.println("do onProgressUpdate,Current Thread ID is:"+Thread.currentThread().getId());
System.out.println("do onProgressUpdate,Current Thread Name is:"+Thread.currentThread().getName());
System.out.println("do onProgressUpdate,Current value get from doInBackGround is:"+getProgress);
}
}
}
2.MusicService.java :
package com.android.huawei.handlemessage;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.media.MediaPlayer;
public class MusicServer extends Service {
private MediaPlayer mPlayer ;
@Override
public IBinder onBind(Intent arg0) {
// TODO Auto-generated method stub
return null;
}
@Override
public void onCreate() {
// TODO Auto-generated method stub
super.onCreate();
mPlayer = new MediaPlayer();
}
@Override
public void onDestroy() {
// TODO Auto-generated method stub
super.onDestroy();
mPlayer.stop();//停止播放
}
@Override
public void onStart(Intent intent, int startId) {
// TODO Auto-generated method stub
super.onStart(intent, startId);
//获取媒体文件
mPlayer = MediaPlayer.create(MusicServer.this, R.raw.dongni);
mPlayer.start();//播放媒体文件
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
// TODO Auto-generated method stub,Android2.2之后推荐用这个,而不用onStart方式
return super.onStartCommand(intent, flags, startId);
}
}
注意这里涉及到得R.raw.dongni文件是我在res目录下建立了个raw文件夹,然后放了个dongni.mp3文件。
3.manifest文件:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.huawei.handlemessage"
android:versionCode="1"
android:versionName="1.0">
<uses-sdk android:minSdkVersion="8" />
<application android:icon="@drawable/icon" android:label="@string/app_name">
<activity android:name=".HandleMessageActivity"
android:label="@string/app_name">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<service android:name=".MusicServer"></service>
</application>
</manifest>
main.xml文件
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<TextView
android:id="@+id/welcomeinfo"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="文字改变前的信息"
/>
<Button
android:id="@+id/play_music"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="播放音乐"
/>
<Button
android:id="@+id/stop_music"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="停止音乐"
/>
<TextView
android:id="@+id/music_name"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:textColor="#FF0000"
android:text="显示音乐"
/>
<Button
android:id="@+id/changeText"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="改变欢迎信息"
/>
</LinearLayout>
运行界面:
点击播放音乐后即可看到“显示音乐”变成了“dongni”
点击“改变欢迎信息”后可以看到最上面的的文字变成了:
AsynTask方式改变UI显示!
当我们点击这个按钮时候我们看看抓取的log:
可以看到点击按钮在主线程中。
onPreExecute操作、onProgressUpdate操作、以及onPostExecute操作都是在主线程中操作的。
唯独InBackgroud操作是在子线程中执行的,当你多次点击的时候会发现线程ID每次不一样,当经历过几次以后ID会有重复,这个主要是依据线程池的能分配的数量和一执行线程是否回收的情况来决定的。
以上写的比较零散,这块其实是不太容易懂的,有问题或者写的不对的地方欢迎交流。