2019年11月9日09:28:20
关于 Android 中的视图,我一直有些疑问,Android 中 Activity 是怎么加载视图的?View 是怎么绘制的? 普通 View 与 SurfaceView 的到底有什么异同,如何去优化 View 的显示等,现在就让我们从 Activity 创建说起,一探 Android 渲染机制。
从 Activity#setContentView 说起
一般创建应用时,Activity 是承载我们视图的首要选择。为了能够正常显示视图,我们一般会通过调用 Activity#setContentView
方法来进行加载。但是,这个方法究竟调用了什么方法实现真正展现视图的?又是如何现实的呢?从源码最终我们能看到,setContentView
最终的实现位于 PhoneWindow 中
@Override
public void setContentView(int layoutResID) {
if (mContentParent == null) {
installDecor();
} else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
mContentParent.removeAllViews();
}
if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
final Scene newScene = Scene.getSceneForLayout(mContentParent, layoutResID,
getContext());
transitionTo(newScene);
} else {
mLayoutInflater.inflate(layoutResID, mContentParent);
}
mContentParent.requestApplyInsets();
final Callback cb = getCallback();
if (cb != null && !isDestroyed()) {
cb.onContentChanged();
}
mContentParentExplicitlySet = true;
}
我们所使用的 View 最终都会被添加到 mContentParent 中,mContentParent 究竟是一个什么样的 ViewGroup 呢?首先看一下 mContentParent 的创建与初始化,其初始化代码位于 installDecor 中,由于代码量比较多,此处只摘取部分重要内容。
private void installDecor() {
mForceDecorInstall = false;
if (mDecor == null) {
mDecor = generateDecor(-1);
mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
mDecor.setIsRootNamespace(true);
} else {
mDecor.setWindow(this);
}
if (mContentParent == null) {
mContentParent = generateLayout(mDecor);
mDecor.makeOptionalFitsSystemWindows();
final DecorContentParent decorContentParent = (DecorContentParent) mDecor.findViewById(
R.id.decor_content_parent);
if (decorContentParent != null) {
mDecorContentParent = decorContentParent;
mDecorContentParent.setWindowCallback(getCallback());
if (mDecorContentParent.getTitle() == null) {
mDecorContentParent.setWindowTitle(mTitle);
}
final int localFeatures = getLocalFeatures();
for (int i = 0; i < FEATURE_MAX; i++) {
if ((localFeatures & (1 << i)) != 0) {
mDecorContentParent.initFeature(i);
}
}
mDecorContentParent.setUiOptions(mUiOptions);
...
} else {
mTitleView = findViewById(R.id.title);
if (mTitleView != null) {
if ((getLocalFeatures() & (1 << FEATURE_NO_TITLE)) != 0) {
final View titleContainer = findViewById(R.id.title_container);
if (titleContainer != null) {
titleContainer.setVisibility(View.GONE);
} else {
mTitleView.setVisibility(View.GONE);
}
mContentParent.setForeground(null);
} else {
mTitleView.setText(mTitle);
}
}
}
if (mDecor.getBackground() == null && mBackgroundFallbackResource != 0) {
mDecor.setBackgroundFallback(mBackgroundFallbackResource);
}
...
}
}
从上面述代码,我们能够了解以下几个事实
- 创建 mDecor,mDecor 的创建过程比较简单,通过直接实例化 DecorView
- 通过 generateLayout 创建 mContentParent
- 创建 mContentParent 后会去找到 DecorContentParent/TitleView,然后对 UI 进行修改
那么 generateLayout 究竟是怎么实现的呢
protected ViewGroup generateLayout(DecorView decor) {
int layoutResource;
int features = getLocalFeatures();
if ((features & (1 << FEATURE_SWIPE_TO_DISMISS)) != 0) {
layoutResource = R.layout.screen_swipe_dismiss;
setCloseOnSwipeEnabled(true);
} else if ((features & ((1 << FEATURE_LEFT_ICON) | (1 << FEATURE_RIGHT_ICON))) != 0) {
if (mIsFloating) {
TypedValue res = new TypedValue();
getContext().getTheme().resolveAttribute(
R.attr.dialogTitleIconsDecorLayout, res, true);
layoutResource = res.resourceId;
} else {
layoutResource = R.layout.screen_title_icons;
}
removeFeature(FEATURE_ACTION_BAR);
} else if ((features & ((1 << FEATURE_PROGRESS) | (1 << FEATURE_INDETERMINATE_PROGRESS))) != 0
&& (features & (1 << FEATURE_ACTION_BAR)) == 0) {
layoutResource = R.layout.screen_progress;
} else if ((features & (1 << FEATURE_CUSTOM_TITLE)) != 0) {
if (mIsFloating) {
TypedValue res = new TypedValue();
getContext().getTheme().resolveAttribute(
R.attr.dialogCustomTitleDecorLayout, res, true);
layoutResource = res.resourceId;
} else {
layoutResource = R.layout.screen_custom_title;
}
removeFeature(FEATURE_ACTION_BAR);
} else if ((features & (1 << FEATURE_NO_TITLE)) == 0) {
if (mIsFloating) {
TypedValue res = new TypedValue();
getContext().getTheme().resolveAttribute(
R.attr.dialogTitleDecorLayout, res, true);
layoutResource = res.resourceId;
} else if ((features & (1 << FEATURE_ACTION_BAR)) != 0) {
layoutResource = a.getResourceId(
R.styleable.Window_windowActionBarFullscreenDecorLayout,
R.layout.screen_action_bar);
} else {
layoutResource = R.layout.screen_title;
}
} else if ((features & (1 << FEATURE_ACTION_MODE_OVERLAY)) != 0) {
layoutResource = R.layout.screen_simple_overlay_action_mode;
} else {
layoutResource = R.layout.screen_simple;
}
mDecor.startChanging();
mDecor.onResourcesLoaded(mLayoutInflater, layoutResource);
ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
if (contentParent == null) {
throw new RuntimeException("Window couldn't find content container view");
}
if ((features & (1 << FEATURE_INDETERMINATE_PROGRESS)) != 0) {
ProgressBar progress = getCircularProgressBar(false);
if (progress != null) {
progress.setIndeterminate(true);
}
}
if ((features & (1 << FEATURE_SWIPE_TO_DISMISS)) != 0) {
registerSwipeCallbacks(contentParent);
}
...
mDecor.finishChanging();
return contentParent;
}
generateLayout 方法就是根据 mLocalFeatures 寻找到对应的 layout。这些 layout 位置位于 frameworks/base/core/res/res/layout / 目录下。这些所有的 layout 都有一个 id 为 com.android.internal.R.id.content 的 FrameLayout,而这个 FrameLayout 就是放置我们自定义布局 ContentView 的 mContentParent,也就是我们 Acitivity 所传递过来的 View 最终都都会被添加到这个FrameLayout 中
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TvckhLAp-1573867933153)(http://train.gz.cvte.cn/documents/%E6%8A%80%E6%9C%AF%E6%A0%88%E5%9F%B9%E8%AE%AD/Andriod/1810-Android%E6%B8%B2%E6%9F%93%E6%9C%BA%E5%88%B6/assets/content_layout.png)]
本节知识点
- Scene 结合 Transiation 实现场景动画切换 Android Transition Framework
- 最简单实现侧滑退出 Activity requestWindowFeature使用 PhoneWindow#requestFeature
DecorView 是什么?
经过上面的分析,我们知道 DecorView 是 PhoneWindow 的独有 ViewGroup,也是承载我们 Acitiviy 视图的 RootView,DecorView 是 FrameLayout 子类。到目前为止我们能可以看到一个普通 Activity 大致的布局如下。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ppyjbwVC-1573867933154)(http://train.gz.cvte.cn/documents/%E6%8A%80%E6%9C%AF%E6%A0%88%E5%9F%B9%E8%AE%AD/Andriod/1810-Android%E6%B8%B2%E6%9F%93%E6%9C%BA%E5%88%B6/assets/decor_view.png)]
其中:
- DecorView 是 Activity 的 RootView,其他所有 View 都是其直接或者间接子 View
- decor_content_parent 即上述分析的 DecorContentParent,包括常用的 ActionBar/Toolbar 和我们自己添加的 View
- navigationBarBackground 与 statusBarBackground 两个 View 只是为了占用空间,预留给状态栏和导航栏使用,具体的实现位于 PhoneStatusBarView
本节知识点
- Android 里阴影实现的一种方式
public void initShadowPaints(Paint shadowPaint, int shadowSize) {
final int startColor = 0x2a000000;
final int endColor = 0x00000000;
final int middleColor = (startColor + endColor) / 2;
shadowPaint.setShader(
new LinearGradient(
0, 0, 0, shadowSize,
new int[]{startColor, middleColor, endColor},
new float[]{0f, 0.3f, 1f}, Shader.TileMode.CLAMP
)
);
}
View 是什么时候显示的?
DecorView 是我们 Activity 的根布局,那么他又是怎么显示出来的呢?如果看过 ActivityThread 源码的话,就会知道在 ActivityThread#performLaunchActivity 中创建 Activity 之后,便会调用 Activity#attach 方法。 完成此方法调用后,ActivityThread 会调用 Activity#performCreate 进而调用 Activity#onCreate 之后就是我们上述分析的 setContentView 流程了。
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, Window window,
ActivityConfigCallback activityConfigCallback) {
attachBaseContext(context);
mFragments.attachHost(null);
mWindow = new PhoneWindow(this, window, activityConfigCallback);
mWindow.setWindowControllerCallback(this);
mWindow.setCallback(this);
mWindow.setOnWindowDismissedCallback(this);
mWindow.getLayoutInflater().setPrivateFactory(this);
...
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;
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;
mWindow.setColorMode(info.colorMode);
}
public interface Callback {
public boolean dispatchKeyEvent(KeyEvent event);
public boolean dispatchKeyShortcutEvent(KeyEvent event);
public boolean dispatchTouchEvent(MotionEvent event);
public boolean dispatchTrackballEvent(MotionEvent event);
public boolean dispatchGenericMotionEvent(MotionEvent event);
public void onWindowAttributesChanged(WindowManager.LayoutParams attrs);
public void onContentChanged();
public void onWindowFocusChanged(boolean hasFocus);
public void onAttachedToWindow();
public void onDetachedFromWindow();
}
到目前为止,我们建立了 Activity PhoneWindow WindowManager 的三者之间的联系,简单的用下图表示。总结来说
- Activity 中持有 WindowManager 与 Window 的引用,Window 通过直接 new PhoneWindow 得到,WindowManager 通过 Window 提供的接口取得
- PhoneWindow 中有一系列的成员变量,一般是各种回调,是通过 Activity 直接设置过来的,都是 Activity 相同实例。然后 Activity 通过 Window 的方法实现初始化 Window 中的成员变量 WindowManager
- WindowManager 通过上述方法,在创建 Window 成员变量 WindowManager 的同时,把 Window 设置给 WindowManager 实现双向关联
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2z6WVxBU-1573867933155)(http://train.gz.cvte.cn/documents/%E6%8A%80%E6%9C%AF%E6%A0%88%E5%9F%B9%E8%AE%AD/Andriod/1810-Android%E6%B8%B2%E6%9F%93%E6%9C%BA%E5%88%B6/assets/activity_window_windowmanager.jpg)]
在说剩下的之前,我们要先了解一个既定的事实,View 只有被 add 到 WindowManager 中才能正常的显示到屏幕上,那么 View 究竟是怎么被添加到 WindowManager 中的呢?onCreate 之后只是把 View 添加到 DecorView 的一个子 View 中,并没有显示的操作。通过阅读 Activity 源码中的注释我们知道,在 onStart 之后,View 才被真正的显示出来。
Activity#onStart Called when the activity is becoming visible to the user.
大胆猜想,DecorView 是在 onStart 后,onResume 前被添加到 WindowManager。继续跟 ActivityThread 中的源码,ActivityThread#handleResumeActivity -> ActivityThread#performResumeActivity -> Activity#performResume -> Activity#onResume 看下来你会发现根本没有 DecorView 的操作,甚至连 Window 都没有。也就是说在 onResume 之前,View 都是不可见的?其实注释中也是说 Activity 可见,并没有说明 View 可见。功夫不负有心人,最终我们在 ActivityThread 中找到了这部分的代码
public void handleResumeActivity(IBinder token, boolean finalStateRequest, boolean isForward,
String reason) {
final ActivityClientRecord r = performResumeActivity(token, finalStateRequest, reason);
final Activity a = r.activity;
...
if (r.window == null && !a.mFinished && willBeVisible) {
r.window = r.activity.getWindow();
View decor = r.window.getDecorView();
decor.setVisibility(View.INVISIBLE);
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) {
if (!a.mWindowAdded) {
a.mWindowAdded = true;
wm.addView(decor, l);
} else {
a.onWindowAttributesChanged(l);
}
}
}
if (!r.activity.mFinished && willBeVisible && r.activity.mDecor != null && !r.hideForNow) {
...
if (r.activity.mVisibleFromClient) {
r.activity.makeVisible();
}
}
}
void makeVisible() {
if (!mWindowAdded) {
ViewManager wm = getWindowManager();
wm.addView(mDecor, getWindow().getAttributes());
mWindowAdded = true;
}
mDecor.setVisibility(View.VISIBLE);
}
看完 View 的显示时机,我们禁想到一个问题,那就是 Activity 中的一些明显 View 的生命周期中的回调,如上述 Callback 中一些接口的实现,是不是在 DecorView 中回调的呢。带着这样的疑问,我们先看下 Callback 到底被设置到了哪里
- Activity 在创建 PhoneWindow 后,通过调用 Window#setCallback 方法传递给 PhoneWindow, 并且 Window 提供了 Window#getCallback 方法
- setContentView 后在 PhoneWindow 中创建 mContentParent 后,调用 DecorContentParent#setWindowCallback
- DecorContentParent 是一个接口,具体实现在 ActionBarView
找到所有相关联的类,就能很简单的找到对应的调用,ActionBarView 与 PhoneWindow,通过 callback 控制 Menu 相关的情况。其他的方法,正如我们所料,都是在 DecorView 被回调,即Activity中所有的有关触摸,按键,onAttachedToWindow 与 onDetachedFromWindow 等都是在 DecorView 中调用的。
本节知识点
- Activity 中的 parent 属性,android:parentActivityName 使用
- UML 图的使用
- ActivityThread 的大致套路
- View 真正显示时机
- Activity 中 CallBack 接口的回调时机
View 是如何显示出来的?
以上分析我们基于这样一个事实,那就是 “View 只要被添加到 WindowManager 中,才能被正常显示”,那这个只要 add 后就能显示的神奇操作是怎么实现的呢?从以上分析我们得知,DecorView 通过 addView 添加到 WindowManager 中,那么这个 WindowManager 是如何创建的呢,在 attach 中通过 context.getSystemService(Context.WINDOW_SERVICE) 创建了 WindowManager,并设置到 PhoneWindow 中。一如往常使用 getSystemService 获得对应的实例。但是不同的是调用 getSystemService 时使用的 context 是从 ActivityThread 直接传递过来的。最终由 ActivityThread#createBaseContextForActivity 创建。
private ContextImpl createBaseContextForActivity(ActivityClientRecord r) {
final int displayId;
try {
displayId = ActivityManager.getService().getActivityDisplayId(r.token);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
ContextImpl appContext = ContextImpl.createActivityContext(
this, r.packageInfo, r.activityInfo,
r.token, displayId, r.overrideConfig);
return appContext;
}
最终实现我们得到此 getSystemService 实现位于 ContextImpl#getSystemService,直接调用 SystemServiceRegistry.getSystemService 获取对应的 Service。SystemServiceRegistry 存放了一个 SYSTEM_SERVICE_FETCHERS 静态 HashMap,存放所有的 SystemService,我们能拿到的所有 Service 都是从此处创建获取。我们使用的 WINDOW_SERVICE 具体实现为
registerService(Context.WINDOW_SERVICE, WindowManager.class,
new CachedServiceFetcher<WindowManager>() {
@Override
public WindowManager createService(ContextImpl ctx) {
return new WindowManagerImpl(ctx);
}});
WindowManager 创建时,会初始化一个成员变量 WindowManagerGlobal,此类为单例类,之后的所有 addView,updateViewLayout,removeView 都是借助 WindowManagerGlobal 实现。一个进程中每次调用 getSystemService 都会创建一个 WindowManager 实例,但是所有的操作都是委托给同一个 WindowManagerGlobal 实现。具体添加一个 View 的实现如下
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 {
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) {
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);
}
int index = findViewLocked(view, false);
if (index >= 0) {
if (mDyingViews.contains(view)) {
mRoots.get(index).doDie();
} else {
throw new IllegalStateException("View " + view
+ " has already been added to the window manager.");
}
}
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);
}
}
}
root = new ViewRootImpl(view.getContext(), display);
view.setLayoutParams(wparams);
mViews.add(view);
mRoots.add(root);
mParams.add(wparams);
root.setView(view, wparams, panelParentView);
}
}
无论是 addView,updateViewLayout 还是 removeView 都是在 WindowManagerGlobal封装好一层,交给 ViewRootImpl 去处理。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JxOd7b63-1573867933155)(http://train.gz.cvte.cn/documents/%E6%8A%80%E6%9C%AF%E6%A0%88%E5%9F%B9%E8%AE%AD/Andriod/1810-Android%E6%B8%B2%E6%9F%93%E6%9C%BA%E5%88%B6/assets/widowmanagerglobal.png)]
本节知识点
- 使用 WindowManagerGlobal可以拿到当前进程任何地方获取所有的 View
- 系统属性修改回调 SystemProperties#addChangeCallback
ViewRootImpl 是什么?
从上面我们了解到所有的视图操作都是交给 ViewRootImpl 去处理的,那么这个 ViewRootImpl 到底是什么呢?我们看下构造函数的实现
public ViewRootImpl(Context context, Display display) {
mWindowSession = WindowManagerGlobal.getWindowSession();
mThread = Thread.currentThread();
mLocation = new WindowLeaked(null);
mLocation.fillInStackTrace();
mWindow = new W(this);
mAttachInfo = new View.AttachInfo(mWindowSession, mWindow, display, this, mHandler, this,
context);
mAccessibilityManager = AccessibilityManager.getInstance(context);
mAccessibilityManager.addAccessibilityStateChangeListener(
mAccessibilityInteractionConnectionManager, mHandler);
mHighContrastTextManager = new HighContrastTextManager();
mAccessibilityManager.addHighTextContrastStateChangeListener(
mHighContrastTextManager, mHandler);
mViewConfiguration = ViewConfiguration.get(context);
mFallbackEventHandler = new PhoneFallbackEventHandler(context);
mChoreographer = Choreographer.getInstance();
mDisplayManager = (DisplayManager)context.getSystemService(Context.DISPLAY_SERVICE);
loadSystemProperties();
}
ViewRootImpl 构造函数中创建了大量的重要对象外,在成员变量定义时也创建了几个相对比较重要的成员变量
- mHander,从名称上看此处应该是一个Handler,具体定义位于 ViewRootImpl.ViewRootHandler,用于将某些应在主线程的操作放到主线程中执行,一般这些操作来自于 WMS
- mSurface,一个空的 Surface
本节知识点
- ViewConfiguration 中获取一些 View 相关
在看 ViewRootImpl 的具体实现之前,需要有一些预备知识。
Surface 是什么?
Android 官方文档中是这样描述 Surface
Handle onto a raw buffer that is being managed by the screen compositor. A Surface is generally created by or from a consumer of image buffers (such as a SurfaceTexture, MediaRecorder, or Allocation), and is handed to some kind of producer (such as OpenGL, MediaPlayer, or CameraDevice) to draw into.
这个描述可以知道:Surface 是用来管理一个 raw buffer 类,Surface 本身是由 screen compositor 来管理的。但是 raw buffer 具体是什么,screen compositor 又是什么,Surface 是如何管理一个 raw buffer,而它又是怎样被 compositor 来管理,后续我们会具体来分析。Surface 是我们绘制的基础。
从上面我们可以看到,ViewRootImpl 在初始化的时候创建了一个Surface 对象,通过调用其默认构造函数,默认构造函数中无任何的实现代码,目前我们那到的仍然是一个内容为空的对象。Surface 主要代码不在 java 层,主要的实现位于 Native。那么究竟是在哪里创建的呢?从后面的代码中,在我们可以在 relayoutWindow 函数中发现通过调用 IWindowSession#relayout 最终把 Surface 传递给 WMS,即应用程序进程的 Surface 创建过程是由 WMS 服务来完成,WMS 服务通过 Binder 跨进程方式将创建好 Surface 返回给应用程序进程,Binder 相关内容在此不表。
IWindowSession#relayout 的具体实现位于 Session#relayout
public int relayout(IWindow window, int seq, WindowManager.LayoutParams attrs,
int requestedWidth, int requestedHeight, int viewFlags,
int flags, Rect outFrame, Rect outOverscanInsets, Rect outContentInsets,
Rect outVisibleInsets, Rect outStableInsets, Rect outsets, Rect outBackdropFrame,
MergedConfiguration mergedConfiguration, Surface outSurface) {
int res = mService.relayoutWindow(this, window, seq, attrs,
requestedWidth, requestedHeight, viewFlags, flags,
outFrame, outOverscanInsets, outContentInsets, outVisibleInsets,
outStableInsets, outsets, outBackdropFrame, mergedConfiguration, outSurface);
return res;
}
我们看 WMS 中关于 relayoutWindow 中关于 Surface 的实现主要是调用 WindowManagerService#createSurfaceControl。
private int createSurfaceControl(Surface outSurface, int result, WindowState win,
WindowStateAnimator winAnimator) {
if (!win.mHasSurface) {
result |= RELAYOUT_RES_SURFACE_CHANGED;
}
WindowSurfaceController surfaceController =
winAnimator.createSurfaceLocked(win.mAttrs.type, win.mOwnerUid);
if (surfaceController != null) {
surfaceController.getSurface(outSurface);
} else {
outSurface.release();
}
return result;
}
可见 Surface 主要通过 WindowSurfaceController#getSurface 获得,WindowSurfaceController 则通过 WindowStateAnimator#createSurfaceLocked
WindowSurfaceController createSurfaceLocked(int windowType, int ownerUid) {
if (mSurfaceController != null) {
return mSurfaceController;
}
w.setHasSurface(false);
try {
mSurfaceController = new WindowSurfaceController(mSession.mSurfaceSession,
attrs.getTitle().toString(),
width, height, format, flags, this, windowType, ownerUid);
w.setHasSurface(true);
} catch (Exception e) {
...
return null;
}
return mSurfaceController;
}
也是对直接 new 做了封装,WindowSurfaceController 的构造函数中初始化 SurfaceControl,嗯哼,出现我们常见的一个类。常用截图接口 SurfaceControl#screenshot,后面就不得不看 native 代码了,SurfaceControl 构造函数中主要调用 nativeCreate 方法。
nativeCreate 实现位于 android_view_SurfaceControl.cpp#nativeCreate 中,具体如下。
static jlong nativeCreate(JNIEnv* env, jclass clazz, jobject sessionObj,
jstring nameStr, jint w, jint h, jint format, jint flags, jlong parentObject,
jint windowType, jint ownerUid) {
ScopedUtfChars name(env, nameStr);
sp<SurfaceComposerClient> client(android_view_SurfaceSession_getClient(env, sessionObj));
SurfaceControl *parent = reinterpret_cast<SurfaceControl*>(parentObject);
sp<SurfaceControl> surface;
status_t err = client->createSurfaceChecked(
String8(name.c_str()), w, h, format, &surface, flags, parent, windowType, ownerUid);
if (err == NAME_NOT_FOUND) {
jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
return 0;
} else if (err != NO_ERROR) {
jniThrowException(env, OutOfResourcesException, NULL);
return 0;
}
surface->incStrong((void *)nativeCreate);
return reinterpret_cast<jlong>(surface.get());
}
从 SurfaceComposerClient#createSurfaceChecked 创建了对应的 SurafceControl
status_t SurfaceComposerClient::createSurfaceChecked(
const String8& name,
uint32_t w,
uint32_t h,
PixelFormat format,
sp<SurfaceControl>* outSurface,
uint32_t flags,
SurfaceControl* parent,
int32_t windowType,
int32_t ownerUid)
{
sp<SurfaceControl> sur;
status_t err = mStatus;
if (mStatus == NO_ERROR) {
sp<IBinder> handle;
sp<IBinder> parentHandle;
sp<IGraphicBufferProducer> gbp;
if (parent != nullptr) {
parentHandle = parent->getHandle();
}
err = mClient->createSurface(name, w, h, format, flags, parentHandle,
windowType, ownerUid, &handle, &gbp);
ALOGE_IF(err, "SurfaceComposerClient::createSurface error %s", strerror(-err));
if (err == NO_ERROR) {
*outSurface = new SurfaceControl(this, handle, gbp, true );
}
}
return err;
}
mClient 即 ISurfaceComposerClient, 此处为跨进程调用,最终实现位于 SurfaceFlinger#Client.cpp
status_t Client::createSurface(
const String8& name,
uint32_t w, uint32_t h, PixelFormat format, uint32_t flags,
sp<IBinder>* handle,
sp<IGraphicBufferProducer>* gbp)
{
/*
* createSurface must be called from the GL thread so that it can
* have access to the GL context.
*/
class MessageCreateLayer : public MessageBase {
SurfaceFlinger* flinger;
Client* client;
sp<IBinder>* handle;
sp<IGraphicBufferProducer>* gbp;
status_t result;
const String8& name;
uint32_t w, h;
PixelFormat format;
uint32_t flags;
public:
MessageCreateLayer(SurfaceFlinger* flinger,
const String8& name, Client* client,
uint32_t w, uint32_t h, PixelFormat format, uint32_t flags,
sp<IBinder>* handle,
sp<IGraphicBufferProducer>* gbp)
: flinger(flinger), client(client),
handle(handle), gbp(gbp),
name(name), w(w), h(h), format(format), flags(flags) {
}
status_t getResult() const { return result; }
virtual bool handler() {
// 调用SurfaceFlinger#createLayer
result = flinger->createLayer(name, client, w, h, format, flags,
handle, gbp);
return true;
}
};
sp<MessageBase> msg = new MessageCreateLayer(mFlinger.get(),
name, this, w, h, format, flags, handle, gbp);
mFlinger->postMessageSync(msg);
return static_cast<MessageCreateLayer*>(msg.get())->getResult();
}
可以看到最终调用的方法是 SurfaceFlinger#createLayer 实现
status_t SurfaceFlinger::createLayer(
const String8& name,
const sp<Client>& client,
uint32_t w, uint32_t h, PixelFormat format, uint32_t flags,
int32_t windowType, int32_t ownerUid, sp<IBinder>* handle,
sp<IGraphicBufferProducer>* gbp, sp<Layer>* parent)
{
sp<Layer> layer;
String8 uniqueName = getUniqueLayerName(name);
switch (flags & ISurfaceComposerClient::eFXSurfaceMask) {
case ISurfaceComposerClient::eFXSurfaceNormal:
result = createBufferLayer(client,
uniqueName, w, h, flags, format,
handle, gbp, &layer);
break;
case ISurfaceComposerClient::eFXSurfaceColor:
result = createColorLayer(client,
uniqueName, w, h, flags,
handle, &layer);
break;
default:
result = BAD_VALUE;
break;
}
if (windowType == 441731) {
windowType = 2024;
layer->setPrimaryDisplayOnly();
}
layer->setInfo(windowType, ownerUid);
result = addClientLayer(client, *handle, *gbp, layer, *parent);
if (result != NO_ERROR) {
return result;
}
mInterceptor->saveSurfaceCreation(layer);
setTransactionFlags(eTransactionNeeded);
return result;
}
后续不需要再深入,留待以后讲 SurfaceFlinger 后再深入了解。目前我们知道最终通过 SurfaceFlinger 创建 Layer,并把 Layer 的代理对象, gdp 就是图像缓冲区代理对象 返回给 SurfaceComposerClient 方便创建 SurfaceControl。
到目前为止,我们得到了 native 的 SurfaceControl,所有对象已经准备完毕,上文说道,最终 WindowSurfaceController#getSurface 获取 Surface 对象内容,大胆的猜测,此方法最终返回的其实是 SurfaceFlinger 创建的这块 layer。
void getSurface(Surface outSurface) {
outSurface.copyFrom(mSurfaceControl);
}
public void copyFrom(SurfaceControl other) {
if (other == null) {
throw new IllegalArgumentException("other must not be null");
}
long surfaceControlPtr = other.mNativeObject;
if (surfaceControlPtr == 0) {
throw new NullPointerException(
"null SurfaceControl native object. Are you using a released SurfaceControl?");
}
long newNativeObject = nativeGetFromSurfaceControl(surfaceControlPtr);
synchronized (mLock) {
if (mNativeObject != 0) {
nativeRelease(mNativeObject);
}
setNativeObjectLocked(newNativeObject);
}
}
后面就是通过 jni 调用把使用 SurfaceControl 来填充 Surface
static jlong nativeGetFromSurfaceControl(JNIEnv* env, jclass clazz,
jlong surfaceControlNativeObj) {
sp<SurfaceControl> ctrl(reinterpret_cast<SurfaceControl *>(surfaceControlNativeObj));
sp<Surface> surface(ctrl->getSurface());
if (surface != NULL) {
surface->incStrong(&sRefBaseOwner);
}
return reinterpret_cast<jlong>(surface.get());
}
sp<Surface> SurfaceControl::getSurface() const
{
Mutex::Autolock _l(mLock);
if (mSurfaceData == 0) {
return generateSurfaceLocked();
}
return mSurfaceData;
}
sp<Surface> SurfaceControl::generateSurfaceLocked() const
{
mSurfaceData = new Surface(mGraphicBufferProducer, false);
return mSurfaceData;
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-96RMwCs0-1573867933155)(http://train.gz.cvte.cn/documents/%E6%8A%80%E6%9C%AF%E6%A0%88%E5%9F%B9%E8%AE%AD/Andriod/1810-Android%E6%B8%B2%E6%9F%93%E6%9C%BA%E5%88%B6/assets/surface.png)]
总结一下 Surface 的创建过程:
- 每一个 Window 都有且只有一个 ViewRootImpl 对象,在该对象中会创建一个 java 层的内容为空的 Surface;
- 当应用程序向 WMS 服务请求 relayout Window,WMS 服务在 WMS 进程创建一个 SurfaceController
- WMS 服务在创建 SurfaceController 过程中,在自身进程空间创建一个 java 层的 SurfaceControl 对象,SurfaceControl 创建时会调用jni 创建 native 的 SurfaceControl 对象。具体实现 SurfaceComposerClient#createSurfaceChecked
- SurfaceComposerClient 通过 Binder 调用请求 SurfaceFlinger#createLayer 在它的进程空间为当前创建的 Surface 创建对应的 Layer 对象,并向 WMS 返回 IGraphicBufferProducer 代理对象。SurfaceComposerClient 通过此代理对象创建 native 的 SurfaceControl。
- WMS 服务在再通过 SurfaceController.getSurface 使用 SurfaceControl 填充我们需要的 Surface
其他总结
- SurfaceControl#screenshot 使用
- 跨进程调用的实现,使用
- JNI 静态注册,动态注册,基本使用
- SurfaceFlinger 的基本功能
Choreographer 是什么?
Android 系统加入 Choreographer 这个类的目的来处理输入 (Input)、动画(Animation)、绘制(Draw) 三个操作。Choreographer 接收显示系统底层传上来的垂直同步信号(VSync 信号),在下一个帧渲染时控制执行这些操作。
理想状态下的 choreographer [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YtjaPqFk-1573867933156)(http://train.gz.cvte.cn/documents/%E6%8A%80%E6%9C%AF%E6%A0%88%E5%9F%B9%E8%AE%AD/Andriod/1810-Android%E6%B8%B2%E6%9F%93%E6%9C%BA%E5%88%B6/assets/choreographer.png)]
丢帧状态下的 choreographer [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Kd342JIC-1573867933156)(http://train.gz.cvte.cn/documents/%E6%8A%80%E6%9C%AF%E6%A0%88%E5%9F%B9%E8%AE%AD/Andriod/1810-Android%E6%B8%B2%E6%9F%93%E6%9C%BA%E5%88%B6/assets/choreographer_jank.png)]
Choreographer 的基本原理如此,下面看下怎么实现的。Choreographer 使用 ThreadLocal 保存 Choreographer 实例,保证每个 Thread 只存在一个 Choreographer。
private static final ThreadLocal<Choreographer> sThreadInstance =
new ThreadLocal<Choreographer>() {
@Override
protected Choreographer initialValue() {
Looper looper = Looper.myLooper();
if (looper == null) {
throw new IllegalStateException("The current thread must have a looper!");
}
return new Choreographer(looper, VSYNC_SOURCE_APP);
}
};
public static final int CALLBACK_INPUT = 0;
public static final int CALLBACK_ANIMATION = 1;
public static final int CALLBACK_TRAVERSAL = 2;
public static final int CALLBACK_COMMIT = 3;
private Choreographer(Looper looper, int vsyncSource) {
mLooper = looper;
mHandler = new FrameHandler(looper);
mDisplayEventReceiver = USE_VSYNC
? new FrameDisplayEventReceiver(looper, vsyncSource)
: null;
mLastFrameTimeNanos = Long.MIN_VALUE;
mFrameIntervalNanos = (long)(1000000000 / getRefreshRate());
mCallbackQueues = new CallbackQueue[CALLBACK_LAST + 1];
for (int i = 0; i <= CALLBACK_LAST; i++) {
mCallbackQueues[i] = new CallbackQueue();
}
}
以上功能有 1. 初始化 FrameHandler,接收处理消息。使用 Handler 的原因是保证所有的操作都是在同一个线程实现的 2. 初始化 FrameDisplayEventReceiver,FrameDisplayEventReceiver 用来接收垂直同步信号。FrameDisplayEventReceiver 继承于 DisplayEventReceiver。当垂直同步信号过来后会回调 DisplayEventReceiver#dispatchVsync 方法,最终调用到 onVsync 方法 3. 初始化 mLastFrameTimeNanos(上一个 frame 的渲染时间) 以及 mFrameIntervalNanos(帧率, fps,一般手机上为 1s/60)。 4. 初始化了大小为 4 的 CallbackQueue
public void onVsync(long timestampNanos, int builtInDisplayId, int frame) {
...
if (mHavePendingVsync) {
Log.w(TAG, "Already have a pending vsync event. There should only be "
+ "one at a time.");
} else {
mHavePendingVsync = true;
}
mTimestampNanos = timestampNanos;
mFrame = frame;
Message msg = Message.obtain(mHandler, this);
msg.setAsynchronous(true);
mHandler.sendMessageAtTime(msg, timestampNanos / TimeUtils.NANOS_PER_MS);
}
public void scheduleVsync() {
if (mReceiverPtr == 0) {
Log.w(TAG, "Attempted to schedule a vertical sync pulse but the display event "
+ "receiver has already been disposed.");
} else {
nativeScheduleVsync(mReceiverPtr);
}
}
private void dispatchVsync(long timestampNanos, int builtInDisplayId, int frame) {
onVsync(timestampNanos, builtInDisplayId, frame);
}
Choreographer 对外提供了 postCallback 与 postFrameCallback,最终这两个方法调用的都是同一个方法 Choreographer#postCallbackDelayedInternal ,postFrameCallback 的 TYPE 为 CALLBACK_ANIMATION
private void postCallbackDelayedInternal(int callbackType,
Object action, Object token, long delayMillis) {
synchronized (mLock) {
final long now = SystemClock.uptimeMillis();
final long dueTime = now + delayMillis;
mCallbackQueues[callbackType].addCallbackLocked(dueTime, action, token);
if (dueTime <= now) {
scheduleFrameLocked(now);
} else {
Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_CALLBACK, action);
msg.arg1 = callbackType;
msg.setAsynchronous(true);
mHandler.sendMessageAtTime(msg, dueTime);
}
}
}
其中 postCallback 的具体流程如下。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-H4xJtTSo-1573867933156)(http://train.gz.cvte.cn/documents/%E6%8A%80%E6%9C%AF%E6%A0%88%E5%9F%B9%E8%AE%AD/Andriod/1810-Android%E6%B8%B2%E6%9F%93%E6%9C%BA%E5%88%B6/assets/choreographer_process.png)]
doFrame 中做了些什么呢?
void doFrame(long frameTimeNanos, int frame) {
try {
AnimationUtils.lockAnimationClock(frameTimeNanos / TimeUtils.NANOS_PER_MS);
mFrameInfo.markInputHandlingStart();
doCallbacks(Choreographer.CALLBACK_INPUT, frameTimeNanos);
mFrameInfo.markAnimationsStart();
doCallbacks(Choreographer.CALLBACK_ANIMATION, frameTimeNanos);
mFrameInfo.markPerformTraversalsStart();
doCallbacks(Choreographer.CALLBACK_TRAVERSAL, frameTimeNanos);
doCallbacks(Choreographer.CALLBACK_COMMIT, frameTimeNanos);
} finally {
AnimationUtils.unlockAnimationClock();
}
}
本节知识点
- 获取设备刷新率,私有方法 Choreographer##getRefreshRate,一般不可靠,直接写死
- 可以在任何地方使用 Choreographer#postFrameCallback,与 View#post 或者 Handler#post 区别是不会立马执行,要等下一个垂直信号过来才会执行
ViewRootImpl#setView 实现
了解以上两个基础知识后,剩下的就是看 ViewRootImpl 是怎么实现 View 的绘制显示的。 ViewRootImpl#setView
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
synchronized (this) {
if (mView == null) {
mView = view;
mAttachInfo.mDisplayState = mDisplay.getState();
mDisplayManager.registerDisplayListener(mDisplayListener, mHandler);
mFallbackEventHandler.setView(view);
mWindowAttributes.copyFrom(attrs);
if (mWindowAttributes.packageName == null) {
mWindowAttributes.packageName = mBasePackageName;
}
attrs = mWindowAttributes;
mClientWindowLayoutFlags = attrs.flags;
if (view instanceof RootViewSurfaceTaker) {
mSurfaceHolderCallback =
((RootViewSurfaceTaker)view).willYouTakeTheSurface();
if (mSurfaceHolderCallback != null) {
mSurfaceHolder = new TakenSurfaceHolder();
mSurfaceHolder.setFormat(PixelFormat.UNKNOWN);
mSurfaceHolder.addCallback(mSurfaceHolderCallback);
}
}
if (!attrs.hasManualSurfaceInsets) {
attrs.setSurfaceInsets(view, false , true );
}
if (mSurfaceHolder == null) {
enableHardwareAcceleration(attrs);
}
...
int res;
requestLayout();
if ((mWindowAttributes.inputFeatures
& WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {
mInputChannel = new InputChannel();
}
try {
res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
getHostVisibility(), mDisplay.getDisplayId(),
mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
mAttachInfo.mOutsets, mInputChannel);
} catch (RemoteException e) {
mFallbackEventHandler.setView(null);
unscheduleTraversals();
throw new RuntimeException("Adding window failed", e);
}
mPendingOverscanInsets.set(0, 0, 0, 0);
mPendingContentInsets.set(mAttachInfo.mContentInsets);
mPendingStableInsets.set(mAttachInfo.mStableInsets);
mPendingVisibleInsets.set(0, 0, 0, 0);
if (res < WindowManagerGlobal.ADD_OKAY) {
mFallbackEventHandler.setView(null);
unscheduleTraversals();
...
throw new RuntimeException(
"Unable to add window -- unknown error code " + res);
}
if (view instanceof RootViewSurfaceTaker) {
mInputQueueCallback =
((RootViewSurfaceTaker)view).willYouTakeTheInputQueue();
}
if (mInputChannel != null) {
if (mInputQueueCallback != null) {
mInputQueue = new InputQueue();
mInputQueueCallback.onInputQueueCreated(mInputQueue);
}
mInputEventReceiver = new WindowInputEventReceiver(mInputChannel,
Looper.myLooper());
}
view.assignParent(this);
}
}
}
ViewRootImpl#setView 中调用比较重要的接口 requestLayout,requestLayout 操作主要调用 ViewRootImpl#scheduleTraversals,具体实现如下.
@Override
public void requestLayout() {
if (!mHandlingLayoutInLayoutRequest) {
checkThread();
mLayoutRequested = true;
scheduleTraversals();
}
}
void scheduleTraversals() {
if (!mTraversalScheduled) {
mTraversalScheduled = true;
mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
if (!mUnbufferedInputDispatch) {
scheduleConsumeBatchedInput();
}
notifyRendererOfFramePending();
pokeDrawLockIfNeeded();
}
}
scheduleTraversals 主要完成了向 Choreographer中 post CALLBACK_TRAVERSAL 与 CALLBACK_INPUT 两种类型的 callback,等待重绘和触摸事件的到来,关于触摸的实现我们暂时不管,主要看绘制的实现。
本节知识点
- 在 MessageQueue 中添加一个特殊的 SyncBarrier 作为一个标记,在这个标记被移除之前,当前 MessageQueue 队列中排在它后面的其它非异步的 message 不会被 handler 处理
ViewRootImpl#performTraversals 流程
由于 performTraversals 代码量相对较大,只是我目前见过一个函数行数最多的,大概 800 行左右,看起来可能不是那么直观。一般自定义的 View 时候,一般 onMeasure onLayout onDraw 三个方法,performTraversals 大致也是这几个流程。
测量阶段
doTraversal 的第一阶段,会对整个控件树进行第一次测量,在此阶段会控件树所显示所需要的尺寸,在这个阶段,控件树中的所有 View 都会被调用到 View#onMeasure 方法,一般是从父布局中调用子 View 的 onMeasure 方法,父布局通过 onMeasure 的参数来把传达的参数给到子 View, View#onMeasure 的具体定义如下,MeasureSpec 虽然是一个整形,但却是一个复合型的变量, 其中前两位是测量模式,剩下的 30 位是 width 或者 height。MeasureSpec 只是子 View 作为设定自身大小参考,只是个参考,要多大,还是 View 自己说了算,但是 View 一旦超过父布局大小,界面可能显示不了。测量模式包括三种,UNSPECIFIED、EXACTLY、AT_MOST。
- UNSPECIFIED:父控件对子控件不加任何束缚,子元素可以得到任意想要的大小,这种 MeasureSpec 一般是由父控件自身的特性决定的。比如 ScrollView,它的子 View 可以随意设置大小,无论多高,都能滚动显示,这个时候,size 一般就没什么意义。
- EXACTLY:父控件为子 View 指定确切大小,希望子 View 完全按照自己给定尺寸来处理,这时的 MeasureSpec 一般是父控件根据自身的 MeasureSpec 跟子 View 的布局参数(MATCH_PARENT)来确定的。
- AT_MOST:父控件为子元素指定最大参考尺寸,希望子 View 的尺寸不要超过这个尺寸,这种模式也是父控件根据自身的 MeasureSpec 跟子 View 的布局参数(WRAP_CONTENT)来确定的。
final View host = mView;
int desiredWindowWidth;
int desiredWindowHeight;
...
Rect frame = mWinFrame;
if (mFirst) {
mFullRedrawNeeded = true;
mLayoutRequested = true;
final Configuration config = mContext.getResources().getConfiguration();
if (shouldUseDisplaySize(lp)) {
Point size = new Point();
mDisplay.getRealSize(size);
desiredWindowWidth = size.x;
desiredWindowHeight = size.y;
} else {
desiredWindowWidth = dipToPx(config.screenWidthDp);
desiredWindowHeight = dipToPx(config.screenHeightDp);
}
host.dispatchAttachedToWindow(mAttachInfo, 0);
} else {
desiredWindowWidth = frame.width();
desiredWindowHeight = frame.height();
if (desiredWindowWidth != mWidth || desiredWindowHeight != mHeight) {
mFullRedrawNeeded = true;
mLayoutRequested = true;
windowSizeMayChange = true;
}
}
boolean layoutRequested = mLayoutRequested && (!mStopped || mReportNextDraw);
if (layoutRequested) {
final Resources res = mView.getContext().getResources();
if (mFirst) {
...
} else {
if (lp.width == ViewGroup.LayoutParams.WRAP_CONTENT
|| lp.height == ViewGroup.LayoutParams.WRAP_CONTENT) {
windowSizeMayChange = true;
if (shouldUseDisplaySize(lp)) {
Point size = new Point();
mDisplay.getRealSize(size);
desiredWindowWidth = size.x;
desiredWindowHeight = size.y;
} else {
Configuration config = res.getConfiguration();
desiredWindowWidth = dipToPx(config.screenWidthDp);
desiredWindowHeight = dipToPx(config.screenHeightDp);
}
}
}
windowSizeMayChange |= measureHierarchy(host, lp, res,
desiredWindowWidth, desiredWindowHeight);
}
最终多有的操作都是交给 measureHierarchy 进行控件树的测量。多次协商后的结果
private boolean measureHierarchy(final View host, final WindowManager.LayoutParams lp,
final Resources res, final int desiredWindowWidth, final int desiredWindowHeight) {
int childWidthMeasureSpec;
int childHeightMeasureSpec;
boolean windowSizeMayChange = false;
boolean goodMeasure = false;
if (lp.width == ViewGroup.LayoutParams.WRAP_CONTENT) {
final DisplayMetrics packageMetrics = res.getDisplayMetrics();
res.getValue(com.android.internal.R.dimen.config_prefDialogWidth, mTmpValue, true);
int baseSize = 0;
if (mTmpValue.type == TypedValue.TYPE_DIMENSION) {
baseSize = (int)mTmpValue.getDimension(packageMetrics);
}
if (baseSize != 0 && desiredWindowWidth > baseSize) {
childWidthMeasureSpec = getRootMeasureSpec(baseSize, lp.width);
childHeightMeasureSpec = getRootMeasureSpec(desiredWindowHeight, lp.height);
performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
if ((host.getMeasuredWidthAndState()&View.MEASURED_STATE_TOO_SMALL) == 0) {
goodMeasure = true;
} else {
baseSize = (baseSize+desiredWindowWidth)/2;
childWidthMeasureSpec = getRootMeasureSpec(baseSize, lp.width);
performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
if ((host.getMeasuredWidthAndState()&View.MEASURED_STATE_TOO_SMALL) == 0) {
goodMeasure = true;
}
}
}
}
if (!goodMeasure) {
childWidthMeasureSpec = getRootMeasureSpec(desiredWindowWidth, lp.width);
childHeightMeasureSpec = getRootMeasureSpec(desiredWindowHeight, lp.height);
performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
if (mWidth != host.getMeasuredWidth() || mHeight != host.getMeasuredHeight()) {
windowSizeMayChange = true;
}
}
return windowSizeMayChange;
}
可见最终是调用了 performMeasure 方法来进行测量, 最终调用的地方 View#measure(childWidthMeasureSpec, childHeightMeasureSpec) measure 也是对 onMeasure 的封装
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),
getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));
}
protected int getSuggestedMinimumWidth() {
return (mBackground == null) ? mMinWidth : max(mMinWidth, mBackground.getMinimumWidth());
}
public static int getDefaultSize(int size, int measureSpec) {
int result = size;
int specMode = MeasureSpec.getMode(measureSpec);
int specSize = MeasureSpec.getSize(measureSpec);
switch (specMode) {
case MeasureSpec.UNSPECIFIED:
result = size;
break;
case MeasureSpec.AT_MOST:
case MeasureSpec.EXACTLY:
result = specSize;
break;
}
return result;
}
到目前为止,应用层的测量几乎完成,剩下的是与 WMS 做相关的沟通,主要以 relayoutWindow 为核心,其主要是通过 mWindowSession 调用 relayout 方法使用,注意 relayout 中回把 mSurface传给 WMS, 其中有些硬件加速相关的代码,暂时不表。
private int relayoutWindow(WindowManager.LayoutParams params, int viewVisibility,
boolean insetsPending) throws RemoteException {
...
int relayoutResult = mWindowSession.relayout(
mWindow, mSeq, params,
(int) (mView.getMeasuredWidth() * appScale + 0.5f),
(int) (mView.getMeasuredHeight() * appScale + 0.5f),
viewVisibility, insetsPending ? WindowManagerGlobal.RELAYOUT_INSETS_PENDING : 0,
mWinFrame, mPendingOverscanInsets, mPendingContentInsets, mPendingVisibleInsets,
mPendingStableInsets, mPendingOutsets, mPendingBackDropFrame,
mPendingMergedConfiguration, mSurface);
return relayoutResult;
}
本节知识点
- 三种测量模式
布局阶段
布局阶段主要完成控件树的布局和一些透明度区域的收集,以及与 WMS 协商。
final boolean didLayout = layoutRequested && (!mStopped || mReportNextDraw);
boolean triggerGlobalLayoutListener = didLayout
|| mAttachInfo.mRecomputeGlobalAttributes;
if (didLayout) {
performLayout(lp, mWidth, mHeight);
if ((host.mPrivateFlags & View.PFLAG_REQUEST_TRANSPARENT_REGIONS) != 0) {
host.getLocationInWindow(mTmpLocation);
mTransparentRegion.set(mTmpLocation[0], mTmpLocation[1],
mTmpLocation[0] + host.mRight - host.mLeft,
mTmpLocation[1] + host.mBottom - host.mTop);
host.gatherTransparentRegion(mTransparentRegion);
if (mTranslator != null) {
mTranslator.translateRegionInWindowToScreen(mTransparentRegion);
}
if (!mTransparentRegion.equals(mPreviousTransparentRegion)) {
mPreviousTransparentRegion.set(mTransparentRegion);
mFullRedrawNeeded = true;
try {
mWindowSession.setTransparentRegion(mWindow, mTransparentRegion);
} catch (RemoteException e) {
}
}
}
}
布局最重要调用 performLayout,其最重要的实现是 View#layout,layout 中通过 setFrame 设置 View 的 LTRB,setFrame 中检测到 size 发生改变或者第一次调用会调用 onSizeChange 方法
mView.layout(0, 0, mView.getMeasuredWidth(), mView.getMeasuredHeight());
布局的另一阶段是计算窗口的透明区域,方便后续底层 SurafceFlinger 做合成,类似在 Surface 上挖了一个洞。可以直接透过这个窗口看下后面的内容,常见于视频播放器。这一机制常用于 SurfaceView。
绘制阶段
测量与布局完成后,既可以绘制了。 绘制的判断不多,主要调用 performDraw 方法。performDraw 方法主要是调用 draw 方法。draw 方法中关于动画的处理我们暂时不处理,主要看具体的实现。
if (mAttachInfo.mThreadedRenderer != null && mAttachInfo.mThreadedRenderer.isEnabled()) {
...
mAttachInfo.mThreadedRenderer.draw(mView, mAttachInfo, this);
} else {
...
if (!drawSoftware(surface, mAttachInfo, xOffset, yOffset, scalingRequired, dirty)) {
return;
}
}
这里 Android 提供了两种绘图的方法。软件绘制与硬件绘制。由于内容相对较多,后面拆成两个小节来处理。
[](#软件绘制)软件绘制
软件绘制,是由 CPU 主导绘图,所有的操作由 CPU 来完成。适用于一些二维的绘图,底层调用的是 Skia 库。下面看下 ViewRootImpl 是如何进行软件绘制
private boolean drawSoftware(Surface surface, AttachInfo attachInfo, int xoff, int yoff,
boolean scalingRequired, Rect dirty) {
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);
if (left != dirty.left || top != dirty.top || right != dirty.right
|| bottom != dirty.bottom) {
attachInfo.mIgnoreDirtyState = true;
}
} catch (Surface.OutOfResourcesException e) {
handleOutOfResourcesException(e);
return false;
} catch (IllegalArgumentException e) {
mLayoutRequested = true;
return false;
}
try {
if (!canvas.isOpaque() || yoff != 0 || xoff != 0) {
canvas.drawColor(0, PorterDuff.Mode.CLEAR);
}
dirty.setEmpty();
try {
canvas.translate(-xoff, -yoff);
mView.draw(canvas);
} finally {
if (!attachInfo.mSetIgnoreDirtyState) {
attachInfo.mIgnoreDirtyState = false;
}
}
} finally {
try {
surface.unlockCanvasAndPost(canvas);
} catch (IllegalArgumentException e) {
mLayoutRequested = true;
return false;
}
}
return true;
}
上述代码中我们可以看到直接通过 Surface#lockCanvas 取得 Canvas,然后偶就可以通过此 Canvas 进行绘制,那么这个 lockCanvas 究竟做了些什么?看代码其实只是调用的 android_view_Surface#nativeLockCanvas 方法。此方法实现
static jlong nativeLockCanvas(JNIEnv* env, jclass clazz,
jlong nativeObject, jobject canvasObj, jobject dirtyRectObj) {
sp<Surface> surface(reinterpret_cast<Surface *>(nativeObject));
Rect dirtyRect(Rect::EMPTY_RECT);
Rect* dirtyRectPtr = NULL;
if (dirtyRectObj) {
dirtyRect.left = env->GetIntField(dirtyRectObj, gRectClassInfo.left);
dirtyRect.top = env->GetIntField(dirtyRectObj, gRectClassInfo.top);
dirtyRect.right = env->GetIntField(dirtyRectObj, gRectClassInfo.right);
dirtyRect.bottom = env->GetIntField(dirtyRectObj, gRectClassInfo.bottom);
dirtyRectPtr = &dirtyRect;
}
ANativeWindow_Buffer outBuffer;
status_t err = surface->lock(&outBuffer, dirtyRectPtr);
SkImageInfo info = SkImageInfo::Make(outBuffer.width, outBuffer.height,
convertPixelFormat(outBuffer.format),
outBuffer.format == PIXEL_FORMAT_RGBX_8888
? kOpaque_SkAlphaType : kPremul_SkAlphaType,
GraphicsJNI::defaultColorSpace());
SkBitmap bitmap;
ssize_t bpr = outBuffer.stride * bytesPerPixel(outBuffer.format);
bitmap.setInfo(info, bpr);
if (outBuffer.width > 0 && outBuffer.height > 0) {
bitmap.setPixels(outBuffer.bits);
} else {
bitmap.setPixels(NULL);
}
Canvas* nativeCanvas = GraphicsJNI::getNativeCanvas(env, canvasObj);
nativeCanvas->setBitmap(bitmap);
if (dirtyRectPtr) {
nativeCanvas->clipRect(dirtyRect.left, dirtyRect.top,
dirtyRect.right, dirtyRect.bottom, SkClipOp::kIntersect);
}
if (dirtyRectObj) {
env->SetIntField(dirtyRectObj, gRectClassInfo.left, dirtyRect.left);
env->SetIntField(dirtyRectObj, gRectClassInfo.top, dirtyRect.top);
env->SetIntField(dirtyRectObj, gRectClassInfo.right, dirtyRect.right);
env->SetIntField(dirtyRectObj, gRectClassInfo.bottom, dirtyRect.bottom);
}
}
通过调用 Surface#lock 获取ANativeWindow_Buffer,里面存放 Surface 的具体宽,高,stride 以及内存等信息。
typedef struct ANativeWindow_Buffer {
int32_t width;
int32_t height;
int32_t stride;
int32_t format;
void* bits;
uint32_t reserved[6];
} ANativeWindow_Buffer;
之后通过所有的绘制操作,都是在这块 Surface 上进行绘制。关于 Skia 的绘制实现不表。后面即听过 View.draw(canvas) 把对应的 View显示内容绘制到 Surface 上。后面我们详细分析这个方法。最后通过 Surface.unlockCanvasAndPost(canvas) 释放 Canvas
public void unlockCanvasAndPost(Canvas canvas) {
synchronized (mLock) {
checkNotReleasedLocked();
if (mHwuiContext != null) {
mHwuiContext.unlockAndPost(canvas);
} else {
unlockSwCanvasAndPost(canvas);
}
}
}
private void unlockSwCanvasAndPost(Canvas canvas) {
...
try {
nativeUnlockCanvasAndPost(mLockedObject, canvas);
} finally {
nativeRelease(mLockedObject);
mLockedObject = 0;
}
}
和 lockCanvas 一样最终调用也是 native 中的 nativeUnlockCanvasAndPost 方法,实现位于 android_view_Surface.cpp
static void nativeUnlockCanvasAndPost(JNIEnv* env, jclass clazz,
jlong nativeObject, jobject canvasObj) {
sp<Surface> surface(reinterpret_cast<Surface *>(nativeObject));
if (!isSurfaceValid(surface)) {
return;
}
Canvas* nativeCanvas = GraphicsJNI::getNativeCanvas(env, canvasObj);
nativeCanvas->setBitmap(SkBitmap());
status_t err = surface->unlockAndPost();
if (err < 0) {
doThrowIAE(env);
}
}
然后我们就来分析具体的绘制流程 View#draw 方法。draw 方法的注释其实已经写明了绘制的过程
Draw traversal performs several drawing steps which must be executed in the appropriate order:
- Draw the background
- If necessary, save the canvas’ layers to prepare for fading
- Draw view’s content
- Draw children
- If necessary, draw the fading edges and restore layers
- Draw decorations (scrollbars for instance)
绘制背景
final int privateFlags = mPrivateFlags;
final boolean dirtyOpaque = (privateFlags & PFLAG_DIRTY_MASK) == PFLAG_DIRTY_OPAQUE &&
(mAttachInfo == null || !mAttachInfo.mIgnoreDirtyState);
mPrivateFlags = (privateFlags & ~PFLAG_DIRTY_MASK) | PFLAG_DRAWN;
if (!dirtyOpaque) {
drawBackground(canvas);
}
private void drawBackground(Canvas canvas) {
final Drawable background = mBackground;
setBackgroundBounds();
if (canvas.isHardwareAccelerated() && mAttachInfo != null
&& mAttachInfo.mThreadedRenderer != null) {
mBackgroundRenderNode = getDrawableRenderNode(background, mBackgroundRenderNode);
final RenderNode renderNode = mBackgroundRenderNode;
if (renderNode != null && renderNode.isValid()) {
setBackgroundRenderNodeProperties(renderNode);
((DisplayListCanvas) canvas).drawRenderNode(renderNode);
return;
}
}
final int scrollX = mScrollX;
final int scrollY = mScrollY;
if ((scrollX | scrollY) == 0) {
background.draw(canvas);
} else {
canvas.translate(scrollX, scrollY);
background.draw(canvas);
canvas.translate(-scrollX, -scrollY);
}
}
在说下面的准备绘制渐变框之前,需要单独的提一下不表示所有的控件都需要绘制这个所谓的渐变框的,因此第 2 步和第 5 部步是可以省略的。
准备绘制渐变框
所谓的渐变,其实就是我们常见的诸如 ListView 等控件在拖动到顶部或者底部后弹出来的渐变效果,如下图。渐变框可以通过 android:fadingEdge 设置渐变的方向,android:fadingEdgeLength 来设置渐变框的长度
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kWdSSXOE-1573867933157)(http://train.gz.cvte.cn/documents/%E6%8A%80%E6%9C%AF%E6%A0%88%E5%9F%B9%E8%AE%AD/Andriod/1810-Android%E6%B8%B2%E6%9F%93%E6%9C%BA%E5%88%B6/assets/fade.jpeg)]
此步骤只是设置了通过计算当前需要绘制的渐变框位置与区域,然后保存 layer
绘制内容
if (!dirtyOpaque) onDraw(canvas);
绘制子控件
之后便是绘制子控件的流程,由于不是所有控件都有子空间,因此在 View 中此方法为空实现,ViewGroup 中才有具体的实现
protected void dispatchDraw(Canvas canvas) {
boolean usingRenderNodeProperties = canvas.isRecordingFor(mRenderNode);
final int childrenCount = mChildrenCount;
final View[] children = mChildren;
int flags = mGroupFlags;
int clipSaveCount = 0;
final boolean clipToPadding = (flags & CLIP_TO_PADDING_MASK) == CLIP_TO_PADDING_MASK;
if (clipToPadding) {
clipSaveCount = canvas.save(Canvas.CLIP_SAVE_FLAG);
canvas.clipRect(mScrollX + mPaddingLeft, mScrollY + mPaddingTop,
mScrollX + mRight - mLeft - mPaddingRight,
mScrollY + mBottom - mTop - mPaddingBottom);
}
mPrivateFlags &= ~PFLAG_DRAW_ANIMATION;
mGroupFlags &= ~FLAG_INVALIDATE_REQUIRED;
boolean more = false;
final long drawingTime = getDrawingTime();
final ArrayList<View> preorderedList = usingRenderNodeProperties
? null : buildOrderedChildList();
final boolean customOrder = preorderedList == null
&& isChildrenDrawingOrderEnabled();
for (int i = 0; i < childrenCount; i++) {
final int childIndex = getAndVerifyPreorderedIndex(childrenCount, i, customOrder);
final View child = getAndVerifyPreorderedView(preorderedList, children, childIndex);
if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE || child.getAnimation() != null) {
more |= drawChild(canvas, child, drawingTime);
}
}
...
if (clipToPadding) {
canvas.restoreToCount(clipSaveCount);
}
flags = mGroupFlags;
invalidate(true);
}
决定了绘制顺序后,ViewGroup 便会通过 drawChild 方法绘制子控件
protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
return child.draw(canvas, this, drawingTime);
}
此处调用了子 View 的 View.draw(ViewGroup,Canvas,long),大致经历以下操作
- 进行动画的计算,将计算结果存储一个 Transformation 中
- 计算控件内容的滚动量
- 使用 Canvas.save() 保存 Canvas 的当前状态。此时 Canvas 的坐标系为父控件的坐标系。在随后将 Canvas 变换到此空间的坐标系并完成绘制后,会通过 Canvas.restoreTo() 将 Canvas 重置到此时的状态,以便 Canvas 可以继续用来绘制父控件的下一个子控件
- 第一次变换,对应控件位置与滚动
- 将动画产生的变换矩阵应用到 Canvas 中。主要是各种 Animation,如 SacleAnimation 等
- 将控件自身的变换矩阵应用到 Canvas 中
- 设置剪裁。这个和 dispatchDraw() 中的裁剪工作不同:dispatchDraw() 中的裁剪是为了保证所有的子控件绘制的内容不得越过父控件的边界。此处是指子控件的绘制内容不得超出子控件自身的边界,由 setClipChildren() 方法启用或禁用
- 使用变换过的 Canvas 进行最终绘制,调用 dispatchDraw() 或者 draw(Canvas) 两个方法
- 恢复 Canvas 的状态到一切开始之前,使得父控件的 dispatchDraw() 便可以将这个 Canvas 交给下一个子控件的 draw(ViewGroup, Canvas, long) 方法
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nMYcJMvF-1573867933157)(http://train.gz.cvte.cn/documents/%E6%8A%80%E6%9C%AF%E6%A0%88%E5%9F%B9%E8%AE%AD/Andriod/1810-Android%E6%B8%B2%E6%9F%93%E6%9C%BA%E5%88%B6/assets/draw.png)]
绘制渐变框
如第 2 步所示,不细讲
绘制装饰
此操作用来绘制一些滚动条,ViewOverlay 等,ViewOverlay 它是位于 View 视图层顶部的一个附加层。
if (mOverlay != null && !mOverlay.isEmpty()) {
mOverlay.getOverlayView().dispatchDraw(canvas);
}
onDrawForeground(canvas);
简单的对软件绘制流程的总结 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pbTbiekV-1573867933158)(http://train.gz.cvte.cn/documents/%E6%8A%80%E6%9C%AF%E6%A0%88%E5%9F%B9%E8%AE%AD/Andriod/1810-Android%E6%B8%B2%E6%9F%93%E6%9C%BA%E5%88%B6/assets/sw_process.png)]
本节知识点
- View#isOpaque() 提高绘制效率
- ViewOverlay 的使用
硬件加速绘制
倘若窗口使用硬件加速,则 ViewRootImpl 会创建一个 ThreadedRenderer 并保存在 mAttachInfo 中,见 ViewRootImpl#setView 中 enableHardwareAcceleration。ThreadedRenderer 是用于硬件加速的渲染器,它封装了硬件加速的图形库,并以 Android 与硬件加速图形库的中间层的身份存在。它负责从 Android 的 Surface 生成一个 HardwareLayer,供硬件加速图形库作为绘制的输出目标,并提供一系列工厂方法用于创建硬件加速绘制过程中所需的 DisplayList、DisplayListCanvas 等工具。
private void enableHardwareAcceleration(WindowManager.LayoutParams attrs) {
mAttachInfo.mHardwareAccelerated = false;
mAttachInfo.mHardwareAccelerationRequested = false;
final boolean hardwareAccelerated =
(attrs.flags & WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED) != 0;
if (hardwareAccelerated) {
...
if (!ThreadedRenderer.sRendererDisabled) {
if (mAttachInfo.mThreadedRenderer != null) {
mAttachInfo.mThreadedRenderer.destroy();
}
...
mAttachInfo.mThreadedRenderer = ThreadedRenderer.create(mContext, translucent,
attrs.getTitle().toString());
mAttachInfo.mThreadedRenderer.setWideGamut(wideGamut);
if (mAttachInfo.mThreadedRenderer != null) {
mAttachInfo.mHardwareAccelerated =
mAttachInfo.mHardwareAccelerationRequested = true;
}
}
}
}
public static ThreadedRenderer create(Context context, boolean translucent, String name) {
ThreadedRenderer renderer = null;
if (isAvailable()) {
renderer = new ThreadedRenderer(context, translucent, name);
}
return renderer;
}
ThreadedRenderer(Context context, boolean translucent, String name) {
...
long rootNodePtr = nCreateRootRenderNode();
mRootNode = RenderNode.adopt(rootNodePtr);
mRootNode.setClipToBounds(false);
...
mNativeProxy = nCreateProxy(translucent, rootNodePtr);
}
由以上代码可知,在开启硬件加速状态下,ViewRootImpl#enableHardwareAcceleration 创建了 ThreadedRenderer 实例,ThreadedRenderer 在构造函数中通过调用 native 方法 nCreateRootRenderNode 创建了一个 RootRenderNode,native 对应会创建一个 RootRenderNode对象。
static jlong android_view_ThreadedRenderer_createRootRenderNode(JNIEnv* env, jobject clazz) {
RootRenderNode* node = new RootRenderNode(env);
node->incStrong(0);
node->setName("RootRenderNode");
return reinterpret_cast<jlong>(node);
}
软件绘制流程我们知道大致经历了 lockCanvas -> draw -> unlockCanvasAndPost 三个阶段。对比这三个阶段,我们看下硬件加速的实现方式。入口函数即 ThreadedRenderer#draw
void draw(View view, AttachInfo attachInfo, DrawCallbacks callbacks) {
attachInfo.mIgnoreDirtyState = true;
final Choreographer choreographer = attachInfo.mViewRootImpl.mChoreographer;
choreographer.mFrameInfo.markDrawStart();
updateRootDisplayList(view, callbacks);
attachInfo.mIgnoreDirtyState = false;
final long[] frameInfo = choreographer.mFrameInfo.mFrameInfo;
int syncResult = nSyncAndDrawFrame(mNativeProxy, frameInfo, frameInfo.length);
if ((syncResult & SYNC_LOST_SURFACE_REWARD_IF_FOUND) != 0) {
setEnabled(false);
attachInfo.mViewRootImpl.mSurface.release();
attachInfo.mViewRootImpl.invalidate();
}
if ((syncResult & SYNC_INVALIDATE_REQUIRED) != 0) {
attachInfo.mViewRootImpl.invalidate();
}
}
private void updateRootDisplayList(View view, DrawCallbacks callbacks) {
Trace.traceBegin(Trace.TRACE_TAG_VIEW, "Record View#draw()");
updateViewTreeDisplayList(view);
if (mRootNodeNeedsUpdate || !mRootNode.isValid()) {
DisplayListCanvas canvas = mRootNode.start(mSurfaceWidth, mSurfaceHeight);
try {
final int saveCount = canvas.save();
canvas.translate(mInsetLeft, mInsetTop);
callbacks.onPreDraw(canvas);
canvas.insertReorderBarrier();
canvas.drawRenderNode(view.updateDisplayListIfDirty());
canvas.insertInorderBarrier();
callbacks.onPostDraw(canvas);
canvas.restoreToCount(saveCount);
mRootNodeNeedsUpdate = false;
} finally {
mRootNode.end(canvas);
}
}
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
}
public DisplayListCanvas start(int width, int height) {
return DisplayListCanvas.obtain(this, width, height);
}
对比软件绘制的实现,直接通过 lockCanvas 获取 Canvas,硬件加速会直接调用, RenderNode#start 方法直接获取到 DisplayListCanvas。而 RenderNode#start方法则是通过 DisplayListCanvas#obtain 获取 DisplayListCanvas
static DisplayListCanvas obtain(@NonNull RenderNode node, int width, int height) {
DisplayListCanvas canvas = sPool.acquire();
if (canvas == null) {
canvas = new DisplayListCanvas(node, width, height);
} else {
nResetDisplayListCanvas(canvas.mNativeCanvasWrapper, node.mNativeRenderNode,
width, height);
}
canvas.mNode = node;
canvas.mWidth = width;
canvas.mHeight = height;
return canvas;
}
DisplayListCanvas 构造函数中会通过 nCreateDisplayListCanvas 在 native 层中创建一个对应 Canvas 对象。
static jlong android_view_DisplayListCanvas_createDisplayListCanvas(jlong renderNodePtr,
jint width, jint height) {
RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
return reinterpret_cast<jlong>(Canvas::create_recording_canvas(width, height, renderNode));
}
Canvas* Canvas::create_recording_canvas(int width, int height, uirenderer::RenderNode* renderNode) {
if (uirenderer::Properties::isSkiaEnabled()) {
return new uirenderer::skiapipeline::SkiaRecordingCanvas(renderNode, width, height);
}
return new uirenderer::RecordingCanvas(width, height);
}
有了 Canvas,我们就有了绘图的工具。紧接我们就可以绘制控件及其子控件了,回到 updateRootDisplayList 方法,绘制子控件的实现 canvas.drawRenderNode(view.updateDisplayListIfDirty()) 那么这个 View#updateDisplayListIfDirty 做了些什么操作?
public RenderNode updateDisplayListIfDirty() {
final RenderNode renderNode = mRenderNode;
if ((mPrivateFlags & PFLAG_DRAWING_CACHE_VALID) == 0
|| !renderNode.isValid()
|| (mRecreateDisplayList)) {
int width = mRight - mLeft;
int height = mBottom - mTop;
int layerType = getLayerType();
final DisplayListCanvas canvas = renderNode.start(width, height);
try {
computeScroll();
canvas.translate(-mScrollX, -mScrollY);
mPrivateFlags |= PFLAG_DRAWN | PFLAG_DRAWING_CACHE_VALID;
mPrivateFlags &= ~PFLAG_DIRTY_MASK;
if ((mPrivateFlags & PFLAG_SKIP_DRAW) == PFLAG_SKIP_DRAW) {
dispatchDraw(canvas);
drawAutofilledHighlight(canvas);
if (mOverlay != null && !mOverlay.isEmpty()) {
mOverlay.getOverlayView().draw(canvas);
}
} else {
draw(canvas);
}
} finally {
renderNode.end(canvas);
setDisplayListProperties(renderNode);
}
} else {
mPrivateFlags |= PFLAG_DRAWN | PFLAG_DRAWING_CACHE_VALID;
mPrivateFlags &= ~PFLAG_DIRTY_MASK;
}
return renderNode;
}
dispatchDraw 方法和软件绘制相同,几乎没有额外的操作,具体也是在 ViewGroup 中才会有实现,主要目的是确认子控件的绘制顺序,最终调用也是 drawChild 接口,进而 View#draw(Canvas canvas, ViewGroup parent, long drawingTime) 这个 draw 方法与软件绘制最大的不同在于, 会去调用 (DisplayListCanvas) canvas).drawRenderNode(renderNode),关于这个方法的解释,参见后面的详细解释。后续和软件绘制基本没有什么区别,不同的是传入的 Canvas 是 DisplayListCanvas。
从以上操作我们能隐隐约约看出来点什么,这里创建的所有 Canvas 都是一种 Recording Canvas,并且无任何真正的像软件绘制中 Canvas.draw 这种绘制操作,不难猜测,此处的 Canvas 应该只是录制了RenderNode 的一些操作,具体的绘制不在此。具体是怎么实现录制的呢,回到 updateRootDisplayList 方法,刚才我们看到通过 Render 的 start 与 end 接口,录制了根 View 的绘制操作,而具体的实现则是通过 DisplayListCanvas#drawRenderNode 实现,把 View 的 RenderNode 录制下来,drawRenderNode 的实现特别简单,直接调用了 native 接口 nDrawRenderNode。
static void android_view_DisplayListCanvas_drawRenderNode(jlong canvasPtr, jlong renderNodePtr) {
Canvas* canvas = reinterpret_cast<Canvas*>(canvasPtr);
RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
canvas->drawRenderNode(renderNode);
}
void RecordingCanvas::drawRenderNode(RenderNode* renderNode) {
auto&& stagingProps = renderNode->stagingProperties();
RenderNodeOp* op = alloc().create_trivial<RenderNodeOp>(
Rect(stagingProps.getWidth(), stagingProps.getHeight()),
*(mState.currentSnapshot()->transform), getRecordedClip(), renderNode);
int opIndex = addOp(op);
if (CC_LIKELY(opIndex >= 0)) {
int childIndex = mDisplayList->addChild(op);
DisplayList::Chunk& chunk = mDisplayList->chunks.back();
chunk.endChildIndex = childIndex + 1;
if (renderNode->stagingProperties().isProjectionReceiver()) {
mDisplayList->projectionReceiveIndex = opIndex;
}
}
}
上文说过 DisplayListCanvas 其实不参与真正的绘制,只是记录绘制的操作 Op。对应我们在 onDraw 方法中自己的一些操作,drawBitmap,drawColor 之类,DisplayListCanvas 也会封装成对应的 Op
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-aKf4KUZO-1573867933158)(http://train.gz.cvte.cn/documents/%E6%8A%80%E6%9C%AF%E6%A0%88%E5%9F%B9%E8%AE%AD/Andriod/1810-Android%E6%B8%B2%E6%9F%93%E6%9C%BA%E5%88%B6/assets/op.png)]
这样我们就不难得出这样的结论,每一个 View 中都包含一个 RenderNode,此 Node 对应 Native 中的 RenderNode,而 ViewRootImpl 则对应 RootRenderNode,每一个 RenderNode 包含 DisplayList,DisplayList 又有多个 Op 组成。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-SJWpGntw-1573867933159)(http://train.gz.cvte.cn/documents/%E6%8A%80%E6%9C%AF%E6%A0%88%E5%9F%B9%E8%AE%AD/Andriod/1810-Android%E6%B8%B2%E6%9F%93%E6%9C%BA%E5%88%B6/assets/hw_draw.jpg)]
按照软件绘制的套路,我们先后续的操作 mRootNode.end(canvas)
public void end(DisplayListCanvas canvas) {
long displayList = canvas.finishRecording();
nSetDisplayList(mNativeRenderNode, displayList);
canvas.recycle();
}
static void android_view_RenderNode_setDisplayList(JNIEnv* env,
jobject clazz, jlong renderNodePtr, jlong displayListPtr) {
RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
DisplayList* newData = reinterpret_cast<DisplayList*>(displayListPtr);
renderNode->setStagingDisplayList(newData);
}
void RenderNode::setStagingDisplayList(DisplayList* displayList) {
mValid = (displayList != nullptr);
mNeedsDisplayListSync = true;
delete mStagingDisplayList;
mStagingDisplayList = displayList;
}
简单总结一下硬件加速绘制流程
- 利用 View 的 RenderNode 获取一个 DisplayListCanvas
- 利用 DisplayListCanvas 构建并缓存所有的 DrawOp
- 将 DisplayListCanvas 缓存的 DrawOp 填充到 RenderNode
- 将根 View 的缓存 DrawOp 设置到 RootRenderNode 中,完成构建
以上我们了解具体的录制流程,但是最终是在何处绘制的呢?这时候我们就要回到 ThreaderRenderer 的构造函数中。还记得那个 native 代理嘛,看下其具体实现。具体的实现位于 Native 中,创建一个 RenderProxy 对象。
static jlong android_view_ThreadedRenderer_createProxy(JNIEnv* env, jobject clazz,
jboolean translucent, jlong rootRenderNodePtr) {
RootRenderNode* rootRenderNode = reinterpret_cast<RootRenderNode*>(rootRenderNodePtr);
ContextFactoryImpl factory(rootRenderNode);
return (jlong) new RenderProxy(translucent, rootRenderNode, &factory);
}
RenderProxy::RenderProxy(bool translucent, RenderNode* rootRenderNode,
IContextFactory* contextFactory)
: mRenderThread(RenderThread::getInstance()), mContext(nullptr) {
mContext = mRenderThread.queue().runSync([&]() -> CanvasContext* {
return CanvasContext::create(mRenderThread, translucent, rootRenderNode, contextFactory);
});
mDrawFrameTask.setContext(&mRenderThread, mContext, rootRenderNode);
}
在 updateRootDisplayList 之后会调用 nSyncAndDrawFrame, 这个就是绘制的具体实现的部分了。但是在了解具体实现之前,我们需要了解 Surface 的创建,在上述过程中,Surface 是在 relayoutWindow 通过 WMS 填充数据。但是在使用硬件加速之后,
static int android_view_ThreadedRenderer_syncAndDrawFrame(JNIEnv* env, jobject clazz,
jlong proxyPtr, jlongArray frameInfo, jint frameInfoSize) {
LOG_ALWAYS_FATAL_IF(frameInfoSize != UI_THREAD_FRAME_INFO_SIZE,
"Mismatched size expectations, given %d expected %d",
frameInfoSize, UI_THREAD_FRAME_INFO_SIZE);
RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr);
env->GetLongArrayRegion(frameInfo, 0, frameInfoSize, proxy->frameInfo());
return proxy->syncAndDrawFrame();
}
int RenderProxy::syncAndDrawFrame() {
return mDrawFrameTask.drawFrame();
}
int DrawFrameTask::drawFrame() {
mSyncResult = SyncResult::OK;
mSyncQueued = systemTime(CLOCK_MONOTONIC);
postAndWait();
return mSyncResult;
}
void DrawFrameTask::postAndWait() {
AutoMutex _lock(mLock);
mRenderThread->queue(this);
mSignal.wait(mLock);
}
void DrawFrameTask::postAndWait() {
AutoMutex _lock(mLock);
mRenderThread->queue().post([this]() { run(); });
mSignal.wait(mLock);
}
void DrawFrameTask::run() {
bool canUnblockUiThread;
bool canDrawThisFrame;
{
TreeInfo info(TreeInfo::MODE_FULL, *mContext);
canUnblockUiThread = syncFrameState(info);
canDrawThisFrame = info.out.canDrawThisFrame;
}
CanvasContext* context = mContext;
if (canUnblockUiThread) {
unblockUiThread();
}
...
if (CC_LIKELY(canDrawThisFrame)) {
context->draw();
} else {
context->waitOnFences();
}
if (!canUnblockUiThread) {
unblockUiThread();
}
}
RenderThread 被唤醒,开始渲染,大致流程如下:
- 先进行 DrawOp 的合并
- 接着绘制特殊的 Layer
- 绘制其余所有的 DrawOpList
- 调用 swapBuffers 将前面已经绘制好的图形缓冲区提交给 Surface Flinger 合成和显示。
对比软件绘制递归调用流程
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-z9aQ1KTA-1573867933159)(http://train.gz.cvte.cn/documents/%E6%8A%80%E6%9C%AF%E6%A0%88%E5%9F%B9%E8%AE%AD/Andriod/1810-Android%E6%B8%B2%E6%9F%93%E6%9C%BA%E5%88%B6/assets/sw_hw_difference.png)]