Android SurfaceFlinger3 申请Buffer

2023-11-13

在这里插入图片描述
本章节思维导图如上。主要讲述了 surafce 测试程序 demo 的第3步中的获取 Buffer,锁定(最关键)并写入 Buffer 的过程。

一 概述

该部分代码是在上一章节中 Surface 测试程序源码的精简版,保存了最关键的流程,如下所示:

#include <cutils/memory.h>
#include <utils/Log.h>
#include <binder/IPCThreadState.h>
#include <binder/ProcessState.h>
#include <binder/IServiceManager.h>
#include <gui/Surface.h>
#include <gui/SurfaceComposerClient.h>
#include <android/native_window.h>
 
using namespace android;
 
int main(int argc, char** argv)
{
    //1 创建surfaceflinger的客户端
    sp<SurfaceComposerClient> client = new SurfaceComposerClient();
    
    //2 获取surface
    sp<SurfaceControl> surfaceControl = client->createSurface(String8("resize"),
            160, 240, PIXEL_FORMAT_RGB_565, 0);
    sp<Surface> surface = surfaceControl->getSurface();
 
    //设置layer,layer值越大,显示层越靠前
    SurfaceComposerClient::openGlobalTransaction();
    surfaceControl->setLayer(100000);
    SurfaceComposerClient::closeGlobalTransaction();
 
    //3 获取buffer->锁定buffer->写入buffer->解锁并提交buffer 
    //这里主要关注:申请Buff 和 提交Buff
    ANativeWindow_Buffer outBuffer;
    surface->lock(&outBuffer, NULL);
    ssize_t bpr = outBuffer.stride * bytesPerPixel(outBuffer.format);
    android_memset16((uint16_t*)outBuffer.bits, 0xF800, bpr*outBuffer.height);
    surface->unlockAndPost();
    //...    
    return 0;
}

主要的步骤为:

  • 获取 SurfaceFlinger(后简称 SF)的客户端,通过 SF 的客户端获取 SurfaceControl,进而获得 Surface
  • 通过 SurfaceControl 设置 Layer 层数值(忽略),通过 Surface 获取 Buffer,锁定 Buffer 并写入 Buffer
  • 最后提交 Buffer

本章节主要关注获取 Buffer,锁定(最关键)并写入 Buffer 的过程。而获取 Buffer 只是通过ANativeWindow_Buffer 来创建一个 outBuffer 并传递给 Surface 的 lock 方法,写入 Buffer 只是最终使用 memset 向 Buffer 中写入数据。因此最关键也最繁琐的部分是 Surface 的 lock 方法,主要分析整个方法,对应代码为:

