您的位置:首页 > 党团工作党团工作
【安卓笔记】热修复_addAssetPath不同版本区别原因x
2025-08-29人已围观
【安卓笔记】热修复_addAssetPath不同版本区别原因x
【安卓笔记】热修复_addAssetPath 不同版本区别原因 在做热修复功能时 Java 层通过反射调用 addAssetPath 在 Android5.0 及以上系统没有问题,在 Android 4.x 版本找不到资源。
addAssetPath 方法:
public final int addAssetPath(String path) {
int res=addAssetPathNative(path);
return res;
}
private native final int addAssetPathNative(String path);
addAssetPath 具体的实现方法是 native 层的。
AssetManager.cpp 源码 4.4 及以前的版本调用 addAssetPath 方法时,只是把补丁包的路径添加到mAssetPath 中,不会去重新解析,真正解析的代码是在 app 第一次执行AssetManager.getResTable()`方法的时候。
一旦解析完一次后,mResource 对象就不为 nil,以后就会直接 return 掉,不会重新解析。
bool AssetManager::addAssetPath(const String8& path, void** cookie)
{
AutoMutex _l(mLock);
asset_path ap;
String8 realPath(path);
if (kAppZipName) {
realPath.appendPath(kAppZipName);
}
ap.type=::getFileType(realPath.string());
if (ap.type==kFileTypeRegular) {
ap.path=realPath;
} else {
ap.path=path;
ap.type=::getFileType(path.string());
if (ap.type !=kFileTypeDirectory && ap.type !=kFileTypeRegular) {
ALOGW("Asset path %s is neither a directory nor file (type=%
d).",
path.string(), (int)ap.type);
return false;
}
}
// Skip if we have it already.
for (size_t i=0; i<mAssetPaths.size(); i++) {
if (mAssetPaths[i].path==ap.path) {
if (cookie) {
*cookie=(void*)(i+1);
}
return true;
}
}
ALOGV("In %p Asset %s path: %s", this,
ap.type==kFileTypeDirectory ? "dir" : "zip", ap.path.string());
mAssetPaths.add(ap);
// new paths are always added at the end
if (cookie) {
*cookie=(void*)mAssetPaths.size();
}
// add overlay packages for /system/framework; apps are handled by the
// (Java) package manager
if (strncmp(path.string(), "/system/framework/", 18)==0) {
// When there is an environment variable for /vendor, this
// should be changed to something similar to how ANDROID_ROOT
// and ANDROID_DATA are used in this file.
String8 overlayPath("/vendor/overlay/framework/");
overlayPath.append(path.getPathLeaf());
if (TEMP_FAILURE_RETRY(access(overlayPath.string(), R_OK))==0) {
asset_path oap;
oap.path=overlayPath;
oap.type=::getFileType(overlayPath.string());
bool addOverlay=(oap.type==kFileTypeRegular); // only .apks supported as overlay
if (addOverlay) {
oap.idmap=idmapPathForPackagePath(overlayPath);
if (isIdmapStaleLocked(ap.path, oap.path, oap.idmap)) {
addOverlay=createIdmapFileLocked(ap.path, oap.path, oap.idmap);
}
}
if (addOverlay) {
mAssetPaths.add(oap);
} else {
ALOGW("failed to add overlay package %s
", overlayPath.string());
}
}
}
return true;
}
const ResTable* AssetManager::getResTable(bool required) const
{
// 执行该方法第一次之后,就都会 return
ResTable* rt=mResources;
if (rt) {
return rt;
}
// Iterate through all asset packages, collecting resources from each.
AutoMutex _l(mLock);
if (mResources !=NULL) {
return mResources;
}
if (required) {
LOG_FATAL_IF(mAssetPaths.size()==0, "No assets added to AssetManager");
}
if (mCacheMode !=CACHE_OFF && !mCacheValid)
const_cast<AssetManager*>(this)->loadFileNameCacheLocked();
const size_t N=mAssetPaths.size();
// 真正解析 package 的地方
for (size_t i=0; i<N; i++) {
Asset* ass=NULL;
ResTable* sharedRes=NULL;
bool shared=true;
const asset_path& ap=mAssetPaths.itemAt(i);
MY_TRACE_BEGIN(ap.path.string());
Asset* idmap=openIdmapLocked(ap);
ALOGV("Looking for resource asset in "%s"
", ap.path.string());
if (ap.type !=kFileTypeDirectory) {
if (i==0) {
// The first item is typically the framework resources,
// which we want to avoid parsing every time.
sharedRes=const_cast<AssetManager*>(this)->
mZipSet.getZipResourceTable(ap.path);
}
if (sharedRes==NULL) {
ass=const_cast<AssetManager*>(this)->
mZipSet.getZipResourceTableAsset(ap.path);
if (ass==NULL) {
ALOGV("loading resource table %s
", ap.path.string());
ass=const_cast<AssetManager*>(this)->
openNonAssetInPathLocked("resources.arsc",
Asset::ACCESS_BUFFER,
ap);
if (ass !=NULL && ass !=kExcludedAsset) {
ass=const_cast<AssetManager*>(this)->
mZipSet.setZipResourceTableAsset(ap.path, ass);
}
}
if (i==0 && ass !=NULL) {
// If this is the first resource table in the asset
// manager, then we are going to cache it so that we
// can quickly copy it out for others.
ALOGV("Creating shared resources for %s", ap.path.string());
sharedRes=new ResTable();
sharedRes->add(ass, (void*)(i+1), false, idmap);
sharedRes=const_cast<AssetManager*>(this)->
mZipSet.setZipResourceTable(ap.path, sharedRes);
}
}
} else {
ALOGV("loading resource table %s
", ap.path.string());
Asset* ass=const_cast<AssetManager*>(this)->
openNonAssetInPathLocked("resources.arsc",
Asset::ACCESS_BUFFER,
ap);
shared=false;
}
if ((ass !=NULL || sharedRes !=NULL) && ass !=kExcludedAsset) {
if (rt==NULL) {
mResources=rt=new ResTable();
updateResourceParamsLocked();
}
ALOGV("Installing resource asset %p in to table %p
", ass, mResources);
if (sharedRes !=NULL) {
ALOGV("Copying existing resources for %s", ap.path.string());
rt->add(sharedRes);
} else {
ALOGV("Parsing resources for %s", ap.path.string());
rt->add(ass, (void*)(i+1), !shared, idmap);
}
if (!shared) {
delete ass;
}
}
if (idmap !=NULL) {
delete idmap;
}
MY_TRACE_END();
}
if (required && !rt) ALOGW("Unable to find resources file resources.arsc");
if (!rt) {
mResources=rt=new ResTable();
}
return rt;
}
const ResTable& AssetManager::getResources(bool required) const
{
const ResTable* rt=getResTable(required);
return *rt;
}
而当我们执行加载补丁的代码的时候,getResTable 已经执行过多次了,Android Framework 里面的代码会多次调用该方法。即使是使用 addAssetPath,也只是添加到了 mAssetPath,并不会发生解析,所以补丁包里面的资源就是完全不生效的。
而在 android 5.0 及以上的代码中: Android 5.0 AssetManager.cpp 源码 bool AssetManager::addAssetPath(const String8& path, int32_t* cookie)
{
AutoMutex _l(mLock);
asset_path ap;
String8 realPath(path);
if (kAppZipName) {
realPath.appendPath(kAppZipName);
}
ap.type=::getFileType(realPath.string());
if (ap.type==kFileTypeRegular) {
ap.path=realPath;
} else {
ap.path=path;
ap.type=::getFileType(path.string());
if (ap.type !=kFileTypeDirectory && ap.type !=kFileTypeRegular) {
ALOGW("Asset path %s is neither a directory nor file (type=%d).",
path.string(), (int)ap.type);
return false;
}
}
// Skip if we have it already.
for (size_t i=0; i<mAssetPaths.size(); i++) {
if (mAssetPaths[i].path==ap.path) {
if (cookie) {
*cookie=static_cast<int32_t>(i+1);
}
return true;
}
}
ALOGV("In %p Asset %s path: %s", this,
ap.type==kFileTypeDirectory ? "dir" : "zip", ap.path.string());
// Check that the path has an AndroidManifest.xml
Asset* manifestAsset=const_cast<AssetManager*>(this)->openNonAssetInPathLocked(
kAndroidManifest, Asset::ACCESS_BUFFER, ap);
if (manifestAsset==NULL) {
// This asset path does not contain any resources.
delete manifestAsset;
return false;
}
delete manifestAsset;
mAssetPaths.add(ap);
// new paths are always added at the end
if (cookie) {
*cookie=static_cast<int32_t>(mAssetPaths.size());
}
#ifdef HAVE_ANDROID_OS
// Load overlays, if any
asset_path oap;
for (size_t idx=0; mZipSet.getOverlay(ap.path, idx, &oap); idx++) {
mAssetPaths.add(oap);
}
#endif
if (mResources !=NULL) {
// 重新调用该方法去解析 package
appendPathToResTable(ap);
}
return true;
}
bool AssetManager::appendPathToResTable(const asset_path& ap) const {
Asset* ass=NULL;
ResTable* sharedRes=NULL;
bool shared=true;
bool onlyEmptyResources=true;
MY_TRACE_BEGIN(ap.path.string());
Asset* idmap=openIdmapLocked(ap);
size_t nextEntryIdx=mResources->getTableCount();
ALOGV("Looking for resource asset in "%s"
", ap.path.string());
if (ap.type !=kFileTypeDirectory) {
if (nextEntryIdx==0) {
// The first item is typically the framework resources,
// which we want to avoid parsing every time.
sharedRes=const_cast<AssetManager*>(this)->
mZipSet.getZipResourceTable(ap.path);
if (sharedRes !=NULL) {
// skip ahead the number of system overlay packages preloaded
nextEntryIdx=sharedRes->getTableCount();
}
}
if (sharedRes==NULL) {
ass=const_cast<AssetManager*>(this)->
mZipSet.getZipResourceTableAsset(ap.path);
if (ass==NULL) {
ALOGV("loading resource table %s
", ap.path.string());
ass=const_cast<AssetManager*>(this)->
openNonAssetInPathLocked("resources.arsc",
Asset::ACCESS_BUFFER,
ap);
if (ass !=NULL && ass !=kExcludedAsset) {
ass=const_cast<AssetManager*>(this)->
mZipSet.setZipResourceTableAsset(ap.path, ass);
}
}
if (nextEntryIdx==0 && ass !=NULL) {
// If this is the first resource table in the asset
// manager, then we are going to cache it so that we
// can quickly copy it out for others.
ALOGV("Creating shared resources for %s", ap.path.string());
sharedRes=new ResTable();
sharedRes->add(ass, idmap, nextEntryIdx + 1, false);
#ifdef HAVE_ANDROID_OS
const char* data=getenv("ANDROID_DATA");
LOG_ALWAYS_FATAL_IF(data==NULL, "ANDROID_DATA not set");
String8 overlaysListPath(data);
overlaysListPath.appendPath(kResourceCache);
overlaysListPath.appendPath("overlays.list");
addSystemOverlays(overlaysListPath.string(), ap.path, sharedRes, nextEntryIdx);
#endif
sharedRes=const_cast<AssetManager*>(this)->
mZipSet.setZipResourceTable(ap.path, sharedRes);
}
}
} else {
ALOGV("loading resource table %s
", ap.path.string());
ass=const_cast<AssetManager*>(this)->
openNonAssetInPathLocked("resources.arsc",
Asset::ACCESS_BUFFER,
ap);
shared=false;
}
if ((ass !=NULL || sharedRes !=NULL) && ass !=kExcludedAsset) {
ALOGV("Installing resource asset %p in to table %p
", ass, mResources);
if (sharedRes !=NULL) {
ALOGV("Copying existing resources for %s", ap.path.string());
mResources->add(sharedRes);
} else {
ALOGV("Parsing resources for %s", ap.path.string());
mResources->add(ass, idmap, nextEntryIdx + 1, !shared);
}
onlyEmptyResources=false;
if (!shared) {
delete ass;
}
} else {
ALOGV("Installing empty resources in to table %p
", mResources);
mResources->addEmpty(nextEntryIdx + 1);
}
if (idmap !=NULL) {
delete idmap;
}
MY_TRACE_END();
return onlyEmptyResources;
}
const ResTable* AssetManager::getResTable(bool required) const
{
ResTable* rt=mResources;
if (rt) {
return rt;
}
// Iterate through all asset packages, collecting resources from each.
AutoMutex _l(mLock);
if (mResources !=NULL) {
return mResources;
}
if (required) {
LOG_FATAL_IF(mAssetPaths.size()==0, "No assets added to AssetManager");
}
if (mCacheMode !=CACHE_OFF && !mCacheValid) {
const_cast<AssetManager*>(this)->loadFileNameCacheLocked();
}
mResources=new ResTable();
updateResourceParamsLocked();
bool onlyEmptyResources=true;
const size_t N=mAssetPaths.size();
// 也是调用 appendPathToResTable 去解析
for (size_t i=0; i<N; i++) {
bool empty=appendPathToResTable(mAssetPaths.itemAt(i));
onlyEmptyResources=onlyEmptyResources && empty;
}
if (required && onlyEmptyResources) {
ALOGW("Unable to find resources file resources.arsc");
delete mResources;
mResources=NULL;
}
return mResources;
}
也就是说在 Android5.0 及以上的系统中 native 层的 addAssetPath 方法会再调用appendPathToResTable 去解析,所以在 5.0 及以上系统通过反射调用addAssetPath 方法就不会有问题。
解决方案就是根据 Google Instant Run 实现的原理:创建一个新的 AssetManager,然后加入完整的新资源包,替换掉原有的 AssetManager。
具体可参考:深度理解 Android InstantRun 原理以及源码分析中的 2.2 monkeyPatchExistingResources 部分 通过 aapt 工具编译 apk 包,package id 是 0x7f,系统的资源包(framework-res.jar),package id 为 0x01,如果 addAssetPath 补丁包中的 package id 也是 0x7f,就会使得同一个 pakcage id 的包被加载两次,在 Android L 后,会把后来的包添加到之前的包的同一个 PackageGoup 下,但是在 get 资源时,会从前往后便利,也就是说先得到原有安装包里的资源,补丁中的资源就永远无法生效。可以构建一个package id 为 0x66 的资源包。这样就不会与已经加载的 0x7f 冲突。
相关热词搜索:
修复
区别
原因
【安卓笔记】热修复_addAssetPath 不同版本区别原因 在做热修复功能时 Java 层通过反射调用 addAssetPath 在 Android5.0 及以上系统没有问题,在 Android 4.x 版本找不到资源。
addAssetPath 方法:
public final int addAssetPath(String path) {
int res=addAssetPathNative(path);
return res;
}
private native final int addAssetPathNative(String path);
addAssetPath 具体的实现方法是 native 层的。
AssetManager.cpp 源码 4.4 及以前的版本调用 addAssetPath 方法时,只是把补丁包的路径添加到mAssetPath 中,不会去重新解析,真正解析的代码是在 app 第一次执行AssetManager.getResTable()`方法的时候。
一旦解析完一次后,mResource 对象就不为 nil,以后就会直接 return 掉,不会重新解析。
bool AssetManager::addAssetPath(const String8& path, void** cookie)
{
AutoMutex _l(mLock);
asset_path ap;
String8 realPath(path);
if (kAppZipName) {
realPath.appendPath(kAppZipName);
}
ap.type=::getFileType(realPath.string());
if (ap.type==kFileTypeRegular) {
ap.path=realPath;
} else {
ap.path=path;
ap.type=::getFileType(path.string());
if (ap.type !=kFileTypeDirectory && ap.type !=kFileTypeRegular) {
ALOGW("Asset path %s is neither a directory nor file (type=%
d).",
path.string(), (int)ap.type);
return false;
}
}
// Skip if we have it already.
for (size_t i=0; i<mAssetPaths.size(); i++) {
if (mAssetPaths[i].path==ap.path) {
if (cookie) {
*cookie=(void*)(i+1);
}
return true;
}
}
ALOGV("In %p Asset %s path: %s", this,
ap.type==kFileTypeDirectory ? "dir" : "zip", ap.path.string());
mAssetPaths.add(ap);
// new paths are always added at the end
if (cookie) {
*cookie=(void*)mAssetPaths.size();
}
// add overlay packages for /system/framework; apps are handled by the
// (Java) package manager
if (strncmp(path.string(), "/system/framework/", 18)==0) {
// When there is an environment variable for /vendor, this
// should be changed to something similar to how ANDROID_ROOT
// and ANDROID_DATA are used in this file.
String8 overlayPath("/vendor/overlay/framework/");
overlayPath.append(path.getPathLeaf());
if (TEMP_FAILURE_RETRY(access(overlayPath.string(), R_OK))==0) {
asset_path oap;
oap.path=overlayPath;
oap.type=::getFileType(overlayPath.string());
bool addOverlay=(oap.type==kFileTypeRegular); // only .apks supported as overlay
if (addOverlay) {
oap.idmap=idmapPathForPackagePath(overlayPath);
if (isIdmapStaleLocked(ap.path, oap.path, oap.idmap)) {
addOverlay=createIdmapFileLocked(ap.path, oap.path, oap.idmap);
}
}
if (addOverlay) {
mAssetPaths.add(oap);
} else {
ALOGW("failed to add overlay package %s
", overlayPath.string());
}
}
}
return true;
}
const ResTable* AssetManager::getResTable(bool required) const
{
// 执行该方法第一次之后,就都会 return
ResTable* rt=mResources;
if (rt) {
return rt;
}
// Iterate through all asset packages, collecting resources from each.
AutoMutex _l(mLock);
if (mResources !=NULL) {
return mResources;
}
if (required) {
LOG_FATAL_IF(mAssetPaths.size()==0, "No assets added to AssetManager");
}
if (mCacheMode !=CACHE_OFF && !mCacheValid)
const_cast<AssetManager*>(this)->loadFileNameCacheLocked();
const size_t N=mAssetPaths.size();
// 真正解析 package 的地方
for (size_t i=0; i<N; i++) {
Asset* ass=NULL;
ResTable* sharedRes=NULL;
bool shared=true;
const asset_path& ap=mAssetPaths.itemAt(i);
MY_TRACE_BEGIN(ap.path.string());
Asset* idmap=openIdmapLocked(ap);
ALOGV("Looking for resource asset in "%s"
", ap.path.string());
if (ap.type !=kFileTypeDirectory) {
if (i==0) {
// The first item is typically the framework resources,
// which we want to avoid parsing every time.
sharedRes=const_cast<AssetManager*>(this)->
mZipSet.getZipResourceTable(ap.path);
}
if (sharedRes==NULL) {
ass=const_cast<AssetManager*>(this)->
mZipSet.getZipResourceTableAsset(ap.path);
if (ass==NULL) {
ALOGV("loading resource table %s
", ap.path.string());
ass=const_cast<AssetManager*>(this)->
openNonAssetInPathLocked("resources.arsc",
Asset::ACCESS_BUFFER,
ap);
if (ass !=NULL && ass !=kExcludedAsset) {
ass=const_cast<AssetManager*>(this)->
mZipSet.setZipResourceTableAsset(ap.path, ass);
}
}
if (i==0 && ass !=NULL) {
// If this is the first resource table in the asset
// manager, then we are going to cache it so that we
// can quickly copy it out for others.
ALOGV("Creating shared resources for %s", ap.path.string());
sharedRes=new ResTable();
sharedRes->add(ass, (void*)(i+1), false, idmap);
sharedRes=const_cast<AssetManager*>(this)->
mZipSet.setZipResourceTable(ap.path, sharedRes);
}
}
} else {
ALOGV("loading resource table %s
", ap.path.string());
Asset* ass=const_cast<AssetManager*>(this)->
openNonAssetInPathLocked("resources.arsc",
Asset::ACCESS_BUFFER,
ap);
shared=false;
}
if ((ass !=NULL || sharedRes !=NULL) && ass !=kExcludedAsset) {
if (rt==NULL) {
mResources=rt=new ResTable();
updateResourceParamsLocked();
}
ALOGV("Installing resource asset %p in to table %p
", ass, mResources);
if (sharedRes !=NULL) {
ALOGV("Copying existing resources for %s", ap.path.string());
rt->add(sharedRes);
} else {
ALOGV("Parsing resources for %s", ap.path.string());
rt->add(ass, (void*)(i+1), !shared, idmap);
}
if (!shared) {
delete ass;
}
}
if (idmap !=NULL) {
delete idmap;
}
MY_TRACE_END();
}
if (required && !rt) ALOGW("Unable to find resources file resources.arsc");
if (!rt) {
mResources=rt=new ResTable();
}
return rt;
}
const ResTable& AssetManager::getResources(bool required) const
{
const ResTable* rt=getResTable(required);
return *rt;
}
而当我们执行加载补丁的代码的时候,getResTable 已经执行过多次了,Android Framework 里面的代码会多次调用该方法。即使是使用 addAssetPath,也只是添加到了 mAssetPath,并不会发生解析,所以补丁包里面的资源就是完全不生效的。
而在 android 5.0 及以上的代码中: Android 5.0 AssetManager.cpp 源码 bool AssetManager::addAssetPath(const String8& path, int32_t* cookie)
{
AutoMutex _l(mLock);
asset_path ap;
String8 realPath(path);
if (kAppZipName) {
realPath.appendPath(kAppZipName);
}
ap.type=::getFileType(realPath.string());
if (ap.type==kFileTypeRegular) {
ap.path=realPath;
} else {
ap.path=path;
ap.type=::getFileType(path.string());
if (ap.type !=kFileTypeDirectory && ap.type !=kFileTypeRegular) {
ALOGW("Asset path %s is neither a directory nor file (type=%d).",
path.string(), (int)ap.type);
return false;
}
}
// Skip if we have it already.
for (size_t i=0; i<mAssetPaths.size(); i++) {
if (mAssetPaths[i].path==ap.path) {
if (cookie) {
*cookie=static_cast<int32_t>(i+1);
}
return true;
}
}
ALOGV("In %p Asset %s path: %s", this,
ap.type==kFileTypeDirectory ? "dir" : "zip", ap.path.string());
// Check that the path has an AndroidManifest.xml
Asset* manifestAsset=const_cast<AssetManager*>(this)->openNonAssetInPathLocked(
kAndroidManifest, Asset::ACCESS_BUFFER, ap);
if (manifestAsset==NULL) {
// This asset path does not contain any resources.
delete manifestAsset;
return false;
}
delete manifestAsset;
mAssetPaths.add(ap);
// new paths are always added at the end
if (cookie) {
*cookie=static_cast<int32_t>(mAssetPaths.size());
}
#ifdef HAVE_ANDROID_OS
// Load overlays, if any
asset_path oap;
for (size_t idx=0; mZipSet.getOverlay(ap.path, idx, &oap); idx++) {
mAssetPaths.add(oap);
}
#endif
if (mResources !=NULL) {
// 重新调用该方法去解析 package
appendPathToResTable(ap);
}
return true;
}
bool AssetManager::appendPathToResTable(const asset_path& ap) const {
Asset* ass=NULL;
ResTable* sharedRes=NULL;
bool shared=true;
bool onlyEmptyResources=true;
MY_TRACE_BEGIN(ap.path.string());
Asset* idmap=openIdmapLocked(ap);
size_t nextEntryIdx=mResources->getTableCount();
ALOGV("Looking for resource asset in "%s"
", ap.path.string());
if (ap.type !=kFileTypeDirectory) {
if (nextEntryIdx==0) {
// The first item is typically the framework resources,
// which we want to avoid parsing every time.
sharedRes=const_cast<AssetManager*>(this)->
mZipSet.getZipResourceTable(ap.path);
if (sharedRes !=NULL) {
// skip ahead the number of system overlay packages preloaded
nextEntryIdx=sharedRes->getTableCount();
}
}
if (sharedRes==NULL) {
ass=const_cast<AssetManager*>(this)->
mZipSet.getZipResourceTableAsset(ap.path);
if (ass==NULL) {
ALOGV("loading resource table %s
", ap.path.string());
ass=const_cast<AssetManager*>(this)->
openNonAssetInPathLocked("resources.arsc",
Asset::ACCESS_BUFFER,
ap);
if (ass !=NULL && ass !=kExcludedAsset) {
ass=const_cast<AssetManager*>(this)->
mZipSet.setZipResourceTableAsset(ap.path, ass);
}
}
if (nextEntryIdx==0 && ass !=NULL) {
// If this is the first resource table in the asset
// manager, then we are going to cache it so that we
// can quickly copy it out for others.
ALOGV("Creating shared resources for %s", ap.path.string());
sharedRes=new ResTable();
sharedRes->add(ass, idmap, nextEntryIdx + 1, false);
#ifdef HAVE_ANDROID_OS
const char* data=getenv("ANDROID_DATA");
LOG_ALWAYS_FATAL_IF(data==NULL, "ANDROID_DATA not set");
String8 overlaysListPath(data);
overlaysListPath.appendPath(kResourceCache);
overlaysListPath.appendPath("overlays.list");
addSystemOverlays(overlaysListPath.string(), ap.path, sharedRes, nextEntryIdx);
#endif
sharedRes=const_cast<AssetManager*>(this)->
mZipSet.setZipResourceTable(ap.path, sharedRes);
}
}
} else {
ALOGV("loading resource table %s
", ap.path.string());
ass=const_cast<AssetManager*>(this)->
openNonAssetInPathLocked("resources.arsc",
Asset::ACCESS_BUFFER,
ap);
shared=false;
}
if ((ass !=NULL || sharedRes !=NULL) && ass !=kExcludedAsset) {
ALOGV("Installing resource asset %p in to table %p
", ass, mResources);
if (sharedRes !=NULL) {
ALOGV("Copying existing resources for %s", ap.path.string());
mResources->add(sharedRes);
} else {
ALOGV("Parsing resources for %s", ap.path.string());
mResources->add(ass, idmap, nextEntryIdx + 1, !shared);
}
onlyEmptyResources=false;
if (!shared) {
delete ass;
}
} else {
ALOGV("Installing empty resources in to table %p
", mResources);
mResources->addEmpty(nextEntryIdx + 1);
}
if (idmap !=NULL) {
delete idmap;
}
MY_TRACE_END();
return onlyEmptyResources;
}
const ResTable* AssetManager::getResTable(bool required) const
{
ResTable* rt=mResources;
if (rt) {
return rt;
}
// Iterate through all asset packages, collecting resources from each.
AutoMutex _l(mLock);
if (mResources !=NULL) {
return mResources;
}
if (required) {
LOG_FATAL_IF(mAssetPaths.size()==0, "No assets added to AssetManager");
}
if (mCacheMode !=CACHE_OFF && !mCacheValid) {
const_cast<AssetManager*>(this)->loadFileNameCacheLocked();
}
mResources=new ResTable();
updateResourceParamsLocked();
bool onlyEmptyResources=true;
const size_t N=mAssetPaths.size();
// 也是调用 appendPathToResTable 去解析
for (size_t i=0; i<N; i++) {
bool empty=appendPathToResTable(mAssetPaths.itemAt(i));
onlyEmptyResources=onlyEmptyResources && empty;
}
if (required && onlyEmptyResources) {
ALOGW("Unable to find resources file resources.arsc");
delete mResources;
mResources=NULL;
}
return mResources;
}
也就是说在 Android5.0 及以上的系统中 native 层的 addAssetPath 方法会再调用appendPathToResTable 去解析,所以在 5.0 及以上系统通过反射调用addAssetPath 方法就不会有问题。
解决方案就是根据 Google Instant Run 实现的原理:创建一个新的 AssetManager,然后加入完整的新资源包,替换掉原有的 AssetManager。
具体可参考:深度理解 Android InstantRun 原理以及源码分析中的 2.2 monkeyPatchExistingResources 部分 通过 aapt 工具编译 apk 包,package id 是 0x7f,系统的资源包(framework-res.jar),package id 为 0x01,如果 addAssetPath 补丁包中的 package id 也是 0x7f,就会使得同一个 pakcage id 的包被加载两次,在 Android L 后,会把后来的包添加到之前的包的同一个 PackageGoup 下,但是在 get 资源时,会从前往后便利,也就是说先得到原有安装包里的资源,补丁中的资源就永远无法生效。可以构建一个package id 为 0x66 的资源包。这样就不会与已经加载的 0x7f 冲突。
相关热词搜索:
修复
区别
原因