Android只有主线程才能更新UI?
2016-01-28 15:32
495 查看
以前在做安卓程序开发的时候,心中有一目标——更新UI只能在主线程。这个思想或许是许多安卓开发者都觉得应该是这样,事实Google也建议我们这样做,它提供了Handler机制,通过传递Message实现主线程与子线程之间的通信。
那依据上面据所说,我们来做一个测试。先看下面代码,就是在SetContentView中设置一个TextView,并开启一个线程来是更新TextView的内容。
看到这段代码,很多人看后会说,程序肯定会Crash,因为我们子线程中直接更新UI了,这是“犯法”的事,我们运行程序看一下结果:
从图中可以看到,程序正常运行,没有Crash,并正确更新了UI,到这里也许有会有点迷惑,但确实更新了,这点我没有太了解,猜测应该是跟CPU时间片有关。这时候我们再在子线程里做一些事情,比如休眠100毫秒,这时运行程序会发现程序Crash了。并抛出一个运行时异常:ViewRootImpl$CalledFromWrongThreadException:Only the original thread that created a view hierarchy can touch its views.这个异常的意思,只有创建这个视图层次结构的原始线程才能更改它的view。这句话中可以读出一个意思,即更新UI并不一定要在主线程,只要在创建UI的那个线程中,就可以更新线程。下面再写一个例子来验证一下:
这个例子也很简单,即在子线程中创建一个TextView,并且为这个TextView赋值,然后发送消息在Handler里面设置View,先来看一下运行结果 :
最终发现,子线程成功更新TestView的值,这就验证了Only the original thread that created a view hierarchy can touch its 这句话。也就是更新UI只要在创建UI的线程中就可以了,我们通常初始化View的时候, 一般是在OnCreate方法里面,也可以说是主线程中,所以会形成一个思维,线程更新UI只能在主线程。
当然通常我们很少在子线程中创建View,所以正常情况都还是子线程处理耗时操作,操作完毕发送Message给Handler处理。
那依据上面据所说,我们来做一个测试。先看下面代码,就是在SetContentView中设置一个TextView,并开启一个线程来是更新TextView的内容。
public class MainActivity extends AppCompatActivity { private TextView mTextView; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); mTextView = new TextView(this); mTextView.setText("旧内容"); setContentView(mTextView); new Thread(new CThread()).start(); } class CThread implements Runnable { @Override public void run() { mTextView.setText("新内容"); } } }
看到这段代码,很多人看后会说,程序肯定会Crash,因为我们子线程中直接更新UI了,这是“犯法”的事,我们运行程序看一下结果:
从图中可以看到,程序正常运行,没有Crash,并正确更新了UI,到这里也许有会有点迷惑,但确实更新了,这点我没有太了解,猜测应该是跟CPU时间片有关。这时候我们再在子线程里做一些事情,比如休眠100毫秒,这时运行程序会发现程序Crash了。并抛出一个运行时异常:ViewRootImpl$CalledFromWrongThreadException:Only the original thread that created a view hierarchy can touch its views.这个异常的意思,只有创建这个视图层次结构的原始线程才能更改它的view。这句话中可以读出一个意思,即更新UI并不一定要在主线程,只要在创建UI的那个线程中,就可以更新线程。下面再写一个例子来验证一下:
public class SecondActivity extends AppCompatActivity { TextView mMainThreadTextView; TextView mChildThreadTextView; private CHandler mUpdateHandler = new CHandler(); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); mMainThreadTextView = new TextView(this); mMainThreadTextView.setText("主线程TextView"); setContentView(mMainThreadTextView); new Thread(new CThread()).start(); } class CThread implements Runnable { @Override public void run() { try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } mChildThreadTextView = new TextView(SecondActivity.this); mChildThreadTextView.setText("子线程创建UI并更新"); Message message = mUpdateHandler.obtainMessage(); message.sendToTarget(); } } class CHandler extends Handler { @Override public void handleMessage(Message msg) { setContentView(mChildThreadTextView); super.handleMessage(msg); } }
这个例子也很简单,即在子线程中创建一个TextView,并且为这个TextView赋值,然后发送消息在Handler里面设置View,先来看一下运行结果 :
最终发现,子线程成功更新TestView的值,这就验证了Only the original thread that created a view hierarchy can touch its 这句话。也就是更新UI只要在创建UI的线程中就可以了,我们通常初始化View的时候, 一般是在OnCreate方法里面,也可以说是主线程中,所以会形成一个思维,线程更新UI只能在主线程。
当然通常我们很少在子线程中创建View,所以正常情况都还是子线程处理耗时操作,操作完毕发送Message给Handler处理。
相关文章推荐
- iOS UIView&CALayer
- UIView-动画
- requestAnimationFrame()
- iOS开发 贝塞尔曲线UIBezierPath
- poj1947 Rebuilding Roads 树形dp 背包
- iOS Storyboard、xib中的UIScrollView使用autolayout
- IOS截屏一个UIView,以png或者jpg格式保存到指定目录下
- iOS UITableView 里的Cell用Xib实现
- 使用UISearchController 实现搜索框
- Handler 更新UI的一些细节问题
- 【转】[STL]vector和deque的内存释放(clear)
- 使用requestAnimationFrame做动画效果一
- Best Quality Bluetooth Version VXSCAN H90 J2534
- com.android.builder.packaging.DuplicateFileException: Duplicate files copied in APK META-INF/NOTICE
- UEditor使用说明
- Android笔记:invalidate()和postInvalidate() 的区别及使用——刷新ui
- 通用easyui查询页面组件
- UITbbar图片变形,或者不是图片原始状态
- MySql+Mybatis+Druid:sql injection violation, multi-statement not allow
- media query 单位