Welcome 微信登录

首页 / 操作系统 / Linux / Android子线程在没有ViewRoot的情况下能刷新UI吗?

如果你看了我写的《Android里子线程真的不能刷新UI吗? 》,会回答:不能。那么到底能不能呢?呵呵,其实是能的了。那么《Android里子线程真的不能刷新UI吗? 》里写错了吗?嗯,没有。呵呵,相信大家看到这里一定是一头雾水,认为笔者自相矛盾了。让我们看个实例吧:package com.david.test.helloworld; import Android.app.Activity;import android.os.Bundle;import android.widget.Button; public class TestActivity extends Activity {    Button btn = null;     /** Called when the activity is first created. */    public void onCreate(Bundle savedInstanceState) {       super.onCreate(savedInstanceState);       setContentView(R.layout.main);        btn = (Button) findViewById(R.id.Button01);        TestThread2 t = new TestThread2(btn);       t.start();    }     class TestThread2 extends Thread {       Button btn = null;        public TestThread2(Button btn) {           this.btn = btn;       }        @Override       public void run() {           btn.setText("TestThread2.run");       }    }}建立一个工程,将上述代码拷贝进去,运行看看吧! Btn的文本一定改变为"TestThread2.run"了。那么这到底是怎么回事呢?当我发现这个问题时,也困惑了。经过一番调查后,真相大白。现在和大家分享一下。奥秘在于ViewRoot的建立时间,它是在ActivityThread.java的final void handleResumeActivity(IBinder token, boolean clearHide, boolean isForward)里创建的。看看代码吧:    final void handleResumeActivity(IBinder token, boolean clearHide, boolean isForward) {        // If we are getting ready to gc after going to the background, well        // we are back active so skip it.        unscheduleGcIdler();         ActivityRecord r = performResumeActivity(token, clearHide);         if (r != null) {            final Activity a = r.activity;             if (localLOGV) Slog.v(                TAG, "Resume " + r + " started activity: " +                a.mStartedActivity + ", hideForNow: " + r.hideForNow                + ", finished: " + a.mFinished);             final int forwardBit = isForward ?                    WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION : 0;             // If the window hasn"t yet been added to the window manager,            // and this guy didn"t finish itself or start another activity,            // then go ahead and add the window.            boolean willBeVisible = !a.mStartedActivity;            if (!willBeVisible) {                try {                    willBeVisible = ActivityManagerNative.getDefault().willActivityBeVisible(                            a.getActivityToken());                } catch (RemoteException e) {                }            }            if (r.window == null && !a.mFinished && willBeVisible) {                r.window = r.activity.getWindow();                View decor = r.window.getDecorView();                decor.setVisibility(View.INVISIBLE);                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);                }             // If the window has already been added, but during resume            // we started another activity, then don"t yet make the            // window visible.            } else if (!willBeVisible) {                if (localLOGV) Slog.v(                    TAG, "Launch " + r + " mStartedActivity set");                r.hideForNow = true;            }             // The window is now visible if it has been added, we are not            // simply finishing, and we are not starting another activity.            if (!r.activity.mFinished && willBeVisible                    && r.activity.mDecor != null && !r.hideForNow) {                if (r.newConfig != null) {                    if (DEBUG_CONFIGURATION) Slog.v(TAG, "Resuming activity "                            + r.activityInfo.name + " with newConfig " + r.newConfig);                    performConfigurationChanged(r.activity, r.newConfig);                    r.newConfig = null;                }                if (localLOGV) Slog.v(TAG, "Resuming " + r + " with isForward="                        + isForward);                WindowManager.LayoutParams l = r.window.getAttributes();                if ((l.softInputMode                        & WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION)                        != forwardBit) {                    l.softInputMode = (l.softInputMode                            & (~WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION))                            | forwardBit;                    if (r.activity.mVisibleFromClient) {                        ViewManager wm = a.getWindowManager();                        View decor = r.window.getDecorView();                        wm.updateViewLayout(decor, l);                    }                }                r.activity.mVisibleFromServer = true;                mNumVisibleActivities++;                if (r.activity.mVisibleFromClient) {                    r.activity.makeVisible();                }            }             r.nextIdle = mNewActivities;            mNewActivities = r;            if (localLOGV) Slog.v(                TAG, "Scheduling idle handler for " + r);            Looper.myQueue().addIdleHandler(new Idler());         } else {            // If an exception was thrown when trying to resume, then            // just end this activity.            try {                ActivityManagerNative.getDefault()                    .finishActivity(token, Activity.RESULT_CANCELED, null);            } catch (RemoteException ex) {            }        }}呵呵,相信到了这里,看过《Android里子线程真的不能刷新UI吗?  》的读者一定明白了。答案就是在Activity.onResume前,ViewRoot实例没有建立,所以没有ViewRoot.checkThread检查。而btn.setText时设定的文本却保留了下来,所以当ViewRoot真正去刷新界面时,就把"TestThread2.run"刷了出来!最后,提个问题结束吧:activity.onStart里通过线程刷新UI能成功吗?别回答太快哟!好好想想!