当前位置: 移动技术网 > IT编程>移动开发>Android > Android 开发之子线程中更新UI的实例讲解

Android 开发之子线程中更新UI的实例讲解

2018年09月13日  | 移动技术网IT编程  | 我要评论

丰胸就像蒸馒头,ca4104,薛蛮子老婆丁玮

前言:

一般都会说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() {
@override
public 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() {
@override
public 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 crash
11-07 06:11:24.118  2296  2398 e androidruntime: fatal exception: thread-2
11-07 06:11:24.118  2296  2398 e androidruntime: process: com.shift.testapp, pid: 2296
11-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 crash
11-07 06:12:01.827  2422  2471 e androidruntime: fatal exception: thread-2
11-07 06:12:01.827  2422  2471 e androidruntime: process: com.shift.testapp, pid: 2422
11-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:

 void 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 中说到的结论。

修改实例

在原来实例的基础上,我们进行一个修改

如对本文有疑问,请在下面进行留言讨论,广大热心网友会与你互动!! 点击进行留言回复

相关文章:

验证码:
移动技术网