近来,小编在学习了Android开发中的多线程之后,自己又去了解了一下关于Android中消息机制,感觉Android的消息机制特别有趣,故在这里和大家分享一下
首先我们先从最基本的开始:如何在Android中实现多线程?
在讲述这个之前,我们可以先来看看Java中是如何实现多线程的:
Java实现多线程可以通过两种方式:1.通过Thread类 2.通过Runnable接口
我们可以看看这里两个的源码:
可以看到,其实Thread类也是继承了Runnable接口,从而实现多线程。
而这里也给我们讲述了显示多线程的两种方式。
那么,我们在Android中实现多线程的方式,其实和java大致相同,那么先来用Java中的方式来试试多线程。假设我们现在有个场景:在界面上实现计数器的功能,如
那么按照java中的方法,我们需要先重写Thread类中的run方法,然后再主线程中实例化这个Thread子类,再调用其start方法。当然,在Android中,我们是MainActivity中调用start方法。以下便是代码:
1.activity_main.xml文件
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context=".MainActivity" >
<TextView
android:id="@+id/countername"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true"
android:layout_marginLeft="58dp"
android:layout_marginTop="100dp"
android:text="计数器" />
<TextView
android:id="@+id/showCounter"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignBottom="@+id/countername"
android:layout_alignParentRight="true"
android:layout_marginRight="70dp"
android:text="0" />
</RelativeLayout>
其中,我们在界面上放置里两个TextView 组件,其中showCounter为我们要选择的用来计数的组件,效果如图
2.runThread类(继承了Thread类)
package com.example.b;
import android.widget.TextView;
public class runThread extends Thread{
//showCounter组件
TextView showCounter;
public runThread(TextView showCounter) {
super();
this.showCounter = showCounter;
}
//重写run方法,实现每一秒,showCounter上的数字增加1,直到99
public void run(){
int i = 0;
while(i<100){
showCounter.setText(""+i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
3.MainActivity:
package com.example.b;
import android.os.Bundle;
import android.app.Activity;
import android.view.Menu;
import android.widget.TextView;
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//找出showCounter组件
TextView showCounter = (TextView)this.findViewById(R.id.showCounter);
//创建线程
runThread run = new runThread(showCounter);
run.start();
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
}
现在我们来运行一下这段代码,我们会发现,程序会报错,错误则是
即,说明只有主线程才能去更新UI界面,其余的子线程都不可操作UI界面。
这其实是Android与java不同的一点,在Android中,我们称主线程为UI线程,而对界面的更新操作只能由UI线程来做,大家可以这样想象:假设我们把界面看成一家商店,而UI线程看成这家商店的店面员工。每当用户有要求,需要购买东西时,员工总要第一时间内处理,同时又不能打乱商品顺序,否则就会乱套,那唯一最好的方式,就是由一个员工来处理用户所提的所有要求,这个员工就是UI线程。如若,请了多名员工(多个子线程)来同时处理事务,结果可能就是整个商店就是乱的。
但是,仅仅靠UI线程一个员工来打理整个商店,肯定是处理不过来的。所以我们势必需要多个子线程来分工,但是不能乱套,那么我们可以想到,由UI线程来调控所有要处理的事物,然后将这些事物分给多个员工(多个子线程),由他们具体处理这些任务,处理完之后,将处理的结果告知UI线程,由UI线程来把最终的商品给予用户。
事实上,Android就是这样处理的。而处理的方法就采用的Handler机制。
在Android中,主线程永远处于无线循环的过程,这样做是为了保证界面始终存在。如同最初学C语言时,为了让黑框框里面有一个菜单界面,我们就采用了while循环,以保证菜单界面的存在。那么Android中也一样。
但是Android不仅要展示界面,同时还要处理用户在界面上产生的各种消息,例如用户点击了某个组件等,故需要去处理这样的消息,那么,也就需要一个消息队列MessageQueue,存储所有消息;一个轮询器Looper来不断检查MessageQueue中是否有消息,同时将消息分配。
首先,我们来看一下,什么是Message?
可以看到,Message充当了一个容器,这个容器装载了事物所有处理的内容。
而Message中本身包含了
public int what;
public int arg1;
public int arg2;
public Object obj;
4个属性,其中obj是用于装载我们所需要的对象,what则是用于用户去记录这是哪个子线程处理的。
光有容器还不够,我们还需要知道怎么处理容器里面的信息,这时候就需要Handler了。
在Handler中,我们有两个很重要的方法
一个是 public void handleMessage(Message msg) { },这个方法用于告知如何处理msg中的信息
另一个就是 public final boolean sendMessage(Message msg){return sendMessageDelayed(msg, 0); },这个方法用于向MessageQueue发送Handler所要处理的消息。
而在Message中,有个target属性,这个属性是用来存储Handler对象的。
那么Android的整个处理方式也就清楚了。
总结一下:
首先,由子线程来写好Message中的信息内容,并同时,通过handler来向MessageQueue中发送这个Message。
之后,Looper在MessageQueen中不断轮询,当轮询到我们所发的Message时,则将其出队,并且通过target来找到是由哪个handler所发,由Looper调用handler的handleMessage()方法来处理Message中的信息内容。
以下便是代码部分:
1.activity_main.xml文件与之前相同,不做改变
2.Handler类
package com.example.painter;
import android.os.Handler;
import android.os.Message;
import android.widget.TextView;
public class handler extends Handler{
TextView showCounter;
//处理消息
public void handleMessage(Message msg) {
String text = (String)msg.obj;
showCounter.setText(text);
}
public handler(TextView showCounter) {
super();
this.showCounter = showCounter;
}
}
3.线程类
package com.example.painter;
import android.os.Message;
import android.util.Log;
import android.widget.TextView;
public class runthread extends Thread{
handler handle;
public runthread(handler handle) {
super();
this.handle = handle;
}
public void run(){
int i = 0;
while(i<100){
//获得消息,并写好message中内容,这里采用了回收机制
Message msg = handle.obtainMessage();
msg.obj = ""+(i++);
//Log.i("run", ""+i);
//发送消息
handle.sendMessage(msg);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
4.MainActivity:
package com.example.painter;
import android.os.Bundle;
import android.os.Message;
import android.app.Activity;
import android.view.Menu;
import android.widget.ImageView;
import android.widget.TextView;
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_draw);
//找出组件
TextView showCounter = (TextView)this.findViewById(R.id.showCounter);
//创建handler类
handler handle = new handler(showCounter);
//创建线程类
runthread r = new runthread(handle);
r.start();
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.draw, menu);
return true;
}
}
这样就在Android中实现对界面更改的多线程了。
大家是不是会感到特别美妙呢,Android通过Handler实现了子线程对UI组件操控,同时又没有影响到UI线程,做到了“一石二鸟”,当然了,这里也只是简单介绍了一下