Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
526 views
in Technique[技术] by (71.8m points)

multithreading - Android: "Class loader may fail for processes that host multiple applications"

What does this message in Eclipse's logcat for Android mean?

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());

Unfortunately, there is no context given as to this warning, so I don't know what causes this problem and how I can resolve it.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

Background information

The message means that Android has setup a dummy ClassLoader with Thread.currentThread().setContextClassLoader(), and something tries to use that dummy class loader. The something can be a lot of things, it's hard to tell exactly what from the information given. There is a trick you can try though, see below. Anyway, Android sets up the dummy class loader when there is a risk that the process might contain code from more than one APK. More specifically, Android looks in your manifest if you have used android:sharedUserId:

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

or if you run in a non-standard android:process

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

How to get rid of the warning

There are two things you can do to get rid of the warning:

  1. Don't use android:sharedUserId or android:process
  2. Explicitly set what APK ClassLoader to use before running any other code

To go with solution 2, there are some key insights you need. First, for any class AnyClass in an APK, AnyClass.class.getClassLoader() will return the same ClassLoader. Second,

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

is the same as

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

Third, you need to call Thread.currentThread().setContextClassLoader(getClass().getClassLoader()) before the code that calls Thread.currentThread().getContextClassLoader(). Fourth, When many APKs are involved, you need to call Thread.setContextClassLoader(getClass().getClassLoader()) after the last APK has been loaded (otherwise loading the last APK will overwrite what you have set manually). Because of that, it would be a good idea to find out who is using the context class loader in you case by using the below debug trick. Then, right before that, you call Thread.setContextClassLoader(getClass().getClassLoader()) for a class from the desired APK, typically the APK that is loaded first (or, in the case when only one APK is involved, that APK ;). Fifth, the context class loader is per thread, which you need to keep in mind if your application is multi-threaded.

Debug trick

If you want to find out what code that calls ClassLoader.getResources(), this should work:

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

if you do this early enough, you should see in the logcat a stack trace that goes back to whoever calls getResources() on the dummy class loader.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...