《代码里的世界》

用文字札记描绘自己 android学习之路

  转载请保留出处 by Qiao
http://blog.csdn.net/qiaoidea/article/details/45128303

【导航】
Android更新Ui的几种方法和见解 android更新ui基本常用方法
Android更新Ui进阶精解(一) android ui线程检查机制
Android更新Ui进阶精解(二) android 线程更新UI机制

1.回顾

  前面一篇简单讲了如何快速使用handler更新ui。稍微补充一些:

  1. 更新ui时可以直接使用这种方法,你不须非要再new一个子线程才使用,比如:
viewPostBtn.setOnClickListener(new OnClickListener() {            @Override            public void onClick(View v) {                viewPostBtn.post(new Runnable() {                    @Override                    public void run() {                        titleView.setText("viewPost——Result");                    }                });            }        });
  1. 这几种方法可以同样延伸到很多类似的反不同意义的用法,比如
    sendMessage()可以延伸的方法
  2. sendMessageAtTime(Message msg, long uptimeMillis)在指定时间uptimeMillis时发送消息msg;
  3. sendMessageDelayed(Message msg, long delayMillis)延迟delayMillis时间后发送消息msg
  4. sendEmptyMessage(int what) 发送一个指定类型what的空消息;
  5. sendEmptyMessageAtTime(int what, long uptimeMillis)在指定时间uptimeMillis时发送一条指定类型what的空消息;
  6. sendEmptyMessageDelayed(int what, long delayMillis)延迟delayMillis时间后发送一条指定类型what的空消息;
  7. sendMessageAtFrontOfQueue(Message msg)在消息队列头(优先)发送这条消息msg;

    同样,post()可以延伸的方法

  8. postAtTime(Runnable r, long uptimeMillis)
  9. postAtTime(Runnable r, Object token, long uptimeMillis)
  10. postDelayed(Runnable r, long delayMillis)
  11. postAtFrontOfQueue(Runnable r)
    请自行查阅相应方法,这里不予一一列出。

2.原理–源码分析

  首先说上篇的第一个问题,android在生成页面的同时生成一个ViewRootImpl的对象,这个对象负责检查checkThread线程是否是在主ui线程,当我们尝试使用非ui线程更新视图时,checkThread则抛出异常。
  

1. 先看看负责检查线程的ViewRootImpl这段逻辑

@SuppressWarnings({"EmptyCatchBlock", "PointlessBooleanExpression"})  public final class ViewRootImpl implements ViewParent, View.AttachInfo.Callbacks, HardwareRenderer.HardwareDrawCallbacks {      // other code..    void checkThread() {          if (mThread != Thread.currentThread()) {              throw new CalledFromWrongThreadException(                      "Only the original thread that created a view hierarchy can touch its views.");          }      }      // other code..}  

 好吧,为什么要这样?  引用一段比较合理解释:
 “那么为什么Android要求只能在UI主线程中更改View呢?这就要说到Android的单线程模型了,因为如果支持多线程修改View的话,由此产生的线程同步和线程安全问题将是非常繁琐的,所以Android直接就定死了,View的操作必须在UI线程,从而简化了系统设计。”
 

2. 再看看视图创建时候是何时添加了这个检查对象的

