在Android 入门 -应用启动分析一文中,我们有两处并未详细介绍,Looper进程内通讯及Binder进程间通讯。现在我们来看看Android的进程内异步通讯机制。如果你做过界面(UI)应用,不论是Win32的还是Java的UI应用,或者是iOS的UI,都会涉及前端、后端,多线程等概念。在一个线程内的程序,是从上到下顺序执行,后面的代码必须等前面的代码执行完成才可以执行。但很多时候,一个应用需要处理多个场景,最常用的就是:后台的数据处理与前端的UI显示。在Win32应用里,一个线程处理完数据后,可以post消息给主窗口线程,显示数据。Android应用借用了这种机制,当然,你可以用自己的实现方式,但既然Android已经提供了,我们就应该直接拿来使用,这就是Looper。
ThreadLocal有什么作用?和本地变量有什么区别?考查下面的代码:
所以,ThreadLocal保存的就是线程的本地变量,这个我在Thead内new一个成员变量就行了,这不是多此一举吗?事实上不是这样的,ThreadLocal可以确保保存在线程中的变量不会被另一个线程修改,而成员变量则可以被修改,这对线程安全中防止意外访问的情况发生非常有用。下面举例说明:
1、ThreadLocal线程安全机制
要了解Looper,我们需要先深入理解Java的线程本地化存储机制(TLS),即ThreadLocal。这是Java提供的,非Android独有的,可以在任何Java程序中使用。ThreadLocal有什么作用?和本地变量有什么区别?考查下面的代码:
public class ThreadLocalTest {
final static ThreadLocal<Object> tl = new ThreadLocal<>();
public static void main(String[] args) {
tl.set(0);
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("thread:" + tl.get());
}
}).start();
System.out.println("main: " + tl.get());
}
}
上面的代码中,我们将tl设置为静态变量,按正常思维,在Main中set了,在Thread中get,应该是有值的。但事实上不是,thread中是get不到这个值的。这就说明,你在哪个线程set了值,就只能在哪个线程中get。
所以,ThreadLocal保存的就是线程的本地变量,这个我在Thead内new一个成员变量就行了,这不是多此一举吗?事实上不是这样的,ThreadLocal可以确保保存在线程中的变量不会被另一个线程修改,而成员变量则可以被修改,这对线程安全中防止意外访问的情况发生非常有用。下面举例说明:
import java.util.ArrayList;
import java.util.List;
class MyRunnable implements Runnable {
// 千万别被static关键字蒙蔽了,tl可以全局使用,但tl.set,tl.get只能是当前线程使用
final static ThreadLocal<Object> tl = new ThreadLocal<>();
Object t;
public MyRunnable() {
}
@Override
public void run() {
t = Math.random();
tl.set(t);
Object pt = null, ptl = null;
while (Thread.currentThread().isAlive()) {
if (pt == null || !pt.equals(t)) // 如果t变量变化
System.out.println(this + ": t " + pt + " -> " + t);
if (ptl == null || !ptl.equals(tl.get())) // 如果tl.get()变化
System.out.println(this + ": tl " + ptl + "->" + tl.get());
pt = t;
ptl = tl.get();
}
}
public String toString() {
return Thread.currentThread().getName();
}
public Object getTL() {
return tl.get();
}
public Object getT() {
return t;
}
public void setTL(Object v) {
tl.set(v);
}
public void setT(Object v) {
t = v;
}
}
public class ThreadLocalTest {
public static void main(String[] args) {
List<MyRunnable> ls = new ArrayList<MyRunnable>();
for (int i = 0; i < 2; i++) {
MyRunnable thread = new MyRunnable();
ls.add(thread);
new Thread(thread, "thread-" + i).start();
}
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("--------");
for (int i = 0; i < 2; i++) {
MyRunnable thread = ls.get(i);
thread.setT("new-" + i);
thread.setTL("new-" + i);
}
}
}
上面例子的运行结果是:
thread-0: t null -> 0.5093053224347734
thread-0: tl null->0.5093053224347734
thread-1: t null -> 0.7972271387384761
thread-1: tl null->0.7972271387384761
--------
thread-0: t 0.5093053224347734 -> new-0
thread-1: t 0.7972271387384761 -> new-1
可以看到,调用thread.setT方法时,会修改线程内部的t值,但调用thread.setTL时,不会修改线程内部的tl.get()值?为什么呢?其实,thread.setTL修改的是main线程的tl.get()值,thread.setTL是在main线程中运行,不是在真正的thread中运行的,所以,ThreadLocal可以确保线程内部的变量,无论在任何情况下,不被其他线程修改,这点成员变量做不到,只要引出方法,就可以被任意修改,你必须要做同步处理。网上的资料基本未说到点子上,看的我云里雾里。
2. Looper消息机制
Looper是一个可以处理消息循环的类,任何线程都可以使用Looper,来处理消息。基本使用方法如下:class LooperThread extends Thread {
public Handler mHandler;
public void run() {
Looper.prepare();
mHandler = new Handler() {
public void handleMessage(Message msg) {
// 真正的消息在这里处理