status_t Surface::lock(ANativeWindow_Buffer* outBuffer, ARect* inOutDirtyBounds)
{
    if (mLockedBuffer != 0) {
        ALOGE("Surface::lock failed, already locked");
        return INVALID_OPERATION;
    }
 
    //获取显示器一些信息
    if (!mConnectedToCpu) {
        int err = Surface::connect(NATIVE_WINDOW_API_CPU);
        if (err) {
            return err;
        }
        // we're intending to do software rendering from this point
        setUsage(GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN);
    }
 
    ANativeWindowBuffer* out;
    int fenceFd = -1;
    //关键点1 
    status_t err = dequeueBuffer(&out, &fenceFd);
    ALOGE_IF(err, "dequeueBuffer failed (%s)", strerror(-err));
    if (err == NO_ERROR) {
        sp<GraphicBuffer> backBuffer(GraphicBuffer::getSelf(out));
        const Rect bounds(backBuffer->width, backBuffer->height);
 
        Region newDirtyRegion;
        if (inOutDirtyBounds) {
            newDirtyRegion.set(static_cast<Rect const&>(*inOutDirtyBounds));
            newDirtyRegion.andSelf(bounds);
        } else {
            newDirtyRegion.set(bounds);
        }
 
        // figure out if we can copy the frontbuffer back
        const sp<GraphicBuffer>& frontBuffer(mPostedBuffer);
        const bool canCopyBack = (frontBuffer != 0 &&
                backBuffer->width  == frontBuffer->width &&
                backBuffer->height == frontBuffer->height &&
                backBuffer->format == frontBuffer->format);
 
        if (canCopyBack) {
            // copy the area that is invalid and not repainted this round
            const Region copyback(mDirtyRegion.subtract(newDirtyRegion));
            if (!copyback.isEmpty())
                copyBlt(backBuffer, frontBuffer, copyback);
        } else {
            // if we can't copy-back anything, modify the user's dirty
            // region to make sure they redraw the whole buffer
            newDirtyRegion.set(bounds);
            mDirtyRegion.clear();
            Mutex::Autolock lock(mMutex);
            for (size_t i=0 ; i<NUM_BUFFER_SLOTS ; i++) {
                mSlots[i].dirtyRegion.clear();
            }
        }
 
        { // scope for the lock
            Mutex::Autolock lock(mMutex);
            int backBufferSlot(getSlotFromBufferLocked(backBuffer.get()));
            if (backBufferSlot >= 0) {
                Region& dirtyRegion(mSlots[backBufferSlot].dirtyRegion);
                mDirtyRegion.subtract(dirtyRegion);
                dirtyRegion = newDirtyRegion;
            }
        }
 
        mDirtyRegion.orSelf(newDirtyRegion);
        if (inOutDirtyBounds) {
            *inOutDirtyBounds = newDirtyRegion.getBounds();
        }
 
        void* vaddr;
        //关键点2
        status_t res = backBuffer->lockAsync(
                GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN,
                newDirtyRegion.bounds(), &vaddr, fenceFd);
 
        if (res != 0) {
            err = INVALID_OPERATION;
        } else {
            mLockedBuffer = backBuffer;
            //构造 outbuffer 变量
            outBuffer->width  = backBuffer->width;
            outBuffer->height = backBuffer->height;
            outBuffer->stride = backBuffer->stride;
            outBuffer->format = backBuffer->format;
            outBuffer->bits   = vaddr;//mmap映射地址
        }
    }
    return err;
}

二 Surface.dequeueBuffer

Surface 的 dequeueBuffer 方法,代码实现如下:

int Surface::dequeueBuffer(android_native_buffer_t** buffer, int* fenceFd) {
    //...
    int buf = -1;
    sp<Fence> fence;
    //生产者,向SurfaceFlinger发出Buffer申请,得到 buffer,一个mslots数组的元素
    status_t result = mGraphicBufferProducer->dequeueBuffer(&buf, &fence, swapIntervalZero,
            reqW, reqH, reqFormat, reqUsage);
    //...
    if ((result & IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION) || gbuf == 0) {
        //向SF请求并返回fd,然后在APP这边mmap
        result = mGraphicBufferProducer->requestBuffer(buf, &gbuf);
        if (result != NO_ERROR) {
            mGraphicBufferProducer->cancelBuffer(buf, fence);
            return result;
        }
    }
    //... 
    *buffer = gbuf.get();
    return OK;
}

这里关注两个关键方法,mGraphicBufferProducer 的 dequeueBuffer 方法和 mGraphicBufferProducer 的 requestBuffer 方法。

2.1 GraphicBufferProducer.dequeueBuffer

status_t BufferQueueProducer::dequeueBuffer(int *outSlot,
        sp<android::Fence> *outFence, bool async,
        uint32_t width, uint32_t height, uint32_t format, uint32_t usage) {
    //...
    { // Autolock scope
        //...
        int found;
        //得到空闲的slots元素并锁定
        status_t status = waitForFreeSlotThenRelock("dequeueBuffer", async,
                &found, &returnFlags);
        if (status != NO_ERROR) {
            return status;
        }
        //...
    } // Autolock scope
    //如果需要则重新分配
    if (returnFlags & BUFFER_NEEDS_REALLOCATION) {
        status_t error;
        BQ_LOGV("dequeueBuffer: allocating a new buffer for slot %d", *outSlot);
        sp<GraphicBuffer> graphicBuffer(mCore->mAllocator->createGraphicBuffer(
                    width, height, format, usage, &error));
        if (graphicBuffer == NULL) {
            BQ_LOGE("dequeueBuffer: createGraphicBuffer failed");
            return error;
        }
 
        { // Autolock scope
            Mutex::Autolock lock(mCore->mMutex);
 
            if (mCore->mIsAbandoned) {
                BQ_LOGE("dequeueBuffer: BufferQueue has been abandoned");
                return NO_INIT;
            }
 
            mSlots[*outSlot].mFrameNumber = UINT32_MAX;
            mSlots[*outSlot].mGraphicBuffer = graphicBuffer;
        } // Autolock scope
    }
 
    if (attachedByConsumer) {
        returnFlags |= BUFFER_NEEDS_REALLOCATION;
    }
    //...
    return returnFlags;
}

