ActivityManagerService解读之Activity启动闲聊--以TaskSnapshot为点切入AndroidView绘制原理分析

2023-05-16

ActivityManagerService解读之进程管理开始,我们大大小小已经有11篇文章专门分析了Android系统服务ActivityManagerService,总结一句话ActivityManagerService在Android系统中主要负责对应用进程和Android四大组件进行管理。本篇将以TaskSnapshot为点切入Android View界面绘制相关的指点并对其进行详细解读分析。前面ActivityManagerService解读之Activity启动闲聊--Activity启动窗口和TaskSnapshot 文章中也曾介绍过相关内容,不过对TaskSnapshot的细节内容都是捎带而过。如题所诉,我们着重分析一下Android O新加入的TaskSnapshot功能的实现,并以此点来分析Android View绘制相关的内容。关于Android View绘制相关的细节内容将会在后续的文章中详细介绍。

本文简介

ActivityManagerService解读之Activity启动闲聊--Activity启动窗口和TaskSnapshot这篇文章告诉了我们什么是TaskSnapshot以及什么是Activity的启动窗口,并提供了一些日常我们运用该类窗口的小思路,但是我们并未提及其中的相关原理,尤其是TaskSnapshot功能。对于Android选用Activity的启动窗口还是TaskSnapshot界面,我们简单概括为以Activity的启动方式作为区分的关键。Activity的启动窗口比较简单,大家可以自行理解,本篇的重点在于理解TaskSnapshot的显示原理。

TaskSnapshot显示过程以及相关数据结构介绍--慢慢来,咱们一步一步来分析,求稳

TaskSnapshot功能入口

