【Android】获取其它apk资源
时间:2023-01-22 21:00:00
1,前置条件
path:apk路径
packageName:apk包名
2.如何获得?
绑定服务时可传参;
或进程间主动通信获取;
3,核心注入类
(1)核心思想是把握ContextImpl中mResources替换我们定制的拦截类别(ResourcesProxy),拦截操作可以实现。而ContextImpl位于ContextWrapper中mBase成员,因为Application是ContextWrapper因此,子类可以通过反射获得ContextImpl,进而实现依赖注入,完成代理操作。
(2)其次,还有其他需要apk路径传给AssetManager,即AssetManager#addAssetPath(String)传输路径的方法。我们可以看到源代码。
跟进到#addAssetPathInternal,传入overlay参数为false,继续跟进#ApkAssets.loadFromPath()
private int addAssetPathInternal(String path, boolean overlay, boolean appAsLib) { Objects.requireNonNull(path, "path"); synchronized (this) { ensureOpenLocked(); final int count = mApkAssets.length; // See if we already have it loaded. for (int i = 0; i < count; i ) { if (mApkAssets[i].getAssetPath().equals(path)) { return i 1; } } final ApkAssets assets; try { if (overlay) { // TODO(b/70343104): This hardcoded path will be removed once // addAssetPathInternal is deleted. final String idmapPath = "/data/resource-cache/" path.substring(1).replace('/', '@') "@idmap"; assets = ApkAssets.loadOverlayFromPath(idmapPath, 0 /* flags */); } else { assets = ApkAssets.loadFromPath(path, appAsLib ? ApkAssets.PROPERTY_DYNAMIC : 0); } } catch (IOException e) { return 0; } mApkAssets = Arrays.copyOf(mApkAssets, count 1); mApkAssets[count] = assets; nativeSetApkAssets(mObject, mApkAssets, true); invalidateCachesLocked(-1); return count 1; } }
(3)跟进#ApkAssets.loadFromPath(),创建ApkAssets类,进而在native层加载path路径中apk资源,
private ApkAssets(@FormatType int format, @NonNull String path, @PropertyFlags int flags, @Nullable AssetsProvider assets) throws IOException { Objects.requireNonNull(path, "path"); mFlags = flags; mNativePtr = nativeLoad(format, path, flags, assets); mStringBlock = new StringBlock(nativeGetStringBlock(mNativePtr), true /*useSparse*/); mAssets = assets; }
四、资源工具类
package com.zjw.demo.util; import android.content.Context; import android.content.res.AssetFileDescriptor; import android.content.res.AssetManager; import android.content.res.Resources; import android.graphics.drawable.Drawable; import android.util.TypedValue; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import java.io.InputStream; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.util.HashSet; import java.util.Set; public final class ResourcesHelper { private static final String TAG = "ResourcesUtils"; private static Resources sOtherResources = null; private static AssetManager sOtherAssetManager = null; private static final int INVALID_ID = -1; private static String sOtherPackageName; public static void inject(Context context, String path, String otherPackageName) { try { //创建assetManager sOtherAssetManager = AssetManager.class.newInstance();R Method addAssetPath = sOtherAssetManager.getClass().getMethod("addAssetPath", String.class); addAssetPath.invoke(sOtherAssetManager, path); //创建resources Context app = context.getApplicationContext(); sOtherResources = new Resources(sOtherAssetManager, app.getResources().getDisplayMetrics(), app.getResources().getConfiguration()); //获取包名 sOtherPackageName = otherPackageName; ///注入代理 injectResourcesProxy(app); } catch (Exception e) { Log.e(TAG, e.getMessage()); } } private static void injectResourcesProxy(Context app) { try { Class> contextImplClass = Class.forName("android.app.ContextImpl"); Class> contextWrapperClass = Class.forName("android.content.ContextWrapper"); if (contextImplClass.isInstance(app)) { Field mResources = app.getClass().getDeclaredField("mResources"); mResources.setAccessible(true); Resources proxyResources = new ProxyResources(app); mResources.set(app, proxyResources); Log.d(TAG, "inject resources success"); } else if (contextWrapperClass.isInstance(pp)) {
//这个是ContextImpl
Field contextImpl = contextWrapperClass.getDeclaredField("mBase");
contextImpl.setAccessible(true);
Object contextImplObj = contextImpl.get(app);
if (contextImplObj == null) {
Log.e(TAG, "check android version :contextImplObj == null");
return;
}
Field mResources = contextImplObj.getClass().getDeclaredField("mResources");
mResources.setAccessible(true);
Resources proxyResources = new ProxyResources(app);
mResources.set(contextImplObj, proxyResources);
Log.d(TAG, "inject resources success");
}
} catch (Exception e) {
Log.e(TAG, e.getMessage());
}
}
private static int getResourcesId(String resName, String defType) {
if (sOtherResources == null) {
return INVALID_ID;
}
return sOtherResources.getIdentifier(resName, defType, sOtherPackageName);
}
public static AssetManager getOtherAssetManager() {
return sOtherAssetManager;
}
public static Drawable getDrawable(String resName, String defType) {
int resourcesId = getResourcesId(resName, defType);
if (resourcesId == INVALID_ID) {
return null;
}
return sOtherResources.getDrawable(resourcesId);
}
public static int getColor(String resName, String defType) {
int resourcesId = getResourcesId(resName, defType);
if (resourcesId == INVALID_ID) {
return 0;
}
return sOtherResources.getColor(resourcesId);
}
/**
* 资源文件静态代理对象
*/
private static class ProxyResources extends Resources {
private final Set ids = new HashSet<>();
public ProxyResources(Context context) {
super(context.getAssets(), context.getResources().getDisplayMetrics(), context.getResources().getConfiguration());
}
@Override
public Drawable getDrawable(int id, @Nullable Theme theme) throws NotFoundException {
if (sOtherResources != null && ids.contains(id)) {
try {
Drawable drawable = sOtherResources.getDrawable(id, theme);
if (drawable != null) {
return drawable;
}
} catch (NotFoundException e) {
Log.d(TAG, e.getMessage());
}
}
return super.getDrawable(id, theme);
}
@Override
public int getColor(int id, @Nullable Theme theme) throws NotFoundException {
if (sOtherResources != null && ids.contains(id)) {
try {
int color = sOtherResources.getColor(id, theme);
if (color != 0) {
return color;
}
} catch (NotFoundException e) {
Log.d(TAG, e.getMessage());
}
}
return super.getColor(id, theme);
}
@Override
public int getColor(int id) throws NotFoundException {
if (sOtherResources != null && ids.contains(id)) {
try {
int color = sOtherResources.getColor(id);
if (color != 0) {
return color;
}
} catch (NotFoundException e) {
Log.d(TAG, e.getMessage());
}
}
return super.getColor(id);
}
@Override
public Drawable getDrawable(int id) throws NotFoundException {
if (sOtherResources != null && ids.contains(id)) {
try {
Drawable drawable = sOtherResources.getDrawable(id);
if (drawable != null) {
return drawable;
}
} catch (NotFoundException e) {
Log.d(TAG, e.getMessage());
}
}
return super.getDrawable(id);
}
@Override
public int getIdentifier(String name, String defType, String defPackage) {
if (sOtherResources != null && ids.contains(id)) {
int resId = sOtherResources.getIdentifier(name, defType, sOtherPackageName);
if (resId != 0) {
ids.add(resId);
return resId;
}
}
return super.getIdentifier(name, defType, defPackage);
}
@Override
public AssetFileDescriptor openRawResourceFd(int id) throws NotFoundException {
if (sOtherResources != null && ids.contains(id)) {
return sOtherResources.openRawResourceFd(id);
}
return super.openRawResourceFd(id);
}
@NonNull
@Override
public InputStream openRawResource(int id, TypedValue value) throws NotFoundException {
if (sOtherResources != null && ids.contains(id)) {
return sOtherResources.openRawResource(id, value);
}
return super.openRawResource(id, value);
}
}
}