这里专注两段代码:waitForFreeSlotThenRelock 方法和 graphicBuffer 的创建。

2.1.1 GraphicBufferProducer.waitForFreeSlotThenRelock

status_t BufferQueueProducer::waitForFreeSlotThenRelock(const char* caller,
        bool async, int* found, status_t* returnFlags) const {
    bool tryAgain = true;
    while (tryAgain) {
        if (mCore->mIsAbandoned) {
            BQ_LOGE("%s: BufferQueue has been abandoned", caller);
            return NO_INIT;
        }
 
        //...
        // Look for a free buffer to give to the client
        *found = BufferQueueCore::INVALID_BUFFER_SLOT;
        int dequeuedCount = 0;
        int acquiredCount = 0;
        for (int s = 0; s < maxBufferCount; ++s) {
            switch (mSlots[s].mBufferState) {
                case BufferSlot::DEQUEUED:
                    ++dequeuedCount;
                    break;
                case BufferSlot::ACQUIRED:
                    ++acquiredCount;
                    break;
                case BufferSlot::FREE:
                    // We return the oldest of the free buffers to avoid
                    // stalling the producer if possible, since the consumer
                    // may still have pending reads of in-flight buffers
                    if (*found == BufferQueueCore::INVALID_BUFFER_SLOT ||
                            mSlots[s].mFrameNumber < mSlots[*found].mFrameNumber) {
                        *found = s;
                    }
                    break;
                default:
                    break;
            }
        }
        //...
    } // while (tryAgain)
    return NO_ERROR;
}

最为关键的就是上面这段,主要功能是在 mSlots 数组中找到已经 FREE 的空余 Buffer 并通过下标方式返回(下标为上段代码中的 found)

2.1.2 graphicBuffer的创建

在创建 graphicBuffer 时,这里传递了一个参数,是这样一句话:

mCore->mAllocator->createGraphicBuffer(width, height, format, usage, &error)

这里主要是调用了 BufferQueueCore 中的成员变量 mAllocator(IGraphicBufferAlloc类型)的 createGraphicBuffer 方法,这段代码比较简单,最终返回一个 GraphicBuffer 类型的变量,代码如下:

sp<GraphicBuffer> GraphicBufferAlloc::createGraphicBuffer(uint32_t w, uint32_t h,
        PixelFormat format, uint32_t usage, status_t* error) {
    sp<GraphicBuffer> graphicBuffer(new GraphicBuffer(w, h, format, usage));
    status_t err = graphicBuffer->initCheck();
    *error = err;
    if (err != 0 || graphicBuffer->handle == 0) {
        if (err == NO_MEMORY) {
            GraphicBuffer::dumpAllocationsToSystemLog();
        }
        ALOGE("GraphicBufferAlloc::createGraphicBuffer(w=%d, h=%d) "
             "failed (%s), handle=%p",
                w, h, strerror(-err), graphicBuffer->handle);
        return 0;
    }
    return graphicBuffer;
}

之后我们详细分析下这里 sp graphicBuffer(…) 对应的构造器,代码如下:

GraphicBuffer::GraphicBuffer(uint32_t w, uint32_t h, 
        PixelFormat reqFormat, uint32_t reqUsage)
    : BASE(), mOwner(ownData), mBufferMapper(GraphicBufferMapper::get()),
      mInitCheck(NO_ERROR), mId(getUniqueId())
{
    width  = w;
    height = h;
    stride = inStride;
    format = inFormat;
    usage  = 0;
    handle = NULL;
    mInitCheck = initSize(w, h, reqFormat, reqUsage);
}