列取添加TaskSnapshot的关键入口代码:

    public boolean addStartingWindow(String pkg, int theme, CompatibilityInfo compatInfo,
            CharSequence nonLocalizedLabel, int labelRes, int icon, int logo, int windowFlags,
            IBinder transferFrom, boolean newTask, boolean taskSwitch, boolean processRunning,
            boolean allowTaskSnapshot, boolean activityCreated, boolean fromRecents) {
        synchronized(mWindowMap) {

            final TaskSnapshot snapshot = mService.mTaskSnapshotController.getSnapshot(
                    mContainer.getTask().mTaskId, mContainer.getTask().mUserId,
                    false /* restoreFromDisk */, false /* reducedResolution */);
            final int type = getStartingWindowType(newTask, taskSwitch, processRunning,
                    allowTaskSnapshot, activityCreated, fromRecents, snapshot);
            Slog.v(TAG_WM, "addStartingWindow type:" + type);
            if (type == STARTING_WINDOW_TYPE_SNAPSHOT) {
                return createSnapshot(snapshot);
            }
    }

我们可以看出当获取的starting window为STARTING_WINDOW_TYPE_SNAPSHOT类型的时候便走的TaskSnapshot功能。我们继续看下去:

    private boolean createSnapshot(TaskSnapshot snapshot) {
        if (snapshot == null) {
            return false;
        }

        if (true) Slog.v(TAG_WM, "Creating SnapshotStartingData");
        mContainer.startingData = new SnapshotStartingData(mService, snapshot);
        scheduleAddStartingWindow();
        return true;
    }

 SnapshotStartingData类很简单总共就没有多少代码:

class SnapshotStartingData extends StartingData {

    private final WindowManagerService mService;
    private final TaskSnapshot mSnapshot;

    SnapshotStartingData(WindowManagerService service, TaskSnapshot snapshot) {
        super(service);
        mService = service;
        mSnapshot = snapshot;
    }

    @Override
    StartingSurface createStartingSurface(AppWindowToken atoken) {
        return mService.mTaskSnapshotController.createStartingSurface(atoken, mSnapshot);
    }
}

我们先看下SnapshotStartingData构造方法的snapshot类型的参数,它是在mService.mTaskSnapshotController.getSnapshot中获取来的,我们来看下TaskSnapshotController的getSnapshot:

    @Nullable TaskSnapshot getSnapshot(int taskId, int userId, boolean restoreFromDisk,
            boolean reducedResolution) {
        return mCache.getSnapshot(taskId, userId, restoreFromDisk, reducedResolution
                || DISABLE_FULL_SIZED_BITMAPS);
    }

mCache为TaskSnapshotCache类型,从命名来看这个类应该是用来做缓存的,事实也确实如此。

TaskSnapshot相关数据信息获取--重点就是显示数据对应的buffer

我们继续看下TaskSnapshotCache的getSnapshot方法:

    @Nullable TaskSnapshot getSnapshot(int taskId, int userId, boolean restoreFromDisk,
            boolean reducedResolution) {

        synchronized (mService.mWindowMap) {
            // Try the running cache.
            final CacheEntry entry = mRunningCache.get(taskId);
            if (entry != null) {
                return entry.snapshot;
            }
        }

        // Try to restore from disk if asked.
        if (!restoreFromDisk) {
            return null;
        }
        return tryRestoreFromDisk(taskId, userId, reducedResolution);
    }

mRunningCache是一个ArrayMap<Integer, CacheEntry>类型的Map。CacheEntry是TaskSnapshotCache的内部类,其结构也比较简单:

    private static final class CacheEntry {

        /** The snapshot. */
        final TaskSnapshot snapshot;

        /** The app token that was on top of the task when the snapshot was taken */
        final AppWindowToken topApp;

        CacheEntry(TaskSnapshot snapshot, AppWindowToken topApp) {
            this.snapshot = snapshot;
            this.topApp = topApp;
        }
    }

就是将TaskSnapshot和Task的顶层AppWindowToken关联起来,因为TaskSnapshot功能在截取图片的时候一般只截取Task中处于top的Activity,这个AppWindowToken属于WindowManagerService相关的内容,但是它和ActivityManagerService有着紧密的联系,后续分析WindowManagerService的时候再着重讲解。回头再看TaskSnapshotCache的getSnapshot方法,该方法先从内存中去找对应的TaskSnapshot,如果不存在便会根据参数restoreFromDisk的设置,选择是否从本地磁盘中进行恢复操作,为什么会从本地中去恢复呢?这也为了关机再重启系统还要运用此功能,至于其中的原理,我们看下tryRestoreFromDisk方法便知:

    private TaskSnapshot tryRestoreFromDisk(int taskId, int userId, boolean reducedResolution) {
        final TaskSnapshot snapshot = mLoader.loadTask(taskId, userId, reducedResolution);
        if (snapshot == null) {
            return null;
        }
        return snapshot;
    }

继续来看下TaskSnapshotLoader的loadTask:Android的注释真是给我们理解该方法很大的帮助,我也舍不得将其删掉

    /**
     * Loads a task from the disk.
     * <p>
     * Do not hold the window manager lock when calling this method, as we directly read data from
     * disk here, which might be slow.
     *
     * @param taskId The id of the task to load.
     * @param userId The id of the user the task belonged to.
     * @param reducedResolution Whether to load a reduced resolution version of the snapshot.
     * @return The loaded {@link TaskSnapshot} or {@code null} if it couldn't be loaded.
     */
    TaskSnapshot loadTask(int taskId, int userId, boolean reducedResolution) {
        final File protoFile = mPersister.getProtoFile(taskId, userId);
        final File bitmapFile = reducedResolution
                ? mPersister.getReducedResolutionBitmapFile(taskId, userId)
                : mPersister.getBitmapFile(taskId, userId);
        if (bitmapFile == null || !protoFile.exists() || !bitmapFile.exists()) {
            return null;
        }
        try {
            final byte[] bytes = Files.readAllBytes(protoFile.toPath());
            final TaskSnapshotProto proto = TaskSnapshotProto.parseFrom(bytes);
            final Options options = new Options();
            options.inPreferredConfig = Config.HARDWARE;
            final Bitmap bitmap = BitmapFactory.decodeFile(bitmapFile.getPath(), options);
            if (bitmap == null) {
                Slog.w(TAG, "Failed to load bitmap: " + bitmapFile.getPath());
                return null;
            }
            final GraphicBuffer buffer = bitmap.createGraphicBufferHandle();
            if (buffer == null) {
                Slog.w(TAG, "Failed to retrieve gralloc buffer for bitmap: "
                        + bitmapFile.getPath());
                return null;
            }
            return new TaskSnapshot(buffer, proto.orientation,
                    new Rect(proto.insetLeft, proto.insetTop, proto.insetRight, proto.insetBottom),
                    reducedResolution, reducedResolution ? REDUCED_SCALE : 1f);
        } catch (IOException e) {
            Slog.w(TAG, "Unable to load task snapshot data for taskId=" + taskId);
            return null;
        }
    }

这里有个protofile,代表的是一个.proto为后缀的文件,这中类型的文件是 Google 公司内部的混合语言数据标准,简称为Protofile,可用于结构化数据串行化,或者说序列化,很适合做数据存储或 RPC 数据交换格式。关于其原理,您也可以去Android源码external/protobuf/javanano/目录去寻找您想要的信息,这里就不插体外话了。 我们来看下task_snapshot.proto文件的结构,其实最后我们就是依据该结构在二进制文件中保存了Task的orientation和Rect显示的边衬范围信息。

 syntax = "proto3";

 package com.android.server.wm;

 option java_package = "com.android.server.wm";
 option java_outer_classname = "WindowManagerProtos";

 message TaskSnapshotProto {
     int32 orientation = 1;
     int32 inset_left = 2;
     int32 inset_top = 3;
     int32 inset_right = 4;
     int32 inset_bottom = 5;
 }

其中mPersister为TaskSnapshotPersister,如果日常开发中您想要一些数据持久化的方案,TaskSnapshotPersister是一个不错的借鉴选择,我们这里就不细说TaskSnapshotPersister了,您只需简单将其理解为专门负责将TaskSnapshot相关的数据保存到本地磁盘和从本地磁盘读取TaskSnapshot相关文件到内存的类,更简单粗暴的说,它就是用来进行磁盘文件读和写。一般系统默认将我们TaskSnapshot的相关文件数据保存在/data/system_ce/0/snapshots,这里的0代表是用户id=0,您可以去对应的目录下看看里面存了什么。我们这里就上一张我测试机对应的内容给大家鲜一下

这里我们重点留意一下final GraphicBuffer buffer = bitmap.createGraphicBufferHandle()这个方法的调用,这便是Tasksnapshot的有点之一,节省内存消耗,至于如何节省的,我们等下再详细介绍。当我们调用完TaskSnapshotLoader的loadTask之后,我们便获取了一个fengzhuaTaskSnapshot对象。TaskSnapshot对象封装了显示所需的buffer信息以及显示的边衬范围。到此我们总算才将获取TaskSnapshot这一知识点理清了,我们先稍微总结一下:当我们启动窗口类型为STARTING_WINDOW_TYPE_SNAPSHOT时,我们通过TaskSnapshotController的GetSnapshot方法获取一个TaskSnapshot对象,该对象保存了我们显示所需的信息,获取该对象的时候,我们首先从内存缓存中获取,如果内存中没有,再依据参数从本地磁盘中恢复一个,最后我们便能获取到我们想要的对象了,当然该方法还是有可能返回一个null值的。

TaskSnapshot相关数据信息保存--会生成.proto和相关的jpg文件

既然能获取,当然就有保存的地方,我们来看下TaskSnapshot信息的是如何保存的:

    private void snapshotTasks(ArraySet<Task> tasks) {
        for (int i = tasks.size() - 1; i >= 0; i--) {
            final Task task = tasks.valueAt(i);
            final int mode = getSnapshotMode(task);
            final TaskSnapshot snapshot;
            switch (mode) {
                case SNAPSHOT_MODE_NONE:
                    continue;
                case SNAPSHOT_MODE_APP_THEME:
                    snapshot = drawAppThemeSnapshot(task);
                    break;
                case SNAPSHOT_MODE_REAL:
                    snapshot = snapshotTask(task);
                    break;
                default:
                    snapshot = null;
                    break;
            }
            if (snapshot != null) {
                final GraphicBuffer buffer = snapshot.getSnapshot();
                if (buffer.getWidth() == 0 || buffer.getHeight() == 0) {
                    buffer.destroy();
                    Slog.e(TAG, "Invalid task snapshot dimensions " + buffer.getWidth() + "x"
                            + buffer.getHeight());
                } else {
                    mCache.putSnapshot(task, snapshot);
                    mPersister.persistSnapshot(task.mTaskId, task.mUserId, snapshot);
                    if (task.getController() != null) {
                        task.getController().reportSnapshotChanged(snapshot);
                    }
                }
            }
        }
    }

该方法指出TaskSnapshot有三种模式:

  • SNAPSHOT_MODE_NONE:此模式下,将不启用TaskSnapshot功能
  • SNAPSHOT_MODE_APP_THEME:一般如果应用程序的窗口设置了FLAG_SECURE属性,便会处于该模式,该模式下TaskSnapshot的内容一般只绘制一些ActionBar,应用内容将会被白色替代。
  • SNAPSHOT_MODE_REAL:正常显示的模式,一般我们都处于该模式。

我们暂且先主要关注正常模式,来看下snapshotTask方法:

    private TaskSnapshot snapshotTask(Task task) {
        final AppWindowToken top = task.getTopChild();
        if (top == null) {
            return null;
        }
        final WindowState mainWindow = top.findMainWindow();
        if (mainWindow == null) {
            return null;
        }
        final boolean isLowRamDevice = ActivityManager.isLowRamDeviceStatic();
        final float scaleFraction = isLowRamDevice ? REDUCED_SCALE : 1f;
        final GraphicBuffer buffer = top.mDisplayContent.screenshotApplicationsToBuffer(top.token,
                -1, -1, false, scaleFraction, false, true);
        if (buffer == null || buffer.getWidth() <= 1 || buffer.getHeight() <= 1) {
            return null;
        }
        return new TaskSnapshot(buffer, top.getConfiguration().orientation,
                minRect(mainWindow.mContentInsets, mainWindow.mStableInsets),
                isLowRamDevice /* reduced */, scaleFraction /* scale */);
    }

TaskSnapshot的snapshotTask方法主要作用就是new一个TaskSnapshot,我们回头再看snapshotTasks,从命名来看snapshotTasks就是保存多个Task的TaskSnapshot信息。snapshotTasks方法中一旦获取到TaskSnapshot,便优先将数据缓存到mCache Map,然后再通过mPersister.persistSnapshot将TaskSnapshot信息存到本地磁盘中,我们这里保存在磁盘中的信息就是之前tryRestoreFromDisk方法使用的。我们重点留意一下top.mDisplayContent.screenshotApplicationsToBuffer方法,还记得之前留意的bitmap.createGraphicBufferHandle方法么?这里具有相似的作用,稍后我们详细分析这其中的原理。好,当我们拥有TaskSnapshot信息时,我们该如何显示呢?

TaskSnapshot界面显示

当我们有了TaskSnapshot数据之后,我们便会通过scheduleAddStartingWindow执行我们的界面显示操作:

    void scheduleAddStartingWindow() {
        // Note: we really want to do sendMessageAtFrontOfQueue() because we
        // want to process the message ASAP, before any other queued
        // messages.
        if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Enqueueing ADD_STARTING");
        mService.mAnimationHandler.postAtFrontOfQueue(mAddStartingWindow);
    }

代码不难,就是向Animation线程post一个Runnable,这里我们也可以看出Starting Window相关的内容显示是由Android中Animation线程完成的,我们来看下Runnable(去除一些检测和条件判断的代码):

    private final Runnable mAddStartingWindow = () -> {
        final StartingData startingData;
        final AppWindowToken container;
        synchronized (mWindowMap) {
        StartingSurface surface = null;
        try {
            surface = startingData.createStartingSurface(container);
        } catch (Exception e) {
            Slog.w(TAG_WM, "Exception when adding starting window", e);
        }
    };

startingData.createStartingSurface调用的便是SnapshotStartingData的createStartingSurface方法:

    @Override
    StartingSurface createStartingSurface(AppWindowToken atoken) {
        return mService.mTaskSnapshotController.createStartingSurface(atoken, mSnapshot);
    }

继续看下TaskSnapshotController.createStartingSurface:

    StartingSurface createStartingSurface(AppWindowToken token,
            TaskSnapshot snapshot) {
        return TaskSnapshotSurface.create(mService, token, snapshot);
    }

好吧,TaskSnapshotSurface.create走起:

    static TaskSnapshotSurface create(WindowManagerService service, AppWindowToken token,
            TaskSnapshot snapshot) {

        final WindowManager.LayoutParams layoutParams = new WindowManager.LayoutParams();//添加窗口时的窗口参数
        final Window window = new Window();//此Window非我们应用的Window,是TaskSnapshotSurface的内部类,作用相当于ViewRootImpl的W类
        final IWindowSession session = WindowManagerGlobal.getWindowSession();//获取与WindowManagerService通信的Session
        window.setSession(session);//session与window绑定
        final Surface surface = new Surface();//new一个Surface,每个应用界面想要将其界面显示到屏幕上,那必须依赖Surface,或者我们也可以使用OpenGL的相关API,这里不讨论这两者的区别
        final Rect tmpRect = new Rect();
        final Rect tmpFrame = new Rect();
        final Rect taskBounds;
        final Rect tmpContentInsets = new Rect();
        final Rect tmpStableInsets = new Rect();
        final MergedConfiguration tmpMergedConfiguration = new MergedConfiguration();
        int backgroundColor = WHITE;
        int statusBarColor = 0;
        int navigationBarColor = 0;
        final int sysUiVis;
        final int windowFlags;
        final int windowPrivateFlags;
        final int currentOrientation;
        synchronized (service.mWindowMap) {
            final WindowState mainWindow = token.findMainWindow();
            final Task task = token.getTask();
            if (task == null) {
                Slog.w(TAG, "TaskSnapshotSurface.create: Failed to find task for token="
                        + token);
                return null;
            }
            final AppWindowToken topFullscreenToken = token.getTask().getTopFullscreenAppToken();
            if (topFullscreenToken == null) {
                Slog.w(TAG, "TaskSnapshotSurface.create: Failed to find top fullscreen for task="
                        + task);
                return null;
            }
            final WindowState topFullscreenWindow = topFullscreenToken.getTopFullscreenWindow();
            if (mainWindow == null || topFullscreenWindow == null) {
                Slog.w(TAG, "TaskSnapshotSurface.create: Failed to find main window for token="
                        + token);
                return null;
            }
            sysUiVis = topFullscreenWindow.getSystemUiVisibility();
            windowFlags = topFullscreenWindow.getAttrs().flags;
            windowPrivateFlags = topFullscreenWindow.getAttrs().privateFlags;

            layoutParams.dimAmount = mainWindow.getAttrs().dimAmount;
            layoutParams.type = TYPE_APPLICATION_STARTING;
            layoutParams.format = snapshot.getSnapshot().getFormat();
            layoutParams.flags = (windowFlags & ~FLAG_INHERIT_EXCLUDES)
                    | FLAG_NOT_FOCUSABLE
                    | FLAG_NOT_TOUCHABLE;
            layoutParams.privateFlags = windowPrivateFlags & PRIVATE_FLAG_INHERITS;
            layoutParams.token = token.token;
            layoutParams.width = LayoutParams.MATCH_PARENT;
            layoutParams.height = LayoutParams.MATCH_PARENT;
            layoutParams.systemUiVisibility = sysUiVis;
            layoutParams.setTitle(String.format(TITLE_FORMAT, task.mTaskId));

            final TaskDescription taskDescription = task.getTaskDescription();
            if (taskDescription != null) {
                backgroundColor = taskDescription.getBackgroundColor();
                statusBarColor = taskDescription.getStatusBarColor();
                navigationBarColor = taskDescription.getNavigationBarColor();
            }
            taskBounds = new Rect();
            task.getBounds(taskBounds);
            currentOrientation = topFullscreenWindow.getConfiguration().orientation;
        }
        try {
            final int res = session.addToDisplay(window, window.mSeq, layoutParams,
                    View.VISIBLE, token.getDisplayContent().getDisplayId(), tmpRect, tmpRect,
                    tmpRect, null);
            if (res < 0) {
                Slog.w(TAG, "Failed to add snapshot starting window res=" + res);
                return null;
            }
        } catch (RemoteException e) {
            // Local call.
        }
        final TaskSnapshotSurface snapshotSurface = new TaskSnapshotSurface(service, window,
                surface, snapshot, layoutParams.getTitle(), backgroundColor, statusBarColor,
                navigationBarColor, sysUiVis, windowFlags, windowPrivateFlags, taskBounds,
                currentOrientation);
        window.setOuter(snapshotSurface);
        try {
            session.relayout(window, window.mSeq, layoutParams, -1, -1, View.VISIBLE, 0, tmpFrame,
                    tmpRect, tmpContentInsets, tmpRect, tmpStableInsets, tmpRect, tmpRect,
                    tmpMergedConfiguration, surface);
        } catch (RemoteException e) {
            // Local call.
        }
        snapshotSurface.setFrames(tmpFrame, tmpContentInsets, tmpStableInsets);
        snapshotSurface.drawSnapshot();
        return snapshotSurface;
    }

这段代码相当完美,可以说这涵盖了Android View界面渲染绘制最重要的知识点,我们将以这个函数抛砖引玉,简单概括一下Android View的绘制渲染内容,Android应用想要显示界面,一般要执行如下三个步骤:

  • 调用Session.addToDisplay告知WindowManagerService,需要添加一个Window,WindowManagerService会根据应用端的IWindow.Stub对象创建一个WindowState(WindowState在WindowManagerService端就代表一个窗口)。
  • 调用Session.relayout请求WindowManagerService执行窗口布局操作,主要就是用来计算系统中各个窗口的位置大小,z顺序以及窗口的许多其他方面,当我们执行此操作之后,我们的参数surface也就可以用来绘制了。
  • 最后应用在surface上进行绘制(dequeue,queue buffer的操作就在这里)。

前两点很理解起来简单,Session.relayout这里其实在WindowManagerService这里还是非常复杂的,我们这里不做详细介绍后续分析WindowManagerService的时候再作介绍,我们接下来主要分析第二点。

TaskSnapshot界面显示原理--窥探View的绘制原理

Session.relayout方法,最终调用的是WindowManagerService的relayoutWindow方法,一起看下(目前只关注和我们Starting Window相关的内容):

    public int relayoutWindow(Session session, IWindow client, int seq,
            WindowManager.LayoutParams attrs, int requestedWidth,
            int requestedHeight, int viewVisibility, int flags,
            Rect outFrame, Rect outOverscanInsets, Rect outContentInsets,
            Rect outVisibleInsets, Rect outStableInsets, Rect outOutsets, Rect outBackdropFrame,
            MergedConfiguration mergedConfiguration, Surface outSurface) {
            
        synchronized(mWindowMap) {
                        final boolean shouldRelayout = viewVisibility == View.VISIBLE &&
                (win.mAppToken == null || win.mAttrs.type == TYPE_APPLICATION_STARTING
                    || !win.mAppToken.isClientHidden());

            if (shouldRelayout) {
                Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "relayoutWindow: viewVisibility_1");

                // We are about to create a surface, but we didn't run a layout yet. So better run
                // a layout now that we already know the right size, as a resize call will make the
                // surface transaction blocking until next vsync and slow us down.
                // TODO: Ideally we'd create the surface after running layout a bit further down,
                // but moving this seems to be too risky at this point in the release.
                if (win.mLayoutSeq == -1) {
                    win.setDisplayLayoutNeeded();
                    mWindowPlacerLocked.performSurfacePlacement(true);
                }
                result = win.relayoutVisibleWindow(result, attrChanges, oldVisibility);

                try {
                    result = createSurfaceControl(outSurface, result, win, winAnimator);
                } catch (Exception e) {
                    return 0;
                }
                Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
            }
        }
    }

我们这里重点留意参数outSurface,因为我们应用能够绘制显示界面全靠这个Surface,来看下createSurfaceControl:

    private int createSurfaceControl(Surface outSurface, int result, WindowState win,
            WindowStateAnimator winAnimator) {
        if (!win.mHasSurface) {
            result |= RELAYOUT_RES_SURFACE_CHANGED;
        }

        WindowSurfaceController surfaceController;
        try {
            Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "createSurfaceControl");
            surfaceController = winAnimator.createSurfaceLocked(win.mAttrs.type, win.mOwnerUid);
        } finally {
            Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
        }
        if (surfaceController != null) {
            surfaceController.getSurface(outSurface);
            if (SHOW_TRANSACTIONS) Slog.i(TAG_WM, "  OUT SURFACE " + outSurface + ": copied");
        } else {
            // For some reason there isn't a surface.  Clear the
            // caller's object so they see the same state.
            Slog.w(TAG_WM, "Failed to create surface control for " + win);
            outSurface.release();
        }

        return result;
    }

