1. 程式人生 > >跟進View的常見週期回撥以及View.post和Activity.runOnUiThread

跟進View的常見週期回撥以及View.post和Activity.runOnUiThread

activity_main.xml:

<LinearLayout android:layout_width="match_parent" android:layout_height="match_parent">
    <TextView android:id="@+id/text_view" android:layout_width="wrap_content" android:layout_height="wrap_content"/>
</LinearLayout>

AndroidManifest.xml:

<activity
android:name=".MainActivity">
<intent-filter> <action android:name="android.intent.action.MAIN"/> <category android:name="android.intent.category.LAUNCHER"/> </intent-filter> </activity>

MainActivity:

protected void onCreate(Bundle savedInstanceState) {
    super
.onCreate(savedInstanceState); setContentView(R.layout.activity_main); final View view = findViewById(R.id.text_view); runOnUiThread(new Runnable() { @Override public void run() { System.err.println(Thread.currentThread() + " + Activity.runOnUiThread(Runnable)"
); } }); if (view != null) { view.post(new Runnable() { @Override public void run() { System.err.println(Thread.currentThread() + " + View.post(Runnable)"); } }); } }

自定義TextView覆寫的”生命週期”回撥方法:

onAttachedToWindow();
onDetachedFromWindow();
onWindowVisibilityChanged(int visibility);
onWindowFocusChanged(boolean hasWindowFocus);
onVisibilityChanged(View changedView, int visibility);
onFinishInflate();
onLayout(boolean changed, int left, int top, int right, int bottom);
onMeasure(int widthMeasureSpec, int heightMeasureSpec);
onDraw(Canvas canvas);
onSizeChanged(int w, int h, int oldw, int oldh);

下面是日誌:
launch:

  1. after first onMeasure, getMeasuredWidth() and getMeasuredHeight() worked;
  2. onSizeChanged or after, getMeasuredWidth()/getMeasuredHeight()/getWidth()/getHeight() worked, until after onDetachedFromWindow;
08-30 12:33:07.680 9816-9816/com.test W/System.err: onFinishInflate
08-30 12:33:07.681 9816-9816/com.test W/System.err: Thread[main,5,main] + Activity.runOnUiThread(Runnable)
08-30 12:33:07.708 9816-9816/com.test W/System.err: onAttachedToWindow
08-30 12:33:07.708 9816-9816/com.test W/System.err: onWindowVisibilityChanged
08-30 12:33:07.708 9816-9816/com.test W/System.err: onVisibilityChanged
08-30 12:33:07.712 9816-9816/com.test W/System.err: onMeasure
08-30 12:33:07.771 9816-9816/com.test W/System.err: onMeasure
08-30 12:33:07.771 9816-9816/com.test W/System.err: onSizeChanged
08-30 12:33:07.771 9816-9816/com.test W/System.err: onLayout
08-30 12:33:07.802 9816-9816/com.test W/System.err: Thread[main,5,main] + View.post(Runnable)
08-30 12:33:07.802 9816-9816/com.test W/System.err: onWindowFocusChanged
08-30 12:33:07.804 9816-9816/com.test W/System.err: onMeasure
08-30 12:33:07.804 9816-9816/com.test W/System.err: onLayout
08-30 12:33:07.804 9816-9816/com.test W/System.err: onDraw

home:

08-30 12:35:38.628 9816-9816/com.test W/System.err: onWindowFocusChanged
08-30 12:35:38.747 9816-9816/com.test W/System.err: onWindowVisibilityChanged
08-30 12:35:38.989 9816-9816/com.test W/System.err: onVisibilityChanged

re-foreground:

08-30 12:35:59.959 9816-9816/com.test W/System.err: onVisibilityChanged
08-30 12:35:59.967 9816-9816/com.test W/System.err: onWindowVisibilityChanged
08-30 12:35:59.988 9816-9816/com.test W/System.err: onWindowFocusChanged
08-30 12:36:00.001 9816-9816/com.test W/System.err: onDraw

back:

08-30 12:36:25.576 9816-9816/com.test W/System.err: onWindowFocusChanged
08-30 12:36:25.668 9816-9816/com.test W/System.err: onWindowVisibilityChanged
08-30 12:36:25.678 9816-9816/com.test W/System.err: onDetachedFromWindow

re-launch or rotate-screen:

