.ViewRootImpl.invalidateChildInParent(ViewRootImpl.java:883)
02-02 16:44:38.786: E/AndroidRuntime(17907): at android.view.ViewGroup.invalidateChild(ViewGroup.java:4320)
02-02 16:44:38.786: E/AndroidRuntime(17907): at android.view.View.invalidate(View.java:10947)
02-02 16:44:38.786: E/AndroidRuntime(17907): at android.view.View.invalidate(View.java:10902)
02-02 16:44:38.786: E/AndroidRuntime(17907): at android.widget.TextView.checkForRelayout(TextView.java:6673)
02-02 16:44:38.786: E/AndroidRuntime(17907): at android.widget.TextView.setText(TextView.java:3860)
02-02 16:44:38.786: E/AndroidRuntime(17907): at android.widget.TextView.setText(TextView.java:3718)
02-02 16:44:38.786: E/AndroidRuntime(17907): at android.widget.TextView.setText(TextView.java:3693)
02-02 16:44:38.786: E/AndroidRuntime(17907): at com.example.demo.MainActivity$LooperThread.run(MainActivity.java:78)
意思就是说,只有创建View层次结构的线程才能修改View,我们在非UI主线程里面更新了View,所以会报错。
但是你还没说为什么第一种调用方法为什么不报错啊!!!
先别着急,先看一下上面的报错信息,里面有好多东西可以研究呢!
我们先从下面开始看,首先是LooperThread.run()报错了,为什么报错呢,再往上,因为我们调用setText了,再往上就是TextView.checkForRelayout,然后上面是invalidate,我们修改了文字,肯定要invalidate啊,谁调用的呢?原来是ViewGroup.invalidateChild,往上找啊找,终于找到了罪魁祸首,ViewRootImpl.checkThread()报错了!
ViewRootImpl是什么玩意?这个玩意可很强大,你现在就只需要知道它能做很多和界面有关的事情就ok了,其实我对这个类也是只了解一点点。。。
checkThread()到底做了什么啊,就报错!ViewRootImpl是一个隐藏类,我们只能去看源码,源码如下
void checkThread() {
if (mThread != Thread.currentThread()) {
throw new CalledFromWrongThreadException(
"Only the original thread that created a view hierarchy can touch its views.");
}
}
就是这个方法报错了!ViewRootImpl是依附在AttachInfo这个类上的,而AttachInfo是View得一个隐藏类,你在Eclipse里面是看不到的,需要直接看framework得源码。所以说,这里的Thread.currentThread()是UI主线程,就是这里判断是不是我们在非UI主线做了修改VIew的操作。
那么问题又来了,为啥第一种方式,不会报错呢!!!
ok,不要抓狂,咱们先回顾一下Activity的生命周期。靠!和Activity的生命周期怎么又扯上关系了?
Activity在onCreate中进行界面的数据准备,onStart()之后,Activity的界面就对用户可见了,那么知道这些有什么用呢?当然有用!上面两种调用方式的差别就在调用的时机不同!一个是在onCreate中开启线程调用,一个是我们手动调用,暗示着,第二种方法是在onResume之后调用!
这种调用时机的差别就决定了代码中setText的意义!
第一种做法中,虽然是在子线程中setText,但是这时候View还没画出来呢,所以并不会调用之后的inva