分析 Activity Window與View的關係

James Lin
8 min readJun 28, 2018

--

他們的角色
Activity 是一個 controller
Window 是負責顯示 View,沒有螢幕元件
View 則是握有螢幕的元件
Activity 是怎麼運作的呢?
簡單的簡介,Activity 的啟動。
從 startActivity,他會呼叫 Instrumentation,然後 Instrumentation 通過Binder 向 AMS(ActivityManagerService) 發送請求,透過 IPC 啟動 Activity。而這個 AIDL 操作的方法被定義在 ApplicationThread 中(裡面會呼叫 Activity生命週期的方法),接著透過 Handler 回到 Main Thread 取啟動 Activity。

附上Insturmentation source code:https://android.googlesource.com/platform/frameworks/base/+/master/core/java/android/app/Instrumentation.java

View 是透過 Window 與 Activity 溝通的
每一個 Activity 都有唯一的 PhoneWindow,他可以在 PhoneWindow 上增加很多的 Window(Dialog …等等)。
看一下 Activity 的 setContentView 的 Source code,他是使用 window 的setContentView。


public void setContentView(@LayoutRes int layoutResID) {
getWindow().setContentView(layoutResID);
initWindowDecorActionBar();
}

public Window getWindow() {
return mWindow;
}

看到 attach 的 Source code。
mWindow = new PhoneWindow(this);
所以在 attach,是真的 new 一個 PhoneWindow。


final void attach(Context context, ActivityThread aThread,
Instrumentation instr, IBinder token, int ident,
Application application, Intent intent, ActivityInfo info,
CharSequence title, Activity parent, String id,
NonConfigurationInstances lastNonConfigurationInstances,
Configuration config, String referrer, IVoiceInteractor voiceInteractor) {
attachBaseContext(context);
mFragments.attachHost(null /*parent*/);mWindow = new PhoneWindow(this);
mWindow.setCallback(this);
mWindow.setOnWindowDismissedCallback(this);
mWindow.getLayoutInflater().setPrivateFactory(this);
if (info.softInputMode != WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED) {
mWindow.setSoftInputMode(info.softInputMode);
}
if (info.uiOptions != 0) {
mWindow.setUiOptions(info.uiOptions);
}
mUiThread = Thread.currentThread();
mMainThread = aThread;
mInstrumentation = instr;
mToken = token;
mIdent = ident;
mApplication = application;
mIntent = intent;
mReferrer = referrer;
mComponent = intent.getComponent();
mActivityInfo = info;
mTitle = title;
mParent = parent;
mEmbeddedID = id;
mLastNonConfigurationInstances = lastNonConfigurationInstances;
if (voiceInteractor != null) {
if (lastNonConfigurationInstances != null) {
mVoiceInteractor = lastNonConfigurationInstances.voiceInteractor;
} else {
mVoiceInteractor = new VoiceInteractor(voiceInteractor, this, this,
Looper.myLooper());
}
}
mWindow.setWindowManager(
(WindowManager)context.getSystemService(Context.WINDOW_SERVICE),
mToken, mComponent.flattenToString(),
(info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);
if (mParent != null) {
mWindow.setContainer(mParent.getWindow());
}
mWindowManager = mWindow.getWindowManager();
mCurrentConfig = config;
}

再來看看 PhoneWindow 的 Source code

private ViewGroup mContentParent;
public void setContentView(int layoutResID) {
if (mContentParent == null) {
installDecor();
} else {
mContentParent.removeAllViews();
}
mLayoutInflater.inflate(layoutResID, mContentParent);
final Callback cb = getCallback();
if (cb != null && !isDestroyed()) {
cb.onContentChanged();
}
}

很明顯地看到 mContentParent 是一個 View Group,所以說 setContentView就是把 layout 設定到這個上面。

目前的 Android 是透過 AppCompatDelegate 去 setContentView 而不是直接使用 Activity 的 setContentView。
簡易的簡報:https://docs.google.com/presentation/d/1c4j8Iaxv_no4DLIMpv6JbAF_atTGi1E0GykjAQaHhTA/edit?usp=sharing

看到這張圖,還有一層 DecorView 就只是要更好的管理 Layout,上面有ActionBar。

P.S

如果是用 LayoutInflater 去動態新增 View 的話,或者使用 View 元件的addView,這是直接對 View 操作,並無經過 Window。

使用 Activity 的 addContentView 去新增 View 的話,會透過 Window 新增 View 到畫面上。

提個外話,有趣的小發現。

Activity VS AppCompatActivity

  • 是最基礎的Activity
  • 基於Activity, FragmentActivity 更有靈活度的使用Fragment.
  • 基於FragmentActivity, AppCompatActivity 可以更佳的使用ActionBar

MotionEvent的座標

1. getRawX and getX
getRawX指的是x座標永遠都根據左上方值去變化,也就是說手機螢幕 最左上方是 (0,0)
getX獲得的是touch的位置,意指永遠不會超過View的寬度
Y軸與X軸是一樣的邏輯。

Window 與 View 中間的溝通是透過 WindowManager。
這個就留到下次再說吧~~

GitHub

--

--

James Lin
James Lin

No responses yet