两个关键方法调用winAnimator.createSurfaceLocked和surfaceController.getSurface,这里参数类型为WindowStateAnimator的winAnimator是和WindowState绑定的,WindowState代表系统中的一个窗口,WindowStateAnimator是与窗口关联的一系列动画。这里会通过WindowStateAnimator的createSurfaceLocked方法创建一个WindowSurfaceController类型的对象,然后再通过WindowSurfaceController的getSurface方法给outSurface赋值一个新的信息,主要就是绑定底层的buffer producer的引用,执行完此步骤后,我们的应用就可以由有效的Surface进行界面绘制了,因为有了buffer producer的引用我们就可以dequeue和queue显示数据的buffer了。这里就不跟踪WindowStateAnimator的createSurfaceLocked方法了,它主要就是new 一个WindowSurfaceController对象而WindowSurfaceController的构造方法,关键工作也就是new了一个SurfaceControlWithBackground对象,SurfaceControlWithBackground是SurfaceControl的子类,来看下他的构造方法,

    public SurfaceControlWithBackground(SurfaceSession s, String name, int w, int h, int format,
            int flags, int windowType, int ownerUid,
            WindowSurfaceController windowSurfaceController) throws OutOfResourcesException {
        super(s, name, w, h, format, flags, windowType, ownerUid);

        // We should only show background behind app windows that are letterboxed in a task.
        if ((windowType != TYPE_BASE_APPLICATION && windowType != TYPE_APPLICATION_STARTING)
                || !windowSurfaceController.mAnimator.mWin.isLetterboxedAppWindow()) {
            return;
        }
        mWindowSurfaceController = windowSurfaceController;
        mLastWidth = w;
        mLastHeight = h;
        mWindowSurfaceController.getContainerRect(mTmpContainerRect);
        mBackgroundControl = new SurfaceControl(s, "Background for - " + name,
                mTmpContainerRect.width(), mTmpContainerRect.height(), PixelFormat.OPAQUE,
                flags | SurfaceControl.FX_SURFACE_DIM);
    }

