代码改变世界

非ui线程更新ui问题

2015-08-31 11:55  指针空间  阅读(529)  评论(0编辑  收藏  举报

android初学者,刚开始并没有意识到,android中更新UI只能用UI线程,写了一个下载线程,在线程里更新progessbar,并用textview显示下载进度

public void listenProgress(){ 
        new Thread(new Runnable() { 
             
            @Override 
            public void run() { 
                while(progress < MsgService.MAX_PROGRESS){ 
                    progress = msgService.getProgress();  
                    progressbar.setProgress(progress); 
                    text.setText("下载" + progress + "%");
                    try { 
                        Thread.sleep(1000); 
                    } catch (InterruptedException e) { 
                        e.printStackTrace(); 
                    } 
                    //text.setText("下载" + progress + "%");
                } 
                 
            } 
        }).start(); 
    }

总会报错 Only the original thread that created a view hierarchy can touch its views

 

后来才意识到,在非ui线程里不能直接更新ui。子线程中数据发生改变来更新主线程的ui,一般是通过消息机制,message和handler结合的方式。

但是,我继续测试发现,把TextView更新注释掉(//text.setText("下载" + progress + "%");),就没有上面报错,运行后progress能正常更新。

android里面的准则是非ui线程是不能去修改ui线程里面的ui控件的, 但是为什么不在主线程调用setProgress 方法没有报错呢,查看源代码可以发现,setProgress 会调用到private synchronized void refreshProgress(int id, int progress, boolean fromTouch) 方法,首先会判断当前线程是否为主UI线程,若是主UI线程则直接调用 doRefreshProgress 方法更新进度;若不是主UI线程则会先创建一个RefreshProgressRunnable 对象,然后调用 view 的 post(Runnable action) 方法,将 RefreshProgressRunnable 放到主UI线程的消息队列等待处理。从表面上看似乎是在非UI线程里面去修改了主线程的控件,而实际上并非这样。同理,SeekBar、ProgressDialog 也是可以在子线程直接更新进度的。

参考博客:http://blog.csdn.net/androidzhaoxiaogang/article/details/8136222

 

参考源码:

private synchronized void refreshProgress(int id, int progress, boolean fromTouch) {
        if (mUiThreadId == Thread.currentThread().getId()) {
            doRefreshProgress(id, progress, fromTouch);
        } else {
            RefreshProgressRunnable r;
            if (mRefreshProgressRunnable != null) {
                // Use cached RefreshProgressRunnable if available
                r = mRefreshProgressRunnable;
                // Uncache it
                mRefreshProgressRunnable = null;
                r.setup(id, progress, fromTouch);
            } else {
                // Make a new one
                r = new RefreshProgressRunnable(id, progress, fromTouch);
            }
            post(r);
        }
    }