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