深入分析Android“卡顿掉帧”问题( 三 )

其中关键就是canvas = mSurface.lockCanvas(dirty) 与 surface.unlockC anvasAndPost(canvas);先lockCanvas , 绘制UI , 最后通过unlockCanvasAndPost通知surfaceFlinger先做zorder组合显示 。
lockCanvas(dirty) 就是通过JNI调用nativeLockCanvas返回一个Canvas下面看nativeLockCanvas的实现 。
sttic void nativeLockCanvas(JNIEnv* env, jclass clazz, jint nativeObject, jobject canvasObj, jobject dirtyRectObj) { sp<Surface> surface(reinterpret_cast<Surface *>(nativeObject));
if (!isSurfaceValid(surface)) {doThrowIAE(env);return;} // get dirty regionRegion dirtyRegion;if (dirtyRectObj) {Rect dirty;dirty.left = env->GetIntField(dirtyRectObj, gRectClassInfo.left);dirty.top = env->GetIntField(dirtyRectObj, gRectClassInfo.top);dirty.right = env->GetIntField(dirtyRectObj, gRectClassInfo.right);dirty.bottom = env->GetIntField(dirtyRectObj, gRectClassInfo.bottom);if (!dirty.isEmpty()) {dirtyRegion.set(dirty);}} else {dirtyRegion.set(Rect(0x3FFF, 0x3FFF));} ANativeWindow_Buffer outBuffer;Rect dirtyBounds(dirtyRegion.getBounds());status_t err = surface->lock(&outBuffer, &dirtyBounds);dirtyRegion.set(dirtyBounds);if (err < 0) {const char* const exception = (err == NO_MEMORY) ?OutOfResourcesException :"java/lang/IllegalArgumentException";jniThrowException(env, exception, NULL);return;} // Associate a SkCanvas object to this surfaceenv->SetIntField(canvasObj, gCanvasClassInfo.mSurfaceFormat, outBuffer.format); SkBitmap bitmap;ssize_t bpr = outBuffer.stride * bytesPerPixel(outBuffer.format);bitmap.setConfig(convertPixelFormat(outBuffer.format), outBuffer.width, outBuffer.height, bpr);if (outBuffer.format == PIXEL_FORMAT_RGBX_8888) {bitmap.setIsOpaque(true);}if (outBuffer.width > 0 && outBuffer.height > 0) {bitmap.setPixels(outBuffer.bits);} else {// be safe with an empty bitmap.bitmap.setPixels(NULL);} SkCanvas* nativeCanvas = SkNEW_ARGS(SkCanvas, (bitmap));swapCanvasPtr(env, canvasObj, nativeCanvas); SkRegion clipReg;if (dirtyRegion.isRect()) { // very common caseconst Rect b(dirtyRegion.getBounds());clipReg.setRect(b.left, b.top, b.right, b.bottom);} else {size_t count;Rect const* r = dirtyRegion.getArray(&count);while (count) {clipReg.op(r->left, r->top, r->right, r->bottom, SkRegion::kUnion_Op);r++, count--;}} nativeCanvas->clipRegion(clipReg); if (dirtyRectObj) {const Rect& bounds(dirtyRegion.getBounds());env->SetIntField(dirtyRectObj, gRectClassInfo.left, bounds.left);env->SetIntField(dirtyRectObj, gRectClassInfo.top, bounds.top);env->SetIntField(dirtyRectObj, gRectClassInfo.right, bounds.right);env->SetIntField(dirtyRectObj, gRectClassInfo.bottom, bounds.bottom);}}在JNI层实现的就是通过surface获取到Layer中的buffer , 并生成一个skiabitmap ,  Android 2D软件绘图使用skia作为核心引擎,这个bitmap的存储空间为Layer buffer 。绘制的UI就是写入到这个buffer中 , 绘制好后通过 unlockCanvasAndPost通知surfaceflinger输出显示 。如果是独立线程绘制UI , 那么流程与上描述基本一致 。但需要注意的是如果独立线程绘制的话 , surface可通过surfaceView来获取
private void draw() {try {//步骤1canvas = sfh.lockCanvas(); // 得到一个canvas实例//步骤2canvas.drawColor(Color.WHITE);// 刷屏canvas.drawText("test", 100, 100, paint);// 画文字文本} catch (Exception ex) {} finally {// 步骤3if (canvas != null)sfh.unlockCanvasAndPost(canvas); // 将画好的画布提交}}总结Android 原生系统是一个不断进化的过程 , 每个版本都会解决非常多的性能问题 , 同时也会引进一些问题 ; 到了手机厂商这里 , 由于硬件差异和软件定制 , 会在系统中加入大量的自己的代码 , 这无疑也会影响系统的性能 . 同样由于 Android 的开放 , App 的质量和行为也影响着整机的用户体验.
本篇主要列出了自身的实现问题导致的流畅性问题 , Android 最大的问题就是质量良莠不齐 , 不同于 App Store 这样的强力管理市场 , Android App 不仅可以在 google Play 上面进行安装 , 也可以在其他的软件市场上面安装 , 甚至可以下载安装包自行安装 , 可以说上架的门槛非常低 , 那么质量就只能由 开发者自己来把握了
有需要文中代码的同学 , 可以顺手给我点赞评论支持一下
获取方式:私信我发送“进阶”
技术是无止境的 , 你需要对自己提交的每一行代码、使用的每一个工具负责 , 不断挖掘其底层原理 , 才能使自己的技术升华到更高的层面
Android 架构师之路还很漫长 , 与君共勉
PS:有问题欢迎指正,可以在评论区留下你的建议和感受;
欢迎大家点赞评论 , 觉得内容可以的话 , 可以转发分享一下


推荐阅读