这里继续分析 initSize,代码如下:

status_t GraphicBuffer::initSize(uint32_t w, uint32_t h, PixelFormat format,
        uint32_t reqUsage)
{
    //打开 Gralloc 模块
    GraphicBufferAllocator& allocator = GraphicBufferAllocator::get();
    //调用 HAL 模块的 alloc 函数
    status_t err = allocator.alloc(w, h, format, reqUsage, &handle, &stride);
    if (err == NO_ERROR) {
        this->width  = w;
        this->height = h;
        this->format = format;
        this->usage  = reqUsage;
    }
    return err;
}

这里主要是调用了 gralloc 模块,获取 allocator 的代码如下:

GraphicBufferAllocator::GraphicBufferAllocator()
    : mAllocDev(0)
{
    hw_module_t const* module;
    int err = hw_get_module(GRALLOC_HARDWARE_MODULE_ID, &module);
    ALOGE_IF(err, "FATAL: can't find the %s module", GRALLOC_HARDWARE_MODULE_ID);
    if (err == 0) {
        gralloc_open(module, &mAllocDev);
    }
}

之后使用 allocator.alloc 来申请内存。代码如下:

status_t GraphicBufferAllocator::alloc(uint32_t w, uint32_t h, PixelFormat format,
        int usage, buffer_handle_t* handle, int32_t* stride)
{
    if (!w || !h)
        w = h = 1;
 
    // we have a h/w allocator and h/w buffer is requested
    status_t err; 
    
    err = mAllocDev->alloc(mAllocDev, w, h, format, usage, handle, stride);    
    if (err == NO_ERROR) {
        Mutex::Autolock _l(sLock);
        KeyedVector<buffer_handle_t, alloc_rec_t>& list(sAllocList);
        int bpp = bytesPerPixel(format);
        if (bpp < 0) {
            // probably a HAL custom format. in any case, we don't know
            // what its pixel size is.
            bpp = 0;
        }
        alloc_rec_t rec;
        rec.w = w;
        rec.h = h;
        rec.s = *stride;
        rec.format = format;
        rec.usage = usage;
        rec.size = h * stride[0] * bpp;
        list.add(*handle, rec);
    }
 
    return err;
}

这里调用了 mAllocDev->alloc 方法,再往下就是 HAL 层 Gralloc 模块的调用了,在 Gralloc 模块初始化的时候,代码如下:

int gralloc_device_open(const hw_module_t* module, const char* name,
        hw_device_t** device)
{
    int status = -EINVAL;
    if (!strcmp(name, GRALLOC_HARDWARE_GPU0)) {
        gralloc_context_t *dev;
        dev = (gralloc_context_t*)malloc(sizeof(*dev));
 
        /* initialize our state here */
        memset(dev, 0, sizeof(*dev));
 
        /* initialize the procs */
        dev->device.common.tag = HARDWARE_DEVICE_TAG;
        dev->device.common.version = 0;
        dev->device.common.module = const_cast<hw_module_t*>(module);
        dev->device.common.close = gralloc_close;
 
        dev->device.alloc   = gralloc_alloc;
        dev->device.free    = gralloc_free;
 
        *device = &dev->device.common;
        status = 0;
    } else {
        status = fb_device_open(module, name, device);
    }
    return status;
}

所以这里的 mAllocDev->alloc 方法 就是 Gralloc 模块对应的 gralloc_alloc 方法,代码如下:

static int gralloc_alloc(alloc_device_t* dev,
        int w, int h, int format, int usage,
        buffer_handle_t* pHandle, int* pStride)
{
    //...
    int err;
    if (usage & GRALLOC_USAGE_HW_FB) {
        err = gralloc_alloc_framebuffer(dev, size, usage, pHandle);
    } else {
        err = gralloc_alloc_buffer(dev, size, usage, pHandle);
    }
    //...
    *pStride = stride;
    return 0;
}

