Android 在工作线程(非主线程)更改UI组件

我们在非UI线程更新UI是会通过以下几种方式来进行的:参考http://vincenttung.blog.51cto.com/6249439/1143761

(1) handler.send(Message msg)或者handler.post(Runnable r)        

(2) View.post(runnable r) ,View.PostDelayed(Runnabe,long)

(3) Activity 的runOnUIThread(runnable r)

(4)通过Handlerthread进行更新 

(5)在子线程中使用looper.prepare 和 looper.loop()

(6)还有一个很重要的AsyncTask()

 

1、先来个小插曲:     

Android: is View.onClick() method invoked on main UI thread?

Yes it is. And then you launch your crazy networking stuff in a background thread.

也就是说,在onClick 中弹出Toast等是算在main UI thread 里面的。

 

2、再来一个,android 在非UI thread 中显示toast ,如果不出意外的话,肯定是会弹出

java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()

提示的,那么要怎么样才能不出错呢?可以这样子! 

        Looper.prepare();
        Toast.makeText(LoginActivity.this,filedId, Toast.LENGTH_LONG).show();
        Looper.loop();

为什么能这样子? 请参考:http://blog.csdn.net/xiaanming/article/details/8904645 他会告诉你为什么能这样子,但是

这样子真的好吗?会不会出什么幺蛾子啊?一定要这样子怎么办!?

先上一段代码:

public class TestLooperThread extends Thread {
    private String TAG="TestLooperThread";
    
    private Context mContext;
    
    /**
     * @param UiMangerHandler
     * @param looper
     * @param mContext
     */
    public TestLooperThread(Handler UiMangerHandler,Looper looper,Context mContext){

        this.mContext=mContext;
//        start();
        
    }

    public void run(){
        Log.e(TAG,"TestLooperThread start!");

        Looper.prepare();

        Toast.makeText(mContext, "Thread is over  ? ", Toast.LENGTH_SHORT).show();

        Looper.loop();
        
        Log.e(TAG,"TestLooperThread ended!  这个是不会打印出来的...");
    }

}

 

 这个线程会在执行完run后结束吗?查看DDMS可以发现,他还是一直活动的!

Android 在工作线程(非主线程)更改UI组件_第1张图片

 Log.e(TAG,"TestLooperThread ended!  这个是不会打印出来的..."); 这行代码是不会执行的。查看

        Looper.loop();

这行代码干了些什么就知道原因了,下面是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;
                }
            ... ...
            
            ... ...
            
        }
 }

 loop 方法是个死循环啊,他是不会结束的,如果这种线程一直放任下去就会发现一段时间后,总是Crash,tombstones中的堆栈信息成百上千简直不是梦啊!这样子是无法通过压力测试的啊@!

那么要怎样才能结束这个循环呢?

            if (msg != null) {
                if (msg.target == null) {
                    // No target is a magic identifier for the quit message.
                    return;
                }

如果msg.target== null的话就会结束的啊,Looper class 中有一个quit( )方法

    public void quit() {
        Message msg = Message.obtain();
        // NOTE: By enqueueing directly into the message queue, the
        // message is left with a null target.  This is how we know it is
        // a quit message.
        mQueue.enqueueMessage(msg, 0);
    }

 他就这样让Loop循环结束了!


Tips:Toast 在线程中加loop还会有一个问题,如果没有结束,回调的时候super后面的语句就不会执行了。

因为super 的loop() 方法阻塞了。

 

你可能感兴趣的:(android,更新UI,非UI线程)