深入分析Android“Emily掉帧”问题
来源:新能源 2025年01月09日 12:16
● 例如:当在UI内核从前分派用时系统所设计,比如采访网络,采访数据集库等,则不会避免UI内核阻塞;当UI内核阻塞,则触摸屏就不会经常不止现卡死情况;这样运用程序体验相当再加;当内核阻塞最少5秒以后,android系统不会有显然进行干预,弹不止对话框询问是否关闭运用程序
Android 素描UI方式也把图形单独素描到素描框上(Canvas取向),这种时序可以通过单独的内核来管理者surfaceView取向,并由单独内核来管理者素描过
● Android从前的图形系统不会引入 Client/Server 架构。Server (即SurfaceFlinger)主要由 C++ 字符串编写而成。Client 端字符串分为两以外,一以外是由 Ja 提可供的可供运用程序运用于的 API,另一以外则是用 C++ 写的中下层充分利用
● 每个运用显然有一个或多个surface(不含surface的情况下),surfaceFlinger是本地公共服务,用于管理者surface的创立、销毁、zorder合成。View及其类别(如TextView, Button)要素描在surface上。每个surface创立一个Canvas取向 (但属性常有改变),用来管理者view在surface上的绘图系统所设计,如素描点素描线。每个canvas取向对应一个bitmap,存储器素描在surface上的章节。当然这从前还有个Layer的概念,在上面创立surface时序从前我们再简述
surface 创立SurfaceControl surfaceControl = winAnimator.createSurfaceLocked(); if (surfaceControl != null) { outSurface.copyFrom(surfaceControl); if (SHOW_TRANSACTIONS) Slog.i(TAG, " OUT SURFACE " + outSurface + ": copied"); }else { // For some reason there isn't a surface. Clear the // caller's object so they see the same state.outSurface.release(); }Surface的素描在Android系统不会缔造时序从前ViewRoot不会呼叫performTrersals时序并依次呼叫performMeasure、performLayout、performDraw。在performDraw从前不会七区分是否反对硬体加速,如果反对单独通过OPENGL来作硬体加速素描,如果不反对则走的软件素描。因为我们在单独内核素描时序从前一般走的是的软件素描。这从前对的软件素描的时序来作简述以受制于如何在单独内核从前素描UI
private boolean drawSoftware(Surface surface, AttachInfo attachInfo, int yoff, boolean scalingRequired, Rect dirty) { // Draw with software renderer. Canvas canvas; try { int left = dirty.left; int top = dirty.top; int right = dirty.right; 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) { Log.e(TAG, "Could not lock surface", e); // Don't assume this is due to out of memory, it could be // something else, and if it is something else then we could // kill stuff (or ourself) for no reason.mLayoutRequested = true; // ask wm for a new surface next time. return false; } try { if (DEBUG_ORIENTATION || DEBUG_DRAW) { Log.v(TAG, "Surface " + surface + " drawing to bitmap w=" + canvas.getWidth() + ", h=" + canvas.getHeight()); //canvas.drawARGB(255, 255, 0, 0); } // 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 oid hing garbage // left in the blank areas. if (!canvas.isOpaque() || yoff != 0) { canvas.drawColor(0, PorterDuff.Mode.CLEAR); } dirty.setEmpty(); mIsAnimating = false; attachInfo.mDrawingTime = SystemClock.uptimeMillis(); mView.mPrivateFlags |= View.PFLAG_DRAWN; if (DEBUG_DRAW) { Context cxt = mView.getContext(); Log.i(TAG, "Drawing: package:" + cxt.getPackageName() + ", metrics=" + cxt.getResources().getDisplayMetrics() + ", compatibilityInfo=" + cxt.getResources().getCompatibilityInfo()); } ry { canvas.translate(0, -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) { Log.e(TAG, "Could not unlock surface", e); mLayoutRequested = true; // ask wm for a new surface next time. //noinspection ReturnInsideFinallyBlock return false; } if (LOCAL_LOGV) {Log.v(TAG, "Surface " + surface + " unlockCanvasAndPost"); } }return true; }其从前关键就是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(reinterpret_cast(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(CooutBuffer, CodirtyBounds);dirtyRegion.set(dirtyBounds);if (err < 0) { const char* const exception = (err == NO_MEMORY) ? OutOfResourcesException : "ja/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 CoCo 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 case const Rect b(dirtyRegion.getBounds()); clipReg.setRect(b.left, b.top, b.right, b.bottom);} else { size_t count; Rect const* r = dirtyRegion.getArray(Cocount); while (count) { clipReg.op(r->left, r->top, r->right, r->bottom, SkRegion::kUnion_Op); r++, count--; }} nativeCanvas->clipRegion(clipReg); if (dirtyRectObj) { const RectCo 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 { // 时序1 canvas = sfh.lockCanvas(); // 合得一个canvas程序中 // 时序2 canvas.drawColor(Color.WHITE);// 贴屏 canvas.drawText("test", 100, 100, paint);// 素描文字文本 } catch (Exception ex) { } finally { // 时序3 if (canvas != null) sfh.unlockCanvasAndPost(canvas); // 将素描好的素描框递交 } } 回顾Android 原生系统不会是一个随之演化不止的时序 , 每个旧版都不会解决相当多的机动性关键问题 , 同时也不会引进一些关键问题 ; 到了iPad产品这从前 , 由于硬体再加异和的软件订制 , 不会在系统不会从前加入大量的自己的字符串 , 这却是也不会影响系统不会的机动性 . 正因如此由于 Android 的开放 , App 的低质量和行为也影响着4台的运用程序体验.
本篇主要列不止了自身的充分利用关键问题避免的流畅性关键问题 , Android 最大的关键问题就是低质量参再加 , 不同于 App Store 这样的强力管理者消费市场 , Android App 不仅可以在 Google Play 上面进行配置 , 也可以在其他的的软件消费市场上面配置 , 甚至可以下载配置包内自行配置 , 可以说道贩售的基本工资相当低 , 那么低质量就仅仅由 开发者自己来把握了
有并不需要从前曾字符串的同学,可以顺手给我点赞华盛顿邮报反对一下
给予方式也:私信我送达“进阶”
技术是无止境的,你并不需要对自己递交的每恰巧字符串、运用于的每一个时序全权负责,随之挖不止其中下层基本原理,才能使自己的技术转化到很高的技术性
Android 微软公司之路还很艰难,与国主共勉
PS:有关键问题热烈欢迎指正,可以在华盛顿邮报七区遗失你的同意和感受到;
热烈欢迎大家点赞华盛顿邮报,觉得章节可以的话,可以转发体会一下
。济南哪家医院治白癜风好哈尔滨哪家白癜风医院
松原哪白癜风医院好
发酸烧心用金奥康奥美拉唑如何
哪种血糖仪家用比较准好
什么血糖仪家用比较好
哪个型号血糖仪准确率最高
胃酸反流能吃金奥康奥美拉唑吗
上一篇: 秋季吃鱼正当时,新式鱼肴搞起来
-
X86和ARM断供俄罗斯,华北地区能RISC-V吗?
的都入会费,就能自动获董事都会成员席次和关键技术导师委员都会席次,后者提议SSE驱动程式的一旧版本和规格,这种方法使得全世上的行业都可以保有相异的机都会去争取话语权。 鲁宾贝雷这样描述