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


UI线程阻塞当一个应用程序启动之后 , android系统会为这个应用创建一个主线程;这个线程非常重要 , 它负责渲染视图 , 分发事件到响应监听器并执行 , 对界面进行轮询监听;因此 , 一般也叫做“UI线程”
● android系统不会给应用程序的多个元素组件 , 建立多个线程来执行;一个视图Activity中的多个view组件运行在同一个UI线程中;因此 , 多个view组件的监听器的执行可能会相互影响
● 例如:当在UI线程中执行耗时操作 , 比如访问网络 , 访问数据库等 , 则会导致UI线程阻塞;当UI线程阻塞 , 则屏幕就会出现卡死情况;这样用户体验非常差;当线程阻塞超过5秒以后 , android系统有可能进行干预 , 弹出对话框询问是否关闭应用程序
Android 绘制UI方式把图形直接绘制到画布上(Canvas对象) , 这种方法可以通过独立的线程来管理surfaceView对象 , 并由独立线程来管理绘制过
● Android中的图形系统采用 Client/Server 架构 。Server (即SurfaceFlinger)主要由 C++ 代码编写而成 。Client 端代码分为两部分 , 一部分是由 JAVA 提供的供应用程序使用的 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会调用performTraversals方法并依次调用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 ConstantConditionsif (left != dirty.left || top != dirty.top || right != dirty.right ||bottom != dirty.bottom) {attachInfo.mIgnoreDirtyState = true;}// TODO: Do this in nativecanvas.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 avoid having 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 ReturnInsideFinallyBlockreturn false; }if (LOCAL_LOGV) {Log.v(TAG, "Surface " + surface + " unlockCanvasAndPost"); } }return true;}


推荐阅读