继续看super调用的是父类SurfaceControl的构造方法:

    public SurfaceControl(SurfaceSession session, String name, int w, int h, int format, int flags,
            SurfaceControl parent, int windowType, int ownerUid)
                    throws OutOfResourcesException {

        mName = name;
        mNativeObject = nativeCreate(session, name, w, h, format, flags,
            parent != null ? parent.mNativeObject : 0, windowType, ownerUid);
        if (mNativeObject == 0) {
            throw new OutOfResourcesException(
                    "Couldn't allocate SurfaceControl native object");
        }
        mCloseGuard.open("release");
    }

进入我们的核心中的核心代码android_view_SurfaceControl.cpp的nativeCreate,这里是通过JNI调用的:

    static jlong nativeCreate(JNIEnv* env, jclass clazz, jobject sessionObj,
            jstring nameStr, jint w, jint h, jint format, jint flags, jlong parentObject,
            jint windowType, jint ownerUid) {
        ScopedUtfChars name(env, nameStr);
        sp<SurfaceComposerClient> client(android_view_SurfaceSession_getClient(env, sessionObj));
        SurfaceControl *parent = reinterpret_cast<SurfaceControl*>(parentObject);
        sp<SurfaceControl> surface = client->createSurface(
                String8(name.c_str()), w, h, format, flags, parent, windowType, ownerUid);
        if (surface == NULL) {
            jniThrowException(env, OutOfResourcesException, NULL);
            return 0;
        }

        surface->incStrong((void *)nativeCreate);
        return reinterpret_cast<jlong>(surface.get());
    }

