概述
分析surface
系统主要从两条线分析
应用程序和surface的关系
Surface
与SurfaceFlinger
之间的关系
在应用中,不管是二维图像还是三维图像,应用最终都要和Surface
交互,Surface
就像UI
画布,应用则像是在Surface
上作画
Surface
与SurfaceFlinger
,Surface
向SurfaceFlinger
提供数据,SurfaceFlinger
则混合数据。
1 一个Activity的显示
1.1 Activity的创建
在之前的Zygote
分析中我们知道,当Zygote
接受到请求后会fork
出一个子进程,这个子进程的入口函数就是ActivityThread
的main
函数,这就是一个应用开启的地方。ActivityThread
类中有一个handleLaunchActivity()
函数,他就是创建Activity
的地方
frameworks/base/core/java/android/app/ActivityThread.java
private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent, String reason) {
// If we are getting ready to gc after going to the background, well
// we are back active so skip it.
unscheduleGcIdler();
mSomeActivitiesChanged = true;
if (r.profilerInfo != null) {
mProfiler.setProfiler(r.profilerInfo);
mProfiler.startProfiling();
}
// Make sure we are running with the most recent config.
handleConfigurationChanged(null, null);
WindowManagerGlobal.initialize();
//调用performLaunchActivity返回一个Activity [#1.1.1]
Activity a = performLaunchActivity(r, customIntent);
if (a != null) {
r.createdConfig = new Configuration(mConfiguration);
reportSizeConfigurations(r);
Bundle oldState = r.state;
//[#1.1.2]
handleResumeActivity(r.token, false, r.isForward,
!r.activity.mFinished && !r.startsNotResumed, r.lastProcessedSeq, reason);
...
}
...
}
1.1.1 创建Activity
frameworks/base/core/java/android/app/ActivityThread.java
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
ActivityInfo aInfo = r.activityInfo;
...
ComponentName component = r.intent.getComponent();
...
Activity activity = null;
try {
java.lang.ClassLoader cl = r.packageInfo.getClassLoader();
//通过类名创建Acrivity
activity = mInstrumentation.newActivity(
cl, component.getClassName(), r.intent);
StrictMode.incrementExpectedActivityCount(activity.getClass());
r.intent.setExtrasClassLoader(cl);
r.intent.prepareToEnterProcess();
if (r.state != null) {
r.state.setClassLoader(cl);
}
} catch (Exception e) {
...
}
try {
Application app = r.packageInfo.makeApplication(false, mInstrumentation);
....
if (activity != null) {
//创建appContext
Context appContext = createBaseContextForActivity(r, activity);
...
}
//下面这个函数会调用Activity的onCreate()函数
if (r.isPersistable()) {
mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
} else {
mInstrumentation.callActivityOnCreate(activity, r.state);
}
...
return activity;
}
performLaunchActivity()
根据类名以ClassLoader.loadClass(className).newInstance()
创建一个Activity
,还会调用到Activity
的onCreate()
函数,在onCreate()
中我们会调用setContentView()
来设置外观。
1.1.2 分析handleResumeActivity
通过performLaunchActivity()
已经创建好了一个Activity
并且设置了布局。
final void handleResumeActivity(IBinder token,
boolean clearHide, boolean isForward, boolean reallyResume, int seq, String reason) {
ActivityClientRecord r = mActivities.get(token);
if (!checkAndUpdateLifecycleSeq(seq, r, "resumeActivity")) {
return;
}
// If we are getting ready to gc after going to the background, well
// we are back active so skip it.
unscheduleGcIdler();
mSomeActivitiesChanged = true;
// TODO Push resumeArgs into the activity for consideration
r = performResumeActivity(token, clearHide, reason);
if (r != null) {
final Activity a = r.activity;
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) {
throw e.rethrowFromSystemServer();
}
}
if (r.window == null && !a.mFinished && willBeVisible) {
r.window = r.activity.getWindow();
//获得顶层view []
View decor = r.window.getDecorView();
decor.setVisibility(View.INVISIBLE);
//获得ViewManager []
ViewManager wm = a.getWindowManager();
WindowManager.LayoutParams l = r.window.getAttributes();
a.mDecor = decor;
l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
l.softInputMode |= forwardBit;
if (r.mPreserveWindow) {
a.mWindowAdded = true;
r.mPreserveWindow = false;
// Normally the ViewRoot sets up callbacks with the Activity
// in addView->ViewRootImpl#setView. If we are instead reusing
// the decor view we have to notify the view root that the
// callbacks may have changed.
ViewRootImpl impl = decor.getViewRootImpl();
if (impl != null) {
impl.notifyChildRebuilt();
}
}
if (a.mVisibleFromClient && !a.mWindowAdded) {
a.mWindowAdded = true;
//把decor view对象添加到ViewManager中
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) {
...//
}
...//
}
2 分析setContentView
在handleResumeAcivity()
中涉及的都是UI相关的,在上一preformLaunchActivity()
函数中,说到会调用onCreate()
,onCreate()
中一般都是设置布局。所以要理解handleResumeActivity()
需要先看看onCreate()
结束,系统为我们提供了什么。
frameworks/base/core/java/android/app/Activity.java
public void setContentView(View view) {
getWindow().setContentView(view);
initWindowDecorActionBar();
}
这个函数有很多重载,但是都是最终都是调用getWindow().setContentView()
,getWindow
返回的是一个Activity
中的Window
对象,Window
是一个抽象类。
Window
:
Abstract base class for a top-level window look and behavior policy. An instance of this class should be used as the top-level view added to the window manager. It provides standard UI policies such as a background, title area, default key processing, etc.
The only existing implementation of this abstract class is android.view.PhoneWindow, which you should instantiate when needing a Window.
window
是一个抽象的基类,控制顶层窗口的外观和行为策略。提供背景啊,标题,按键处理等。同时也提到唯一的实现类是PhoneWindow
,getWindow()
返回的应该就是一个PhoneWindow
实例。
另一个和UI有关的就是View
:
This class represents the basic building block for user interface components. A View occupies a rectangular area on the screen and is responsible for drawing and event handling. View is the base class for widgets, which are used to create interactive UI components (buttons, text fields, etc.). The
ViewGroup
subclass is the base class for layouts, which are invisible containers that hold other Views (or other ViewGroups) and define their layout properties.
View
代表了一个基本的UI待援,一个View
占据一个块矩形的区域,负责绘制和处理事件...
2.1 Activity的Window
在performLaunchActivity()
中,创建了Activity
对象后,接着又执行了Activity.attch()
。
frameworks/base/core/java/android/app/Activity.java
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,
Window window) {
attachBaseContext(context);
mFragments.attachHost(null /*parent*/);
//真正的Window
mWindow = new PhoneWindow(this, window);
mWindow.setWindowControllerCallback(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());
}
}
//设置windowManager
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());
}
//保存windowManager
mWindowManager = mWindow.getWindowManager();
mCurrentConfig = config;
}
通过上面代码就可以明确知道Activity
中的mWindow
是PhoneWindow
实例,那么mWindowManager
又是谁?
2.2 真正的windowManager
frameworks/base/core/java/android/view/Window.java
public void setWindowManager(WindowManager wm, IBinder appToken, String appName,
boolean hardwareAccelerated) {
mAppToken = appToken;
mAppName = appName;
mHardwareAccelerated = hardwareAccelerated
|| SystemProperties.getBoolean(PROPERTY_HARDWARE_UI, false);
if (wm == null) {
wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
}
//这里调用了windowManagerImpl的方法创建windowmanager
mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this);
}
frameworks/base/core/java/android/view/WindowManagerImpl.java
public WindowManagerImpl createLocalWindowManager(Window parentWindow) {
return new WindowManagerImpl(mContext, parentWindow);
}
翻了一个WindowManagerImpl
实例对象。所以mWindow
中设置和activity
中保存的mWindowManager
为WindowManagerImpl
2.3 setContentView总结
通过上面分析,setContentView()
最终交给了PhoneWindow.setContentView()
frameworks/base/core/java/com/android/internal/policy/PhoneWindow.java
public void setContentView(View view, ViewGroup.LayoutParams params) {
// Note: FEATURE_CONTENT_TRANSITIONS may be set in the process of installing the window
// decor, when theme attributes and the like are crystalized. Do not check the feature
// before this happens.
if (mContentParent == null) {
//
installDecor();
} else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
mContentParent.removeAllViews();
}
//支持转场动画
if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
view.setLayoutParams(params);
final Scene newScene = new Scene(mContentParent, view);
transitionTo(newScene);
} else {
mContentParent.addView(view, params);
}
mContentParent.requestApplyInsets();
final Callback cb = getCallback();
if (cb != null && !isDestroyed()) {
cb.onContentChanged();
}
mContentParentExplicitlySet = true;
}
FEATURE_CONTENT_TRANSITIONS
这是L后添加的转场动画功能。通过mContentParent.addView()
把我设置的不去添加到decor
中的mCtentParent
中。
3 重回handleResumeActivity
frameworks/base/core/java/android/app/ActivityThread.java
final void handleResumeActivity(IBinder token,
boolean clearHide, boolean isForward, boolean reallyResume, int seq, String reason) {
ActivityClientRecord r = mActivities.get(token);
...//
r = performResumeActivity(token, clearHide, reason);
if (r != null) {
final Activity a = r.activity;
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) {
throw e.rethrowFromSystemServer();
}
}
if (r.window == null && !a.mFinished && willBeVisible) {
r.window = r.activity.getWindow();
//获得顶层view []
View decor = r.window.getDecorView();
decor.setVisibility(View.INVISIBLE);
//获得ViewManager []
ViewManager wm = a.getWindowManager();
WindowManager.LayoutParams l = r.window.getAttributes();
a.mDecor = decor;
l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
l.softInputMode |= forwardBit;
if (r.mPreserveWindow) {
a.mWindowAdded = true;
r.mPreserveWindow = false;
// Normally the ViewRoot sets up callbacks with the Activity
// in addView->ViewRootImpl#setView. If we are instead reusing
// the decor view we have to notify the view root that the
// callbacks may have changed.
ViewRootImpl impl = decor.getViewRootImpl();
if (impl != null) {
impl.notifyChildRebuilt();
}
}
if (a.mVisibleFromClient && !a.mWindowAdded) {
a.mWindowAdded = true;
//把decor view对象添加到ViewManager中
wm.addView(decor, l);
}
...//
}
通过分析setContentView()
知道了这里的decor
,wm
的内容,wm
就是windowManagerImpl
的实例,通过调用addView()
将decor
添加
frameworks/base/core/java/android/view/WindowManagerImpl.java
public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
applyDefaultToken(params);
mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);
}
然后并没有做什么不得了的事,仅是调用了另外的对象。mGlobal
看名称就知道全局对象。
frameworks/base/core/java/android/view/WindowManagerGlobal.java
public void addView(View view, ViewGroup.LayoutParams params,
Display display, Window parentWindow) {
final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;
if (parentWindow != null) {
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.
...//
int index = findViewLocked(view, false);
if (index >= 0) {
if (mDyingViews.contains(view)) {
// Don't wait for MSG_DIE to make it's way through root's queue.
mRoots.get(index).doDie();
} else {
....//
}
// 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.add(view);
mRoots.add(root);
mParams.add(wparams);
// do this last because it fires off messages to start doing things
try {
//把decorView设置到viewRootImpl中
root.setView(view, wparams, panelParentView);
} catch (RuntimeException e) {
...//
}
}
}
有引出了行的对象ViewRootImpl
,还实现了ViewParent
。
3.1 ViewRootImpl
查看ViewRootImpl
源码可以发现里面有一个Surface
字段,和一个W
类型的字段,W
是其内部类,集成至IWindow.Stub
,很熟悉,binder
接口就喜欢Ixxx
。同时还保存存了decorView
3.1.1 ViewRootImpl的创建
frameworks/base/core/java/android/view/ViewRootImpl.java
public ViewRootImpl(Context context, Display display) {
mContext = context;
//得到一个会话
mWindowSession = WindowManagerGlobal.getWindowSession();
..//
//创建一个W对象
mWindow = new W(this);
..//
mAttachInfo = new View.AttachInfo(mWindowSession, mWindow, display, this, mHandler, this);
...//
mViewConfiguration = ViewConfiguration.get(context);
mDensity = context.getResources().getDisplayMetrics().densityDpi;
mNoncompatDensity = context.getResources().getDisplayMetrics().noncompatDensityDpi;
mFallbackEventHandler = new PhoneFallbackEventHandler(context);
mChoreographer = Choreographer.getInstance();
mDisplayManager = (DisplayManager)context.getSystemService(Context.DISPLAY_SERVICE);
loadSystemProperties();
}
3.1.1.1 getWindowSesseion()
session
我们在Binder
系统中还是比较常见,表示客户端与服务端的一次会话。
frameworks/base/core/java/android/view/WindowManagerGlobal.java
public static IWindowSession getWindowSession() {
synchronized (WindowManagerGlobal.class) {
if (sWindowSession == null) {
try {
InputMethodManager imm = InputMethodManager.getInstance();
//获得WindowManagerService的binder代理
IWindowManager windowManager = getWindowManagerService();
//binder调用获得session
sWindowSession = windowManager.openSession(
new IWindowSessionCallback.Stub() {
@Override
public void onAnimatorScaleChanged(float scale) {
ValueAnimator.setDurationScale(scale);
}
},
imm.getClient(), imm.getInputContext());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
return sWindowSession;
}
}
3.1.1.2 ViewRootImpl.setView()
源码中可见,先调用了requestLayout
,然后又调用了IWindowsSession
的add()
函数,向WindowManagerService
发消息。
源码真的太多了...发一个Handler消息绕了好多方法
3.2 Surface
View
是UI的单元,绘制工作都是在onDraw
函数中完成。而Surface
就相当于画布。
Surface
:
Handle onto a raw buffer that is being managed by the screen compositor.
Surface
操作一块Raw buffer
,Screen compositor
管理这块Raw buffer
4 ViewRootImpl与WindowManagerService
ViewRootImpl
通过IWindowSession
与WindowManager
进程进行跨进程交互,也就是binder
调用。
ViewRootImpl
内部有一个W
类型的对象,它也是基于binder
通信,但是它是客户端,处理WindowManagerService
的请求。例如按键,触屏事件的通知:
WindowManagerService
接收到事件,WindowManagerService
找到UI位于顶层的进程所对应的IWindow
对象,这是一个Bp
端,调用这个IWindow
对象的dispatchxxx()
,ViewRootImpl
的Bn
端IWindow
接受事件,再派发给相应的处理者。
5 Activity的UI绘制
前面提到,ViewRootImpl
的setView()
函数会调用会有一个requestLayout()
,它的调用栈很深:
requestLayout
--> scheduleTraversals()
-- > mChoreographer.postCallback(Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null)
--> doTraversal()
--> performTraversals()
按照原书中所说的在performTraversals()
找到两个重要的函数:
relayoutWindow()
和draw()
,在7.0中显现调用了performDraw()
,在performDraw()
中调用的draw()
方法。
relayoutWindow()
函数中调用了WindowSession
的layout()
函数。
draw()
方中做了比较多的计算,真正的绘制放在了drawSoftware()
frameworks/base/core/java/android/view/ViewRootImpl.java
private boolean drawSoftware(Surface surface, AttachInfo attachInfo, int xoff, int yoff,
boolean scalingRequired, Rect dirty) {
// Draw with software renderer.
final Canvas canvas;
try {
final int left = dirty.left;
final int top = dirty.top;
final int right = dirty.right;
final int bottom = dirty.bottom;
canvas = mSurface.lockCanvas(dirty);
// The dirty rectangle can be modified by Surface.lockCanvas()
//noinspection ConstantConditions
if (left != dirty.left || top != dirty.top || right != dirty.right
|| bottom != dirty.bottom) {
attachInfo.mIgnoreDirtyState = true;
}
// TODO: Do this in native
canvas.setDensity(mDensity);
} catch (Surface.OutOfResourcesException e) {
handleOutOfResourcesException(e);
return false;
} catch (IllegalArgumentException e) {
...//
}
..//
// If this bitmap's format includes an alpha channel, we
// need to clear it before drawing so that the child will
// properly re-composite its drawing on a transparent
// background. This automatically respects the clip/dirty region
// or
// If we are applying an offset, we need to clear the area
// where the offset doesn't appear to avoid having garbage
// left in the blank areas.
if (!canvas.isOpaque() || yoff != 0 || xoff != 0) {
canvas.drawColor(0, PorterDuff.Mode.CLEAR);
}
dirty.setEmpty();
mIsAnimating = false;
mView.mPrivateFlags |= View.PFLAG_DRAWN;
try {
canvas.translate(-xoff, -yoff);
if (mTranslator != null) {
mTranslator.translateCanvas(canvas);
}
canvas.setScreenDensity(scalingRequired ? mNoncompatDensity : 0);
attachInfo.mSetIgnoreDirtyState = false;
mView.draw(canvas);
drawAccessibilityFocusedDrawableIfNeeded(canvas);
} finally {
if (!attachInfo.mSetIgnoreDirtyState) {
// Only clear the flag if it was not set during the mView.draw() call
attachInfo.mIgnoreDirtyState = false;
}
}
} finally {
try {
surface.unlockCanvasAndPost(canvas);
} catch (IllegalArgumentException e) {
...//
}
}
return true;
}
6 总结
-
Activity
的顶层View
是DecorView
,通过在Activity
的onCreate()
调用setContentView()
设置的View
只是DecorView
种的一部分,成为contentView
,DecorView
是一个FrameLayout
类型的ViewGroup
。 -
Activity
和UI有关,他包含一个Window
(实际实例为PhoneWindow
)和一个WindowManager
(实际实例为WindowManagerImpl
)。这两者控制Activity
的显示、 - 在
WindowManagerImpl
中有一个ViewRootImpl
对象,它实现了ViewParent
接口,它有两个重要成员,一个是DecorView
一个是Surface
,ViewRootImpl
还通过Binder
与WindowManagerService
交互,涉及两个类IWindowSession
和IWindow
。 -
Activity
的绘制是从Surface
所处一块Canvas
开始的,最后释放这个Canvas
.