Android Camera 流程学习记录(五)—— Camera.takePicture() 流程解析
简介
- 在前面的几篇笔记中,我已经把
Camera
控制流的部分梳理得比较清楚了。在Camera
流程中,还有一个重要的部分,即数据流。 Camera API 1
中,数据流主要是通过函数回调的方式,依照从下往上的方向,逐层return
到 Applications 中。- 由于数据流的部分相对来说比较简单,所以我就将其与
Camera
的控制流结合起来,从takePicture()
方法切入,追踪一个比较完整的Camera
流程,这个系列的笔记到这篇也就可以结束了。
takePicture() flow
1. Open 流程
Camera Open
的流程,在之前的一篇笔记中已经比较详细地描述了。- 在这里,再关注一下这个流程中,
HAL
层的部分。
1.1 CameraHardwareInterface.h
- 位置:
frameworks/av/services/camera/libcameraservice/device1/CameraHardwareInterface.h
setCallback()
:- 设置
notify
回调,这用来通知数据已经更新。 - 设置
data
回调以及dataTimestamp
回调,对应的是函数指针mDataCb
与mDataCvTimestamp
。 - 注意到,设置
mDevice->ops
对应回调函数时,传入的不是之前设置的函数指针,而是__data_cb
这样的函数。在该文件中,实现了__data_cb
,将回调函数做了一层封装。
- 设置
/** Set the notification and data callbacks */
void setCallbacks(notify_callback notify_cb,data_callback data_cb,data_callback_timestamp data_cb_timestamp,void* user)
{mNotifyCb = notify_cb;mDataCb = data_cb;mDataCbTimestamp = data_cb_timestamp;mCbUser = user;ALOGV("%s(%s)", __FUNCTION__, mName.string());if (mDevice->ops->set_callbacks) {mDevice->ops->set_callbacks(mDevice,__notify_cb,__data_cb,__data_cb_timestamp,__get_memory,this);}
}
__data_cb()
:- 对原
callback
函数简单封装,附加了一个防止数组越界判断。
- 对原
static void __data_cb(int32_t msg_type,const camera_memory_t *data, unsigned int index,camera_frame_metadata_t *metadata,void *user)
{ALOGV("%s", __FUNCTION__);CameraHardwareInterface *__this =static_cast<CameraHardwareInterface *>(user);sp<CameraHeapMemory> mem(static_cast<CameraHeapMemory *>(data->handle));if (index >= mem->mNumBufs) {ALOGE("%s: invalid buffer index %d, max allowed is %d", __FUNCTION__,index, mem->mNumBufs);return;}__this->mDataCb(msg_type, mem->mBuffers[index], metadata, __this->mCbUser);
}
2. 控制流
2.1 Framework
2.1.1 Camera.java
- 位置:
frameworks/base/core/java/android/hardware/Camera.java
takePicture()
:- 设置快门回调。
- 设置各种类型的图片数据回调。
- 调用
JNI takePicture
方法。 - 注意,传入的参数
msgType
是根据相应CallBack
是否存在而确定的,每种Callback
应该对应一个二进制中的数位(如 1,10,100 中 1 的位置),于是这里采用|=
操作给它赋值。
public final void takePicture(ShutterCallback shutter, PictureCallback raw,PictureCallback postview, PictureCallback jpeg) {mShutterCallback = shutter;mRawImageCallback = raw;mPostviewCallback = postview;mJpegCallback = jpeg;// If callback is not set, do not send me callbacks.int msgType = 0;if (mShutterCallback != null) {msgType |= CAMERA_MSG_SHUTTER;}if (mRawImageCallback != null) {msgType |= CAMERA_MSG_RAW_IMAGE;}if (mPostviewCallback != null) {msgType |= CAMERA_MSG_POSTVIEW_FRAME;}if (mJpegCallback != null) {msgType |= CAMERA_MSG_COMPRESSED_IMAGE;}native_takePicture(msgType);mFaceDetectionRunning = false;
}
2.2 Android Runtime
2.2.1 android_hardware_Camera.cpp
- 位置:
frameworks/base/core/jni/android_hardware_Camera.cpp
takePicture()
:- 获取已经打开的
camera
实例,调用其takePicture()
接口。 - 注意,在这个函数中,对于
RAW_IMAGE
有一些附加操作:- 如果设置了
RAW
的callback
,则要检查上下文中,是否能找到对应Buffer
。 - 若无法找到
Buffer
,则将CAMERA_MSG_RAW_IMAGE
的信息去掉,换成CAMERA_MSG_RAW_IMAGE_NOTIFY
。 - 替换后,就只会获得
notification
的消息,而没有对应的图像数据。
- 如果设置了
static void android_hardware_Camera_takePicture(JNIEnv *env, jobject thiz, jint msgType)
{ALOGV("takePicture");JNICameraContext* context;sp<Camera> camera = get_native_camera(env, thiz, &context);if (camera == 0) return;/** When CAMERA_MSG_RAW_IMAGE is requested, if the raw image callback* buffer is available, CAMERA_MSG_RAW_IMAGE is enabled to get the* notification _and_ the data; otherwise, CAMERA_MSG_RAW_IMAGE_NOTIFY* is enabled to receive the callback notification but no data.** Note that CAMERA_MSG_RAW_IMAGE_NOTIFY is not exposed to the* Java application.*/if (msgType & CAMERA_MSG_RAW_IMAGE) {ALOGV("Enable raw image callback buffer");if (!context->isRawImageCallbackBufferAvailable()) {ALOGV("Enable raw image notification, since no callback buffer exists");msgType &= ~CAMERA_MSG_RAW_IMAGE;msgType |= CAMERA_MSG_RAW_IMAGE_NOTIFY;}}if (camera->takePicture(msgType) != NO_ERROR) {jniThrowRuntimeException(env, "takePicture failed");return;}
}
2.3 C/C++ Libraries
2.3.1 Camera.cpp
- 位置:
frameworks/av/camera/Camera.cpp
takePicture()
:- 获取一个
ICamera
,调用其takePicture
接口。 - 这里直接用
return
的方式调用,比较简单。
- 获取一个
// take a picture
status_t Camera::takePicture(int msgType)
{ALOGV("takePicture: 0x%x", msgType);sp <::android::hardware::ICamera> c = mCamera;if (c == 0) return NO_INIT;return c->takePicture(msgType);
}
2.3.2 ICamera.cpp
- 位置:
frameworks/av/camera/ICamera.cpp
takePicture()
:- 利用
Binder
机制发送相应指令到服务端。 - 实际调用到的是
CameraClient::takePicture()
函数。
- 利用
// take a picture - returns an IMemory (ref-counted mmap)
status_t takePicture(int msgType)
{ALOGV("takePicture: 0x%x", msgType);Parcel data, reply;data.writeInterfaceToken(ICamera::getInterfaceDescriptor());data.writeInt32(msgType);remote()->transact(TAKE_PICTURE, data, &reply);status_t ret = reply.readInt32();return ret;
}
2.3.3 CameraClient.cpp
- 位置:
frameworks/av/services/camera/libcameraservice/api1/CameraClient.cpp
takePicture()
:- 注意,
CAMERA_MSG_RAW_IMAGE
指令与CAMERA_MSG_RAW_IMAGE_NOTIFY
指令不能同时有效,需要进行对应的检查。 - 对传入的指令过滤,只留下与
takePicture()
操作相关的。 - 调用
CameraHardwareInterface
中的takePicture()
接口。
- 注意,
// take a picture - image is returned in callback
status_t CameraClient::takePicture(int msgType) {LOG1("takePicture (pid %d): 0x%x", getCallingPid(), msgType);Mutex::Autolock lock(mLock);status_t result = checkPidAndHardware();if (result != NO_ERROR) return result;if ((msgType & CAMERA_MSG_RAW_IMAGE) &&(msgType & CAMERA_MSG_RAW_IMAGE_NOTIFY)) {ALOGE("CAMERA_MSG_RAW_IMAGE and CAMERA_MSG_RAW_IMAGE_NOTIFY"" cannot be both enabled");return BAD_VALUE;}// We only accept picture related message types// and ignore other types of messages for takePicture().int picMsgType = msgType& (CAMERA_MSG_SHUTTER |CAMERA_MSG_POSTVIEW_FRAME |CAMERA_MSG_RAW_IMAGE |CAMERA_MSG_RAW_IMAGE_NOTIFY |CAMERA_MSG_COMPRESSED_IMAGE);enableMsgType(picMsgType);return mHardware->takePicture();
}
2.4 HAL
2.4.1 CameraHardwareInterface.h
- 位置:
frameworks/av/services/camera/libcameraservice/device1/CameraHardwareInterface.h
takePicture()
:- 通过
mDevice
中设置的函数指针,调用HAL
层中具体平台对应的takePicture
操作的实现逻辑。 - 接下来就是与具体的平台相关的流程了,这部分内容对我并非主要,而且在上一篇笔记中已经有比较深入的探索,所以在这里就不继续向下挖掘了。
- 控制流程到了
HAL
层后,再向Linux Drivers
发送控制指令,从而使具体的Camera
设备执行指令,并获取数据。
- 通过
/*** Take a picture.*/
status_t takePicture()
{ALOGV("%s(%s)", __FUNCTION__, mName.string());if (mDevice->ops->take_picture)return mDevice->ops->take_picture(mDevice);return INVALID_OPERATION;
}
3. 数据流
- 由于数据流是通过
callback
函数实现的,所以探究其流程的时候我是从底层向上层进行分析的。
3.1 HAL
3.1.1 CameraHardwareInterface.h
- 位置:
frameworks/av/services/camera/libcameraservice/device1/CameraHardwareInterface.h
- 这里我们只选择
dataCallback
相关流程进行分析。 __data_cb()
:- 该回调函数是在同文件中实现的
setCallbacks()
函数中设置的。 Camera
设备获得数据后,就会往上传输,在HAL
层中会调用到这个回调函数。- 通过函数指针
mDataCb
调用从上一层传入的回调,从而将数据上传。 - 这个
mDataCb
指针对应的,是CameraClient
类中实现的dataCallback()
。
- 该回调函数是在同文件中实现的
static void __data_cb(int32_t msg_type,const camera_memory_t *data, unsigned int index,camera_frame_metadata_t *metadata,void *user)
{ALOGV("%s", __FUNCTION__);CameraHardwareInterface *__this =static_cast<CameraHardwareInterface *>(user);sp<CameraHeapMemory> mem(static_cast<CameraHeapMemory *>(data->handle));if (index >= mem->mNumBufs) {ALOGE("%s: invalid buffer index %d, max allowed is %d", __FUNCTION__,index, mem->mNumBufs);return;}__this->mDataCb(msg_type, mem->mBuffers[index], metadata, __this->mCbUser);
}
3.2 C/C++ Libraries
3.2.1 CameraClient.cpp
- 位置:
frameworks/av/services/camera/libcameraservice/api1/CameraClient.cpp
dataCallback()
:- 这个回调在该文件实现的
initialize()
函数中设置到CameraHardwareInterface
中。 - 启动这个回调后,就从
Cookie
中获取已连接的客户端。 - 根据
msgType
,启动对应的handle
操作。 - 选择其中一个分支的
handle
函数来看。
- 这个回调在该文件实现的
void CameraClient::dataCallback(int32_t msgType,const sp<IMemory>& dataPtr, camera_frame_metadata_t *metadata, void* user) {LOG2("dataCallback(%d)", msgType);sp<CameraClient> client = static_cast<CameraClient*>(getClientFromCookie(user).get());if (client.get() == nullptr) return;if (!client->lockIfMessageWanted(msgType)) return;if (dataPtr == 0 && metadata == NULL) {ALOGE("Null data returned in data callback");client->handleGenericNotify(CAMERA_MSG_ERROR, UNKNOWN_ERROR, 0);return;}switch (msgType & ~CAMERA_MSG_PREVIEW_METADATA) {case CAMERA_MSG_PREVIEW_FRAME:client->handlePreviewData(msgType, dataPtr, metadata);break;case CAMERA_MSG_POSTVIEW_FRAME:client->handlePostview(dataPtr);break;case CAMERA_MSG_RAW_IMAGE:client->handleRawPicture(dataPtr);break;case CAMERA_MSG_COMPRESSED_IMAGE:client->handleCompressedPicture(dataPtr);break;default:client->handleGenericData(msgType, dataPtr, metadata);break;}
}
handleRawPicture()
:- 在
open
流程中,connect()
函数调用时,mRemoteCallback
已经设置为一个客户端实例,其对应的是ICameraClient
的强指针。 - 通过这个实例,这里基于
Binder
机制来启动客户端的dataCallback
。 - 客户端的
dataCallback
是实现在Camera
类中。
- 在
// picture callback - raw image ready
void CameraClient::handleRawPicture(const sp<IMemory>& mem) {disableMsgType(CAMERA_MSG_RAW_IMAGE);ssize_t offset;size_t size;sp<IMemoryHeap> heap = mem->getMemory(&offset, &size);sp<hardware::ICameraClient> c = mRemoteCallback;mLock.unlock();if (c != 0) {c->dataCallback(CAMERA_MSG_RAW_IMAGE, mem, NULL);}
}
3.2.2 Camera.cpp
- 位置:
frameworks/av/camera/Camera.cpp
dataCallback()
:- 调用
CameraListener
的postData
接口,将数据继续向上传输。 postData
接口的实现是在android_hardware_Camera.cpp
中。
- 调用
// callback from camera service when frame or image is ready
void Camera::dataCallback(int32_t msgType, const sp<IMemory>& dataPtr,camera_frame_metadata_t *metadata)
{sp<CameraListener> listener;{Mutex::Autolock _l(mLock);listener = mListener;}if (listener != NULL) {listener->postData(msgType, dataPtr, metadata);}
}
3.3 Android Runtime
3.3.1 android_hardware_Camera.cpp
- 位置:
frameworks/base/core/jni/android_hardware_Camera.cpp
postData()
:- 是
JNICameraContext
类的成员函数,该类继承了CameraListener
。 - 首先获取虚拟机指针。
- 然后过滤掉
CAMERA_MSG_PREVIEW_METADATA
信息。 - 进入分支处理。
- 对于数据传输路径,关键是在于
copyAndPost()
函数。
- 是
void JNICameraContext::postData(int32_t msgType, const sp<IMemory>& dataPtr,camera_frame_metadata_t *metadata)
{// VM pointer will be NULL if object is releasedMutex::Autolock _l(mLock);JNIEnv *env = AndroidRuntime::getJNIEnv();if (mCameraJObjectWeak == NULL) {ALOGW("callback on dead camera object");return;}int32_t dataMsgType = msgType & ~CAMERA_MSG_PREVIEW_METADATA;// return data based on callback typeswitch (dataMsgType) {case CAMERA_MSG_VIDEO_FRAME:// should never happenbreak;// For backward-compatibility purpose, if there is no callback// buffer for raw image, the callback returns null.case CAMERA_MSG_RAW_IMAGE:ALOGV("rawCallback");if (mRawImageCallbackBuffers.isEmpty()) {env->CallStaticVoidMethod(mCameraJClass, fields.post_event,mCameraJObjectWeak, dataMsgType, 0, 0, NULL);} else {copyAndPost(env, dataPtr, dataMsgType);}break;// There is no data.case 0:break;default:ALOGV("dataCallback(%d, %p)", dataMsgType, dataPtr.get());copyAndPost(env, dataPtr, dataMsgType);break;}// post frame metadata to Javaif (metadata && (msgType & CAMERA_MSG_PREVIEW_METADATA)) {postMetadata(env, CAMERA_MSG_PREVIEW_METADATA, metadata);}
}
copyAndPost()
:- 首先确认
Memory
中数据是否存在。 - 申请
Java
字节数组(jbyteArray
,jbyte*
),并将Memory
数据赋予到其中。 - 重点是这个函数:
env->CallStaticVoidMethod(mCameraJClass, fields.post_event, mCameraJObjectWeak, msgType, 0, 0, obj);
- 它的功能是将图像传给
Java
端。 - 通过字段
post_event
,在c++
中调用Java
的方法,并传入对应的参数。 - 最终调用到
Java
端的postEventFromNative()
方法。
- 首先确认
void JNICameraContext::copyAndPost(JNIEnv* env, const sp<IMemory>& dataPtr, int msgType)
{jbyteArray obj = NULL;// allocate Java byte array and copy dataif (dataPtr != NULL) {ssize_t offset;size_t size;sp<IMemoryHeap> heap = dataPtr->getMemory(&offset, &size);ALOGV("copyAndPost: off=%zd, size=%zu", offset, size);uint8_t *heapBase = (uint8_t*)heap->base();if (heapBase != NULL) {const jbyte* data = reinterpret_cast<const jbyte*>(heapBase + offset);if (msgType == CAMERA_MSG_RAW_IMAGE) {obj = getCallbackBuffer(env, &mRawImageCallbackBuffers, size);} else if (msgType == CAMERA_MSG_PREVIEW_FRAME && mManualBufferMode) {obj = getCallbackBuffer(env, &mCallbackBuffers, size);if (mCallbackBuffers.isEmpty()) {ALOGV("Out of buffers, clearing callback!");mCamera->setPreviewCallbackFlags(CAMERA_FRAME_CALLBACK_FLAG_NOOP);mManualCameraCallbackSet = false;if (obj == NULL) {return;}}} else {ALOGV("Allocating callback buffer");obj = env->NewByteArray(size);}if (obj == NULL) {ALOGE("Couldn't allocate byte array for JPEG data");env->ExceptionClear();} else {env->SetByteArrayRegion(obj, 0, size, data);}} else {ALOGE("image heap is NULL");}}// post image data to Javaenv->CallStaticVoidMethod(mCameraJClass, fields.post_event,mCameraJObjectWeak, msgType, 0, 0, obj);if (obj) {env->DeleteLocalRef(obj);}
}
3.4 Framework
3.4.1 Camera.java
- 位置:
frameworks/base/core/java/android/hardware/Camera.java
- 以下两个方法都是
EventHandler
的成员,这个类继承了Handler
类。 postEventFromNative()
:- 首先确定
Camera
是否已经实例化。 - 确认后,通过
Camera
的成员mEventHandler
的obtainMessage
方法将从Native
环境中获得的数据封装成Message
类的一个实例,然后调用sendMessage()
方法将数据传出。
- 首先确定
private static void postEventFromNative(Object camera_ref,int what, int arg1, int arg2, Object obj)
{Camera c = (Camera)((WeakReference)camera_ref).get();if (c == null)return;if (c.mEventHandler != null) {Message m = c.mEventHandler.obtainMessage(what, arg1, arg2, obj);c.mEventHandler.sendMessage(m);}
}
handleMessage()
:sendMessage()
方法传出的数据会通过这个方法作出处理,从而发送到对应的回调类中。- 注意到几个不同的回调类(
mRawImageCallback
、mJpegCallback
等)中都有onPictureTaken()
方法,通过调用这个方法,底层传输到此的数据最终发送到最上层的 Java 应用中,上层应用通过解析Message
就可以得到拍到的图像,从而得以进行后续的操作。 - 我所分析的数据流的流程到此就可以结束了。
@Override
public void handleMessage(Message msg) {switch(msg.what) {case CAMERA_MSG_SHUTTER:if (mShutterCallback != null) {mShutterCallback.onShutter();}return;case CAMERA_MSG_RAW_IMAGE:if (mRawImageCallback != null) {mRawImageCallback.onPictureTaken((byte[])msg.obj, mCamera);}return;case CAMERA_MSG_COMPRESSED_IMAGE:if (mJpegCallback != null) {mJpegCallback.onPictureTaken((byte[])msg.obj, mCamera);}return;case CAMERA_MSG_PREVIEW_FRAME:PreviewCallback pCb = mPreviewCallback;if (pCb != null) {if (mOneShot) {// Clear the callback variable before the callback// in case the app calls setPreviewCallback from// the callback functionmPreviewCallback = null;} else if (!mWithBuffer) {// We're faking the camera preview mode to prevent// the app from being flooded with preview frames.// Set to oneshot mode again.setHasPreviewCallback(true, false);}pCb.onPreviewFrame((byte[])msg.obj, mCamera);}return;case CAMERA_MSG_POSTVIEW_FRAME:if (mPostviewCallback != null) {mPostviewCallback.onPictureTaken((byte[])msg.obj, mCamera);}return;case CAMERA_MSG_FOCUS:AutoFocusCallback cb = null;synchronized (mAutoFocusCallbackLock) {cb = mAutoFocusCallback;}if (cb != null) {boolean success = msg.arg1 == 0 ? false : true;cb.onAutoFocus(success, mCamera);}return;case CAMERA_MSG_ZOOM:if (mZoomListener != null) {mZoomListener.onZoomChange(msg.arg1, msg.arg2 != 0, mCamera);}return;case CAMERA_MSG_PREVIEW_METADATA:if (mFaceListener != null) {mFaceListener.onFaceDetection((Face[])msg.obj, mCamera);}return;case CAMERA_MSG_ERROR :Log.e(TAG, "Error " + msg.arg1);if (mErrorCallback != null) {mErrorCallback.onError(msg.arg1, mCamera);}return;case CAMERA_MSG_FOCUS_MOVE:if (mAutoFocusMoveCallback != null) {mAutoFocusMoveCallback.onAutoFocusMoving(msg.arg1 == 0 ? false : true, mCamera);}return;default:Log.e(TAG, "Unknown message type " + msg.what);return;}
}
流程简图
小结
- 在这篇笔记中,我们从
Camera.takePicture()
方法着手,联系之前学习的Open
流程,将整个Camera
流程简单地追踪了一遍。 - 不管是控制流还是数据流,都是要通过五大层次依次执行下一步的。控制流是将命令从顶层流向底层,而数据流则是将底层的数据流向顶层。
- 如果要自定义一个对数据进行处理的
C++
功能库,并将其加入相机中,我们可以通过对HAL
层进行一些修改,将RAW
图像流向我们的处理过程,再将处理后的RAW
图像传回HAL
层(需要在HAL
层对RAW
格式进行一些处理才能把图像上传),最后通过正常的回调流程把图像传到顶层应用中,就可以实现我们的自定义功能了。 - 至此,对于整个
Camera
的框架,及其运作方式,我们就已经有了比较清晰的了解了。
- 在
Android 5.0
版本后,Camera
推出了Camera API 2
,它有着全新的流程(但总体架构是不会有大变化的)。接下来我会找空余的时间去学习学习这个新的东西,到时候再另开一系列的学习笔记吧。
转载于:https://www.cnblogs.com/stonedemo/p/7533634.html
Android Camera 流程学习记录(五)—— Camera.takePicture() 流程解析相关推荐
- Android音视频学习系列(五) — 掌握音频基础知识并使用AudioTrack、OpenSL ES渲染PCM数据
系列文章 Android音视频学习系列(一) - JNI从入门到精通 Android音视频学习系列(二) - 交叉编译动态库.静态库的入门 Android音视频学习系列(三) - Shell脚本入门 ...
- MySQL学习记录 (五) ----- 存储函数、存储过程和触发器
相关文章: <MySQL学习记录 (一) ----- 有关数据库的基本概念和MySQL常用命令> <MySQL学习记录 (二) ----- SQL数据查询语句(DQL)> &l ...
- Cadence Allegro 设计流程学习记录
Cadence Allegro 设计流程学习记录 前提摘要 软件设计版本: 电路仿真软件:NI Multisim 14.0,TINA-TI. 原理图设计:Design Entry CIS 16.6. ...
- Java学习记录五(多线程、网络编程、Lambda表达式和接口组成更新)
Java学习记录五(多线程.网络编程.Lambda表达式和接口组成更新) Java 25.多线程 25.1实现多线程 25.1.1进程 25.1.2线程 25.1.3多线程的实现 25.1.4设置和获 ...
- Android camera2 框架学习记录
安卓相机架构概览 Android系统利用分层思想,将各层的接口定义与实现分离开来,以接口作为各层的脉络连接整体框架,将具体实现的主导权交由各自有具体实现需求的平台厂商或者Android 开发者,这样既 ...
- 基于Android的学生学习记录与提醒管理系统
末尾获取源码 开发语言:Java Java开发工具:JDK1.8 后端框架:SSM 前端:采用Vue技术开发 数据库:MySQL5.7和Navicat管理工具结合 服务器:Tomcat8.5 开发软件 ...
- UE4 学习记录五 使用合体触发器触发过场动画移动其他物体 开门
这只是用来记录我学习UE4过程的,可能帮不到你,先说声抱歉.为了防止误导他人,请勿转载,请勿转载,请勿转载. 本文的主题是通过过场动画,实现物体移动并旋转,通过人物运动触发触发器,然后播放动画.总章目 ...
- Android 8.0 学习(23)---recovery 流程分析
Android 8.0 recovery 流程分析 这里主要分析non A/B模式下的recovery流程 A/B模式下的recovery在boot中 后续会不断补充,如果有疏漏或者错误的地方,请 ...
- Android安全启动学习(五):Android Verified Boot 2.0
1.AVB概要 AVB2.0被用于启动引导,此用法添加一个"vbmeta.img"镜像. public key被编译到bootloader中用于校验vbmeta数据,vbmeta. ...
最新文章
- 引用类型(一):Object类型
- 自增主键为什么不连续_没关紧的水龙头为什么滴水不连续呢?
- 新浪微博RSS Feed实现中的问题
- Eclipse RCP 中将窗口始终保持在最前
- 连接查询中的ON 子句和 WHERE 子句
- IDEA生成toString方法的快捷键
- linux mkdir错误,thinkphp在linux下报mkdir()错误
- 协方差 之 随机变量间的协方差及向量之间的协方差之间的微妙的区别
- Java线程池newSingleThreadExecutor newFixedThreadPool newCachedThreadPool newScheduledThreadPool
- 动画理解Dijkstra算法过程
- Java并发编程实战~CountDownLatch
- Spring配置属性文件
- ndk编译libiconv
- 使用快捷指令和carplay发送停车位置(高德地图)
- 苹果手机来电归属地_手机号码归属地能否取消?工信部回应...
- SSL-ZYC 2133 腾讯大战360
- 软件测试 | 测试开发 | Sikuli 基于图形识别的自动化测试技术
- 教资(信息技术学科知识与教学能力)13-1信息技术课程知识
- 可视化,市场分析表Excel模板,财务统计EXCEL模版,人员变动EXCEL模版,销量完成度Excel模板
- 淘宝授权登录对接文档
热门文章
- 人工机器:jetbot小车密码
- SLAM:(编译ORB)fatal error LNK1181: 无法打开输入文件“libboost_mpi-vc110-mt-1_57.lib”
- spring 入门 1
- SQL Server安装计划
- 在ubuntu14.04中安装搜狗输入法
- linux小知识之终端
- Yii Framework2.0开发教程(5)数据库mysql性能
- java 昨天今天明天
- linux常用命令之权限
- 浏览器中关于事件的那点事儿