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

[AppWidget/RemoteViews] IllegalArgumentException: You must call this method on the main thread #1363

Closed
MGaetan89 opened this issue Jul 24, 2016 · 5 comments
Labels

Comments

@MGaetan89
Copy link

Glide Version: 3.7.0
Integration libraries:

Device/Android Version: Nexus 5 Emulator, Android 6.0 (with Google APIs)
Issue details / Repro steps / Use case background:
I am trying to use Glide to load an image from the network and display it inside an app widget.
To do so, I use an AppWidgetTarget as a parameter of the into method, but it ends up in a crash because the call is made on the wrong Thread.

Glide load line / GlideModule (if any) / list Adapter code (if any):

BitmapRequestBuilder<ModelType, TranscodeType> glide = Glide.with(context)
    .load(url)
    .asBitmap()
    .diskCacheStrategy(DiskCacheStrategy.ALL)

    if (circleTransform) {
        glide.transform(new CropCircleTransformation(context))
    }

    glide.into(new AppWidgetTarget(context, remoteViews, viewId, appWidgetId))

This code runs inside a RemoteViewsFactory, in the getViewAt(int) method.

  • context is the instance of the RemoteViewsService, which created this RemoteViewsFactory
  • remoteViews is an instance of the RemoteViews class, created in the getViewAt(int) method
  • viewId is the id of the remote ImageView that should display the loaded image.

Stack trace / LogCat:

E/AndroidRuntime: FATAL EXCEPTION: Binder_1
        Process: com.mgaetan89.showsrage.dev, PID: 14272
        java.lang.IllegalArgumentException: You must call this method on the main thread
        at com.bumptech.glide.util.Util.assertMainThread(Util.java:135)
        at com.bumptech.glide.GenericRequestBuilder.into(GenericRequestBuilder.java:642)
        at com.mgaetan89.showsrage.helper.ImageLoader.load(ImageLoader.kt:39)
        at com.mgaetan89.showsrage.widget.HistoryWidgetFactory.getViewAt(HistoryWidgetFactory.kt:43)
        at android.widget.RemoteViewsService$RemoteViewsFactoryAdapter.getViewAt(RemoteViewsService.java:164)
        at com.android.internal.widget.IRemoteViewsFactory$Stub.onTransact(IRemoteViewsFactory.java:85)
        at android.os.Binder.execTransact(Binder.java:453)

I found this issue, which looks similar to mine, but it's for an older version of Glide and I didn't find anything to help me there.

@TWiStErRob
Copy link
Collaborator

TWiStErRob commented Jul 24, 2016

The last #310 (comment) is still valid. You simply need to be on the main thread which has Looper.getMainLooper(). Glide is async anyway, so even if you were able to call into, the image wouldn't be ready immediately. I think what you need is something like:

// prepare `glide` request builder
runOnUiThread({
    // execute later
    glide.into(new AppWidgetTarget(context, remoteViews, viewId, appWidgetId));
});
return remoteViews;

Look into the into(int,int) exception Sam mentioned, you'll see that it does the same.

If you know a better way, feel free to open a PR. AppWidgetTarget was contributed by someone too, it may only cover a very specific use case.

@MGaetan89
Copy link
Author

Oh, you're right. I miss-interpreted that comment.
I updated my code as follow:

BitmapRequestBuilder<ModelType, TranscodeType> glide = Glide.with(context)
    .load(url)
    .asBitmap()
    .diskCacheStrategy(DiskCacheStrategy.ALL)

    if (circleTransform) {
        glide.transform(new CropCircleTransformation(context))
    }

FutureTarget<TranscodeType> bitmap = it.into(Target.SIZE_ORIGINAL, Target.SIZE_ORIGINAL)
remoteViews.setImageViewBitmap(viewId, bitmap.get())
Glide.clear(bitmap)

But I have the following exception now:

java.util.concurrent.ExecutionException: java.io.FileNotFoundException: No such file or directory
   at com.bumptech.glide.request.RequestFutureTarget.doGet(RequestFutureTarget.java:189)
   at com.bumptech.glide.request.RequestFutureTarget.get(RequestFutureTarget.java:100)
   at com.mgaetan89.showsrage.helper.ImageLoader.load(ImageLoader.kt:41)
   at com.mgaetan89.showsrage.widget.HistoryWidgetFactory.getViewAt(HistoryWidgetFactory.kt:40)
   at android.widget.RemoteViewsService$RemoteViewsFactoryAdapter.getViewAt(RemoteViewsService.java:164)
   at com.android.internal.widget.IRemoteViewsFactory$Stub.onTransact(IRemoteViewsFactory.java:85)
   at android.os.Binder.execTransact(Binder.java:453)
Caused by: java.io.FileNotFoundException: No such file or directory
   at android.os.Parcel.openFileDescriptor(Native Method)
   at android.os.ParcelFileDescriptor.openInternal(ParcelFileDescriptor.java:270)
   at android.os.ParcelFileDescriptor.open(ParcelFileDescriptor.java:200)
   at android.content.ContentResolver.openAssetFileDescriptor(ContentResolver.java:937)
   at android.content.ContentResolver.openAssetFileDescriptor(ContentResolver.java:865)
   at com.bumptech.glide.load.data.FileDescriptorLocalUriFetcher.loadResource(FileDescriptorLocalUriFetcher.java:21)
   at com.bumptech.glide.load.data.FileDescriptorLocalUriFetcher.loadResource(FileDescriptorLocalUriFetcher.java:14)
   at com.bumptech.glide.load.data.LocalUriFetcher.loadData(LocalUriFetcher.java:44)
   at com.bumptech.glide.load.model.ImageVideoModelLoader$ImageVideoFetcher.loadData(ImageVideoModelLoader.java:83)
   at com.bumptech.glide.load.model.ImageVideoModelLoader$ImageVideoFetcher.loadData(ImageVideoModelLoader.java:53)
   at com.bumptech.glide.load.engine.DecodeJob.decodeSource(DecodeJob.java:170)
   at com.bumptech.glide.load.engine.DecodeJob.decodeFromSource(DecodeJob.java:128)
   at com.bumptech.glide.load.engine.EngineRunnable.decodeFromSource(EngineRunnable.java:122)
   at com.bumptech.glide.load.engine.EngineRunnable.decode(EngineRunnable.java:101)
   at com.bumptech.glide.load.engine.EngineRunnable.run(EngineRunnable.java:58)
   at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:423)
   at java.util.concurrent.FutureTask.run(FutureTask.java:237)
   at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1113)
   at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:588)
   at java.lang.Thread.run(Thread.java:818)
   at com.bumptech.glide.load.engine.executor.FifoPriorityThreadPoolExecutor$DefaultThreadFactory$1.run(FifoPriorityThreadPoolExecutor.java:118)

@TWiStErRob
Copy link
Collaborator

Did you double-check that the Uri is correct and it exists? Glide can't do anything if you pass in invalid models, it only reads what you tell it. You probably had the same exception, but .get() throws it out, while in an async load you only get notified in a .listener. See https://github.com/bumptech/glide/wiki/Debugging-and-Error-Handling

@MGaetan89
Copy link
Author

The URL is correct, because it works fine in the application itself.
But I guess there is an issue when I run my application in the emulator. Because if I remove the widget and add it again, it works fine.
Thanks for your help and information!

@ygl750994280
Copy link

How to resole this problem?

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