在Android的app來說,他的外表示靠Activity來建立的,因此我們就從Activity開始來看。當一個 app要被啟動是利用zygote去fork一個process來啟動。最先會跑到 main()@ActivityThread.java 然後直到handleLaunchActivity()@ActivityThread.java 來建立Activity。
handleLaunchActivity()@ActivityThread.java
//利用performLaunchActivity()回傳一個activity
Activity a = performLaunchActivity(r, customIntent);
if (a != null) {
r.createdConfig = new Configuration(mConfiguration);
Bundle oldState = r.state;
handleResumeActivity(r.token, false, r.isForward);
...
}
...
我們可以發現藉由performLaunchActivity()來建立一個activity之後呼叫handleResumeActivity() 先了解一下performLaunchActivity好了
performLaunchActivity()@ActivityThread.java
...
//launch一個activity
activity = mInstrumentation.newActivity(
cl, component.getClassName(), r.intent);
...
activity.mCalled = false;
//在這裡呼叫activity裡頭的onCreate()
mInstrumentation.callActivityOnCreate(activity, r.state);
if (!activity.mCalled) {
throw new SuperNotCalledException(
"Activity " + r.intent.getComponent().toShortString() +
" did not call through to super.onCreate()");
}
r.activity = activity;
r.stopped = true;
...
return activity;
}
我們繼續看一下mInstrumentation.newActivity() 的部份。
newActivity()@Instrumentation.java
public Activity newActivity(Class clazz, Context context,
IBinder token, Application application, Intent intent, ActivityInfo info,
CharSequence title, Activity parent, String id,
Object lastNonConfigurationInstance) throws InstantiationException,
IllegalAccessException {
Activity activity = (Activity)clazz.newInstance();
ActivityThread aThread = null;
//設定一些activity的東西
activity.attach(context, aThread, this, token, application, intent,
info, title, parent, id,
(Activity.NonConfigurationInstances)lastNonConfigurationInstance,
new Configuration());
return activity;
}
我們看一下activity.attach()
attach()@Activity.java
... //利用PolicyManager.makeNewWindow來產生一個window mWindow = PolicyManager.makeNewWindow(this); mWindow.setCallback(this); mWindow.getLayoutInflater().setPrivateFactory(this); ...那PolicyManager.makeNewWindow()是什麼呢?
makeNewWindow()@PolicyManager.java
...
static {
// Pull in the actual implementation of the policy at run-time
try {
//在run time的時候讀取
//frameworks/base/policy/src/com/android/internal/policy/impl/Policy.java
//一個PhonePolicy
Class policyClass = Class.forName(POLICY_IMPL_CLASS_NAME);
sPolicy = (IPolicy)policyClass.newInstance();
} catch (ClassNotFoundException ex) {
...
}
}
// The static methods to spawn new policy-specific objects
public static Window makeNewWindow(Context context) {
return sPolicy.makeNewWindow(context);
}
如果我們去看Policy.java就可以發現真正的makeNewWindow了makeNewWindow()@Policy.java
public Window makeNewWindow(Context context) {
return new PhoneWindow(context);
}
回到performLaunchActivity(),他會去呼叫Activity裡面的onCreate()而在這裡面我們會利用setContentView()來設定我們的UI,接著看一下handleResumeActivity()handleResumeActivity()@ActivityThread.java
...
if (r.window == null && !a.mFinished && willBeVisible) {
r.window = r.activity.getWindow();
//取得decorView
View decor = r.window.getDecorView();
decor.setVisibility(View.INVISIBLE);
//取得WindowManager
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;
//將取得的 decor加入取得的WindowManager
wm.addView(decor, l);
}
}
...
這裡有兩個地方可以看的。第一個這個decorView是什麼呢?第二個,這個WindowManager又是哪來的呢?
第一部份,r.window.getDecorView()的前面r.window是什麼呢?剛剛我們追過就是phoneWindow所以getDecorView()我們就直接看PhoneWindow.getDecorView()
getDecorView()@PhoneWindow.java
@Override
public final View getDecorView() {
if (mDecor == null) {
installDecor();
}
return mDecor;
}
private void installDecor() {
if (mDecor == null) {
mDecor = generateDecor();
mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
mDecor.setIsRootNamespace(true);
}
if (mContentParent == null) {
//generateLayout input是mDecor return 一個viewgroup
mContentParent = generateLayout(mDecor);
...
}
protected ViewGroup generateLayout(DecorView decor) {
...
//取得title 並加入decor中
View in = mLayoutInflater.inflate(layoutResource, null);
decor.addView(in, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
//findViewById實際上是return getDecorView().findViewById(id);
//他會去尋找{@link android.app.Activity#onCreate}所定義的xml
ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
if (contentParent == null) {
throw new RuntimeException("Window couldn't find content container view");
}
...
return contentParent;
}
因此我們可以發現,最剛開始windows manager裡面的view是一個PhoneWindow而PhoneWindow裡面是一個decorView裡面又包含了programmer定義的view 這是一個Decorator的design pattern 接著分析完了handleResumeActivity中的view, viewmanager之後我們繼續看 wm.addView(decor,l)
addView()@Window.java
public final void addView(View view, ViewGroup.LayoutParams params) {
// Let this throw an exception on a bad params.
WindowManager.LayoutParams wp = (WindowManager.LayoutParams)params;
CharSequence curTitle = wp.getTitle();
...
super.addView(view, params);
}
而LocalWindowManager 他是extends WindowManagerImpl.CompatModeWrapper, 因此我們去看一下WindowManagerImpl 的CompatModeWrapper才知道addView是寫些什麼addView()@WindowManagerImpl.java
public void addView(View view, ViewGroup.LayoutParams params) {
addView(view, params, null, false);
}
private void addView(View view, ViewGroup.LayoutParams params,
CompatibilityInfoHolder cih, boolean nest) {
...
//ViewRootImpl是我們接下來的重點
ViewRootImpl root;
View panelParentView = null;
synchronized (this) {
...
root = new ViewRootImpl(view.getContext());
root.mAddNesting = 1;
...
mViews[index] = view;
mRoots[index] = root;
mParams[index] = wparams;
}
// do this last because it fires off messages to start doing things
//這裡的view是我們一開始addView(mDecor,l)中的mDecor
root.setView(view, wparams, panelParentView);
}
我們進入ViewRootImpl了解一下ViewRootImpl.java
//ViewRootImpl繼承了Handler
public final class ViewRootImpl extends Handler implements ViewParent,
View.AttachInfo.Callbacks, HardwareRenderer.HardwareDrawCallbacks {
//這裡建立一個surface 的 object
private final Surface mSurface = new Surface();
//還有一個W 的object, W這個class 是繼承IWindow.stub
final W mWindow;
....
public ViewRootImpl(Context context) {
....
//初始化對
getWindowSession(context.getMainLooper());
....
mWindow = new W(this);
....
}
....
}
public static IWindowSession getWindowSession(Looper mainLooper) {
synchronized (mStaticInit) {
if (!mInitialized) {
try {
InputMethodManager imm = InputMethodManager.getInstance(mainLooper);
//open session透過Binder與WindowManagerService做溝通做溝通
sWindowSession = Display.getWindowManager().openSession(
imm.getClient(), imm.getInputContext());
mInitialized = true;
} catch (RemoteException e) {
}
}
return sWindowSession;
}
}
ok看完了ViewRootImpl的一些global variable及constructure之後我們回到剛剛的root.setView()
setView()@ViewRootImpl.java
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
synchronized (this) {
if (mView == null) {
mView = view;
...
// Schedule the first layout -before- adding to the window
// manager, to make sure we do the relayout before receiving
// any other events from the system.
requestLayout();//實際上就是scheduleTraversals()
...
try {
mOrigWindowType = mWindowAttributes.type;
res = sWindowSession.add(mWindow, mSeq, mWindowAttributes,
getHostVisibility(), mAttachInfo.mContentInsets,
mInputChannel);
}
...
}
public void requestLayout() {
checkThread();
mLayoutRequested = true;
scheduleTraversals();
}
public void scheduleTraversals() {
if (!mTraversalScheduled) {
mTraversalScheduled = true;
//noinspection ConstantConditions
if (ViewDebug.DEBUG_LATENCY && mLastTraversalFinishedTimeNanos != 0) {
final long now = System.nanoTime();
Log.d(TAG, "Latency: Scheduled traversal, it has been "
+ ((now - mLastTraversalFinishedTimeNanos) * 0.000001f)
+ "ms since the last traversal finished.");
}
sendEmptyMessage(DO_TRAVERSAL);
}
}
public void handleMessage(Message msg) {
switch (msg.what) {
...
case DO_TRAVERSAL:
...
performTraversals();
...
}
private void performTraversals() {
....
//取得一個surface
relayoutResult = relayoutWindow(params, viewVisibility, insetsPending);
....
//從surface裡面取得一個canvas然後畫在上面再把他show到螢幕
draw(fullRedrawNeeded);
....
}
private int relayoutWindow(WindowManager.LayoutParams params, int viewVisibility,
boolean insetsPending) throws RemoteException {
....
//透過IWindowSession與WindowManagerService溝通取得一個surface
int relayoutResult = sWindowSession.relayout(
mWindow, mSeq, params,
(int) (mView.getMeasuredWidth() * appScale + 0.5f),
(int) (mView.getMeasuredHeight() * appScale + 0.5f),
viewVisibility, insetsPending ? WindowManagerImpl.RELAYOUT_INSETS_PENDING : 0,
mWinFrame, mPendingContentInsets, mPendingVisibleInsets,
mPendingConfiguration, mSurface);
//Log.d(TAG, "<<<<<< BACK FROM relayout");
....
return relayoutResult;
}
private void draw(boolean fullRedrawNeeded) {
//mSurface是剛剛ViewRootImpl的global variable
Surface surface = mSurface;
...
if (!dirty.isEmpty() || mIsAnimating) {
Canvas canvas;
try {
int left = dirty.left;
int top = dirty.top;
int right = dirty.right;
int bottom = dirty.bottom;
....
//lock一塊canvas
canvas = surface.lockCanvas(dirty);
....
try {
....
//把東西畫到canvas上
mView.draw(canvas);
} finally {
....
}
....
}
} finally {
//把canvas show到螢幕上
surface.unlockCanvasAndPost(canvas);
}
}
....
if (animating) {
mFullRedrawNeeded = true;
scheduleTraversals();
}
}
總結一下,在這一篇我們說了- 建立一個Activity之後會有一個PhoneWindow 這個PhoneWindow裡面包含了DecorView他是一個framelayout 我們的app所畫得ui會包含在Decorview裡頭
- 建立一個Activity之後也會產生一個LocalWindowManager主要實做在WindowManagerImpl而他包含了ViewRootImpl
- ViewRootImpl包含了一個view指向DecorView及透過Binder從WindowServiceManager取得的surface
- 要畫出ui首先透過mSurface lock一塊canvas 然後透過mView.draw畫在上面 然後unlock 之後把他show在螢幕上
此篇是依照鄧凡平的Android系統原理深入解析 去trace 而成
