【原始碼學習】window 新增 view
此類文章主要是用來記錄學習原始碼的過程,更多的參考別人的分析過程自己去追蹤原始碼,然後做下的記錄。看 Android 原始碼是一個痛苦的過程,之前幾次嘗試都以失敗而告終,這裡把這個過程記錄下來,算是對自己的一種激勵。
下面的所有原始碼都是基於Android_7.1.1而來。
View 的新增過程
可以知道的是 Android 中的所有檢視都是通過 Window 來呈現的,Window 是 View 的管理者。
如果做過懸浮窗的話一定對下面這個程式碼不陌生
windowManager.addView(layout,params);
可以看到向 Window 新增 View 是通過 WindowManager 來實現的。那麼就從 WindowManager 作為入口來進行研究。
WindowManager
WindowManager 是一個介面,並且繼承自 ViewManager。關於 ViewManager
public interface ViewManager{
public void addView(View view, ViewGroup.LayoutParams params);
public void updateViewLayout(View view, ViewGroup.LayoutParams params);
public void removeView(View vaiew);
}
ViewManager只有三個方法,新增、更新、刪除 View。
WindowManager 也是一個介面,它的實現類是 WindowManagerImpl(這裡可以通過activity.getWindowManager()
追蹤到)。檢視 WindowManagerImpl:
public final class WindowManagerImpl implements WindowManager {
private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();
@Override
public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
applyDefaultToken(params);
mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);
}
@Override
public void updateViewLayout(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
applyDefaultToken(params);
mGlobal.updateViewLayout(view, params);
}
@Override
public void removeView(View view) {
mGlobal.removeView(view, false);
}
}
可以看到 WindowManagerImpl 也沒有真正的實現檢視的新增、更新、刪除,而是交給了 WindowManagerGlobal 來處理,繼續追蹤。找到 WindowManagerGlobal.addView()
WindowManagerGlobal.addView()
下面是 WindowManagerGlobal.addView() 的部分程式碼:
public void addView(View view, ViewGroup.LayoutParams params,
Display display, Window parentWindow) {
// 首先檢查合法性
if (view == null) {
throw new IllegalArgumentException("view must not be null");
}
if (display == null) {
throw new IllegalArgumentException("display must not be null");
}
if (!(params instanceof WindowManager.LayoutParams)) {
throw new IllegalArgumentException("Params must be WindowManager.LayoutParams");
}
// 檢查父級 View 是否存在
final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;
if (parentWindow != null) {
// adjustLayoutParamsForSubWindow 從字面意思是調整窗口布局引數
parentWindow.adjustLayoutParamsForSubWindow(wparams);
} else {
// If there's no parent, then hardware acceleration for this view is
// set from the application's hardware acceleration setting.
final Context context = view.getContext();
if (context != null
&& (context.getApplicationInfo().flags
& ApplicationInfo.FLAG_HARDWARE_ACCELERATED) != 0) {
wparams.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
}
}
ViewRootImpl root;
View panelParentView = null;
synchronized (mLock) {
// Start watching for system property changes.
if (mSystemPropertyUpdater == null) {
mSystemPropertyUpdater = new Runnable() {
@Override public void run() {
synchronized (mLock) {
for (int i = mRoots.size() - 1; i >= 0; --i) {
mRoots.get(i).loadSystemProperties();
}
}
}
};
SystemProperties.addChangeCallback(mSystemPropertyUpdater);
}
// 檢測是否已經存在這個 view
int index = findViewLocked(view, false);
if (index >= 0) {
// 如果已經存在但是是快要移除的 view 就 移除它
if (mDyingViews.contains(view)) {
// Don't wait for MSG_DIE to make it's way through root's queue.
mRoots.get(index).doDie();
} else {
// 否則丟擲異常
throw new IllegalStateException("View " + view
+ " has already been added to the window manager.");
}
// The previous removeView() had not completed executing. Now it has.
}
// If this is a panel window, then find the window it is being
// attached to for future reference.
if (wparams.type >= WindowManager.LayoutParams.FIRST_SUB_WINDOW &&
wparams.type <= WindowManager.LayoutParams.LAST_SUB_WINDOW) {
final int count = mViews.size();
for (int i = 0; i < count; i++) {
if (mRoots.get(i).mWindow.asBinder() == wparams.token) {
panelParentView = mViews.get(i);
}
}
}
// 建立 ViewRootImpl
root = new ViewRootImpl(view.getContext(), display);
view.setLayoutParams(wparams);
// 這裡的 mViews 是儲存所有 Window 所對應的 view
mViews.add(view);
// mRoots 是儲存所有 window 對應的 ViewRootImpl
mRoots.add(root);
// mParams 儲存所有 window 對應的佈局引數
mParams.add(wparams);
}
// do this last because it fires off messages to start doing things
try {
// **最終是通過 ViewRootImpl 的 setView 方法來新增View**
root.setView(view, wparams, panelParentView);
} catch (RuntimeException e) {
// BadTokenException or InvalidDisplayException, clean up.
synchronized (mLock) {
final int index = findViewLocked(view, false);
if (index >= 0) {
removeViewLocked(index, true);
}
}
throw e;
}
}
ViewRootImpl.setView()
經過上面的分析可以知道,最終是通過 ViewRootImpl.setView() 方法來新增
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
...
// 在新增之前先進行了重新整理
requestLayout();
...
collectViewAttributes();
// 通過mWindowSession.addToDisplay 來進行顯示
res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
getHostVisibility(), mDisplay.getDisplayId(),
mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
mAttachInfo.mOutsets, mInputChannel);
...
}
在 ViewRootImpl 中可以找到 mWindowSession 初始化過程。
IWindowSession mWindowSession = WindowManagerGlobal.getWindowSession();
IWindowSession 是一個 Binder 物件,所以在新增過程中進行了 IPC 呼叫,那麼 mWindowSession 的具體實現是什麼,繼續追蹤程式碼,WindowManagerGlobal.getWindowSession() 方法如下:
public static IWindowSession getWindowSession() {
synchronized (WindowManagerGlobal.class) {
...
// getWindowManagerService 就是獲取 WindowManagerService
IWindowManager windowManager = getWindowManagerService();
sWindowSession = windowManager.openSession()
...
}
return sWindowSession;
}
}
//WindowManagerService.openSession
@Override
public IWindowSession openSession(IWindowSessionCallback callback, IInputMethodClient client,
IInputContext inputContext) {
if (client == null) throw new IllegalArgumentException("null client");
if (inputContext == null) throw new IllegalArgumentException("null inputContext");
Session session = new Session(this, callback, client, inputContext);
return session;
}
可以看到上面的 mWindowSession 物件真正實現是一個 Session。那就可以繼續追蹤到 Session.addToDisplay()
final WindowManagerService mService;
@Override
public int addToDisplay(IWindow window, int seq, WindowManager.LayoutParams attrs,
int viewVisibility, int displayId, Rect outContentInsets, Rect outStableInsets,
Rect outOutsets, InputChannel outInputChannel) {
return mService.addWindow(this, window, seq, attrs, viewVisibility, displayId,
outContentInsets, outStableInsets, outOutsets, outInputChannel);
}
通過上面的程式碼可以看到,最終是通過 WindowManagerService.addWindow 來進行新增。至於 WindowManagerService 怎麼處理的暫時不做深入了。
總結
經過上面的一通分析,向 window 新增 view 的過程已經基本清晰:
通過寫文章算是對 window 的新增過程再進行一邊梳理。水平所限可能不夠詳細,僅供參考。
參考:
- 《Android 開發藝術探索》——任玉剛