Android, как использовать DexClassLoader для динамической замены Activity или Service

Я пытаюсь сделать что-то похожее на эту qaru.site/info/205029/.... Что я хочу сделать, так это прочитать определение активности или услуги с SD-карты. Чтобы избежать явных проблем с разрешениями, я создаю оболочную версию этого действия в .apk, но пытаюсь заменить его активностью с тем же именем, который находится на SD-карте во время выполнения. К сожалению, я могу загрузить определение класса активности с SD-карты с помощью DexClassLoader, но исходное определение класса - это тот, который выполняется. Есть ли способ указать, что новое определение класса заменяет старый, или какие-либо предложения по устранению проблем с разрешениями манифеста без фактического предоставления необходимой активности в пакете? Пример кода:

    ClassLoader cl = new DexClassLoader("/sdcard/mypath/My.apk",
            getFilesDir().getAbsolutePath(),
            null,
            MainActivity.class.getClassLoader());

    try {
        Class<?> c = cl.loadClass("com.android.my.path.to.a.loaded.activity");
        Intent i = new Intent(getBaseContext(), c);
        startActivity(i);
    }
    catch (Exception e) {

Интегрированный запуск com.android.my.path.to.a.loaded.activity, указанный в /sdcard/mypath/My.apk, запускает активность, статически загруженную в проект.

Ответы

Ответ 1

Запуск Activity через DexClassLoader будет очень сложным, потому что вы не установили APK, и поэтому вам нечего справиться с вашим намерением.

У вас должен быть тот же класс активности в вашей APK платформы и объявить его в AndroidManifest.xml. Затем вы должны изменить текущий ClassLoader на желаемый DexClassLoader. В результате он запустит ваш плагин APK. (Примечание: "APK платформы" относится к приложению, которое уже установлено в телефоне, тогда как "плагин APK" относится к файлу apk, сохраненному на вашей SD-карте.)

Приложение платформы должно выглядеть примерно так:

public static ClassLoader ORIGINAL_LOADER;
public static ClassLoader CUSTOM_LOADER = null;

@Override
public void onCreate() {
    super.onCreate();
    try {
        Context mBase = new Smith<Context>(this, "mBase").get();

        Object mPackageInfo = new Smith<Object>(mBase, "mPackageInfo")
                .get();
        //get Application classLoader
        Smith<ClassLoader> sClassLoader = new Smith<ClassLoader>(
                mPackageInfo, "mClassLoader");
        ClassLoader mClassLoader = sClassLoader.get();
        ORIGINAL_LOADER = mClassLoader;

        MyClassLoader cl = new MyClassLoader(mClassLoader);
        //chage current classLoader to MyClassLoader
        sClassLoader.set(cl);
    } catch (Exception e) {
        e.printStackTrace();
    }
}

class MyClassLoader extends ClassLoader {
    public MyClassLoader(ClassLoader parent) {
        super(parent);
    }

    @Override
    public Class<?> loadClass(String className)
            throws ClassNotFoundException {
        if (CUSTOM_LOADER != null) {
            try {
                Class<?> c = CUSTOM_LOADER.loadClass(className);
                if (c != null)
                    return c;
            } catch (ClassNotFoundException e) {
            }
        }
        return super.loadClass(className);
    }
}

Для получения большего количества кода вы можете посетить https://github.com/Rookery/AndroidDynamicLoader

Я читаю исходный код Android, чтобы найти более элегантный метод для реализации этой функции. Если у вас есть идеи, не стесняйтесь обращаться ко мне.