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());
К сожалению, нет никакого контекста относительно этого предупреждения, поэтому я не знаю, что вызывает эту проблему и как я могу решить ее.
2 ответа
Исходная информация
Сообщение означает, что 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()
на манекене загрузчик класса.
Я получил это предупреждение без android:sharedUserId или android:process в моем манифесте...
Нашел только представленный на эмуляторе...
Устройства не представили предупреждающее сообщение. Протестировано на Smartphone KitKat 4.4 API 19 и Tablet 5.0.1 API 21.