题外话-SurfaceFlinger相关内容的一知半解

在应用端,应用想要显示界面,就会创建一个PhoneWindow,一般每一个Activity和Dialog都会绑定一个PhoneWindow。应用想要和WindowManagerService系统通信,便会创建一个ViewRootImpl,然后通过ViewRootImpl有个成员mWindowSession通过Binder进行交流,并将ViewRootImlp的内部类W对象传递给WindowManagerService,WindowManagerService有了这个W实例对象后,便能通过W对象与应用就行通信。简单来说应用主动和WindowManagerService通信用Session,WindowManagerService和应用通信用W。然而Android系统中WindowManagerService主要负责监视应用窗口的生命周期,输入和焦点事件和屏幕方向,窗口的过渡,动画,位置,变换,z顺序以及窗口的许多其他方面。想要正真将界面显示到屏幕,WindowManagerService必须还要和SurfaceFlinger通信,那么她俩之前通信靠什么呢?我们来看下应用添加窗口时的一段代码:

    public int addWindow(Session session, IWindow client, int seq,
            WindowManager.LayoutParams attrs, int viewVisibility, int displayId,
            Rect outContentInsets, Rect outStableInsets, Rect outOutsets,
            InputChannel outInputChannel) {
        synchronized(mWindowMap) {
        win.attach();
        mWindowMap.put(client.asBinder(), win);    
        }
    }

这个win类型为WindowState,我们来看下它的attach方法:

    void attach() {
        if (localLOGV) Slog.v(TAG, "Attaching " + this + " token=" + mToken);
        mSession.windowAddedLocked(mAttrs.packageName);
    }

调用了Session的windowAddedLocked方法:

    void windowAddedLocked(String packageName) {
        mPackageName = packageName;
        mRelayoutTag = "relayoutWindow: " + mPackageName;
        if (mSurfaceSession == null) {
            if (WindowManagerService.localLOGV) Slog.v(
                TAG_WM, "First window added to " + this + ", creating SurfaceSession");
            mSurfaceSession = new SurfaceSession();
            if (SHOW_TRANSACTIONS) Slog.i(
                    TAG_WM, "  NEW SURFACE SESSION " + mSurfaceSession);
            mService.mSessions.add(this);
            if (mLastReportedAnimatorScale != mService.getCurrentAnimatorScale()) {
                mService.dispatchNewAnimatorScaleLocked(this);
            }
        }
        mNumWindow++;
    }

哦哦哦哦,原来在这里new了一个SurfaceSession,我们来段Android Google对SurfaceSession的解释,您就明白了它的作用:

