您的位置:首页 > 产品设计 > UI/UE

浅谈Handler,Loop,MessageQueue,Thread的小秘密

2015-10-26 19:19 441 查看
多线程,多线程,多线程!重要的事情多说几遍。在Android编程中不可能不遇到多线程的问题,我们都知道UI界面即主线程不能做耗时的操作,必须要利用多线程的特性进行耗时的操作,然后UI界面负责实时更新就哦了。

BUT!Thread,Handler都是什么鬼,我迷糊很久很久啦!这不,课题需要,今天好好整理了一下思路,结合课本和示例,终于弄清楚这都是什么了,于是迫不及待写了这一Blog!

First,Thread这里我们就不多说了,学过JAVA的筒子应该都知道,这就是java中用来另起线程的类,继承Thread,覆盖run()方法即另起一个新线程,在run()方法中进行耗时的操作。那么问题来了,Handler什么?是Android里特有的另起线程的类吗?和Thread一样吗?非也!下面我来详细讲一讲Handler这哥们!

Android的消息传递机制是另一种形式的“事件处理”,这个机制是为了解决Android应用的多线程的问题——Android平台只允许UI线程修改Activity里的UI自检,这样就导致一种矛盾,耗时的操作不能再UI线程中执行,另起的线程又不能修改Activity里的UI组件,这让老夫如何是好呢!憋捉急,Handler的消息传递机制帮你实现!

Handler类的主要作用:

1、在新启动的线程中发送消息

2、在主线程中获取、处理消息

即:在主线程中出现如下代码:

final Handler myHandler = new Handler(){
public void handleMessage(Message msg){
if(msg.what == 0x123){
/**
* 修改UI的相关操作
*/
}
}
};
然后另起一个新线程发送消息:

new Timer().schedule(new TimerTask(){

@Override
public void run() {
// TODO Auto-generated method stub
myHandler.sendEmptyMessage(0x123);
}

}, 0,1200);
上述new TimerTask()本质就是启动一条新线程。

上述就是handler在android的多线程干了什么事情的演示。

貌似说到这里,我可以关闭页面了,不行,我们必须深入了解Handler的工作原理,学习与Handler一起工作的几个组件:

Message:Handler接受和处理的消息对象(上面代码中的0x123);

Looper:每个线程只能拥有一个Looper,它的loop()方法负责读取MessageQueue中的消息,读到消息之后就把消息发送给该消息的Handler进行处理,BUT,我们在上面的代码中好像没有发现这个Looper,还有他的loop()方法,憋捉急,一会儿说;

MessageQueue:消息队列,先进先出的方式来管理Message,程序创建Looper对象时会在它的构造器中创建MessageQueue对象;

我们之前说,Handler有两个作用,发送消息和处理消息,程序用Handler发送的消息送到指定的MessageQueue,也就是说,如果希望Handler正常工作,则在当前线程中必须有一个MessageQueue,否则消息没有地方保存了,不过MessageQueue是Looper管理的,也就是说,如果希望Handler正常工作,在当前线程必须有一个Looper对象!如果希望Handler正常工作,在当前线程必须有一个Looper对象!重要的事情多说几遍,反正复制粘贴。

那么怎么样让当前线程必须有一个Looper对象,而我们前面的代码怎么没有呢!答案来了:

主UI线程中,系统已经初始化了一个Looper对象,系统已经初始化了一个Looper对象,因此程序直接创建Handler即可,然后就可以通过Handler来发送消息、处理消息,这就是为什么我们上面的代码没有看到Looper这个鬼;

程序员自己启动的子线程,程序员必须自己创建一个Looper对象,程序员自己启动的子线程,程序员必须自己创建一个Looper对象,并启动它,创建Looper对象调用它的prepare()方法即可。然后调用Looper的静态loop()方法来启动它。loop()方法使用一个死循环不断取出MessageQueue中的消息,将取出的消息分给该消息对应Handler进行处理。

在线程中使用Handler的步骤如下:

1、调用Looper的prepare()方法为当前线程创建Looper对象,创建Looper对象的时候,它的构造器也创建了与之配套的MessageQueue

2、有了Looper之后,创建Handler子类的实例,重写handleMessage()方法,该方法负责处理来自于其他线程的消息

3、调用Looper的loop()方法启动Looper

下面看一个Demo,帮助大家理解:

用户输入一个数值上限,点击“计算”按钮时,该应用会把该上限数值发送到新启动的线程,在新线程中计算该范围内的所有质数(特别耗时的操作):

package com.example.handlertest;

import java.util.ArrayList;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;

import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;

public class MainActivity extends Activity {

EditText text;
Button cal;
TextView show;
mThread calThread;

class mThread extends Thread{
public Handler mHandler;
@Override
public void run() {
// TODO Auto-generated method stub
mHandler = new Handler(){

@Override
public void handleMessage(Message msg) {
// TODO Auto-generated method stub
if(msg.what == 0x123){
int upper = msg.getData().getInt("KEY");
List<Integer> list = new ArrayList<Integer>();
outer:
for(int i=2; i<upper ; i++){
for(int j=2; j<i;j++){
if(i!=2 && i%j==0){
continue outer;//想跳出两层循环,则需要这样处理
}
}
list.add(i);
}
Toast.makeText(MainActivity.this, list.toString(), Toast.LENGTH_LONG).show();
show.setText(list.toString());
}
}

};
}

}
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);

text = (EditText) findViewById(R.id.editText1);
cal = (Button) findViewById(R.id.button1);

calThread = new mThread();
calThread.start();

cal.setOnClickListener(new OnClickListener() {

@Override
public void onClick(View v) {
// TODO Auto-generated method stub
int x = Integer.parseInt(text.getText().toString());
Message msg = new Message();
msg.what = 0x123;
Bundle bundler = new Bundle();
bundler.putInt("KEY", x);
msg.setData(bundler);
calThread.mHandler.sendMessage(msg);
}
});
}

@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;
}

@Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}
}


上述代码运行会出现如下问题:



报错信息显示,没有Looper呀亲!

所以代码改为如下:

class mThread extends Thread{
public Handler mHandler;
@Override
public void run() {
// TODO Auto-generated method stub
Looper.prepare();
mHandler = new Handler(){

@Override
public void handleMessage(Message msg) {
// TODO Auto-generated method stub
if(msg.what == 0x123){
int upper = msg.getData().getInt("KEY");
List<Integer> list = new ArrayList<Integer>();
outer:
for(int i=2; i<upper ; i++){
for(int j=2; j<i;j++){
if(i!=2 && i%j==0){
continue outer;//想跳出两层循环,则需要这样处理
}
}
list.add(i);
}
Toast.makeText(MainActivity.this, list.toString(), Toast.LENGTH_LONG).show();
show.setText(list.toString());
}
}

};
Looper.loop();
}

}


其他部分一样,这样就可以获得正确结果:

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: