歡迎您光臨本站 註冊首頁

Android子線程在沒有ViewRoot的情況下能刷新UI嗎?

←手機掃碼閱讀     火星人 @ 2014-03-12 , reply:0
  

如果你看了我寫的《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能成功嗎?別回答太快喲!好好想想!



[火星人 ] Android子線程在沒有ViewRoot的情況下能刷新UI嗎?已經有754次圍觀

http://coctec.com/docs/program/show-post-71502.html