Android应用开发之子线程中更新UI的实例讲解
白羽 2018-09-14 来源 :网络 阅读 1210 评论 0

摘要:本文将带你了解Android应用开发之子线程中更新UI的实例讲解,希望本文对大家学Android有所帮助

        本文将带你了解Android应用开发之子线程中更新UI的实例讲解,希望本文对大家学Android有所帮助


前言:
一般都会说Android 的UI 只能在主线程中更新,最新在自启动开发的过程中也碰到这样的问题,这里结合source code详细分析一下。
实例引路:
public class ShowThreadUI extends Activity implements OnClickListener { private static final String TAG = "ShowThreadUI";  private Button mTestButton; @Override protected void onCreate(Bundle savedInstanceState) {  super.onCreate(savedInstanceState);     setContentView(R.layout.show_thread_ui);   mTestButton = (Button) findViewById(R.id.show_1);   initTestButton(); }  private void initTestButton() {  mTestButton.setOnClickListener(this);   Button show_2 = (Button) findViewById(R.id.show_2);  show_2.setOnClickListener(this);   Button show_3 = (Button) findViewById(R.id.show_3);  show_3.setOnClickListener(this); }  private void testShow1() {  new Thread(new Runnable() {@Overridepublic void run() { WindowManager windowManager = getWindowManager(); TextView textView = new TextView(getApplicationContext()); textView.setText("test 1"); textView.setTextColor(0x54FF9F); windowManager.addView(textView, new WindowManager.LayoutParams());}  }).start(); }   private void testShow2() {  new Thread(new Runnable() {@Overridepublic void run() { mTestButton.setText("button text changed");}  }).start(); }   private void testShow3() {  new TestThread().start(); }   class TestThread extends Thread{  @Override  public void run() {Looper.prepare();TextView tx = new TextView(ShowThreadUI.this);tx.setText("show me, show me");tx.setTextColor(0x0000EE);tx.setGravity(Gravity.CENTER);WindowManager wm = ShowThreadUI.this.getWindowManager();WindowManager.LayoutParams params = new WindowManager.LayoutParams(  250, 150, 200, 200, WindowManager.LayoutParams.FIRST_SUB_WINDOW,  WindowManager.LayoutParams.TYPE_TOAST, PixelFormat.OPAQUE);wm.addView(tx, params);Looper.loop();  } }  @Override public void onClick(View view) {  int id = view.getId();  switch (id) {case R.id.show_1: testShow1(); break;case R.id.show_2: testShow2(); break;case R.id.show_3: testShow3(); break;default: break;  } }}
1、点击button 1
这个时候会报错:
--------- beginning of crash11-07 06:11:24.118  2296  2398 E AndroidRuntime: FATAL EXCEPTION: Thread-211-07 06:11:24.118  2296  2398 E AndroidRuntime: Process: com.shift.testapp, PID: 229611-07 06:11:24.118  2296  2398 E AndroidRuntime: java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()11-07 06:11:24.118  2296  2398 E AndroidRuntime:    at android.os.Handler.(Handler.java:204)11-07 06:11:24.118  2296  2398 E AndroidRuntime:    at android.os.Handler.(Handler.java:118)11-07 06:11:24.118  2296  2398 E AndroidRuntime:    at android.view.ViewRootImpl$ViewRootHandler.(ViewRootImpl.java:3679)11-07 06:11:24.118  2296  2398 E AndroidRuntime:    at android.view.ViewRootImpl.(ViewRootImpl.java:4012)11-07 06:11:24.118  2296  2398 E AndroidRuntime:    at android.view.WindowManagerGlobal.addView(WindowManagerGlobal.java:346)11-07 06:11:24.118  2296  2398 E AndroidRuntime:    at android.view.WindowManagerImpl.addView(WindowManagerImpl.java:94)11-07 06:11:24.118  2296  2398 E AndroidRuntime:    at com.shift.testapp.ShowThreadUI$1.run(ShowThreadUI.java:46)11-07 06:11:24.118  2296  2398 E AndroidRuntime:    at java.lang.Thread.run(Thread.java:764)
从堆栈信息来看最终会在 ViewRootImpl 中的ViewRootHandler 触发错误,先来看下ViewRootHandler:
final class ViewRootHandler extends Handler { @Override public String getMessageName(Message message) {
在构造的时候出错的,来看下Handler 的204行:
public Handler(Callback callback, boolean async) {  if (FIND_POTENTIAL_LEAKS) {final Class 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 = callback;  mAsynchronous = async; }
可以看到最终原因是Handler 中获取Looper 为null。
重新梳理流程,button 1 点击的时候会新开一个线程,在这个线程里创建了UI,WindowManager在addView 的时候会创建ViewRootImpl,其中的Handler 必须要依赖线程中的Looper,Android异步消息处理线程之----Looper+MessageQueue+Handler?中提到Handler 是运行在创建它的线程中,而每个Handler 中的Looper 需要跟Thread 一一对应,换句话说,就是Thread中的Handler 必须有个跟Thread对应的Looper,而这里显然是为null。
结论,通过WindowManger addView 方式创建UI 的时候,需要伴随着创建Looper,Handler 需要。
2、点击button 2
这个时候会报错:
--------- beginning of crash11-07 06:12:01.827  2422  2471 E AndroidRuntime: FATAL EXCEPTION: Thread-211-07 06:12:01.827  2422  2471 E AndroidRuntime: Process: com.shift.testapp, PID: 242211-07 06:12:01.827  2422  2471 E AndroidRuntime: android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.11-07 06:12:01.827  2422  2471 E AndroidRuntime:    at android.view.ViewRootImpl.checkThread(ViewRootImpl.java:7334)11-07 06:12:01.827  2422  2471 E AndroidRuntime:    at android.view.ViewRootImpl.requestLayout(ViewRootImpl.java:1165)11-07 06:12:01.827  2422  2471 E AndroidRuntime:    at android.view.View.requestLayout(View.java:21999)11-07 06:12:01.827  2422  2471 E AndroidRuntime:    at android.view.View.requestLayout(View.java:21999)11-07 06:12:01.827  2422  2471 E AndroidRuntime:    at android.view.View.requestLayout(View.java:21999)11-07 06:12:01.827  2422  2471 E AndroidRuntime:    at android.view.View.requestLayout(View.java:21999)11-07 06:12:01.827  2422  2471 E AndroidRuntime:    at android.view.View.requestLayout(View.java:21999)11-07 06:12:01.827  2422  2471 E AndroidRuntime:    at android.widget.TextView.checkForRelayout(TextView.java:8531)11-07 06:12:01.827  2422  2471 E AndroidRuntime:    at android.widget.TextView.setText(TextView.java:5394)11-07 06:12:01.827  2422  2471 E AndroidRuntime:    at android.widget.TextView.setText(TextView.java:5250)11-07 06:12:01.827  2422  2471 E AndroidRuntime:    at android.widget.TextView.setText(TextView.java:5207)11-07 06:12:01.827  2422  2471 E AndroidRuntime:    at com.shift.testapp.ShowThreadUI$2.run(ShowThreadUI.java:55)11-07 06:12:01.827  2422  2471 E AndroidRuntime:    at java.lang.Thread.run(Thread.java:764)
在ViewRootImpl requestLayout 的时候会调用checkThread:
?123456void checkThread() {  if (mThread != Thread.currentThread()) {throw new CalledFromWrongThreadException(  "Only the original thread that created a view hierarchy can touch its views.");  } }
mThread 现在是创建ViewRootImpl 时候的Thread,而这里Thread.currentThread 现在是当前运行的Thread,上面的button 1 中再WindowManger addView 的时候会创建ViewRootImpl,那在activity 中正常运行情况下是什么时候呢?下面会继续说明的。
结论,线程之前创建的View或者UI,在线程中是无法更新的,只有在创建UI的线程中更新该UI。
3、点击button 3
顺利运行,跟button 1 中流程唯一区别就是添加了Looper,证明了button 1 中说到的结论。
修改实例
在原来实例的基础上,我们进行一个修改    

本文由职坐标整理并发布,希望对同学们有所帮助。了解更多详情请关注职坐标移动开发之Android频道!

本文由 @白羽 发布于职坐标。未经许可,禁止转载。
喜欢 | 0 不喜欢 | 0
看完这篇文章有何感觉?已经有0人表态,0%的人喜欢 快给朋友分享吧~
评论(0)
后参与评论

您输入的评论内容中包含违禁敏感词

我知道了

助您圆梦职场 匹配合适岗位
验证码手机号,获得海同独家IT培训资料
选择就业方向:
人工智能物联网
大数据开发/分析
人工智能Python
Java全栈开发
WEB前端+H5

请输入正确的手机号码

请输入正确的验证码

获取验证码

您今天的短信下发次数太多了,明天再试试吧!

提交

我们会在第一时间安排职业规划师联系您!

您也可以联系我们的职业规划师咨询:

小职老师的微信号:z_zhizuobiao
小职老师的微信号:z_zhizuobiao

版权所有 职坐标-一站式AI+学习就业服务平台 沪ICP备13042190号-4
上海海同信息科技有限公司 Copyright ©2015 www.zhizuobiao.com,All Rights Reserved.
 沪公网安备 31011502005948号    

©2015 www.zhizuobiao.com All Rights Reserved