Подтвердить что ты не робот

Android: "загрузчик классов может не работать для процессов с несколькими приложениями"

Что означает это сообщение в Eclipse logcat для Android?

W/ActivityThread: ClassLoader.getResources: The class loader returned by Thread.getContextClassLoader() may fail for processes that host multiple applications. You should explicitly specify a context class loader. For example: Thread.setContextClassLoader(getClass().getClassLoader());

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

4b9b3361

Ответ 1

Фоновая информация

Сообщение означает, что Android установил манекен ClassLoader с помощью Thread.currentThread().setContextClassLoader(), и что-то пытается использовать этот загрузочный загрузчик. Что-то может быть много, трудно точно сказать, что из приведенной информации. Есть трюк, который вы можете попробовать, см. Ниже. В любом случае, Android устанавливает загрузчик фиктивного класса, если существует риск того, что процесс может содержать код из нескольких APK. Более конкретно, Android выглядит в вашем манифесте, если вы использовали android:sharedUserId:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    ...
    android:sharedUserId="triggers.dummy.loader" >

или если вы запускаете нестандартный android:process

    <application android:process="triggers.dummy.loader">

Как избавиться от предупреждения

Есть две вещи, которые вы можете сделать, чтобы избавиться от предупреждения:

  • Не используйте android:sharedUserId или android:process
  • Явным образом задайте APK ClassLoader для использования перед запуском любого другого кода.

Чтобы пойти с решением 2, вам нужны некоторые ключевые идеи. Во-первых, для любого класса AnyClass в APK, AnyClass.class.getClassLoader() вернет тот же ClassLoader. Во-вторых,

AnyClass obj = new AnyClass();
Thread.currentThread().setContextClassLoader(obj.getClass().getClassLoader())

совпадает с

Thread.currentThread().setContextClassLoader(AnyClass.class.getClassLoader())

В-третьих, вам нужно вызвать Thread.currentThread().setContextClassLoader(getClass().getClassLoader()) перед кодом, который вызывает Thread.currentThread().getContextClassLoader(). В-четвертых, когда задействовано много APK, вам нужно позвонить Thread.setContextClassLoader(getClass().getClassLoader()) после того, как был загружен последний APK (в противном случае загрузка последнего APK перезапишет то, что вы установили вручную). Из-за этого было бы неплохо узнать, кто использует загрузчик контекстного класса в вашем случае, используя нижеприведенный трюк отладки. Затем, прямо перед этим, вы вызываете Thread.setContextClassLoader(getClass().getClassLoader()) для класса из требуемого APK, обычно APK, который загружается первым (или, в случае, когда задействован только один APK, этот APK;). В-пятых, загрузчик контекстного класса относится к потоку, который вам нужно иметь в виду, если ваше приложение многопоточное.

Трюк отладки

Если вы хотите узнать, какой код вызывает ClassLoader.getResources(), это должно работать:

Thread.currentThread().setContextClassLoader(new ClassLoader() {
    @Override
    public Enumeration<URL> getResources(String resName) throws IOException {
        Log.i("Debug", "Stack trace of who uses " +
                "Thread.currentThread().getContextClassLoader()." +
                "getResources(String resName):", new Exception());
        return super.getResources(resName);
    }
});

если вы сделаете это достаточно рано, вы должны увидеть в logcat трассировку стека, которая возвращается к тому, кто называет getResources() в загрузчике фиктивного класса.

Ответ 2

Я получил это предупреждение без андроида: sharedUserId или android: процесс в моем манифесте...

Нашел его только на эмуляторе...

Устройства не представили предупреждающее сообщение. Протестировано на смартфоне KitKat 4.4 API 19 и Tablet 5.0.1 API 21.