Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Can't create handler inside thread that has not called Looper.prepare() #295

Closed
stepango opened this issue Dec 21, 2014 · 7 comments
Closed
Labels
Milestone

Comments

@stepango
Copy link

Glide init with following code takes ~30ms on nexus 5/Android 5.0.1

    Glide.get(this).register(GlideUrl.class, InputStream.class,
                new OkHttpUrlLoader.Factory(new OkHttpClient()));

I tried to init glide in background thread but get a following error

java.lang.RuntimeException: Cant create handler inside thread that has not called Looper.prepare()
            at android.os.Handler.<init>(Handler.java:200)
            at android.os.Handler.<init>(Handler.java:128)
            at com.bumptech.glide.load.engine.ResourceRecycler.<init>(ResourceRecycler.java:13)
            at com.bumptech.glide.load.engine.Engine.<init>(Engine.java:90)
            at com.bumptech.glide.load.engine.Engine.<init>(Engine.java:58)
            at com.bumptech.glide.GlideBuilder.createGlide(GlideBuilder.java:178)
            at com.bumptech.glide.Glide.get(Glide.java:147)
            at com.bandlab.bandlab.App.initGlide(App.java:60)
            at com.bandlab.bandlab.App_.access$101(App_.java:12)
            at com.bandlab.bandlab.App_$2.execute(App_.java:67)
            at org.androidannotations.api.BackgroundExecutor$Task.run(BackgroundExecutor.java:393)
            at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:422)
            at java.util.concurrent.FutureTask.run(FutureTask.java:237)
            at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$201(ScheduledThreadPoolExecutor.java:152)
            at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:265)
            at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1112)
            at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:587)
            at java.lang.Thread.run(Thread.java:818)
@sjudd
Copy link
Collaborator

sjudd commented Dec 21, 2014

Is it initializing the OkHttpClient that takes time, or the call to get()? Do you have a sample app that reproduces the problem or a trace?

I'll look in to whether registering off the main thread is/should be safe.

@stepango
Copy link
Author

Here is an example how to reproduce the problem https://gist.github.com/stepango/52e9c69186317af1e95b

@sjudd
Copy link
Collaborator

sjudd commented Dec 24, 2014

Sorry by problem I meant the 30ms delay, not the exception. Registering factories should be fast enough that a background thread isn't needed.

@stepango
Copy link
Author

Indeed, but the main problem is that many other libraries also should be initialised in the main thread so for big projects it dramatically increase app start time. Second problem here that on devices older that Nexus 5 it will be longer than 30ms. Unfortunately, I don't have any benchmarks for now.

@sjudd sjudd added this to the 3.5 milestone Dec 26, 2014
@sjudd sjudd added the bug label Dec 26, 2014
@sjudd
Copy link
Collaborator

sjudd commented Dec 26, 2014

Branched the 30ms issue into #298, I'd imagine that strict mode violation is the reason for the delay.

@sjudd sjudd closed this as completed in fad970a Dec 26, 2014
@TWiStErRob
Copy link
Collaborator

@sjudd I did a microbenchmark on my Samsung Galaxy S4 4.4.2:

@Override public void Application.onCreate() {
    Log.v("TRACE", "NOW"); ConcurrentTools.ignorantSleep(10000);

    long time1 = System.nanoTime(); Glide glide = Glide.get(this);
    long time2 = System.nanoTime(); OkHttpClient client = new OkHttpClient();
    long time3 = System.nanoTime(); Factory factory = new Factory(client);
    long time4 = System.nanoTime(); glide.register(GlideUrl.class, InputStream.class, factory);
    long time5 = System.nanoTime();

    Log.v("TRACE", "STOP"); ConcurrentTools.ignorantSleep(10000);
    Log.v("TIMING", String.format(Locale.ROOT, "Glide.get: %.2f, Glide.register: %.2f, new Client: %.2f, new Factory: %.2f",
            diff(time1, time2), diff(time4, time5), diff(time2, time3), diff(time3, time4)));
}
private float diff(long time1, long time2) { return (time2 - time1) / 1000000f; }

... and the result is:

Normal startup (5 samples):
TIMING﹕ Glide.get: 15.87, Glide.register: 0.09, new Client: 0.70, new Factory: 0.12
TIMING﹕ Glide.get: 16.60, Glide.register: 0.09, new Client: 0.76, new Factory: 0.15
TIMING﹕ Glide.get: 20.78, Glide.register: 0.12, new Client: 1.10, new Factory: 0.21
TIMING﹕ Glide.get: 23.38, Glide.register: 0.12, new Client: 1.07, new Factory: 0.21
TIMING﹕ Glide.get: 31.86, Glide.register: 0.18, new Client: 5.16, new Factory: 0.34

with Start Method Tracing:
TIMING﹕ Glide.get: 64.97, Glide.register: 0.82, new Client: 2.47, new Factory: 0.40

From the method trace: Glide.get is 92% of Glide.get().register(new(new)), however 77% is spent loading classes and the rest is mostly the lot of news in GlideBuilder.createGlide and Glide.<init>... so I guess this means that fixing the strict mode violation won't speed things up much since getPhotoCacheDir() is only 1.5 ms.

@sjudd
Copy link
Collaborator

sjudd commented Dec 26, 2014

Hmm I wonder if it's different on L. It's also possible the strict mode logging isn't particularly accurate. I added the actual stack trace to the other issue and opened #299 to track the Glide.get() timing.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

3 participants