因为是申请 buffer,所以这里选择的是 gralloc_alloc_buffer,继续分析代码,如下所示:

static int gralloc_alloc_buffer(alloc_device_t* dev,
        size_t size, int /*usage*/, buffer_handle_t* pHandle)
{
    //...
    fd = ashmem_create_region("gralloc-buffer", size);
    //...
    if (err == 0) {
        private_handle_t* hnd = new private_handle_t(fd, size, 0);
        gralloc_module_t* module = reinterpret_cast<gralloc_module_t*>(
                dev->common.module);
        err = mapBuffer(module, hnd);
        if (err == 0) {
            *pHandle = hnd;
        }
    }
    return err;
}

这里主要通过 ashmem 共享内存机制来分配内存 Buffer,得到 fd;同时使用 fd 和 mmap 生成的 Buffer 一同来构造一个 handle。这其中 mmap 生成的地址直接供 SF 使用。

2.2 GraphicBufferProducer.requestBuffer

此时 SF 已经得到了 handle(要申请的 Buffer 的句柄 fd 和 mmap 的首地址的集合)。接下来调用 requestBuffer 使用 binder 机制把 SF 的 handle 传递给 APP(这个分析过程中忽略了 Binder 通信部分的分析,Binder 通信部分详见系列文章链接:专题分纲目录 android 系统核心机制 binder)。BufferQueueProducer 的 requestBuffer 代码实现如下:

status_t BufferQueueProducer::requestBuffer(int slot, sp<GraphicBuffer>* buf) {
    Mutex::Autolock lock(mCore->mMutex);
	//...
    mSlots[slot].mRequestBufferCalled = true;
    *buf = mSlots[slot].mGraphicBuffer;
    return NO_ERROR;
}

2.3 总结

APP 对应一个 client,一个 SurfaceControl 对应一个 Layer,每个 Surface 都对应一个 mslots 数组(mslots 数组中共有64个元素,每个元素中包含一个 GraphicBuffer 和一个 handle,包含 fd 和 mmap 的映射地址)。Surface 获得 Buffer 的过程如下:

  • 首先执行 dequeueBuffer。若 Surface 无 Buffer,则向生产者 producer(GraphicBufferProducer 类对象,最终通过 SF 操作)申请,查看 mslots 有无余项,若有直接返回,若无则向 Gralloc HAL 申请,得到一个 handle(要申请的 Buffer 的句柄 fd 和 mmap 的首地址的集合)
  • 其次执行 requestBuffer,APP(Client 端)通过该方法将 SF 获得的 handle 转移到 Client 端的 handle 中
  • APP 获得 fd,mmap 获得地址(通过 Gralloc HAL 来执行 mmap),接下来向 Buffer 中写入内容即可

三 GraphicBuffer.lockAsync

status_t GraphicBuffer::lockAsync(uint32_t usage, const Rect& rect, void** vaddr,
     int fenceFd)
{
    //...
    status_t res = getBufferMapper().lockAsync(handle, usage, rect, vaddr, fenceFd);
    return res;
}

继续分析 GraphicBufferMapper 的 lockAsync 方法,代码实现如下:

status_t GraphicBufferMapper::lockAsync(buffer_handle_t handle,
        int usage, const Rect& bounds, void** vaddr, int fenceFd)
{
    status_t err;
    if (mAllocMod->common.module_api_version >= GRALLOC_MODULE_API_VERSION_0_3) {
        err = mAllocMod->lockAsync(mAllocMod, handle, usage,
                bounds.left, bounds.top, bounds.width(), bounds.height(),
                vaddr, fenceFd);
    } else {
        sync_wait(fenceFd, -1);
        close(fenceFd);
        err = mAllocMod->lock(mAllocMod, handle, usage,
                bounds.left, bounds.top, bounds.width(), bounds.height(),
                vaddr);
    }
    return err;
}

这里继续分析会进入到 HAL 层,对应的是 Gralloc 模块的 gralloc_lock 函数,代码实现如下:

