android分析之Parcel
将数据打包,跨进程传输(通过Binder)。看看这货究竟是啥玩意:
Parcel.java :
public final class Parcel {
private static final boolean DEBUG_RECYCLE = false;
private static final String TAG = "Parcel"; @SuppressWarnings({"UnusedDeclaration"})
private int mNativePtr; // used by native code,非static /**
* Flag indicating if {@link #mNativePtr} was allocated by this object,
* indicating that we're responsible for its lifecycle.
*/
private boolean mOwnsNativeParcelObject;//非static,从上面解释可以看到,它标识"mNativePtr”是否可用,如果是从NativeCode分配的,则要负责它的生命周期 private RuntimeException mStack; private static final int POOL_SIZE = 6;
private static final Parcel[] sOwnedPool = new Parcel[POOL_SIZE];//static,类拥有,下同。作为Parcel的缓冲池使用。
private static final Parcel[] sHolderPool = new Parcel[POOL_SIZE];
下面看调用Parcel的obtain()时的过程:
static protected final Parcel obtain() {
final Parcel[] pool = sHolderPool;//尝试从sHolderPool这个缓冲池取
synchronized (pool) {
Parcel p;
for (int i=0; i<POOL_SIZE; i++) {//POOL_SIZE为6
p = pool[i];
if (p != null) {
pool[i] = null;
if (DEBUG_RECYCLE) {
p.mStack = new RuntimeException();
}
p.init(obj);//找到一个可用的(非null),初始化这个Parcel
return p;
}
}
}
return new Parcel(0);//如果6个都不可用(即缓冲池空了),则新new一个出来
} private Parcel(int nativePtr) {
if (DEBUG_RECYCLE) {
mStack = new RuntimeException();
}
//Log.i(TAG, "Initializing obj=0x" + Integer.toHexString(obj), mStack);
init(nativePtr);//简单调用
} private void init(int nativePtr) {
if (nativePtr != 0) {
mNativePtr = nativePtr;
mOwnsNativeParcelObject = false;
} else {
mNativePtr = nativeCreate();//调用Native CODE
mOwnsNativeParcelObject = true;//表明,需要负责本Parcel对象的生命周期。后面有几个方法会根据该boolean值决定是否释放Native CODE生成的对象。
}
}
小结:Parcel(.java)逻辑很简单,从sHolderPool或者sOwnedPool中找不等于null的,取出来重新使用。否则,调用Native Code重新生成一个Parcel。在这里,有mNativePtr和mOwnsNativeParcelObject两个对象的成员变量,用来标识所生成的Native层的Parcel是否需要释放/销毁。
Parcel.h(.cpp)分析:
在Parcel.h中,存在许多字段。实际上,可以将Parcel看作管理一块内存的一个管理者。
status_t mError;
uint8_t* mData;//指针,从字面上看应该是指向数据的指针
size_t mDataSize;//表明数据大小,已经存储的数据大小
size_t mDataCapacity;//应该是内存空间的容量
mutable size_t mDataPos;//mutable修饰,说明这个变量要及时反映出最新值,类比数组中position下标
size_t* mObjects;//指针,可以看作数组。其存储的是每个保存在Parcel对象所申请的内存的大小
size_t mObjectsSize;//与上面数组配合使用
size_t mObjectsCapacity;
mutable size_t mNextObjectHint; mutable bool mFdsKnown;
mutable bool mHasFds;
bool mAllowFds; release_func mOwner;
void* mOwnerCookie;
从上面各个字段来看,还是很经典的内存管理方式,这样一般有:内存起始地址(对应上面mData)、内存总容量(对应mDataCapacity)、内存已用容量(对应mDataSize)、当前可用的内存位置(mDataPos)。在Parcel里还更加细分了,每个存储在内存中的对象大小。粒度更细。
Parcel::Parcel()//构造函数
{
initState();
}
...
void Parcel::initState()//简单给各个成员变量赋初值
{
mError = NO_ERROR;
mData = 0;
mDataSize = 0;
mDataCapacity = 0;
mDataPos = 0;
ALOGV("initState Setting data size of %p to %d\n", this, mDataSize);
ALOGV("initState Setting data pos of %p to %d\n", this, mDataPos);
mObjects = NULL;
mObjectsSize = 0;
mObjectsCapacity = 0;
mNextObjectHint = 0;
mHasFds = false;
mFdsKnown = true;
mAllowFds = true;
mOwner = NULL;
}
在setDataCapacity、setDataSize等函数中,调用到continueWrite,这个函数是真正的申请内存函数:
status_t Parcel::continueWrite(size_t desired)
{
// If shrinking, first adjust for any objects that appear
// after the new data size.
size_t objectsSize = mObjectsSize;
if (desired < mDataSize) {//表明需要缩小申请的内存容量
if (desired == 0) {
objectsSize = 0;
} else {
while (objectsSize > 0) {
if (mObjects[objectsSize-1] < desired)//找到一个对象的内存大小小于所要申请的内存大小
break;
objectsSize--;
}
}
} if (mOwner) {//mOwner是一个回调函数指针
// If the size is going to zero, just release the owner's data.
if (desired == 0) {
freeData();//释放数据
return NO_ERROR;
} // If there is a different owner, we need to take
// posession.
uint8_t* data = (uint8_t*)malloc(desired);//分配内存
if (!data) {
mError = NO_MEMORY;
return NO_MEMORY;
}
size_t* objects = NULL; if (objectsSize) {
objects = (size_t*)malloc(objectsSize*sizeof(size_t));//分配objectSize*sizeof(size_t)大小的内存
if (!objects) {
mError = NO_MEMORY;
return NO_MEMORY;
} // Little hack to only acquire references on objects
// we will be keeping.
size_t oldObjectsSize = mObjectsSize;
mObjectsSize = objectsSize;
acquireObjects();//给各个对象增加强、弱引用计数——加入需要的话
mObjectsSize = oldObjectsSize;
} if (mData) {
memcpy(data, mData, mDataSize < desired ? mDataSize : desired);//拷贝到data(新申请的)
}
if (objects && mObjects) {
memcpy(objects, mObjects, objectsSize*sizeof(size_t));
}
//ALOGI("Freeing data ref of %p (pid=%d)\n", this, getpid());
mOwner(this, mData, mDataSize, mObjects, mObjectsSize, mOwnerCookie);
mOwner = NULL; mData = data;
mObjects = objects;
mDataSize = (mDataSize < desired) ? mDataSize : desired;
ALOGV("continueWrite Setting data size of %p to %d\n", this, mDataSize);
mDataCapacity = desired;
mObjectsSize = mObjectsCapacity = objectsSize;
mNextObjectHint = 0; } else if (mData) {
if (objectsSize < mObjectsSize) {
// Need to release refs on any objects we are dropping.
const sp<ProcessState> proc(ProcessState::self());
for (size_t i=objectsSize; i<mObjectsSize; i++) {
const flat_binder_object* flat
= reinterpret_cast<flat_binder_object*>(mData+mObjects[i]);
if (flat->type == BINDER_TYPE_FD) {
// will need to rescan because we may have lopped off the only FDs
mFdsKnown = false;
}
release_object(proc, *flat, this);//尝试释放对象
}
size_t* objects =
(size_t*)realloc(mObjects, objectsSize*sizeof(size_t));
if (objects) {
mObjects = objects;
}
mObjectsSize = objectsSize;
mNextObjectHint = 0;
} // We own the data, so we can just do a realloc().
if (desired > mDataCapacity) {
uint8_t* data = (uint8_t*)realloc(mData, desired);//在原mData位置上重新分配内存
if (data) {
mData = data;
mDataCapacity = desired;
} else if (desired > mDataCapacity) {
mError = NO_MEMORY;
return NO_MEMORY;
}
} else {
if (mDataSize > desired) {
mDataSize = desired;
ALOGV("continueWrite Setting data size of %p to %d\n", this, mDataSize);
}
if (mDataPos > desired) {
mDataPos = desired;
ALOGV("continueWrite Setting data pos of %p to %d\n", this, mDataPos);
}
} } else {
// This is the first data. Easy!
uint8_t* data = (uint8_t*)malloc(desired);//直接申请所需大小
if (!data) {
mError = NO_MEMORY;
return NO_MEMORY;
} if(!(mDataCapacity == 0 && mObjects == NULL
&& mObjectsCapacity == 0)) {
ALOGE("continueWrite: %d/%p/%d/%d", mDataCapacity, mObjects, mObjectsCapacity, desired);
} mData = data;
mDataSize = mDataPos = 0;
ALOGV("continueWrite Setting data size of %p to %d\n", this, mDataSize);
ALOGV("continueWrite Setting data pos of %p to %d\n", this, mDataPos);
mDataCapacity = desired;
} return NO_ERROR;
}
小结:上面内存分配管理比较细致,总的来说就是“要么新申请一块内存”、“要么复用一块内存”,“释放内存”,外加对象的生命周期的控制。
android分析之Parcel的更多相关文章
- Android中的Parcel机制 实现Bundle传递对象
Android中的Parcel机制 实现了Bundle传递对象 使用Bundle传递对象,首先要将其序列化,但是,在Android中要使用这种传递对象的方式需要用到Android Parc ...
- 探索Android中的Parcel机制(上)
一.先从Serialize说起 我们都知道JAVA中的Serialize机制,译成串行化.序列化……,其作用是能将数据对象存入字节流其中,在须要时又一次生成对象.主要应用是利用外部存储设备保存对象状态 ...
- Android中的Parcel机制(上)
一.先从Serialize说起 我们都知道JAVA中的Serialize机制,译成串行化.序列化--,其作用是能将数据对象存入字节流当中,在需要时重新生成对象.主要应用是利用外部存储设备保存对象状态, ...
- android分析之Binder 01
终于还是得写一篇关于Binder的文章了.从最初接触Android到花大把时间研究Android源码,Binder一直是分析道路的拦路虎.看了几本最流行的Android源码分析书籍,每次基本上都不能把 ...
- Android 分析工具 APKAnalyser
APKAnalyser 是 Android 静态,虚拟分析工具,用来测试和验证 Android 应用的开发工作.ApkAnalyser 是个完整的工具链,可以修改二进制应用.用户可以改装,安装,运行, ...
- cocos android分析
来自:http://xiebaochun.github.io/ cocos2d-x Android环境搭建 cocos2d-x环境搭建比較简单,可是小问题还是不少,我尽量都涵盖的全面一些. 下载软件 ...
- Android分析应用程序的构建过程
为了方便Android应用开发要求我们Androidproject编制和包装有了更深入的了解,例如,我们知道这是做什么的每一步,境和工具.输入和输出是什么.等等. 在前文<命令行下Android ...
- android分析windowManager、window、viewGroup之间关系(二)
三.接上一节,分析windowManager中添加一个悬浮框的方式,首先看代码 WindowManager.LayoutParams params = new LayoutParams(); para ...
- android分析windowManager、window、viewGroup之间关系(一)
本文将主要介绍addview方法,在windowManager.window.viewGroup中的实现原理.首先将介绍这些类结构关系,然后分析其内在联系,介绍实现原理,最后介绍重要的一个参数wind ...
随机推荐
- zzuli-2259 matrix
题目描述 在麦克雷的面前有N个数,以及一个R*C的矩阵.现在他的任务是从N个数中取出 R*C 个,并填入这个矩阵中.矩阵每一行的法值为本行最大值与最小值的差,而整个矩阵的法值为每一行的法值的最大值.现 ...
- bzoj1013球形空间产生器sphere 高斯消元(有系统差的写法
Description 有一个球形空间产生器能够在n维空间中产生一个坚硬的球体.现在,你被困在了这个n维球体中,你只知道球面上n+1个点的坐标,你需要以最快的速度确定这个n维球体的球心坐标,以便于摧毁 ...
- C++ Primer笔记
C++ Primer笔记 ch2 变量和基本类型 声明 extern int i; extern int i = 3.14;//定义 左值引用(绑定零一变量初始值,别名) 不能定义引用的引用:引用必须 ...
- 互联网公司技术岗实习/求职经验(实习内推+简历+面试+offer篇)
找工作的事基本尘埃落定了,打算把这大半年来积累的经验写下来,基本都是我希望当年找实习的时候自己能够知道的东西,帮师弟师妹们消除一点信息不平等,攒攒RP~ 不要像我当年那样,接到电话吓成狗,没接到电话吓 ...
- Google Developer Days 2019 & GDD
Google Developer Days 2019 2019 Google 开发者大会 GDD Google Developer Days https://events.google.cn/intl ...
- 什么是USDN稳定币?USDN的应用价值是什么?
9月22日,美国货币监理署(OCC)发布了一项稳定币指南,主要内容围绕的是稳定币的监管及相关规定.一时间,稳定币得到了市场上广泛的关注.那么,什么是稳定币呢?什么又是USDN稳定币呢? 1.什么是稳定 ...
- DeFi 热潮下,NGK将成为下一个财富密码
区块链正在脱虚向实,处于大规模落地,赋能实体产业的前夜,而在这个关键的关口,一个万亿市场的蓝海正在缓缓生成,成为区块链落地的急先锋,这个先锋便是DeFi. DeFi,即Decentralized Fi ...
- yaml配置和ini配置的数据源配置和数据获取
1.前言 关于yaml和ini的相关理论暂不做记录,不影响代码编写,百度即可. 2.关于配置文件的选择 yaml 和 ini 都使用过, 但是yaml更符合人类使用,已要弃用ini,后期各项目均采用y ...
- AtCoder Beginner Contest 192 F - Potion
题目链接 点我跳转 题目大意 给定 \(N\) 个物品和一个 \(X\) ,第 \(i\) 个物品的重量为 \(ai\),你可以从中选择任意个物品(不能不选) 假定选择了 \(S\) 个物品,物品的总 ...
- 死磕Spring之IoC篇 - 文章导读
该系列文章是本人在学习 Spring 的过程中总结下来的,里面涉及到相关源码,可能对读者不太友好,请结合我的源码注释 Spring 源码分析 GitHub 地址 进行阅读 Spring 版本:5.1. ...