内容简介:最近在看这个问题是使我写下这边文章的原因,下边我们带着问题来找答案~!~!~!这个问题的解释网上答案比较少,在滴滴的插件化框架相关文章
最近在看 《深入探索Android热修复技术原理7.3Q.pdf》 时,遇到一个之前没有注意过的问题:关于资源修更新的Android的版本兼容?作为 程序员 我们需要非常严谨的思路,是什么导致了资源的修复更新需要做版本兼容?
这个问题是使我写下这边文章的原因,下边我们带着问题来找答案~!~!~!
这个问题的解释网上答案比较少,在滴滴的插件化框架相关文章 VirtualAPK 资源篇 和 阿里云移动热修复(Sophix) 相关文章
Android热修复升级探索——资源更新之新思路 中 都有一句概括性质的话语:
AndroidL之后资源在初始化之后可以加载,而在AndroidL之前是不可以的。因为在Android KK及以下版本,addAssetPath只是把补丁包的路径添加到了mAssetPath中,而真正解析的资源包的逻辑是在app第一次执行AssetManager::getResTable的时候。
FTSC
为了比较完整的对前面提出的问题做解答,下边我在老罗写的 Android应用程序资源管理器(Asset Manager)的创建过程分析 这篇文章的基础上分析。
跟踪getResourceText
我们都知道在Android中获取资源调用的 Resources.getText(int id) 内部都是在调用 AssetManager.getResourceText(id) 真正对资源进行管理的是 AssetManager 。
下边我们就以 getResourceText 方法的调用顺序引子查找:
public final class AssetManager {
......
/*package*/ static AssetManager sSystem = null;
private native final void init(boolean isSystem);
......
//构造方法
public AssetManager() {
synchronized (this) {
......
init(false);
......
//每个AssetManager实例都会初始化系统的资源
ensureSystemAssets();
}
}
private static void ensureSystemAssets() {
synchronized (sSync) {
if (sSystem == null) {
AssetManager system = new AssetManager(true);
system.makeStringBlocks(false);
sSystem = system;
}
}
}
......
//添加资源路径
public final int addAssetPath(String path) {
synchronized (this) {
int res = addAssetPathNative(path);
if (mStringBlocks != null) {
makeStringBlocks(mStringBlocks);
}
return res;
}
}
private native final int addAssetPathNative(String path);
/*package*/ final CharSequence getResourceText(int ident) {
synchronized (this) {
TypedValue tmpValue = mValue;
int block = loadResourceValue(ident, (short) 0, tmpValue, true);
if (block >= 0) {
if (tmpValue.type == TypedValue.TYPE_STRING) {
return mStringBlocks[block].get(tmpValue.data);
}
return tmpValue.coerceToString();
}
}
return null;
}
//查找并加载资源
private native final int loadResourceValue(int ident, short density, TypedValue outValue,
boolean resolve);
}
AssetManager.init方法的C层实现:
//frameworks/base/core/jni/android_util_AssetManager.cpp
static void android_content_AssetManager_init(JNIEnv* env, jobject clazz, jboolean isSystem)
{
if (isSystem) {
verifySystemIdmaps();
}
//构造C++层的AssetManager的对象
AssetManager* am = new AssetManager();
if (am == NULL) {
jniThrowException(env, "java/lang/OutOfMemoryError", "");
return;
}
//添加系统资源
am->addDefaultAssets();
ALOGV("Created AssetManager %p for Java object %p\n", am, clazz);
env->SetLongField(clazz, gAssetManagerOffsets.mObject, reinterpret_cast<jlong>(am));
}
AssetManager.loadResourceValue方法的C层实现:
//frameworks/base/core/jni/android_util_AssetManager.cpp
static jint android_content_AssetManager_loadResourceValue(JNIEnv* env, jobject clazz, jint ident, jshort density, jobject outValue, jboolean resolve)
{
/***部分代码省略***/
//这行代码最重要,通过获取C层的AssetManager的成员变量ResTable来获取资源
const ResTable& res(am->getResources());
Res_value value;
ResTable_config config;
uint32_t typeSpecFlags;
ssize_t block = res.getResource(ident, &value, false, density, &typeSpecFlags, &config);
/***部分代码省略***/
if (block >= 0) {
return copyValue(env, outValue, &res, value, ref, block, typeSpecFlags, &config);
}
return static_cast<jint>(block);
}
//getResources实际获取的是ResTable
const ResTable& AssetManager::getResources(bool required) const
{
const ResTable* rt = getResTable(required);
return *rt;
}
我们可以看到获取资源实际是在操作 C层的AssetManager的成员变量ResTable 。如果没有将资源加入到 ResTable 那么是无法获取到的。下边我们分别看看AndroidL和AndroidL之前 addAssetPath 方法的实现。
ResTable的构造
//frameworks/base/libs/androidfw/AssetManager.cpp
const ResTable* AssetManager::getResTable(bool required) const
{
ResTable* rt = mResources;
if (rt) {
return rt;
}
/***部分代码省略***/
const size_t N = mAssetPaths.size();
for (size_t i=0; i<N; i++) {
//遍历mAssetPaths将其ResTable中
/***部分代码省略***/
}
/***部分代码省略***/
return rt;
}
如果已经创建那么直接返回,如果没有那么将 mAssetPaths 集合中的资源路径全部添加到 ResTable 中后返回。下边我们继续看看资源的路径是怎么被插入到 mAssetPaths 中的,或者是怎么被直接插入到 ResTable 中的。
addAssetPath的差异
//frameworks/base/libs/androidfw/AssetManager.cpp
bool AssetManager::addDefaultAssets()
{
const char* root = getenv("ANDROID_ROOT");
LOG_ALWAYS_FATAL_IF(root == NULL, "ANDROID_ROOT not set");
String8 path(root);
path.appendPath(kSystemAssets);
String8 pathCM(root);
pathCM.appendPath(kCMSDKAssets);
return addAssetPath(path, NULL) & addAssetPath(pathCM, NULL);
}
bool AssetManager::addAssetPath(const String8& path, int32_t* cookie)
{
AutoMutex _l(mLock);
asset_path ap;
String8 realPath(path);
if (kAppZipName) {
realPath.appendPath(kAppZipName);
}
/***部分代码省略***/
//将path添加到mAssetPaths中
mAssetPaths.add(ap);
if (mResources != NULL) {
size_t index = mAssetPaths.size() - 1;
//添加到ResTable中
appendPathToResTable(ap, &index);
}
// new paths are always added at the end
if (cookie) {
*cookie = static_cast<int32_t>(mAssetPaths.size());
}
/***部分代码省略***/
return true;
}
以上是Android5.1的源码,我们发现无论是否初始化过 ResTable 我们都可以直接调用 addAssetPath 是可以添加资源。
下边我们看看Android4.4的源码:
//frameworks/base/libs/androidfw/AssetManager.cpp
bool AssetManager::addAssetPath(const String8& path, void** cookie)
{
AutoMutex _l(mLock);
asset_path ap;
String8 realPath(path);
if (kAppZipName) {
realPath.appendPath(kAppZipName);
}
/***部分代码省略***/
//将path添加到mAssetPaths中
mAssetPaths.add(ap);
// new paths are always added at the end
if (cookie) {
*cookie = (void*)mAssetPaths.size();
}
/***部分代码省略***/
return true;
}
我们发现 addAssetPath 只是将 path 添加到了 mAssetPaths 里面,但是并没法添加到 ResTable 中。
如果AndroidL之前调用 addAssetPath 没有初始化 ResTable 那么这次添加就是有效的,否则添加无效。下边我们接着看看 ResTable 的初始化时机。
ResTable的初始化时机
public final class AssetManager {
/***部分代码省略***/
//构造方法
public AssetManager() {
synchronized (this) {
......
init(false);
......
//每个AssetManager实例都会初始化系统的资源
ensureSystemAssets();
}
}
private static void ensureSystemAssets() {
synchronized (sSync) {
if (sSystem == null) {
AssetManager system = new AssetManager(true);
//初始化系统的字符串块
system.makeStringBlocks(false);
sSystem = system;
}
}
}
/*package*/ final void makeStringBlocks(StringBlock[] seed) {
final int seedNum = (seed != null) ? seed.length : 0;
final int num = getStringBlockCount();
mStringBlocks = new StringBlock[num];
if (localLOGV) Log.v(TAG, "Making string blocks for " + this
+ ": " + num);
for (int i=0; i<num; i++) {
if (i < seedNum) {
mStringBlocks[i] = seed[i];
} else {
mStringBlocks[i] = new StringBlock(getNativeStringBlock(i), true);
}
}
}
private native final int getStringBlockCount();
/***部分代码省略***/
}
避免大家往上翻看,我又将 AssetManager 的沟通方法摘出来放大家看看。我们在初始化系统的资源时调用了 AssetManager 的 makeStringBlocks 方法,最后调用了 C层 的 getStringBlockCount 方法。
AssetManager.getStringBlockCount的C层的实现:
//frameworks/base/core/jni/android_util_AssetManager.cpp
static jint android_content_AssetManager_getStringBlockCount(JNIEnv* env, jobject clazz)
{
AssetManager* am = assetManagerForJavaObject(env, clazz);
if (am == NULL) {
return 0;
}
//初次调用资源,进行初始化ResTable
return am->getResources().getTableCount();
}
好了,我们找到了 ResTable 的时机,它是发生在 java 层的 AssetManager 构造的时候。
结论
- Android中进行资源管理的是
AssetManager; - 资源由C层
AssetManager的ResTable提供; -
ResTable构造是遍历mAssetPaths中的资源路径; - AndroidL
addAssetPath方法可以直接将资源路添加到ResTable中使用; - AndroidL之前
addAssetPath方法只是将资源路径添加到了mAssetPaths中; -
ResTable构造包含在Java层AssetManager的构造中的;
文章到这里就全部讲述完啦,若有其他需要交流的可以留言哦~!~!
想阅读作者的更多文章,可以查看我个人博客 和公共号:
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Blockchain Basics
Daniel Drescher / Apress / 2017-3-16 / USD 20.99
In 25 concise steps, you will learn the basics of blockchain technology. No mathematical formulas, program code, or computer science jargon are used. No previous knowledge in computer science, mathema......一起来看看 《Blockchain Basics》 这本书的介绍吧!