图形系统
SurfaceFlinger负责合成所有的Layer并送显到Display,在Android系统中,SurfaceFlinger是一个独立进程
Surface是一个窗口,例如:一个Activity是一个Surface、一个Dialog也是一个Surface,承载了上层的图形数据,与SurfaceFlinger侧的Layer相对应
在收到VSync pulse后,将马上开始下一帧的渲染。即一旦收到VSync通知,CPU和GPU就立刻开始计算然后把数据写入buffer。而这一"drawing with VSync" 的实现就是Choreographer
- 所有UI的变化都是走到ViewRootImpl的scheduleTraversals()方法。
- 在VSync信号到来时才会执行绘制,即ViewRootImpl.performTraversals()
- 首先使用mTraversalScheduled字段保证同时间多次更改只会刷新一次,例如TextView连续两次setText(),也只会走一次绘制流程。
- 然后把当前线程的消息队列Queue添加了同步屏障,这样就屏蔽了正常的同步消息,保证VSync到来后立即执行绘制,而不是要等前面的同步消息。后面会具体分析同步屏障和异步消息的代码逻辑。
- 调用了mChoreographer.postCallback()方法,发送一个会在下一帧执行的回调,即在下一个VSync到来时会执行TraversalRunnable–>doTraversal()—>performTraversals()–>绘制流程。
- 在ViewRootImpl构造方法中获取了Choreographer实例
- 保持在了自己单例的ThreadLocal中
- 创建了一个mHandler,FrameHandler:传入当前线程Looper,用来分发每一帧事件,主要有三种:有延迟的任务发延迟消息、不在原线程的发到原线程、没开启VSYNC的直接走 doFrame 方法取执行绘制
- VSync事件接收器mDisplayEventReceiver,继承runnable:在onVsync回调中将本身将接收器本身作为runnable传入异步消息msg,并使用mHandler发送msg,最终执行的就是doFrame()方法了,这里因为是使用handler发送消息到MessageQueue中,不一定是立刻执行,如何MessageQueue中前面有较为耗时的操作,那么就要等完成,才会执行本次的doFrame()
- 任务链表数组mCallbackQueues:建一个链表类型CallbackQueue的数组,大小为5,也就是数组中有五个链表,每个链表存相同类型的任务,按照处理优先级依次为:输入、动画、遍历绘制等任务(CALLBACK_INPUT、CALLBACK_ANIMATION、CALLBACK_TRAVERSAL)
- 丢帧(掉帧) ,是说 这一帧延迟显示 还是丢弃不再显示 ? 答:延迟显示,因为缓存交换的时机只能等下一个VSync了。
- 布局层级较多/主线程耗时 是如何造成 丢帧的呢? 答:布局层级较多/主线程耗时 会影响CPU/GPU的执行时间,大于16.6ms时只能等下一个VSync了。
- 16.6ms刷新一次 是啥意思?是每16.6ms都走一次 measure/layout/draw ? 答:屏幕的固定刷新频率是60Hz,即16.6ms。不是每16.6ms都走一次 measure/layout/draw,而是有绘制任务才会走,并且绘制时间间隔是取决于布局复杂度及主线程耗时。
- measure/layout/draw 走完,界面就立刻刷新了吗? 答:不是。measure/layout/draw 走完后 会在VSync到来时进行缓存交换和刷新。
- 如果界面没动静止了,还会刷新吗? 答:屏幕会固定没16.6ms刷新,但CPU/GPU不走绘制流程。见下面的SysTrace图。
- 可能你知道VSYNC,这个具体指啥?在屏幕刷新中如何工作的? 答:当扫描完一个屏幕后,设备需要重新回到第一行以进入下一次的循环,此时会出现的vertical sync pulse(垂直同步脉冲)来保证双缓冲在最佳时间点才进行交换。并且Android4.1后 CPU/GPU的绘制是在VSYNC到来时开始。
- 可能你还听过屏幕刷新使用 双缓存、三缓存,这又是啥意思呢? 答:双缓存是Back buffer、Frame buffer,用于解决画面撕裂。三缓存增加一个Back buffer,用于减少Jank。
- 可能你还听过神秘的Choreographer,这又是干啥的? 答:用于实现——“CPU/GPU的绘制是在VSYNC到来时开始”
HWC(hwcomposer)是Android中进行窗口(Layer)合成和显示的HAL层模块,其实现是特定于设备的,而且通常由显示设备制造商 (OEM)完成,为SurfaceFlinger服务提供硬件支持
VSync信号是由HWC硬件模块根据屏幕刷新率产生
- 当View.invalidate调用后,最终会触发ViewRootImpl向Choreographer注册一个TraversalRunnable。
- Choreographer本地保存这个TraversalRunnable后,会通过DisplayEventReceiver.java调用到Native层,最终一步步调用到mEventThread线程,修改connection->count = 0(请求接收下一次VSync)。
- Vsync到来后,会从SurfaceFlinger进程一步步回调到客户端进程,最终触发ViewRootImpl之前注册TraversalRunnable,启动View树的渲染。
双缓存的交换 是在Vsyn到来时进行,交换后屏幕会取Frame buffer内的新数据,而实际 此时的Back buffer 就可以供GPU准备下一帧数据了。 如果 Vsyn到来时 CPU/GPU就开始操作的话,是有完整的16.6ms的,这样应该会基本避免jank的出现了,Google在Android 4.1系统中对Android Display系统进行了重构,实现了Project Butter(黄油工程):系统在收到VSync pulse后,将马上开始下一帧的渲染。即一旦收到VSync通知(16ms触发一次),CPU和GPU 才立刻开始计算然后把数据写入buffer
/* 装载资源 */
mBitQQ = ((BitmapDrawable) getResources().getDrawable(R.drawable.qq)).getBitmap();
/* 创建屏幕大小的缓冲区 */
mSCBitmap = Bitmap.createBitmap(320, 480, Config.ARGB_8888);
/* 创建Canvas */
backCanvas = new Canvas();
/* 设置将内容绘制在mSCBitmap上 */
backCanvas.setBitmap(mSCBitmap);
mPaint = new Paint();
/* 将mBitQQ绘制到mSCBitmap上 */
backCanvas.drawBitmap(mBitQQ, 0, 0, mPaint);
public void onDraw(Canvas canvas) {
super.onDraw(canvas);
/* 将mSCBitmap显示到屏幕上 */
canvas.drawBitmap(mSCBitmap, 0, 0, mPaint);
}
SurfaceView从Android 1.0(API level 1)时就有 。它继承自类View,因此它本质上是一个View。但与普通View不同的是,它有自己的Surface
- 可以放到单独线程去做,渲染时可以有自己的GL context,不会影响主线程对事件的响应
- 因为Surface不在View hierachy中,它的显示也不受View的属性控制,所以不能进行平移,缩放等变换
- SurfaceView是放在其他最底层的视图层次中,所有其他视图层都在它上面,所以在它之上可以添加一些层,而且它不能是透明的
- 可以控制显示的fps
//用于显示的surfaceview
public class SurfaceAnimatorView extends SurfaceView implements SurfaceHolder.Callback {
private IAnimator iAnimator;
private Thread mRenderThread;
private SurfaceHolder surfaceHolder;
private Paint mPaintClear;
// 控制帧率
private int fps = 1000 / 35;
private volatile boolean isStop = false;
// 负责不断绘制的runnable,在子线程执行
private Runnable mRenderRunnable = new Runnable() {
@Override
public void run() {
while (!isStop) {
long start = System.currentTimeMillis();
onDrawAnimator();
long spendTime = System.currentTimeMillis() - start;
try {
if ((fps - spendTime) > 0) {
Thread.sleep(fps - spendTime);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
};
private void onDrawAnimator() {
// 从surfaceHolder 获取离屏的canvas
Canvas canvas = surfaceHolder.lockCanvas();
if (canvas == null) {
return;
}
// 清屏。这步很重要,不然画布会有上次绘制的内容
canvas.drawPaint(mPaintClear);
// 将画布给animator,实现对应的动画
iAnimator.onDraw(canvas);
// 释放canvas
surfaceHolder.unlockCanvasAndPost(canvas);
}
public SurfaceAnimatorView(Context context) {
super(context);
init();
}
public SurfaceAnimatorView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public void init() {
setFocusable(true);
if (surfaceHolder == null) {
surfaceHolder = getHolder();
surfaceHolder.addCallback(this);
}
this.setZOrderOnTop(true);
this.getHolder().setFormat(PixelFormat.TRANSLUCENT);
mPaintClear = new Paint();
mPaintClear.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
setBackgroundColor(getContext().getResources().getColor(R.color.teal_200));
}
@Override
public void surfaceCreated(SurfaceHolder surfaceHolder) {
// 初始化animator
iAnimator = new CircleLoadingAnimator(getContext());
iAnimator.onLayout(getWidth(), getHeight());
isStop = false;
// 启动绘制的线程
mRenderThread = new Thread(mRenderRunnable);
mRenderThread.start();
}
@Override
public void surfaceChanged(SurfaceHolder surfaceHolder, int i, int i1, int i2) {
}
@Override
public void surfaceDestroyed(SurfaceHolder surfaceHolder) {
isStop = true;
try {
mRenderThread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
//动画接口
public interface IAnimator {
void onLayout(int width, int height);
void onDraw(Canvas canvas);
}
// 具体实现动画的类
public class CircleLoadingAnimator implements IAnimator {
private DecelerateInterpolator fastToSlow = new DecelerateInterpolator();
// 每帧变化的幅度,越小越慢,帧区别也越小
private int INCREASE_VALUE = 4;
private int MAX_CIRCLE_RADIUS = 200;
private int CIRCLE_RADIUS = MAX_CIRCLE_RADIUS >> 1;
private int mSmallCircleRadius = 0;
private int mBigCircleRadius = MAX_CIRCLE_RADIUS;
private int increaseValue = -1;
private int recordValue = MAX_CIRCLE_RADIUS;
private Paint mSmallPaint = new Paint();
private Paint mBigPaint = new Paint();
private int mX;
private int mY;
private static final String TAG = "CircleLoadingAnimator";
public CircleLoadingAnimator(Context context) {
mBigPaint.setStyle(Paint.Style.FILL);
mBigPaint.setStrokeCap(Paint.Cap.ROUND);
mBigPaint.setAntiAlias(true);
mBigPaint.setColor(context.getResources().getColor(R.color.white_90));
mSmallPaint.setStyle(Paint.Style.FILL);
mSmallPaint.setStrokeCap(Paint.Cap.ROUND);
mSmallPaint.setAntiAlias(true);
mSmallPaint.setColor(context.getResources().getColor(R.color.white));
}
@Override
public void onLayout(int width, int height) {
mX = width >> 1;
mY = height >> 1;
}
@Override
public void onDraw(Canvas canvas) {
updateInCreaseValue();
recordValue += increaseValue;
// 模拟属性动画 0 - > 1 的过程
float value = (float) ((MAX_CIRCLE_RADIUS - recordValue) * 1.0 / (CIRCLE_RADIUS));
// 更新圆半径
mBigCircleRadius = (int) (fastToSlow.getInterpolation(1 - value) * CIRCLE_RADIUS + CIRCLE_RADIUS);
mSmallCircleRadius = (int) (CIRCLE_RADIUS - fastToSlow.getInterpolation(1 - value) * CIRCLE_RADIUS);
// 画圆
canvas.drawCircle(mX, mY, mSmallCircleRadius, mSmallPaint);
canvas.drawCircle(mX, mY, mBigCircleRadius, mBigPaint);
}
// 更新边界值
private void updateInCreaseValue() {
if (mBigCircleRadius >= MAX_CIRCLE_RADIUS) {
increaseValue = -1 * INCREASE_VALUE;
} else if (mBigCircleRadius <= (CIRCLE_RADIUS)) {
increaseValue = INCREASE_VALUE;
}
}
}
GLSurfaceView从Android 1.5(API level 3)开始加入,作为SurfaceView的补充。它可以看作是SurfaceView的一种典型使用模式。在SurfaceView的基础上,它加入了EGL的管理,并自带了渲染线程
和SurfaceView不同的是,它对图像流的处理并不直接显示,而是转为GL外部纹理,因此可用于图像流数据的二次处理(如Camera滤镜,桌面特效等
- 它核心管理着一个BufferQueue的Consumer和Producer两端。Producer端用于内容流的源输出数据,Consumer端用于拿GraphicBuffer并生成纹理
- SurfaceTexture.OnFrameAvailableListener用于让SurfaceTexture的使用者知道有新数据到来
TextureView 在4.0(API level 14)中引入。它可以将内容流直接投影到View中,可以用于实现Live preview等功能
- 它不会在WMS中单独创建窗口,而是作为View hierachy中的一个普通View,因此可以和其它普通View一样进行移动,旋转,缩放,动画等变化
- 必须在硬件加速的窗口中。它显示的内容流数据可以来自App进程或是远端进程
SurfaceTexture mOESSurfaceTexture;
TextureView mTextureView = new TextureView(this);
mTextureView.setSurfaceTextureListener(mTextureListener);//设置Texture作为展示界面
public TextureView.SurfaceTextureListener mTextureListener = new TextureView.SurfaceTextureListener() {
@Override
public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
mOESTextureId = Utils.createOESTextureObject();
mEglSurface = mEgl.eglCreateWindowSurface(mEGLDisplay, mEGLConfig[0], surfaceTexture, null);//创建openGL显示窗口
mOESSurfaceTexture = new SurfaceTexture(mOESTextureId);
mOESSurfaceTexture.setOnFrameAvailableListener(mFrameLsn);//初始化SurfaceTexture并绑定FrameAvailable回调
mCameraId = Camera.CameraInfo.CAMERA_FACING_BACK;
DisplayMetrics dm = new DisplayMetrics();
mCamera = new CameraV1(CameraV1TextureViewActivity.this);
if (!mCamera.openCamera(dm.widthPixels, dm.heightPixels, mCameraId)) {
return;
}
mCamera.setPreviewTexture(mOESSurfaceTexture);
mCamera.startPreview();
}
@Override
public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
}
@Override
public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
if (mCamera != null) {
mCamera.stopPreview();
mCamera.releaseCamera();
mCamera = null;
}
return true;
}
@Override
public void onSurfaceTextureUpdated(SurfaceTexture surface) {
}
};
public SurfaceTexture.OnFrameAvailableListener mFrameLsn = new SurfaceTexture.OnFrameAvailableListener(){
@Override
public void onFrameAvailable(SurfaceTexture surfaceTexture) {
long t1, t2;
t1 = System.currentTimeMillis();
if (mOESSurfaceTexture != null) {
mOESSurfaceTexture.updateTexImage();
mOESSurfaceTexture.getTransformMatrix(transformMatrix);
}
mEgl.eglMakeCurrent(mEGLDisplay, mEglSurface, mEglSurface, mEGLContext);
GLES20.glViewport(0,0,mTextureView.getWidth(),mTextureView.getHeight());
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
GLES20.glClearColor(1f, 1f, 0f, 0f);
aPositionLocation = glGetAttribLocation(mShaderProgram, FilterEngine.POSITION_ATTRIBUTE);
aTextureCoordLocation = glGetAttribLocation(mShaderProgram, FilterEngine.TEXTURE_COORD_ATTRIBUTE);
uTextureMatrixLocation = glGetUniformLocation(mShaderProgram, FilterEngine.TEXTURE_MATRIX_UNIFORM);
uTextureSamplerLocation = glGetUniformLocation(mShaderProgram, FilterEngine.TEXTURE_SAMPLER_UNIFORM);
//绘制图像到openGL缓存中
glActiveTexture(GLES20.GL_TEXTURE0);
glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, mOESTextureId);
glUniform1i(uTextureSamplerLocation, 0);
glUniformMatrix4fv(uTextureMatrixLocation, 1, false, transformMatrix, 0);
if (mBuffer != null) {
mBuffer.position(0);
glEnableVertexAttribArray(aPositionLocation);
glVertexAttribPointer(aPositionLocation, 2, GL_FLOAT, false, 16, mBuffer);
mBuffer.position(2);
glEnableVertexAttribArray(aTextureCoordLocation);
glVertexAttribPointer(aTextureCoordLocation, 2, GL_FLOAT, false, 16, mBuffer);
glDrawArrays(GL_TRIANGLES, 0, 6);
}
//交换显示内存
mEgl.eglSwapBuffers(mEGLDisplay, mEglSurface);
t2 = System.currentTimeMillis();
Log.i(TAG, "drawFrame: time = " + (t2 - t1));
}
};