int gralloc_lock(gralloc_module_t const* /*module*/,
        buffer_handle_t handle, int /*usage*/,
        int /*l*/, int /*t*/, int /*w*/, int /*h*/,
        void** vaddr)
{
    if (private_handle_t::validate(handle) < 0)
        return -EINVAL;
 
    private_handle_t* hnd = (private_handle_t*)handle;
    *vaddr = (void*)hnd->base;
    return 0;
}

这里主要是将 handle->base 赋值给 backBuffer->lockAsync 的参数 vaddr,这个同样也是 APP 中使用 mmap 映射的地址。最后会通过 outBuffer->bits = vaddr 这个赋值操作用 backBuffer 的成员变量初始化 outBuffer。

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

Android SurfaceFlinger3 申请Buffer 的相关文章

  • Android SurfaceFlinger分析

    SufaceFlinger的构成并不是太复杂 复杂的是他的客户端建构 SufaceFlinger主要功能是 1 将Layers Surfaces 内容的刷新到屏幕上 2 维持Layer的Zorder序列 并对Layer 最终输出做出裁剪计算
  • 深入PMS源码(三)—— PMS中intent-filter的匹配架构

    1 简介 由前面深入PMS源码 一 PMS的启动过程和执行流程和深入PMS源码 二 APK的安装和卸载源码分析两篇文章知道 无论是Android系统启动后执行的PMS启动 还是使用PackageInstaller安装APK的过程 最终都会使
  • android GMS认证之CTS 常用命令

    http blog csdn net hfreeman2008 article details 51084821 前面的一篇文章 lt
  • Android 全局手势识别原理

    本文是对全局手势识别进行分析 那什么是全局手势呢 简单来说就是在任何界面都需要识别的手势 比如 在任何界面从手机屏幕左侧滑动 当前的界面会退出 类似 back 键 我们知道 在 Android 系统中一个 Activity 在显示时 当对屏
  • Android zygote进程启动过程

    zygote启动过程中涉及到以下模块 app process zygote USAP socket FileDescriptor FD AndroidRuntime AppRuntime 定义于app process模块 继承自Androi
  • 从源码出发浅析 Android TV 的焦点移动原理 (上篇)

    转自 https cloud tencent com developer article 1006289 焦点 焦点 Focus 可以理解为选中态 在Android TV上起很重要的作用 一个视图控件只有在获得焦点的状态下 才能响应按键的C
  • Android数据的四种存储方式

    Android数据的四种存储方式 SharePreferences SQLite Contert Provider File 网络存储 作为一个完整的应用程序 数据存储的操作是必不可少的 Android系统提供了四种存储数据方式 分别为 S
  • FBE中的Home界面FallbackHome

    FallbackHome FallbackHome继承Activity 是安卓系统启动后未解锁时的home界面 Settings程序AndroidManifest xml中定义了FallbackHome的intent filter 可以看到
  • Service能够创建界面(addView)吗?

    一个Service能够创建界面 addView 吗 一个app 只有Service 没有Activity 能够通过WindowManager调用addView 添加可视界面吗 答案是可以 但是能够创建的界面类型 WindowManager
  • Android InputEventReceiver事件接收流程分析

    本文基于Android 12 InputEvent经过inputflinger读取后 通过Inputchannel发送到Java层的InputEventReceiver对象 输入事件和View的状态强相关 事件发送需要确定当前的焦点App
  • UserHandle.ALL、UserHandle.CURRENT、UserHandle.CURRENT_OR_SELF、UserHandle.OWNER的定义与区别

    UserHandle ALL UserHandle CURRENT UserHandle CURRENT OR SELF UserHandle OWNER的定义与区别 UserHandle ALL 设备上所有用户均可接收到广播 UserHa
  • Android Looper Handler 机制浅析

    最近想写个播放器demo 里面要用到 Looper Handler 看了很多资料都没能理解透彻 于是决定自己看看相关的源码 并在此记录心得体会 希望能够帮助到有需要的人 本文会以 猜想 log验证 的方式来学习 Android Looper
  • Android中的Loaders机制

    转自 http blog csdn net guoshaobei article details 17451647 Loaders机制在Android 3 0版本后引入 Loaders机制使一个Activity或者一个Fragment更加容
  • adb install安装流程

    把一个编译好的APK通过 include BUILD PREBUILT 预制到系统中 但是启动后一直crash log中显示 dlopen failed cannot locate symbol 02 25 16 18 20 143 126
  • Binder (一) mmap与一次拷贝原理

    Binder机制 跨进程通信IPC 远程过程调用手段RPC 4个角色进行粘合 Client Server Service Manager和Binder驱动程序 整个过程只需要一次拷贝 Binder Driver misc设备 dev bin
  • Android进阶知识树——应用进程的启动过程

    程序的启动是从进程启动开始的 换句话说只有程序进程启动后 程序才会加载和执行 在AMS启动程序时首先会判断当前进程是否启动 对未启动的进程会发送请求 Zygote在收到请求后创建新的进程 1 Zygote监听客户端请求 由Android进阶
  • linkToDeath机制了解和使用

    转自 http www jianshu com p e38f08e34686 在学习Binder和AIDL的过程中遇到的一些有意思的事情 linkToDeath机制 我们先看看官网如何介绍 When working with remote
  • Android图形显示系统4 图像生产者(下)

    一 概述 在上一篇文章 Android图形显示系统2 图像消费者 中 我们详细地讲解了图像消费者 我们已经了解了 Android 中的图像元数据是如何被 SurfaceFlinger HWComposer 或者 OpenGL ES 消费的
  • Android 13 - binder阅读(6)- 使用ServiceManager获取服务

    最近事情好多 使用ServiceManager获取服务就暂时先不学习了 不过从之前的学习中我们也大致可以猜到使用ServiceManager获取服务的过程 根据服务名称获取到ServiceManager中服务代理对应的Handle 再到Bi
  • 从zygote到onCreate应用启动过程分析

    在之前的文章讲了app进程大概的启动过程 https blog csdn net qq 36063677 article details 125638137 这篇文章细致的分析一下app进程从zygote进程fork出来到onCreate等

