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参数,然后放入其中,等待读取这个信息