Android应用程序都运行在一个dalvik虚拟机进程中,进程开始的时候会启动一个主线程(MainThread),主线程负责处理和ui相关的事件,因此主线程通常又叫UI线程。而由于Android采用UI单线程模型,所以只能在主线程中对UI元素进行操作,
简单的例子如下:
package com.fangdo.android.ui;
import org.slf4j.Logger;
public class LoginActivity extends BaseActivity {
Logger logger = LoggerFactory.getLogger(LoginActivity.class);
/** Called when the activity is first created. */
private EditText phoneidET;
private EditText passwordET;
private Button logBT;
private CheckBox savePasswordCB;
private String phoneid;
private String password;
private Handler mHandler ;
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
System.setProperty("java.net.preferIPv6Addresses", "false");
super.onCreate(savedInstanceState);
setContentView(R.layout.login);
phoneidET = (EditText) findViewById(R.id.phoneidET);
passwordET = (EditText) findViewById(R.id.passwordET);
logBT = (Button) findViewById(R.id.logBT);
savePasswordCB = (CheckBox) findViewById(R.id.savePasswordCB);
savePasswordCB.setChecked(true);// 默认为记住密码
passwordET.setInputType(InputType.TYPE_CLASS_TEXT
| InputType.TYPE_TEXT_VARIATION_PASSWORD);
// 隐藏密码为InputType.TYPE_TEXT_VARIATION_PASSWORD,也就是0x81
// 显示密码为InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD,也就是0x91
logBT.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
phoneid = phoneidET.getText().toString();
password = passwordET.getText().toString();
login();
}
});
}
public void login(){
mHandler = new Handler() {
public void handleMessage (Message msg) {//此方法在ui线程运行
switch(msg.what) {
case 1:
Toast.makeText(getApplication(),msg.obj.toString(), Toast.LENGTH_SHORT).show();
break;
}
}
};
new Thread(){
public void run() {
Message msg = new Message();
msg.what = 1 ;
msg.obj = "显示";
handler.sendMessage(msg);
}
}.start();
} public Handler getmHandler() { return mHandler; } }
Android使用消息机制实现线程间的通信,线程通过Looper建立自己的消息循环,MessageQueue是FIFO的消息队列,Looper负责从MessageQueue中取出消息,并且分发到消息指定目标Handler对象。Handler对象绑定到线程的局部变量Looper,封装了发送消息和处理消息的接口
1、new Handler()源码:当中重要的代码:mLooper = Looper.myLooper();
mQueue = mLooper.mQueue;
/**
* Default constructor associates this handler with the queue for the
* current thread.
*
* If there isn't one, this handler won't be able to receive messages.
*/
public Handler() {
if (FIND_POTENTIAL_LEAKS) {
final Class<? extends Handler> klass = getClass();
if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
(klass.getModifiers() & Modifier.STATIC) == 0) {
Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
klass.getCanonicalName());
}
}
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;
mCallback = null;
}
2、什么先不说,看
Looper.myLooper()这个方法,Handler通过mLooper = Looper.myLooper();绑定到线程的局部变量Looper上去,同时Handler通过mQueue =mLooper.mQueue;获得线程的消息队列。此时,Handler就绑定到创建此Handler对象的线程的消息队列上了。
/**
* Return the Looper object associated with the current thread. Returns
* null if the calling thread is not associated with a Looper.
*/
public static Looper myLooper() {
return sThreadLocal.get();
}
2.0 当你看到上面sThreadLocal.get()这行代码,你会产生疑问,明明代码中没有创建怎么会有值,详细解释一下:
Looper用于在android线程中进行消息处理,默认情况下,一个线程并不和任何Looper绑定。当我们调用Looper.prepare()时,如果当前线程还没有和任何Looper绑定,那么将创建一个Looper让它和当前线程绑定。当我们调用Looper.loop()时,它将对当前线程所对应的Looper的消息进行处理,从消息队列里取消息,处理消息,一直循环直到对该Looper调用quit()函数。
注意:默认情况下,线程是没有Looper的,所以要调用 Looper.prepare()来给线程创建消息循环,然后再通过,Looper.loop()来使消息循环起作用。
ctivity的MainUI线程已经新建并绑定了个Looper(在源码,Main函数中你可以看到)。所以在Activity中新建Handler时,不需要先调用Looper.prepare()
public final class ActivityThread {
。。。。
public static void main(String[] args) {
SamplingProfilerIntegration.start();
// CloseGuard defaults to true and can be quite spammy. We
// disable it here, but selectively enable it later (via
// StrictMode) on debug builds, but using DropBox, not logs.
CloseGuard.setEnabled(false);
Process.setArgV0("<pre-initialized>");
Looper.prepareMainLooper();
if (sMainThreadHandler == null) {
sMainThreadHandler = new Handler();
}
ActivityThread thread = new ActivityThread();
thread.attach(false);
if (false) {
Looper.myLooper().setMessageLogging(new
LogPrinter(Log.DEBUG, "ActivityThread"));
}
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}
}
2.0.1看到上面Looper.prepareMainLooper()和Looper.loop()方法了吗?就是在这调用的。好了大家来看看
Looper.prepareMainLooper()的源码:
public static void prepare() {
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper());
}
public static void prepareMainLooper() {
prepare();
setMainLooper(myLooper());
myLooper().mQueue.mQuitAllowed = false;
}
private synchronized static void setMainLooper(Looper looper) {
mMainLooper = looper;
}
2.1一个线程在调用Looper的静态方法prepare()时,这个线程会新建一个Looper对象,并放入到线程的局部变量中,而这个变量是不和其他线程共享的(关于ThreadLocal的介绍)
那看看Looper(),建了一个消息队列对象mQueue,斌企鹅获取当前线程
private Looper() {
mQueue = new MessageQueue();
mRun = true;
mThread = Thread.currentThread();
}
3、在Looper.java 中也定义了sThreadLocal变量
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
4、
类需要覆盖这个方法,实现接受到消息后的处理方法。
/**
* Subclasses must implement this to receive messages.
*/
public void handleMessage(Message msg) {
}
5、是时候启动消息循环了!Looper的静态方法loop()实现了消息循环。
public static void loop() {
Looper me = myLooper();
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
MessageQueue queue = me.mQueue;
// Make sure the identity of this thread is that of the local process,
// and keep track of what that identity token actually is.
Binder.clearCallingIdentity();
final long ident = Binder.clearCallingIdentity();
while (true) {
Message msg = queue.next(); // might block
if (msg != null) {
if (msg.target == null) {
// No target is a magic identifier for the quit message.
return;
}
long wallStart = 0;
long threadStart = 0;
// This must be in a local variable, in case a UI event sets the logger
Printer logging = me.mLogging;
if (logging != null) {
logging.println(">>>>> Dispatching to " + msg.target + " " +
msg.callback + ": " + msg.what);
wallStart = SystemClock.currentTimeMicro();
threadStart = SystemClock.currentThreadTimeMicro();
}
msg.target.dispatchMessage(msg);
if (logging != null) {
long wallTime = SystemClock.currentTimeMicro() - wallStart;
long threadTime = SystemClock.currentThreadTimeMicro() - threadStart;
logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
if (logging instanceof Profiler) {
((Profiler) logging).profile(msg, wallStart, wallTime,
threadStart, threadTime);
}
}
// Make sure that during the course of dispatching the
// identity of the thread wasn't corrupted.
final long newIdent = Binder.clearCallingIdentity();
if (ident != newIdent) {
Log.wtf(TAG, "Thread identity changed from 0x"
+ Long.toHexString(ident) + " to 0x"
+ Long.toHexString(newIdent) + " while dispatching to "
+ msg.target.getClass().getName() + " "
+ msg.callback + " what=" + msg.what);
}
msg.recycle();
}
}
}
解释:msg.target 在Message.java 中是这样定义的:/*package*/ Handler target; (明白了吧)
再看看Handle中是样定义的 msg.target.dispatchMessage(msg)这个方法。
/**
* Handle system messages here.
*/
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
看到没最后调用的是 handleMessage(msg);这个方法,这就是为什么步骤4要重写
ha
ndleMessage
(msg)这个方法,就是最后把自己的伙计代码加入其中
public boolean sendMessageAtTime(Message msg, long uptimeMillis)
{
boolean sent = false;
MessageQueue queue = mQueue;
if (queue != null) {
msg.target = this;
sent = queue.enqueueMessage(msg, uptimeMillis);
}
else {
RuntimeException e = new RuntimeException(
this + " sendMessageAtTime() called with no mQueue");
Log.w("Looper", e.getMessage(), e);
}
return sent;
}
这是就会取到Handler中的mQueue参数,然后放入其中,等待读取这个信息