随机推荐

  • 玉柴spn码故障对照表_卡车处理故障后不烧尿素也头疼!

    之前说到车辆烧尿素太多 让很多车主头疼 但是如果车辆不烧尿素了也会让车主头疼 若长期不使用SCR系统拆掉 系统里的化学物质会很快被氧化 尿素喷嘴也会堵塞 致使整套系统报废 更换一套SCR系统的成本比消耗尿素的成本要贵得多 化学物质发生氧化
  • 常用中间件控制台默认登陆

    以下用本地127 0 0 1作为ip前缀方便本地直接点击跳转使用 实际部署时替换该ip即可 中间件名称 访问地址端口 默认账号密码 rabbitmq http 127 0 0 1 15672 guest guest activemq htt
  • 有各组方差怎么算组间平方和_组内离差平方和,组间离差平方和与总离差平方和各反映了什么?...

    展开全部 组内离差平方和反映了试验过程中各种随机因素所引起32313133353236313431303231363533e59b9ee7ad9431333431353331的试验误差 组间离差平方和反映了各组样本之间的差异程度 即由变异因
  • TortoiseGit密钥配置图文教程

    1 在 开始菜单 中找到 TortoiseGit 下的 PuTTYgen 建议直接搜索PuTTYgen 单机运行 2 点击 Generate按钮 鼠标需要在 Key区域 活动直至绿色进度条完成 切记 鼠标一定要动哦 鼠标不动的时候 绿色进度
  • 华为HCS之私有云工具FCD搭建

    华为HCS之私有云工具FCD搭建 一 FCD虚拟机创建 1 选择Vmware版本 2 选择虚拟机操作系统版本 3 虚拟机cpu配置 4 虚拟机分配内存 5 选择磁盘大小 6 指定镜像目录 7 开机安装系统 二 系统的初始配置 1 IP配置
  • Java文件的读写

    文件与流 在实际生活中 我们总要对文件进行操作 Java提供了流可以帮助我们读写文件 下面是常用的流类型结构图 在对文件的读写操作中 字节流可用于读写二进制文件 字符流用于读写文本文件 所谓二进制文件 指文件无字符编码格式 均由字节 Byt
  • SpringBoot--基础--02--配置属性

    SpringBoot 基础 02 加载配置 代码位置 https gitee com DanShenGuiZu learnDemo tree mysql mybaties DB springboot learn springboot 1 1
  • springboot整合Swagger2 被拦截问题解决

    springboot整合Swagger2 被拦截问题解决 本来是想整合swagger3 弄了3个依赖进来 就一直注入不进来 推测版本的问题 springboot公司项目是2 0 6的 就先把swagger3降到2 项目用的是OAuth2 就
  • 程序员/媛的日常——coding/debugging/查bug/改bug

    程序员 媛的日常 coding debugging 查bug 改bug 一 在面对失误而带来的指责和沮丧时 你是如何做的 二 发生了失误之后 你是如何找到问题的 如何避免只看到表面现象而忽略了根本问题 三 在工作中我们应该怎么做才能降低失误
  • 矩阵分析

    看了这个视频后觉得自己是个没有学习过线代的人啊 感谢七月算法讲解的矩阵应用 本篇博文完全用作个人学习笔记 一 我们通过图形化来描述一下矩阵的行列和转置间的关系和特性 1 矩阵的行视图 我们以矩阵的每一行为组合 会得到一个几何图案 比如下图中
  • 深度学习初探——yolov3经典目标检测算法

    提示 文章写完后 目录可以自动生成 如何生成可参考右边的帮助文档 目录 前言 一 yolov3的网络结构 二 利用Darknet 53进行特征提取 1 残差网络 2 代码实现 三 利用FPN特征金字塔进行特征增强和预测输出 1 利用FPN特
  • LeetCode 面试最热100题 跳跃游戏

    作者 Linux猿 简介 CSDN博客专家 华为云享专家 Linux C C 云计算 物联网 面试 刷题 算法尽管咨询我 关注我 有问题私聊 关注专栏 点击关注 LeetCode面试必备100题 专栏 优质好文持续更新中 目录 一 题目描述
  • python3解决读取到的邮件中的乱码问题

    点进来看这篇文章的你 是不是有这种情况 不管用什么模块去读取邮箱中的邮件 都能看到类似 gb18030 B 1vfM4g 这样的仿佛像乱码一样的东西 你比如说这样子的 X QQ FEAT YSSoAXAEBlFn3rgysgiXLVc6JA
  • 关于虚拟机下Linux的opencv获取摄像头图像问题

    最近在虚拟机Ubuntu14 04下运行PTAM 配置过程参考 http makaidong com zhuquan945 1 129074 12259634 html 在最后运行时想使用购买外接USB摄像头 YUYV 进行实验 但发现怎么
  • 搭建Hexo博客+部署到Github

    前言 本文章灵感来源于B站up主codesheep 以及视频评论区里的课代表的博客 为了学习总结经验 经过一定修改和总结后写下了这篇文章 感谢他们的指导 目录 前言 一 准备工作 安装Node js 和 Git 1 安装Node js 2
  • 欧姆龙 CJ1W-SCU 无协议功能

  • dcdiag诊断域控制信息注解

    Domain Controller Diagnosis Performing initial setup 初始化安装 Verifying that the local machine test w301 is a DC 检验目标主机test
  • 混淆代码总结

    混淆代码网址很棒的网址 1 Android项目实战 二十五 Android studio 混淆 打包 验证是否成功 2 Android Studio 十一 代码混淆及打包apk 3 Android代码混淆之混淆规则 4 Error warn
  • Docker Desktop Windows 无法启动

    问题描述 windows下安装了Docker Desktop后 无法启动 连界面都不展示 尝试解决 windows组件 一开始以为是没装完整相关组件 把Hyper V组件全部都装上了 还是不行 docker服务 Google搜索相关问题 发
  • Android SurfaceFlinger3 申请Buffer

    本章节思维导图如上 主要讲述了 surafce 测试程序 demo 的第3步中的获取 Buffer 锁定 最关键 并写入 Buffer 的过程 一 概述 该部分代码是在上一章节中 Surface 测试程序源码的精简版 保存了最关键的流程 如