【Android】获取其它apk资源

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;
    }

4,资源工具类

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(app)) {
                //这个是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<Integer> 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);
        }
    }
}
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值