An instance of this class represents a connection to the surface flinger, from which you can create one or more Surface instances that will be composited to the screen.

 一切都了然了,他就是WindowManagerService与SurfaceFlinger通信的工具,不过我们这里还是跟踪一下SurfaceSession的构造方法:

    public SurfaceSession() {
        mNativeClient = nativeCreate();
    }

哇哦,又来JNI:

static jlong nativeCreate(JNIEnv* env, jclass clazz) {
    SurfaceComposerClient* client = new SurfaceComposerClient();
    client->incStrong((void*)nativeCreate);
    return reinterpret_cast<jlong>(client);
}

哦?new了一个SurfaceComposerClient,what?这不就是构造SurfaceControl获取的那个sp<SurfaceComposerClient> client(android_view_SurfaceSession_getClient(env, sessionObj));对象么?一切了然了。我们再稍微关注一下:

SurfaceComposerClient::SurfaceComposerClient(const sp<IGraphicBufferProducer>& root)
    : mStatus(NO_INIT), mComposer(Composer::getInstance()), mParent(root)
{
}

void SurfaceComposerClient::onFirstRef() {
    sp<ISurfaceComposer> sm(ComposerService::getComposerService());
    if (sm != 0) {
        auto rootProducer = mParent.promote();
        sp<ISurfaceComposerClient> conn;
        conn = (rootProducer != nullptr) ? sm->createScopedConnection(rootProducer) :
                sm->createConnection();
        if (conn != 0) {
            mClient = conn;
            mStatus = NO_ERROR;
        }
    }
}

 这个SurfaceComposerClient有两个对象值得我们关注一下:

sp<ISurfaceComposerClient>  mClient;
wp<IGraphicBufferProducer>  mParent;

 mClient是通过ISurfaceComposer的createConnection初始化的。这个ISurfaceComposer其实指的就是SurfaceFlinger,因此我们跟踪到SurfaceFlinger的createConnection方法:

sp<ISurfaceComposerClient> SurfaceFlinger::createConnection() {
    return initClient(new Client(this));
}

static sp<ISurfaceComposerClient> initClient(const sp<Client>& client) {
    status_t err = client->initCheck();
    if (err == NO_ERROR) {
        return client;
    }
    return nullptr;
}

主要作用就是new一个Client。因此我们这里可以认为SurfaceSession可以和SurfaceFlinger的交流,关键还是依靠这个Client,而SurfaceComposerClient只不过是在SurfaceSession这段用的一个封装。ClientBinder实现相关类如下图所示

我们在SurfaceSession中拿到的SurfaceComposerClient的成员mClient其实是BpSurfaceComposerClient对象。

切回正题--View能够在屏幕上绘制的原理

我们要知道初始化SurfaceControl的目的就是要创建一个有效的Surface给应用,让它能够进行界面绘制。来继续我们的核心中的核心代码android_view_SurfaceControl.cpp的nativeCreate分析,它将会通过SurfaceComposerClient的createSurface函数创建native层的SurfaceControl对象,此时WindowManagerService会与SurfaceFlinger通信获取两个参数一个handle一个gbp,这两个引用将会传给Native层的SurfaceControl进行保存。当我们Native层SurfaceControl创建好了之后,会将其对象的引用传到Java层,保存在SurfaceControl.java的mNativeObject中。我们继续跟踪一下代码来看整个SurfaceControl到底是如何创建的:

client->createSurface(
                String8(name.c_str()), w, h, format, flags, parent, windowType, ownerUid);

 调用的其实是SurfaceComposerClient的createSurface方法,继续看:

sp<SurfaceControl> SurfaceComposerClient::createSurface(
        const String8& name,
        uint32_t w,
        uint32_t h,
        PixelFormat format,
        uint32_t flags,
        SurfaceControl* parent,
        uint32_t windowType,
        uint32_t ownerUid)
{
    sp<SurfaceControl> sur;
    if (mStatus == NO_ERROR) {
        sp<IBinder> handle;
        sp<IBinder> parentHandle;
        sp<IGraphicBufferProducer> gbp;

        if (parent != nullptr) {
            parentHandle = parent->getHandle();
        }
        status_t err = mClient->createSurface(name, w, h, format, flags, parentHandle,
                windowType, ownerUid, &handle, &gbp);
        ALOGE_IF(err, "SurfaceComposerClient::createSurface error %s", strerror(-err));
        if (err == NO_ERROR) {
            sur = new SurfaceControl(this, handle, gbp);
        }
    }
    return sur;
}

这里我们着重关注一个参数sp<IGraphicBufferProducer> gbp,我们再来看下mClient的createSurface方法,从前面的SurfaceSession初始化的介绍中我们可以看出,mClient是通过ISurfaceComposer的createConnection方法初始化的其最终调用的是Client的方法,同时我们也可以通过参数猜测出handle和gbp这两个参数就是在这里通过这个方法赋值的。我们来看下是Client的的方法createSurface:

status_t Client::createSurface(
        const String8& name,
        uint32_t w, uint32_t h, PixelFormat format, uint32_t flags,
        const sp<IBinder>& parentHandle, uint32_t windowType, uint32_t ownerUid,
        sp<IBinder>* handle,
        sp<IGraphicBufferProducer>* gbp)
{
    /*
     * createSurface must be called from the GL thread so that it can
     * have access to the GL context.
     */
    class MessageCreateLayer : public MessageBase {
        SurfaceFlinger* flinger;
        Client* client;
        sp<IBinder>* handle;
        sp<IGraphicBufferProducer>* gbp;
        status_t result;
        const String8& name;
        uint32_t w, h;
        PixelFormat format;
        uint32_t flags;
        sp<Layer>* parent;
        uint32_t windowType;
        uint32_t ownerUid;
    public:
        MessageCreateLayer(SurfaceFlinger* flinger,
                const String8& name, Client* client,
                uint32_t w, uint32_t h, PixelFormat format, uint32_t flags,
                sp<IBinder>* handle, uint32_t windowType, uint32_t ownerUid,
                sp<IGraphicBufferProducer>* gbp,
                sp<Layer>* parent)
            : flinger(flinger), client(client),
              handle(handle), gbp(gbp), result(NO_ERROR),
              name(name), w(w), h(h), format(format), flags(flags),
              parent(parent), windowType(windowType), ownerUid(ownerUid) {
        }
        status_t getResult() const { return result; }
        virtual bool handler() {
            result = flinger->createLayer(name, client, w, h, format, flags,
                    windowType, ownerUid, handle, gbp, parent);
            return true;
        }
    };

    sp<MessageBase> msg = new MessageCreateLayer(mFlinger.get(),
            name, this, w, h, format, flags, handle,
            windowType, ownerUid, gbp, &parent);
    mFlinger->postMessageSync(msg);
    return static_cast<MessageCreateLayer*>( msg.get() )->getResult();
}

