Android 里子线程真的不能刷新UI吗?

Android 里子线程真的不能刷新UI吗?

在开发应用中,如果子线程中更新UI会抛出异常,但并不是因为只有UI线程才能更新UI,
而是因为ViewRootImpl会进行检查,如果 mThread!=当前线程 时会抛出异常CalledFromWrongThreadException异常

ViewRootImple.java

void checkThread() {
    if (mThread != Thread.currentThread()) {
        throw new CalledFromWrongThreadException(
                "Only the original thread that created a view hierarchy can touch its views.");
    }
}  

那mThread是什么呢?
通过ViewRootImpl的构造函数我们可以发现mThread会被赋值为创建ViewRootImp的那个线程

public ViewRootImpl(Context context, Display display) {
    mContext = context;
    mWindowSession = WindowManagerGlobal.getWindowSession();

    //此处进行mThread的赋值
    mThread = Thread.currentThread();
    //代码省略...
}

而ViewRootImpl是在主线程中创建的,所以,才需要在主线程中更新UI

不过,设定为在主线程更新UI也是为了安全和简化起见吧

那么,能否在子线程中更新UI呢

如果ViewRootImpl是由子线程创造的,那么自然可以在该子线程中更新UI

但是如果我们直接创建ViewRootImpl实例的话,会发现找不到该类。

可以通过WindowManager.addView来间接创建一个ViewRootImpl

比如

 class TestThread1 extends Thread{
          @Override
          public void run() {
              Looper.prepare();

              TextView tx = new TextView(MainActivity.this);
              tx.setText("test11111111111111111");

              WindowManager wm = MainActivity.this.getWindowManager();
              WindowManager.LayoutParams params = new WindowManager.LayoutParams(
                250, 250, 200, 200, WindowManager.LayoutParams.FIRST_SUB_WINDOW, 
                WindowManager.LayoutParams.TYPE_TOAST,PixelFormat.OPAQUE);
              wm.addView(tx, params); 

              Looper.loop();
          }
}

MainActivity是建立android工程时生成的入口类,TestThread1是MainActivity的内部类。感兴趣的话,试试吧!看看是不是在屏幕上看到了”test11111111111111111”?

具体创建ViewRoot的地方在wm.addView(tx, params)
具体流程:
WindowManagerImpl.addView(View view, ViewGroup.LayoutParams params)
->
WindowManagerImpl.addView(View view, ViewGroup.LayoutParams params, boolean nest)

代码(精简):

public void addView(View view, ViewGroup.LayoutParams params,
        Display display, Window parentWindow) {

    final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams)params;

    ViewRootImpl root;
    View panelParentView = null;

    synchronized (mLock) {

        //新建ViewRootImpl
        root = new ViewRootImpl(view.getContext(), display);

        view.setLayoutParams(wparams);

        mViews.add(view);
        mRoots.add(root);
        mParams.add(wparams);
    }

    root.setView(view, wparams, panelParentView);
}

其他

参考
http://www.oschina.net/question/54100_29585

你可能感兴趣的:(android,更新,主线程,UI线程,子线程)