08-30 12:36:50.175 9816-9816/com.test W/System.err: onFinishInflate
08-30 12:36:50.175 9816-9816/com.test W/System.err: Thread[main,5,main] + Activity.runOnUiThread(Runnable)
08-30 12:36:50.182 9816-9816/com.test W/System.err: onAttachedToWindow
08-30 12:36:50.182 9816-9816/com.test W/System.err: onWindowVisibilityChanged
08-30 12:36:50.182 9816-9816/com.test W/System.err: onVisibilityChanged
08-30 12:36:50.183 9816-9816/com.test W/System.err: onMeasure
08-30 12:36:50.219 9816-9816/com.test W/System.err: onMeasure
08-30 12:36:50.220 9816-9816/com.test W/System.err: onSizeChanged
08-30 12:36:50.220 9816-9816/com.test W/System.err: onLayout
08-30 12:36:50.222 9816-9816/com.test W/System.err: Thread[main,5,main] + View.post(Runnable)
08-30 12:36:50.222 9816-9816/com.test W/System.err: onWindowFocusChanged
08-30 12:36:50.232 9816-9816/com.test W/System.err: onMeasure
08-30 12:36:50.232 9816-9816/com.test W/System.err: onLayout
08-30 12:36:50.232 9816-9816/com.test W/System.err: onDraw

lock-screen or show-status-panel:

08-30 12:38:12.882 9816-9816/com.test W/System.err: onWindowFocusChanged

unlock-screen or hide-status-panel:

08-30 12:38:12.882 9816-9816/com.test W/System.err: onWindowFocusChanged

關於View.post和Activity.runOnUiThread的札記

先看Activity的runOnUiThread的原始碼:

final Handler mHandler = new Handler();
public final void runOnUiThread(Runnable action) {
    if (Thread.currentThread() != mUiThread) {
        mHandler.post(action);
    } else {
        action.run();
    }
}

可以看出, 如果當前是主執行緒則直接執行, 否則立即投遞到主執行緒Handler執行;
再看View的post的原始碼:

public boolean post(Runnable action) {
    final AttachInfo attachInfo = mAttachInfo;
    if (attachInfo != null) {
        return attachInfo.mHandler.post(action);
    }
    // Assume that post will succeed later
    ViewRootImpl.getRunQueue().post(action);
    return true;
}

如果mAttachInfo不為null, 則投遞到它的Handler中執行, 否則加入到ViewRootImpl.getRunQueue()這個佇列裡.
那麼什麼時候, mAttachInfo不為null呢? 這裡我直接給出答案: (請按照此流程瀏覽原始碼)

我們跳過一些路徑, 從Activity的onResume()開始說, 它是在handleResumeActivity()方法裡的performResumeActivity()裡完成的.
在這之後, handleResumeActivity()裡, 有呼叫WindowManagerImpl.addView()將PhoneWindow.mDecorView(DecorView型別, 繼承自FrameLayout)新增到窗體中.
然後路徑到了WindowManagerGlobal.addView()(一個 Activity對應一個WindowManagerImpl, 但WindowManagerGlobal程序唯一), 這裡new ViewRootImpl(), 呼叫此物件的setView().
setView()裡賦值ViewRootImpl的mAttachInfo, 然後首次呼叫requestLayout(), 此方法裡呼叫scheduleTraversals(), 進而呼叫 performTraversals().
performTraversals()裡呼叫頂層view的dispatchAttachedToWindow(), 因為頂層view是FrameLayout, 所以呼叫ViewGroup的dispatchAttachedToWindow(), 裡面遍歷呼叫所有子view的dispatchAttachedToWindow(), 並傳入mAttachInfo(一直傳遞), 直到呼叫View類的dispatchAttachedToWindow().
此時, mAttachInfo不再為null, 而且各層的onAttachedToWindow()得到回撥.
另外, onAttachedToWindow()真正執行順序依次是 View.onAttachedToWindow(), ViewGroup.onAttachedToWindow(), DecorView.onAttachedToWindow()(當然表面的呼叫順序是反過來的, 使用super執行).
所以你在Activity的onCreate()裡呼叫view.post, 實際走的是:ViewRootImpl.getRunQueue().post(action); 看程式碼:

static final class RunQueue {
        private final ArrayList<HandlerAction> mActions = new ArrayList<HandlerAction>();

        void post(Runnable action) {
            postDelayed(action, 0);
        }

        void postDelayed(Runnable action, long delayMillis) {
            HandlerAction handlerAction = new HandlerAction();
            handlerAction.action = action;
            handlerAction.delay = delayMillis;

            synchronized (mActions) {
                mActions.add(handlerAction);
            }
        }

        void executeActions(Handler handler) {
            synchronized (mActions) {
                final ArrayList<HandlerAction> actions = mActions;
                final int count = actions.size();

                for (int i = 0; i < count; i++) {
                    final HandlerAction handlerAction = actions.get(i);
                    handler.postDelayed(handlerAction.action, handlerAction.delay);
                }

                actions.clear();
            }
        }
    }

也就是post只是加入到了陣列中, 關鍵是什麼時候呼叫executeActions(Handler)?
答案還是在ViewRootImpl的performTraversals()裡呼叫, 而且還是使用mAttachInfo的Handler.
其實, 這個Handler是在ViewRootImpl的構造方法中傳遞給mAttachInfo(AttachInfo)的構造器的, 這個Handler在ViewRootImpl中使用主執行緒Looper構建, 屬於ViewRootHandler例項.