android加载第三方图标机制分析
系统如何获取图片等资源
当我们调用Resources.java 来获取图片等资源的时候会调用getDrawable 方法, getDrawable最终把这还是使用loadDrawable 方法来处理.
Resources.java
public Drawable getDrawable(int id) throws NotFoundException { TypedValue value; synchronized (mAccessLock) { value = mTmpValue; if (value == null) { value = new TypedValue(); } else { mTmpValue = null; } getValue(id, value, true); } Drawable res = loadDrawable(value, id); synchronized (mAccessLock) { if (mTmpValue == null) { mTmpValue = value; } } return res; } |
loadDrawable 方法在获取资源的时候会先判断该资源是否在缓存中,如果在缓存中就直接获取返回,如果没有就继续查询.同时使用color配置的图片的资源也会先去缓存中读取,这样可以提高代码的效率.如果还是没有找到就会调用AssetManager.java 的openNonAsset 方法,再接着分析会发现调用的是Jni 的一个方法openNonAssetNative,Jni 最终会去调用AssetManager.cpp 的openNonAsset的方法来根据不同主题去获取对应的资源.
frameworks/base/core/jni/android_util_AssetManager.cpp àopenNonAssetNative
static jint android_content_AssetManager_openNonAssetNative(JNIEnv* env, jobject clazz, jint cookie, jstring fileName, jint mode) { AssetManager* am = assetManagerForJavaObject(env, clazz); if (am == NULL) { return 0; }
ALOGV("openNonAssetNative in %p (Java object %p)\n", am, clazz);
ScopedUtfChars fileName8(env, fileName); if (fileName8.c_str() == NULL) { return -1; }
if (mode != Asset::ACCESS_UNKNOWN && mode != Asset::ACCESS_RANDOM && mode != Asset::ACCESS_STREAMING && mode != Asset::ACCESS_BUFFER) { jniThrowException(env, "java/lang/IllegalArgumentException", "Bad access mode"); return -1; }
Asset* a = cookie ? am->openNonAsset((void*)cookie, fileName8.c_str(), (Asset::AccessMode)mode) : am->openNonAsset(fileName8.c_str(), (Asset::AccessMode)mode);
if (a == NULL) { jniThrowException(env, "java/io/FileNotFoundException", fileName8.c_str()); return -1; }
//printf("Created Asset Stream: %p\n", a);
return (jint)a; } |
Resources.java
Drawable loadDrawable(TypedValue value, int id) throws NotFoundException {
if (TRACE_FOR_PRELOAD) { // Log only framework resources if ((id >>> 24) == 0x1) {//framework resources id 是以0x1 开头, mediatek Resource id 是一0x2 开头. 第3方资源一般以0x7 开头 final String name = getResourceName(id); if (name != null) android.util.Log.d("PreloadDrawable", name); } }
boolean isColorDrawable = false; if (value.type >= TypedValue.TYPE_FIRST_COLOR_INT && value.type <= TypedValue.TYPE_LAST_COLOR_INT) { isColorDrawable = true; } final long key = isColorDrawable ? value.data : (((long) value.assetCookie) << 32) | value.data;
Drawable dr = getCachedDrawable(isColorDrawable ? mColorDrawableCache : mDrawableCache, key);//从缓存中读取,如果缓存中已经存在,就直接获取并返回,缓存中没有的话就继续执行下面的代码去查询获取对应的资源文件.
if (dr != null) { return dr; } Drawable.ConstantState cs = null;
/// M: Theme manager @{ Boolean checkPreload = true; if (FeatureOption.MTK_THEMEMANAGER_APP) { String skin= Configuration.getSkin(); if ((skin != null) && !(skin.equals(sDefaultSkin))){ checkPreload = false; } } /// @}
//使用color配置的图片可以也直接从缓存中读取,提高效率. if (checkPreload) {/// M: Theme manager if (isColorDrawable) { cs = sPreloadedColorDrawables.get(key); } else { cs = sPreloadedDrawables[mConfiguration.getLayoutDirection()].get(key); } }/// M: Theme manager
if (cs != null) { dr = cs.newDrawable(this); } else { if (isColorDrawable) { dr = new ColorDrawable(value.data); }
if (dr == null) { if (value.string == null) { throw new NotFoundException( "Resource is not a Drawable (color or path): " + value); }
String file = value.string.toString();
if (TRACE_FOR_MISS_PRELOAD) { // Log only framework resources if ((id >>> 24) == 0x1) { final String name = getResourceName(id); if (name != null) android.util.Log.d(TAG, "Loading framework drawable #" + Integer.toHexString(id) + ": " + name + " at " + file); } }
if (DEBUG_LOAD) Log.v(TAG, "Loading drawable for cookie " + value.assetCookie + ": " + file);
if (file.endsWith(".xml")) { Trace.traceBegin(Trace.TRACE_TAG_RESOURCES, file); try { XmlResourceParser rp = loadXmlResourceParser( file, id, value.assetCookie, "drawable"); dr = Drawable.createFromXml(this, rp); rp.close(); } catch (Exception e) { Trace.traceEnd(Trace.TRACE_TAG_RESOURCES); NotFoundException rnf = new NotFoundException( "File " + file + " from drawable resource ID #0x" + Integer.toHexString(id)); rnf.initCause(e); throw rnf; } Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
} else { Trace.traceBegin(Trace.TRACE_TAG_RESOURCES, file); try { InputStream is = mAssets.openNonAsset( value.assetCookie, file, AssetManager.ACCESS_STREAMING); //System.out.println("Opened file " + file + ": " + is); dr = Drawable.createFromResourceStream(this, value, is, file, null); is.close(); //System.out.println("Created stream: " + dr); } catch (Exception e) { Trace.traceEnd(Trace.TRACE_TAG_RESOURCES); NotFoundException rnf = new NotFoundException( "File " + file + " from drawable resource ID #0x" + Integer.toHexString(id)); rnf.initCause(e); throw rnf; } Trace.traceEnd(Trace.TRACE_TAG_RESOURCES); } } }
if (dr != null) { dr.setChangingConfigurations(value.changingConfigurations); cs = dr.getConstantState(); if (cs != null) { if (mPreloading) { final int changingConfigs = cs.getChangingConfigurations(); if (isColorDrawable) { if (verifyPreloadConfig(changingConfigs, 0, value.resourceId, "drawable")) { sPreloadedColorDrawables.put(key, cs); } } else { if (verifyPreloadConfig(changingConfigs, LAYOUT_DIR_CONFIG, value.resourceId, "drawable")) { if ((changingConfigs&LAYOUT_DIR_CONFIG) == 0) { // If this resource does not vary based on layout direction, // we can put it in all of the preload maps. sPreloadedDrawables[0].put(key, cs); sPreloadedDrawables[1].put(key, cs); } else { // Otherwise, only in the layout dir we loaded it for. final LongSparseArray<Drawable.ConstantState> preloads = sPreloadedDrawables[mConfiguration.getLayoutDirection()]; preloads.put(key, cs); } } } } else { synchronized (mAccessLock) { //Log.i(TAG, "Saving cached drawable @ #" + // Integer.toHexString(key.intValue()) // + " in " + this + ": " + cs); if (isColorDrawable) { mColorDrawableCache.put(key, new WeakReference<Drawable.ConstantState>(cs)); } else { mDrawableCache.put(key, new WeakReference<Drawable.ConstantState>(cs)); } } } } }
return dr; } |
如何替换第三方应用里面的资源
frameworks/base/libs/androidfw/AssetManager.cpp
bool AssetManager::addAssetPath(constString8& path,void** cookie)
{
......
// 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\n", overlayPath.string());
}
}
}
returntrue;
}
比如将上面的代码改成
-if (strncmp(path.string(), "/system/framework/", 18) == 0) {
+if (strncmp(path.string(), "/system/app/", 12) == 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/");
+String8 overlayPath("/system/vendor/thirds/");
overlayPath.append(path.getPathLeaf());
if (TEMP_FAILURE_RETRY(access(overlayPath.string(), R_OK)) == 0) {
然后再vendor目录下面放对应的文件
vendor/mediatek/eastaeon92_wet_tdd/artifacts/out/target/product/eastaeon92_wet_tdd/system/vendor/thirds/iReader.apk