-
public void scheduleTraversals() {
-
if (!mTraversalScheduled) {
-
mTraversalScheduled = true;
-
sendEmptyMessage(DO_TRAVERSAL);
-
}
-
}
我们看看跨进程的那个调用。sWindowSession.add。它的最终实现在WindowManagerService中。
[—>WindowSession::add()]
-
public int add(IWindow window, WindowManager.LayoutParams attrs,
-
int viewVisibility, Rect outContentInsets) {
-
return addWindow(this, window, attrs, viewVisibility, outContentInsets);
-
}
WindowSession是个内部类,会调用外部类的addWindow。
这个函数巨复杂无比,但是我们的核心目标是找到创建显示相关的部分。所以,最后精简的话就简单了。
[—>WindowManagerService:: addWindow]
-
public int addWindow(Session session, IWindow client,
-
WindowManager.LayoutParams attrs, int viewVisibility,
-
Rect outContentInsets) {
-
//创建一个WindowState,这个又是什么玩意儿呢?
-
win = new WindowState(session, client, token,
-
attachedWindow, attrs, viewVisibility);
-
win.attach();
-
return res;
-
}
WindowState类中有一个和Surface相关的成员变量,叫SurfaceSession。它会在attach函数中被创建。SurfaceSession嘛,就和SurfaceFlinger有关系了。我们待会看。
好,我们知道ViewRoot创建及调用add后,我们客户端的View系统就和WindowManagerService建立了牢不可破的关系。
另外,我们知道ViewRoot是一个handler,而且刚才我们调用了requestLayout,所以接下来消息循环下一个将调用的就是ViewRoot的handleMessage。
-
public void handleMessage(Message msg) {
-
switch (msg.what) {
-
case DO_TRAVERSAL:
-
performTraversals();
performTraversals更加复杂无比,经过我仔细挑选,目标锁定为下面几个函数。当然,后面我们还会回到performTraversals,不过我们现在更感兴趣的是Surface是如何创建的。
-
private void performTraversals() {
-
// cache mView since it is used so much below…
-
final View host = mView;
-
boolean initialized = false;
-
boolean contentInsetsChanged = false;
-
boolean visibleInsetsChanged;
-
try {
-
//ViewRoot也有一个Surface成员变量,叫mSurface,这个就是代表SurfaceFlinger的客户端
-
//ViewRoot在这个Surface上作画,最后将由SurfaceFlinger来合成显示。刚才说了mSurface还没有什么内容。
-
relayoutResult = relayoutWindow(params, viewVisibility, insetsPending);
[---->ViewRoot:: relayoutWindow()]
-
private int relayoutWindow(WindowManager.LayoutParams params, int viewVisibility,
-
boolean insetsPending) throws RemoteException {
-
//relayOut是跨进程调用,mSurface做为参数传进去了,看来离真相越来越近了呀!
-
int relayoutResult = sWindowSession.relayout(
-
mWindow, params,
-
(int) (mView.mMeasuredWidth * appScale + 0.5f),
-
(int) (mView.mMeasuredHeight * appScale + 0.5f),
-
viewVisibility, insetsPending, mWinFrame,
-
mPendingContentInsets, mPendingVisibleInsets,
-
mPendingConfiguration, mSurface); mSurface做为参数传进去了。
-
}
我们赶紧转到WindowManagerService去看看吧。
-
public int relayoutWindow(Session session, IWindow client,
-
WindowManager.LayoutParams attrs, int requestedWidth,
-
int requestedHeight, int viewVisibility, boolean insetsPending,
-
Rect outFrame, Rect outContentInsets, Rect outVisibleInsets,
-
Configuration outConfig, Surface outSurface){
-
…
-
try {
-
//看到这里,我内心一阵狂喜,有戏,太有戏了!
-
//其中win是我们最初创建的WindowState!
-
Surface surface = win.createSurfaceLocked();
-
if (surface != null) {
-
//先创建一个本地surface,然后把传入的参数outSurface copyFrom一下
-
outSurface.copyFrom(surface);
-
win.mReportDestroySurface = false;
-
win.mSurfacePendingDestroy = false;
-
} else {
-
outSurface.release();
-
}
-
}
-
}
[—>WindowState::createSurfaceLocked]
-
Surface createSurfaceLocked() {
-
try {
-
mSurface = new Surface(
-
mSession.mSurfaceSession, mSession.mPid,
-
mAttrs.getTitle().toString(),
-
0, w, h, mAttrs.format, flags);
-
}
-
Surface.openTransaction();
这里使用了Surface的另外一个构造函数。
-
public Surface(SurfaceSession s,
-
int pid, String name, int display, int w, int h, int format, int flags)
-
throws OutOfResourcesException {
-
mCanvas = new CompatibleCanvas();
-
init(s,pid,name,display,w,h,format,flags); ---->调用了native的init函数。
-
mName = name;
-
}
到这里,不进入JNI是不可能说清楚了。不过我们要先回顾下之前的关键步骤。
◆ add中,new了一个SurfaceSession
◆创建new了一个Surface
◆调用copyFrom,把本地Surface信息传到outSurface中
JNI层
上面两个类的JNI实现都在framework/base/core/jni/android_view_Surface.cpp中。
[---->SurfaceSession:: SurfaceSession()]
-
public class SurfaceSession {
-
/** Create a new connection with the surface flinger. */
-
public SurfaceSession() {
-
init();
-
}
它的init函数对应为:
[—>SurfaceSession_init]
-
static void SurfaceSession_init(JNIEnv* env, jobject clazz)
-
{
-
//SurfaceSession对应为SurfaceComposerClient
-
sp client = new SurfaceComposerClient;
-
client->incStrong(clazz);
-
//Google常用做法,在JAVA对象中保存C++对象的指针。
-
env->SetIntField(clazz, sso.client, (int)client.get());
-
}
Surface的init对应为:
[—>Surface_init]
-
static void Surface_init(
-
JNIEnv* env, jobject clazz,
-
jobject session,
-
jint pid, jstring jname, jint dpy, jint w, jint h, jint format, jint flags)
-
{
-
SurfaceComposerClient* client =
-
(SurfaceComposerClient*)env->GetIntField(session, sso.client);
-
sp surface;
-
if (jname == NULL) {
-
//client是SurfaceComposerClient,返回的surface是一个SurfaceControl
-
//真得很复杂!
-
surface = client->createSurface(pid, dpy, w, h, format, flags);
-
} else {
-
const jchar* str = env->GetStringCritical(jname, 0);
-
const String8 name(str, env->GetStringLength(jname));
-
env->ReleaseStringCritical(jname, str);
-
surface = client->createSurface(pid, name, dpy, w, h, format, flags);
-
}
-
//把surfaceControl信息设置到Surface对象中
-
setSurfaceControl(env, clazz, surface);
-
}
-
static void setSurfaceControl(JNIEnv* env, jobject clazz,
-
const sp& surface)
-
{
-
SurfaceControl* const p =
-
(SurfaceControl*)env->GetIntField(clazz, so.surfaceControl);
-
if (surface.get()) {
-
surface->incStrong(clazz);
-
}
-
if § {
-
p->decStrong(clazz);
-
}
-
env->SetIntField(clazz, so.surfaceControl, (int)surface.get());
-
}
[—>Surface_copyFrom]
-
static void Surface_copyFrom(
-
JNIEnv* env, jobject clazz, jobject other)
-
{
-
const sp& surface = getSurfaceControl(env, clazz);
-
const sp& rhs = getSurfaceControl(env, other);
-
if (!SurfaceControl::isSameSurface(surface, rhs)) {
-
setSurfaceControl(env, clazz, rhs);
-
//把本地那个surface的surfaceControl对象转移到outSurface上
-
}
-
}
这里仅仅是surfaceControl的转移,但是并没有看到Surface相关的信息。
那么Surface在哪里创建的呢?为了解释这个问题,我使用了终极武器,aidl。
1 终极武器AIDL
aidl可以把XXX.aidl文件转换成对应的java文件。我们刚才调用的是WindowSession的
relayOut函数。如下:
-
sWindowSession.relayout(
-
mWindow, params,
-
(int) (mView.mMeasuredWidth * appScale + 0.5f),
-
(int) (mView.mMeasuredHeight * appScale + 0.5f),
-
viewVisibility, insetsPending, mWinFrame,
-
mPendingContentInsets, mPendingVisibleInsets,
-
mPendingConfiguration, mSurface);
它的aidl文件在framework/base/core/java/android/view/IWindowSession.aidl中
-
interface IWindowSession {
-
int add(IWindow window, in WindowManager.LayoutParams attrs,
-
in int viewVisibility, out Rect outContentInsets);
-
void remove(IWindow window);
-
//注意喔,这个outSurface前面的是out,表示输出参数,这个类似于C++的引用。
-
int relayout(IWindow window, in WindowManager.LayoutParams attrs,
-
int requestedWidth, int requestedHeight, int viewVisibility,
-
boolean insetsPending, out Rect outFrame, out Rect outContentInsets,
-
out Rect outVisibleInsets, out Configuration outConfig,
-
out Surface outSurface);
刚才说了,JNI及其JAVA调用只是copyFrom了SurfaceControl对象到outSurface中,但是没看到哪里创建Surface。这其中的奥秘就在aidl文件编译后生成的java文件中。
你在命令行下可以输入:
aidl -Id:\android-2.2-froyo-20100625-source\source\frameworks\base\core\java\ -Id:\android-2.2-froyo-20100625-source\source\frameworks\base\Graphics\java d:\android-2.2-froyo-20100625-source\source\frameworks\base\core\java\android\view\IWindowSession.aidl test.java
以生成test.java文件。-I参数指定include目录,例如aidl有些参数是在别的java文件中指定的,那么这个-I就需要把这些目录包含进来。
先看看ViewRoot这个客户端生成的代码是什么。
-
public int relayout(
-
android.view.IWindow window,
-
android.view.WindowManager.LayoutParams attrs,
-
int requestedWidth, int requestedHeight,
-
int viewVisibility, boolean insetsPending,
-
android.graphics.Rect outFrame,
-
android.graphics.Rect outContentInsets,
-
android.graphics.Rect outVisibleInsets,
-
android.content.res.Configuration outConfig,
-
android.view.Surface outSurface) ---->outSurface是第11个参数
-
throws android.os.RemoteException
-
{
-
android.os.Parcel _data = android.os.Parcel.obtain();
-
android.os.Parcel _reply = android.os.Parcel.obtain();
-
int _result;
-
try {
-
_data.writeInterfaceToken(DESCRIPTOR);
-
_data.writeStrongBinder((((window!=null))?(window.asBinder()):(null)));
-
if ((attrs!=null)) {
-
_data.writeInt(1);
-
attrs.writeToParcel(_data, 0);
-
}
-
else {
-
_data.writeInt(0);
-
}
-
_data.writeInt(requestedWidth);
-
_data.writeInt(requestedHeight);
-
_data.writeInt(viewVisibility);
-
_data.writeInt(((insetsPending)?(1):(0)));
-
//奇怪,outSurface的信息没有写到_data中。那…
-
mRemote.transact(Stub.TRANSACTION_relayout, _data, _reply, 0);
-
_reply.readException();
-
_result = _reply.readInt();
-
if ((0!=_reply.readInt())) {
-
outFrame.readFromParcel(_reply);
-
}
-
…
-
if ((0!=_reply.readInt())) {
-
outSurface.readFromParcel(_reply); //从Parcel中读取信息来填充outSurface
-
}
-
}
-
finally {
-
_reply.recycle();
-
_data.recycle();
-
}
-
return _result;
-
}
真奇怪啊,Binder客户端这头竟然没有把outSurface的信息发过去。我们赶紧看看服务端。
服务端这边处理是在onTranscat函数中。
-
@Override public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException
-
{
-
switch (code)
-
{
-
case TRANSACTION_relayout:
-
{
-
data.enforceInterface(DESCRIPTOR);
-
android.view.IWindow _arg0;
-
android.view.Surface _arg10;
-
//刚才说了,Surface信息并没有传过来,那么我们在relayOut中看到的outSurface是怎么
-
//出来的呢?看下面这句,原来在服务端这边竟然new了一个新的Surface!!!
-
_arg10 = new android.view.Surface();
-
int _result = this.relayout(_arg0, _arg1, _arg2, _arg3, _arg4, _arg5, _arg6, _arg7, _arg8, _arg9, _arg10);
-
reply.writeNoException();
-
reply.writeInt(_result);
-
//_arg10是copyFrom了,那怎么传到客户端呢?
-
if ((_arg10!=null)) {
-
reply.writeInt(1);//调用Surface的writeToParcel,把信息加入reply
-
_arg10.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
-
}
-
return true;
-
}
太诡异了!竟然有这么多花花肠子。我相信如果没有aidl的帮助,我无论如何也不会知道这其中的奥妙。
那好,我们的流程明白了。
◆客户端虽然传了一个surface,但其实没传递给服务端
◆服务端调用writeToParcel,把信息写到Parcel中,然后数据传回客户端
◆客户端调用Surface的readFromParcel,获得surface信息。
那就去看看writeToParcel吧。
[---->Surface_writeToParcel]
-
static void Surface_writeToParcel(
-
JNIEnv* env, jobject clazz, jobject argParcel, jint flags)
-
{
-
Parcel* parcel = (Parcel*)env->GetIntField(
-
argParcel, no.native_parcel);
-
const sp& control(getSurfaceControl(env, clazz));
-
//还好,只是把数据序列化到Parcel中
-
SurfaceControl::writeSurfaceToParcel(control, parcel);
-
if (flags & PARCELABLE_WRITE_RETURN_VALUE) {
-
setSurfaceControl(env, clazz, 0);
-
}
-
}
那看看客户端的Surface_readFromParcel吧。
[----->Surface_readFromParcel]
-
static void Surface_readFromParcel(
-
JNIEnv* env, jobject clazz, jobject argParcel)
-
{
-
Parcel* parcel = (Parcel*)env->GetIntField( argParcel, no.native_parcel);
-
//客户端这边还没有surface呢
-
const sp& control(getSurface(env, clazz));
-
//不过我们看到希望了,根据服务端那边Parcel信息来构造一个新的surface
-
sp rhs = new Surface(*parcel);
-
if (!Surface::isSameSurface(control, rhs)) {
-
setSurface(env, clazz, rhs); //把这个新surface赋给客户端。终于我们有了surface!
-
}
-
}
到此,我们终于七拐八绕的得到了surface,这其中经历太多曲折了。下一节,我们将精简这其中复杂的操作,统一归到Native层,以这样为切入点来了解Surface的工作流程和原理。
好,反正你知道ViewRoot调用了relayout后,Surface就真正从WindowManagerService那得到了。继续回到ViewRoot,其中还有一个重要地方是我们知道却不了解的。
-
private void performTraversals() {
-
// cache mView since it is used so much below…
-
final View host = mView;
-
boolean initialized = false;
-
boolean contentInsetsChanged = false;
-
boolean visibleInsetsChanged;
-
try {
-
relayoutResult = relayoutWindow(params, viewVisibility, insetsPending);
-
// relayoutWindow完后,我们得到了一个无比宝贵的Surface
-
//那我们画界面的地方在哪里?就在这个函数中,离relayoutWindow不远处。
-
…
-
boolean cancelDraw = attachInfo.mTreeObserver.dispatchOnPreDraw();
-
if (!cancelDraw && !newSurface) {
-
mFullRedrawNeeded = false;
-
draw(fullRedrawNeeded); //draw?draw什么呀?
-
}
[—>ViewRoot::draw()]
-
private void draw(boolean fullRedrawNeeded) {
-
Surface surface = mSurface; //嘿嘿,不担心了,surface资源都齐全了
-
if (surface == null || !surface.isValid()) {
-
return;
-
}
-
if (mAttachInfo.mViewScrollChanged) {
-
mAttachInfo.mViewScrollChanged = false;
-
mAttachInfo.mTreeObserver.dispatchOnScrollChanged();
-
}
-
int yoff;
-
final boolean scrolling = mScroller != null && mScroller.computeScrollOffset();
-
if (scrolling) {
-
yoff = mScroller.getCurrY();
-
} else {
-
yoff = mScrollY;
-
}
-
if (mCurScrollY != yoff) {
-
mCurScrollY = yoff;
-
fullRedrawNeeded = true;
-
}
-
float appScale = mAttachInfo.mApplicationScale;
-
boolean scalingRequired = mAttachInfo.mScalingRequired;
-
Rect dirty = mDirty;
-
if (mUseGL) { //我们不用OPENGL
-
…
-
}
-
Canvas canvas;
-
try {
-
int left = dirty.left;
-
int top = dirty.top;
-
int right = dirty.right;
-
int bottom = dirty.bottom;
-
//从Surface中锁定一块区域,这块区域是我们认为的需要重绘的区域
-
canvas = surface.lockCanvas(dirty);
-
// TODO: Do this in native
-
canvas.setDensity(mDensity);
-
}
-
try {
-
if (!dirty.isEmpty() || mIsAnimating) {
-
long startTime = 0L;
-
try {
-
canvas.translate(0, -yoff);
-
if (mTranslator != null) {
-
mTranslator.translateCanvas(canvas);
-
}
-
canvas.setScreenDensity(scalingRequired
-
? DisplayMetrics.DENSITY_DEVICE : 0);
-
//mView就是之前的decoreView,
-
mView.draw(canvas);
-
}
-
} finally {
-
//我们的图画完了,告诉surface释放这块区域
-
surface.unlockCanvasAndPost(canvas);
-
}
-
if (scrolling) {
-
mFullRedrawNeeded = true;
-
scheduleTraversals();
-
}
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则近万的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:Android)
学习分享
①「Android面试真题解析大全」PDF完整高清版+②「Android面试知识体系」学习思维导图压缩包
《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》,点击传送门即可获取!
{
-
if (!dirty.isEmpty() || mIsAnimating) {
-
long startTime = 0L;
-
try {
-
canvas.translate(0, -yoff);
-
if (mTranslator != null) {
-
mTranslator.translateCanvas(canvas);
-
}
-
canvas.setScreenDensity(scalingRequired
-
? DisplayMetrics.DENSITY_DEVICE : 0);
-
//mView就是之前的decoreView,
-
mView.draw(canvas);
-
}
-
} finally {
-
//我们的图画完了,告诉surface释放这块区域
-
surface.unlockCanvasAndPost(canvas);
-
}
-
if (scrolling) {
-
mFullRedrawNeeded = true;
-
scheduleTraversals();
-
}
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则近万的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
[外链图片转存中…(img-CoRNz83M-1712052917106)]
[外链图片转存中…(img-zbESHM3R-1712052917106)]
[外链图片转存中…(img-OSKP9NzX-1712052917107)]
[外链图片转存中…(img-MTO9XOtX-1712052917107)]
[外链图片转存中…(img-72yHLZCi-1712052917107)]
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:Android)
学习分享
①「Android面试真题解析大全」PDF完整高清版+②「Android面试知识体系」学习思维导图压缩包
[外链图片转存中…(img-os6P7RUN-1712052917108)]
[外链图片转存中…(img-vWjpVS1W-1712052917108)]
[外链图片转存中…(img-qvOMfIs5-1712052917108)]