  我们从activity创建说起,首先获取一个窗口管理器WindowManager,然后设置并初始化其container。接着通过activity得到根视图DecorView(FramLayout),最后将DecorView添加到 activity的ViewManager 中去,而这个ViewManager 在addView时候就会生成一个ViewRootImpl对象。说这么多感觉表述不清楚,更容易犯糊涂了。 看代码

/** * 以下方法是在调用activity Resume时候执行 * @ActivityClientRecord r 记录activity相关状态及参数 */ if (r.window == null && !a.mFinished && willBeVisible) {                   //获取根视图DecorView                r.window = r.activity.getWindow();                  View decor = r.window.getDecorView();                  decor.setVisibility(View.INVISIBLE);                //获取并添加至ViewManager                 ViewManager wm = a.getWindowManager();                  WindowManager.LayoutParams l = r.window.getAttributes();                  a.mDecor = decor;                  l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;                  l.softInputMode |= forwardBit;                  if (a.mVisibleFromClient) {                      a.mWindowAdded = true;                      wm.addView(decor, l);                  }              }

然后更新和显示activity ,调用了r.activity.makeVisible()方法

 if (!r.activity.mFinished && willBeVisible                      && r.activity.mDecor != null && !r.hideForNow) {                  //判断配置等是否需要更新view最后调用wm.updateViewLayout(decor, l); 方法,略过。。。                 //最后显示activity                r.activity.mVisibleFromServer = true;                  mNumVisibleActivities++;                  if (r.activity.mVisibleFromClient) {                      r.activity.makeVisible();  //显示                }              }  

再看activity的makeVisible()方法

    void makeVisible() {          if (!mWindowAdded) {              ViewManager wm = getWindowManager();              wm.addView(mDecor, getWindow().getAttributes());  //注意这里。。            mWindowAdded = true;          }          mDecor.setVisibility(View.VISIBLE);      } 

这个 ViewManager 的addview方法正是关键,它将添加我们提到的ViewRootImpl,其具体实现可以看WindowManagerGlobal:

//主要展示添加ViewRootImpl的过程,其他代码略public final class WindowManagerGlobal {      public void addView(View view, ViewGroup.LayoutParams params,              Display display, Window parentWindow) {         ViewRootImpl root;          synchronized (mLock) {             root = new ViewRootImpl(view.getContext(), display);              mRoots.add(root);          }      }  }  

  至此,我们大略知道了ui线程是在何时对更新过程和加以控制检查,并了解了检查的内部原理。
  看到这里很多小伙伴们肯定会不满了,你他瞄不是说讲解handler更新Ui的原理吗,净扯这些有屁用呢!额,由于篇幅限制,这段放在下一篇Android更新Ui进阶精解(二) 讲解。咱线继续上一话题:
  那么,我们真的就不能在子线程里更新Ui了吗?显然不是,基于前面讲解的部分,既然是在onResume时候生成检查ViewRootImpl对象,所以我们其实可以在oncreate里更新Ui,比如
  

         //onCreate调用这段         new Thread(new Runnable() {              @Override              public void run() {                  titleView.setText("OtherThread");              }          }).start();  

当然,你是不能这样用的

   new Thread(new Runnable() {              @Override              public void run() {                  try {                      Thread.sleep(200);  //睡了就再起不来了                } catch (InterruptedException e) {                      e.printStackTrace();                  }                  titleView.setText("OtherThread");              }          }).start();  

但是为什么我我们又能在OnResume里这么用?

@Override      protected void onResume() {          super.onResume();          new Thread(new Runnable() {              @Override              public void run() {                  titleView.setText("OtherThread");              }          }).start();      }  

爱哥说是因为消息队列Message Queue在接收和处理过程并非立即的,需要一个过程。(这一部分我大爱哥 爱哥 –非UI线程更新UI 其实有精讲,大家不妨看一下。)其实我觉得不妨可以大胆猜想,只要view是在渲染到视图之前,我们都是可以通过其他线程来更改的。大家有空可以研究下。

更多相关文章

  1. Android更新Ui的几种方法和见解
  2. Android:实现无标题的两种方法
  3. 去掉RecycleView或者ListView上下滑动阴影的方法
  4. Android异步处理系列文章四篇之一使用Thread+Handler实现非UI线
  5. 使用 ViewStub 延迟展开视图
  6. Android Studio导入第三方类库的方法
  7. Android 之 GrideView网格视图
  8. android 的常标签和方法 android 初学者
  9. Android给TextView添加点击事件的实现方法

随机推荐

  1. Android shape 绘制图形的实例详解
  2. Android(安卓)2.2完全退出程序, 使用广播
  3. android 拍照 onCreate() 调用两次的问题
  4. Android 状态栏全透明策略
  5. android Application学习之一
  6. android之GSON解析JSON
  7. Android常见监听事件
  8. Android 助力云计算
  9. android listview继承BaseAdapter,自定义
  10. EditText支持Search按键搜索