这里就是向SurfaceFlinger post一个消息MessageCreateLayer,暂且先不管此消息何时执行,我们只需关注当这个消息被处理的时候会执行SurfaceFlinger的createLayer方法,啥也不说了继续跟踪:

status_t SurfaceFlinger::createLayer(
        const String8& name,
        const sp<Client>& client,
        uint32_t w, uint32_t h, PixelFormat format, uint32_t flags,
        uint32_t windowType, uint32_t ownerUid, sp<IBinder>* handle,
        sp<IGraphicBufferProducer>* gbp, sp<Layer>* parent)
{
    status_t result = NO_ERROR;

    sp<Layer> layer;

    String8 uniqueName = getUniqueLayerName(name);

    switch (flags & ISurfaceComposerClient::eFXSurfaceMask) {
        case ISurfaceComposerClient::eFXSurfaceNormal:
            result = createNormalLayer(client,
                    uniqueName, w, h, flags, format,
                    handle, gbp, &layer);
            break;
        case ISurfaceComposerClient::eFXSurfaceDim:
            result = createDimLayer(client,
                    uniqueName, w, h, flags,
                    handle, gbp, &layer);
            break;
        default:
            result = BAD_VALUE;
            break;
    }

    // window type is WINDOW_TYPE_DONT_SCREENSHOT from SurfaceControl.java
    // TODO b/64227542
    if (windowType == 441731) {
        windowType = 2024; // TYPE_NAVIGATION_BAR_PANEL
        layer->setPrimaryDisplayOnly();
    }

    layer->setInfo(windowType, ownerUid);

    result = addClientLayer(client, *handle, *gbp, layer, *parent);
    mInterceptor.saveSurfaceCreation(layer);
    setTransactionFlags(eTransactionNeeded);
    return result;
}

 越来越深了,快要到达本人目前的认知边界了,继续再看一波createNormalLayer

status_t SurfaceFlinger::createNormalLayer(const sp<Client>& client,
        const String8& name, uint32_t w, uint32_t h, uint32_t flags, PixelFormat& format,
        sp<IBinder>* handle, sp<IGraphicBufferProducer>* gbp, sp<Layer>* outLayer)
{
    // initialize the surfaces
    switch (format) {
    case PIXEL_FORMAT_TRANSPARENT:
    case PIXEL_FORMAT_TRANSLUCENT:
        format = PIXEL_FORMAT_RGBA_8888;
        break;
    case PIXEL_FORMAT_OPAQUE:
        format = PIXEL_FORMAT_RGBX_8888;
        break;
    }

    *outLayer = DisplayUtils::getInstance()->getLayerInstance(this,
                            client, name, w, h, flags);

    status_t err = (*outLayer)->setBuffers(w, h, format, flags);
    if (err == NO_ERROR) {
        *handle = (*outLayer)->getHandle();
        *gbp = (*outLayer)->getProducer();
    }

    ALOGE_IF(err, "createNormalLayer() failed (%s)", strerror(-err));
    return err;
}

Layer* DisplayUtils::getLayerInstance(SurfaceFlinger* flinger,
                            const sp<Client>& client, const String8& name,
                            uint32_t w, uint32_t h, uint32_t flags) {
    if (sUseExtendedImpls) {
        return new ExLayer(flinger, client, name, w, h, flags);
    } else {
        return new Layer(flinger, client, name, w, h, flags);
    }
}

void Layer::onFirstRef() {
    // Creates a custom BufferQueue for SurfaceFlingerConsumer to use
    sp<IGraphicBufferProducer> producer;
    sp<IGraphicBufferConsumer> consumer;
    BufferQueue::createBufferQueue(&producer, &consumer, true);
    mProducer = new MonitoredProducer(producer, mFlinger, this);
    mSurfaceFlingerConsumer = new SurfaceFlingerConsumer(consumer, mTextureName, this);
    mSurfaceFlingerConsumer->setConsumerUsageBits(getEffectiveUsage(0));
    mSurfaceFlingerConsumer->setContentsChangedListener(this);
    mSurfaceFlingerConsumer->setName(mName);

    if (mFlinger->isLayerTripleBufferingDisabled()) {
        mProducer->setMaxDequeuedBufferCount(2);
    }

    const sp<const DisplayDevice> hw(mFlinger->getDefaultDisplayDevice());
    updateTransformHint(hw);
}

 别的咱先不关注,我们只关注我们之前提示的留意重点参数gbp,这里其实就是创建了一个Layer,我们该怎么理解这个Layer呢?从SurfaceFlinger角度来说一个应用程序的Window在SurfaceFlinger这边对应一个Layer。不知道大家有没有玩过Photoshop?这个Layer,我们从P图角度来说就是代表Photoshop里面的一个图层,如果我们这么理解,个人感觉P图的工作者所担当工作就可以看做是SurfaceFlinger在Android中所负责的(不知道我这么理解有没有问题,SurfaceFlinger这里我还是一个小白)。看到这个Layer初始的代码,我们也该明白从之前着重关注的参数sp<IGraphicBufferProducer> gbp是怎么初始化的了。这个gbp引用的是一个BufferQueue的Producer对象,我们可以简单理解成屏幕显示数据的生产方,而IGraphicBufferConsumer则是代码图形的消费方一般情况下消费方指的就是SurfaceFlinger,这是一个典型的生产消费者模式。我们最后回头再来看addToDisplay和relayoutWindow方法,系统在添加应用窗口的时候先建立了WindowManagerService和SurfaceFlinger之间的通信,然后通过WindowState的成员WindowStateAnimator创建一个WindowSurfaceController,创建的同时还会创建对应java层的SurfaceControl和Native层SurfaceControl,创建SurfaceControl同时会请求在SurfaceFlinger端创建一个Layer,并将对应的handle和gbp返回,然后将Layer中包含的IGraphicBufferProducer和SurfaceControl关联到我们创建的SurfaceControl中。最后我们再通过WindowSurfaceController的getSurface方法,从SurfaceControl中获取我们想要的Surface,

    void getSurface(Surface outSurface) {
        outSurface.copyFrom(mSurfaceControl);
    }

 来看下Surface的copyFrom方法

    public void copyFrom(SurfaceControl other) {
        if (other == null) {
            throw new IllegalArgumentException("other must not be null");
        }

        long surfaceControlPtr = other.mNativeObject;
        if (surfaceControlPtr == 0) {
            throw new NullPointerException(
                    "null SurfaceControl native object. Are you using a released SurfaceControl?");
        }
        long newNativeObject = nativeGetFromSurfaceControl(surfaceControlPtr);

        synchronized (mLock) {
            if (mNativeObject != 0) {
                nativeRelease(mNativeObject);
            }
            setNativeObjectLocked(newNativeObject);
        }
    }

