安卓中的Looper使用就是用来给线程创建一个循环,类似while或for循环等。
public class LooperDemo extends Activity {
private String tag = "LooperDemo";
private Handler handler;
private int num = 0;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
startThread();
findViewById(R.id.textView).setOnClickListener(
new View.OnClickListener() {
@Override
public void onClick(View v) {
Message msg = new Message();
Bundle data = new Bundle();
num++;
data.putString("looper", "I am from main thread : "+ num);
msg.setData(data);
handler.sendMessage(msg);
}
});
}
protected void startThread() {
new Thread(new Runnable() {
@Override
public void run() {
Looper.prepare();
handler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
Bundle bundle = msg.getData();
Log.i(tag, "" + bundle.getString("looper"));
Toast.makeText(LooperDemo.this,
"[" + bundle.getString("looper")+"]",
Toast.LENGTH_SHORT).show();
if(num > 5){
// 退出之后再发会报错
Looper.myLooper().quit();
}
}
};
SystemClock.sleep(1000);
Log.i(tag, "线程启动");
Looper.loop();
}
}).start();
;
}
}
上面的运行结果就是
线程启动
I am from main thread : 1
I am from main thread : 2
I am from main thread : 3
可以看出“线程启动”只打印一次,但是 I am from main thread : n 就是每次按下按键的时候触发打印一次,这告诉我们一个故事:被 Looper.prepare(); 和 Looper.loop(); 包围的部分不表明就是循环的一部分。要在Handler里面的,同时 Handler 又别这两语句包围,才能实现循环监听,类似注册一个广播接收器BroadcastReceiver。上面的例子就是一个子线程接收UI线程的消息并可以处理数据,也可以说是线程之间的通信,就是子线程跟子线程也是可以混在一起的互相发消息的。
安卓的消息处理机制中存在着3个核心类Looper,Handler和Message,这3个核心又是为Message Queue(消息队列)而工作的,Message Queue又被封装到Looper里面。
这摆明了Looper在消息处理中的重要位置;直接翻译就是循环者。那就是用来进行循环,与while,for循环相等,但是Looper常用在线程中。
Looper说白了就是给线程添加一个消息队列,然后进入循环等待,等待有消息到来时唤起线程来处理消息,直到线程结束为止。通常的主线程是不会被开发者看到,但是UI线程还是有一个由Looper带来的消息队列,主线程的消息循环就会一直进行,处理用户事件,直到触发onDestroy;
如果,我们需要新建一个线程,并且这个线程要能够循环处理其他线程发来的消息事件,或者需要长期与其他线程进行复杂的交互,这时就需要用到Looper来给线程建立消息队列。
使用Looper也非常的简单,它的方法比较少,最主要的有四个:
public static prepare();
public static myLooper();
public static loop();
public void quit();
在每个线程的run()方法中的最开始调用Looper.prepare(),这是为线程初始化消息队列;
调用Looper.myLooper()获取此Looper对象的引用。这不是必须的,但是prepare()之后才能做相关的操作;
在run()方法中添加Handler来处理消息;
添加Looper.loop()调用,这是让线程的消息队列开始运行,可以接收消息了。
调用Looper.quit()退出消息循环;
需要注意的是当一个线程的消息循环已经退出后,不能再给其发送消息,否则会有异常抛出"RuntimeException: Handler{4051e4a0} sending message to a Handler on a dead thread"。所以,建议在Looper.prepare()后,调用Looper.myLooper()来获取对此Looper的引用,一来是用于终止(quit()必须在对象上面调用); 另外就是用于接收消息时检查消息循环是否已经退出(如上例)。发送消息的时候也要进行判断循环体是否已经退出,否则也会报错异常说没有Handler处理消息。
说一下用法:
1当子线程需要接收UI的数据再进行相关处理的时候可以Looper来进行接收消息队列的消息;
2当UI线程需要一直监听某些事件,比如按键处理等,可以Looper,因为程序一启动就给UI线程建立了一个Looper来监听来自界面或者其他的一些消息;
3子线程之间需要通信也可以使用Looper来监听消息队列的消息,因为消息队列可以是某一线程放入的消息;
初步了解Looper的用法,暂时记录,留以后增加修改;