您的位置:首页 > 移动开发 > Android开发

线程与消息机制

2013-12-15 00:00 239 查看
摘要: 一、线程
二、Handler
三、通过Handler实现线程间的通信

一、线程

1.JAVA中的线程

1.1线程的概念

线程是程序运行的基本执行单元。当操作系统在执行一个程序时,会在系统中建立一个进程,而在这个进程中,必须至少建立一个线程(这个线程被称为主线程)来作为这个程序运行的入口点。因此,在操作系统中运行的任何程序都至少有一个主线程。

1.2实现线程的方法

在Java中建立线程有两种方法,一种是继承Thread类,另一种是实现Runnable接口,其实这两种方法从本质上说是一种方法,即都是通过Thread类来建立线程,并运行run方法的。

1.3线程的生命周期

线程也同样要经历开始(等待)、运行、挂起和停止四种不同的状态。这四种状态都可以通过Thread类中的方法进行控制。

2.MainThread与WorkerThread

MainThread是主线程,又叫UI线程,WorkerThread是除了主线程之外的其他线程。

原则上来讲,worder thread里不允许操作UI。只有主线程main thread可以。

我们新建一个项目,在MainActivity里设置一个Button,点击它时启动一个线程。

public class MainActivity extends Activity {

private TextView textView;

private Button button;

@Override

protected voidonCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

textView = (TextView) findViewById(R.id.textView);

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

button.setOnClickListener(new listener());

}

class listener implementsOnClickListener{

@Override

public void onClick(View v) {

Thread t = new thread();

t.start();

}

}

//worker thread

class thread extends Thread{

public void run(){

try {

Thread.sleep(1*1000);//阻塞当前线程指定的时间

} catch(InterruptedException e) {

e.printStackTrace();

}

textView.setText("来自于线程的修改");

}

}

}

上面是已经写好的代码,我们在监听器里实例化了一个自定义的Thread,调用了它的start方法。

我们在thread类的run方法里修改了textView的文本,即操作了UI。

这个项目在运行后,点击按钮,出现了如下错误:



这就是因为在worder thread里操作了UI。但是有一些特殊的控件是允许的,比如progressBar。

但如果把所有事情放在主线程里做,非常容易出现AR错误,就是“应用程序没有响应”。

比如,我们把监听器里的代码改成

class listener implementsOnClickListener{

@Override

public void onClick(View v) {

try {

Thread.sleep(10*1000);//让主线程休眠十秒

} catch(InterruptedException e) {

e.printStackTrace();

}

}

}

我们点击按钮,它接近于死机状态,过个几秒,会出现如下画面:



这是因为主线程阻塞了。

结论:

main thread与worder thread各有所职。

主线程主要用于接收用户的输入,以及将运算结果反馈给用户。主线程不允许阻塞,不要进行耗时较长的操作,如访问网络,大量读写等。这些操作必须放在worder thread中。

main thread负责UI,worder thread负责运算。

提问:

那么,worder thread中返回的信息如何影响UI呢?如何与main thread通信呢?这需要用到handler。

二、Handler

1.运行基本原理

Handler、Looper和MessageQueue三者构成了android的消息机制。

看下图:



MessageQueue是一种数据结构,符合先进先出的原则,类似一个隧道。

MessageQueue可以存放多个消息对象。

Handler负责将消息对象加入到MessageQueue。

Looper是一个循环器,不停的从MessageQueue中取出消息对象。

如果MessageQueue中没有消息对象,Looper会处于等待状态。

Looper将消息对象从MessageQueue中取出,再交给Handler处理。

2.一个简单的例子

我们建立一个简单的handler例子:

新建一个项目,mainActivity里加一个button,设置监听器。

接下来,我们定义一个handler,它需要继承Handler。

class handler extends Handler{

}

注意:

在引入包的时候,要选择android.os.Handler。



然后,我们要复写一个方法,是handleMessage方法。

菜单栏Source->Override/Implement Methods



然后我们声明一handler对象

private Handler handler;

在oncreate里给它赋值

handler = new handler();

当用户点击按钮时,我们创建一个消息对象,并使用handler发送该对象。监听器里代码如下:

class listener implementsOnClickListener{

@Override

public void onClick(View v) {

Message msg = handler.obtainMessage();//获取一个消息对象

msg.what = 2;//给message的一个属性赋值

handler.sendMessage(msg);//用handler发送这个对象到message queue中去

}

}

一旦message queue里有消息对象了,looper立刻将消息对象取出,找到与消息对象对应的handler,然后调用handler对象的handlerMessage方法,处理消息对象。

这时,我们回到自定义的handler的handlerMessage方法,填充代码:

class handler extends Handler{

@Override

public voidhandleMessage(Message msg) {

int what = msg.what;

System.out.println(what);

}

}

三、通过Handler实现线程间的通信

1.worker thread向main thread发送消息

前面的例子是把handler放在主线程中运行。现在做另外一个例子,在主线程中实现Handler的handleMessage方法,在worker thread中通过handler发送消息。

还是新建一个项目,放一个按钮和文本,点击按钮后启动一个线程,让这个线程休眠两秒之后把文本的内容修改掉。这个例子的实际意义在于,把休眠替换成访问服务器就是开发中经常用到的例子。

准备好按钮和文本控件,我们来定义一个线程,在run函数里模拟了一次访问服务器和取值:

//worker thread

class thread extendsThread{

@Override

public void run() {

try {

Thread.sleep(2*1000);//模拟访问网络,休眠两秒钟

} catch (InterruptedException e) {

e.printStackTrace();

}

String s = "从网络中获取的数据";//模拟服务器的返回值

}

}

然后在button的监听器里启动这个线程。

class listener implementsOnClickListener{

@Override

public void onClick(View v) {

Thread t = new thread();

t.start();

}

}

重点来了,刚才我们定义的Thread是Worker Thread,它不能直接操作textView。我们需要Handler。

先把它定义出来,先放着:

class handler extends Handler{

@Override

public voidhandleMessage(Message msg) {

}

}

我们回到线程里去生成消息对象,把S赋给消息对象,再发送消息:

//worker thread

class thread extendsThread{

@Override

public void run() {

//System.out.println("worker thread:"+Thread.currentThread().getName());//打印当前线程的名字

try {

Thread.sleep(2*1000);//模拟访问网络,休眠两秒钟

} catch (InterruptedException e) {

e.printStackTrace();

}

String s = "从网络中获取的数据";//模拟服务器的返回值

Message msg = handler.obtainMessage();

msg.obj = s;

handler.sendMessage(msg);

}

}

发送消息完成,接下来就是处理消息,我们回到自定义的handler,接收S,并写入textview的文本:

class handler extends Handler{

@Override

public voidhandleMessage(Message msg) {

//System.out.println("handleMessage:"+Thread.currentThread().getName());//打印当前线程的名字

String s = (String) msg.obj;

textView.setText(s);

}

}

这样,我们实现了worker thread向main thread发送消息。访问网络都是这样操作的。

2. main thread向worker thread发送消息

与前一个例子不同的是,我们需要在worker thread中准备一个looper对象,并把handler定义在worker thread中。

首先还是新建一个项目,准备一个Button。然后定义一个线程:

//worker thread

class thread extendsThread{

@Override

public void run() {

//准备Looper对象

Looper.prepare();

//在worker thread中定义一个handler(匿名内部类)

handler = new Handler(){

@Override

public void handleMessage(Message msg) {

}

};

//让Looper开始循环,不断的从消息队列中取出消息对象,如果没有消息对象,则该线程阻塞。

Looper.loop();

}

}

这个线程有点复杂,首先是准备一个Looper对象,然后用匿名内部类的方式定义一个handler,为什么?因为handleMessage方法是定义在接收消息的一方。我们这个例子是要worker thread接收并处理消息。

先把handleMessage的内容空着,接下来,让Looper开始循环,它会不断的从消息队列中取出消息对象。

接下来,我们在主线程里让worker thread启动:

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

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

thread t = new thread();

t.start();

}

然后,我们让点击按钮时,发送消息:

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

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

button.setOnClickListener(newOnClickListener() {

@Override

public void onClick(View v) {

Message msg = handler.obtainMessage();

handler.sendMessage(msg);

}

});

thread t = new thread();

t.start();

}

最后完成handleMessage方法,打印一个值。

public voidhandleMessage(Message msg) {

System.out.println("收到了消息对象");

}

这样就实现了main thread向worker thread发送消息。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息