并不是我们想象的那么简单,来JNI android_view_Surface.cpp走起:

static jlong nativeGetFromSurfaceControl(JNIEnv* env, jclass clazz,
        jlong surfaceControlNativeObj) {
    /*
     * This is used by the WindowManagerService just after constructing
     * a Surface and is necessary for returning the Surface reference to
     * the caller. At this point, we should only have a SurfaceControl.
     */

    sp<SurfaceControl> ctrl(reinterpret_cast<SurfaceControl *>(surfaceControlNativeObj));
    sp<Surface> surface(ctrl->getSurface());
    if (surface != NULL) {
        surface->incStrong(&sRefBaseOwner);
    }
    return reinterpret_cast<jlong>(surface.get());
}

其实就是通过Java层保存的SurfaceControl的引用,找到对应Native层的SurfaceControl对象,然后通过Native层的SurfaceControl对象的getSurface方法获取一个Native层的Surface:

sp<Surface> SurfaceControl::getSurface() const
{
    Mutex::Autolock _l(mLock);
    if (mSurfaceData == 0) {
        return generateSurfaceLocked();
    }
    return mSurfaceData;
}

sp<Surface> SurfaceControl::generateSurfaceLocked() const
{
    // This surface is always consumed by SurfaceFlinger, so the
    // producerControlledByApp value doesn't matter; using false.
    mSurfaceData = new Surface(mGraphicBufferProducer, false);

    return mSurfaceData;
}

其主要作用就是将之前赋值的gbp引用再通过mGraphicBufferProducer传给了Surface。

拨开云雾见天日--来看看TaskSnapshot的界面数据如何传递的

    static TaskSnapshotSurface create(WindowManagerService service, AppWindowToken token,
            TaskSnapshot snapshot) {
.....................省去若干代码
        snapshotSurface.setFrames(tmpFrame, tmpContentInsets, tmpStableInsets);
        snapshotSurface.drawSnapshot();
        return snapshotSurface;
    }

再次回到Java代码,我们来看下drawSnapshot方法:

    private void drawSnapshot() {
        final GraphicBuffer buffer = mSnapshot.getSnapshot();
        if (DEBUG_STARTING_WINDOW) Slog.v(TAG, "Drawing snapshot surface sizeMismatch="
                + mSizeMismatch);
        if (mSizeMismatch) {
            // The dimensions of the buffer and the window don't match, so attaching the buffer
            // will fail. Better create a child window with the exact dimensions and fill the parent
            // window with the background color!
            drawSizeMismatchSnapshot(buffer);
        } else {
            drawSizeMatchSnapshot(buffer);
        }
        synchronized (mService.mWindowMap) {
            mShownTime = SystemClock.uptimeMillis();
            mHasDrawn = true;
        }
        reportDrawn();

        // In case window manager leaks us, make sure we don't retain the snapshot.
        mSnapshot = null;
    }

    private void drawSizeMatchSnapshot(GraphicBuffer buffer) {
        mSurface.attachAndQueueBuffer(buffer);
        mSurface.release();
    }

最终就是调用了这个Surface的attachAndQueueBuffer方法。我们这里猜测一下,等下还是回到Native中去,不说了继续跟踪吧:

    public void attachAndQueueBuffer(GraphicBuffer buffer) {
        synchronized (mLock) {
            checkNotReleasedLocked();
            int err = nativeAttachAndQueueBuffer(mNativeObject, buffer);
            if (err != 0) {
                throw new RuntimeException(
                        "Failed to attach and queue buffer to Surface (bad object?)");
            }
        }
    }

JNI继续走起:

static jint nativeAttachAndQueueBuffer(JNIEnv *env, jclass clazz, jlong nativeObject,
        jobject graphicBuffer) {
    Surface* surface = reinterpret_cast<Surface*>(nativeObject);
    sp<GraphicBuffer> bp = graphicBufferForJavaObject(env, graphicBuffer);
    if (bp == nullptr) {
        return BAD_VALUE;
    }
    int err = ((ANativeWindow*)surface)->perform(surface, NATIVE_WINDOW_API_CONNECT,
            NATIVE_WINDOW_API_CPU);
    if (err != OK) {
        return err;
    }
    err = surface->attachBuffer(bp->getNativeBuffer());
    if (err != OK) {
        return err;
    }
    err = ((ANativeWindow*)surface)->queueBuffer(surface, bp->getNativeBuffer(), -1);
    if (err != OK) {
        return err;
    }
    err = surface->disconnect(NATIVE_WINDOW_API_CPU);
    return err;
}

最终还是将这个显示的buffer queue到了SurfaceFlinger。到了SurfaceFlinger之后,我们就可以等着界面显示出来了。

写在最后

本文内容较多,理解起来较为困难,不妨可以随着本文的思路,自己也尝试到源码中跟踪一下,说句实在的其中C++代码平时我也没有仔细阅读过,也就整理本篇的时候看了看,在理解这部分代码的时候Android的source官网给了很大的帮忙,建议大家可以自行阅读阅读。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

ActivityManagerService解读之Activity启动闲聊--以TaskSnapshot为点切入AndroidView绘制原理分析 的相关文章

随机推荐