Skip to content

The application may be doing too much work on its main thread. #84

Closed
@ultimaters

Description

@ultimaters

I used SimpleDraweeView in adapter, but my app runs slowly, and I saw the log below:

I/Choreographer(1378): Skipped 55 frames! The application may be doing too much work on its main thread.

so I comment the code mSimpleDraw.setImageUri, the app runs fluently. I think the SimpleDraw blocks the main thread when fetching image from network.

Activity

plamenko

plamenko commented on Apr 3, 2015

@plamenko
Contributor

Are you using OkHttp networking stack?
There is an issue with OkHttp networking stack that it cancels requests on the calling thread (which is going to be the main thread). We have a fix ready for that.
Other than that, SimpleDraweeView should not be doing anything heavy on the main thread. If you give us more context on how you use Fresco, we may be able to isolate the issue more easily.

ultimaters

ultimaters commented on Apr 4, 2015

@ultimaters
Author

@plamenko I just used in a CircleView Adapter and load image, very simple like the tutorial.
in Application: Fresco.initialize(mContext);
in adapter: mImageView.setImageURI(Uri.parse(data.getImage()));
that's it.

plamenko

plamenko commented on Apr 4, 2015

@plamenko
Contributor

What's CircleView Adapter?

mikeknoop

mikeknoop commented on Apr 6, 2015

@mikeknoop

The application may be doing too much work on its main thread.

Hitting the same problem here. Pretty basic usage of SimpleDraweeView in a GridView through an adapter:

// MainActivity.java
Fresco.initialize(this);
GridView gv = findViewById(R.id.grid_view);
ImageAdapter ia = new ImageAdapter(this);
gv.setAdapter(ia);
// ImageAdapter.java
public class ImageAdapter extends BaseAdapter {
   ...
   public View getView(int position, View convertView, final ViewGroup parent) {
        if (convertView == null) {
            convertView = LayoutInflater.from(mContext).inflate(R.layout.grid_item, null);
            convertView.setTag(convertView.findViewById(R.id.grid_item_image_view));
        }
        SimpleDraweeView view = (SimpleDraweeView) convertView.getTag();

        // with the following line: major UI slowdown, "the application may be doing too much work on its main thread."
        // without the following line: performant as expected
        view.setImageURI(Uri.parse("content://media/external/images/media/172699");

        return view;
   }
   ...
}
<!-- grid_item.xml -->
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:fresco="http://schemas.android.com/apk/res-auto"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent">

    <com.facebook.drawee.view.SimpleDraweeView
        android:id="@+id/grid_item_image_view"
        android:layout_width="150dp"
        android:layout_height="150dp"
        />

</LinearLayout>

It seems that any parallel calls to setImageURI result in the slowdown.

ultimaters

ultimaters commented on Apr 6, 2015

@ultimaters
Author

@plamenko My code just the same as above, in an adapter

plamenko

plamenko commented on Apr 6, 2015

@plamenko
Contributor

I'll investigate

self-assigned this
on Apr 6, 2015
mikeknoop

mikeknoop commented on Apr 9, 2015

@mikeknoop

Replacing

view.setImageURI(Uri.parse("content://...");

with

ImageRequest imageRequest =
        ImageRequestBuilder.newBuilderWithSource(Uri.parse("content://...")
                .setResizeOptions(
                        new ResizeOptions(150, 150))
                .build();
DraweeController draweeController = Fresco.newDraweeControllerBuilder()
        .setImageRequest(imageRequest)
        .setOldController(view.getController())
        .setAutoPlayAnimations(true)
        .build();
view.setController(draweeController);

in my code above fixes the performance problem. If I comment out the setResizeOptions line the performance problem comes back. Perhaps something to do with rescaling on the main thread?

mikeknoop

mikeknoop commented on Apr 9, 2015

@mikeknoop

FWIW I can also hack the Fresco Sample app to Skipped 129 frames! by taking the opposite approach to my last comment:

Replace these lines with

Uri uri = getRandomLocalContentURI();
view.setImageURI(uri);

I've found it only hits performance problems dealing with local content:// URIs. http:// do just fine.

plamenko

plamenko commented on Apr 9, 2015

@plamenko
Contributor

That's really helpful information, thanks!

plamenko

plamenko commented on Apr 10, 2015

@plamenko
Contributor

@fuwaneko , yeah OkHttp cancellation is a known issue and we have a fix for that. However, local content uri is something we yet need to figure.

mikeknoop

mikeknoop commented on Apr 12, 2015

@mikeknoop

One other potential/related clue. Even when using my workaround above (which fixed the performance problem), I still see one error line for every content://... image that is loaded:

04-11 22:00:25.677  29554-29592/com.example.app E/JHEAD﹕ can't open '/external/images/media/172693'
04-11 22:00:25.677  29554-29592/com.example.app E/JHEAD﹕ can't open '/external/images/media/172692'
04-11 22:00:25.677  29554-29592/com.example.app E/JHEAD﹕ can't open '/external/images/media/172691'

The image still successfully loads but an error is generated for each.

The fix from this Picasso thread is to include file:// in front, presumably something that Fresco needs to do internally or an upstream lib needs to be doing.

plamenko

plamenko commented on Apr 17, 2015

@plamenko
Contributor

I deleted the two comments related to OkHttp cancellation as they are not related to this issue and has already been fixed.

plamenko

plamenko commented on Apr 17, 2015

@plamenko
Contributor

@mikeknoop , I missed part of your commend earlier, where you said "If I comment out the setResizeOptions line the performance problem comes back."
Can you please check what are the dimensions of those local images? If the image is much bigger than the view than there is going to be a noticeable overhead unless we resize the image first. Resize has to be explicitly specified as calling setImageURI won't do that.

2 remaining items

mikeknoop

mikeknoop commented on Apr 18, 2015

@mikeknoop

Thanks for the info. I imagine Camera images are the 80% use case for content:// URIs, maybe it'd make sense to do resizing in setImageURI.

I feel like I'm really hacking things to get Fresco to work:

ImageRequestBuilder.newBuilderWithSource(mCameraImages.getUris().get(position))
  // I tried this option too
  //.setLocalThumbnailPreviewsEnabled(false)
  .setResizeOptions(new ResizeOptions(1, 1))
  .build();

Resizing to 1x1 seems to pick some minimum size that is suitable. I should add that I haven't been able to beat Picasso w.r.t. loading performance for content:// images yet (and Picasso is pretty slow itself which is why I was checking out Fresco).

plamenko

plamenko commented on Apr 18, 2015

@plamenko
Contributor

You should definitely specify resizing, butResizeOptions(1, 1) doesn't seem right, why don't you pass the view's dimensions?
Btw, if you do .setLocalThumbnailPreviewsEnabled(true) with .setResizeOptions(new ResizeOptions(1, 1)), and also do setControllerListener with the following listener, what do you get in the adb logcat?

ControllerListener listener = new BaseControllerListener<ImageInfo>() {
  @Override
  public void onIntermediateImageSet(String id, ImageInfo imageInfo) {
    android.util.Log.w(TAG, "intermediate: width " + imageInfo.getWidth() + ", " + "height " + imageInfo.getHeight());
  }
  @Override
  public void onFinalImageSet(String id, ImageInfo imageInfo, Animatable a) {
    android.util.Log.w(TAG, "final: width " + imageInfo.getWidth() + ", " + "height " + imageInfo.getHeight());
  }
};
avenwu

avenwu commented on Jul 14, 2015

@avenwu

Same issue, after using SimpleDraweeView:

The application may be doing too much work on its main thread;

In my case I have init the Fresco in Application, in the adapter use setImageUrl, then the app is closing to ANR with few response;
The issue can be fixed setting ResizeOptions to SimpleDraweeView mentioned by @mikeknoop

ImageRequest request = ImageRequestBuilder.newBuilderWithSource(Uri.parse(data.image))
                .setResizeOptions(new ResizeOptions(width, height))
                .build();
        holder.pic.setController(Fresco.newDraweeControllerBuilder()
                .setOldController(holder.pic.getController())
                .setImageRequest(request)
                .build());
ohshi000

ohshi000 commented on Jul 23, 2015

@ohshi000

I just get the width and height when setImageUri()

public void setImageURI(Uri uri, @nullable Object callerContext) {
if (!hasHierarchy()) {
inflateHierarchy(getContext());
}
DraweeController controller;
int width = getLayoutWidth() , height = getLayoutHeight();
if (this.mSimpleDraweeControllerBuilder instanceof PipelineDraweeControllerBuilder
&& width>0 && height >0) {
ImageRequest request = ImageRequestBuilder.newBuilderWithSource(uri)
.setResizeOptions(new ResizeOptions(width, height))
.setAutoRotateEnabled(true)
.build();
PipelineDraweeControllerBuilder builder =
(PipelineDraweeControllerBuilder) mSimpleDraweeControllerBuilder;
controller = builder
.setAutoPlayAnimations(true)
.setCallerContext(callerContext)
.setOldController(this.getController())
.setImageRequest(request)
.build();

    } else
        controller = this.mSimpleDraweeControllerBuilder
                .setCallerContext(callerContext)
                .setUri(uri)
                .setOldController(this.getController())
                .build();

    this.setController(controller);

/**
* {@inheritdoc}
*


* Width is defined by target {@link android.view.View view} parameters, configuration
* parameters or device display dimensions.

* Size computing algorithm (go by steps until get non-zero value):

* 1) Get the actual drawn getWidth() of the View

* 2) Get layout_width
*/
public int getLayoutWidth() {
final ViewGroup.LayoutParams params = getLayoutParams();
int width = 0;
if (params != null && params.width != ViewGroup.LayoutParams.WRAP_CONTENT) {
width = getWidth(); // Get actual image width
}
if (width <= 0 && params != null) width = params.width; // Get layout width parameter
if (width <= 0) {
width = getImageViewFieldValue(this, "mMaxWidth"); // Check maxWidth parameter
}
return width;
}

/**
 * {@inheritDoc}
 * <p/>
 * Height is defined by target {@link android.view.View view} parameters, configuration
 * parameters or device display dimensions.<br />
 * Size computing algorithm (go by steps until get non-zero value):<br />
 * 1) Get the actual drawn <b>getHeight()</b> of the View<br />
 * 2) Get <b>layout_height</b>
 */
public int getLayoutHeight() {
    final ViewGroup.LayoutParams params = getLayoutParams();
    int height = 0;
    if (params != null && params.height != ViewGroup.LayoutParams.WRAP_CONTENT) {
        height = getHeight(); // Get actual image height
    }
    if (height <= 0 && params != null) height = params.height; // Get layout height parameter
    if (height <= 0) {
        height = getImageViewFieldValue(this, "mMaxHeight"); // Check maxHeight parameter
    }
    return height;
}

private static int getImageViewFieldValue(Object object, String fieldName) {
    int value = 0;
    try {
        Field field = ImageView.class.getDeclaredField(fieldName);
        field.setAccessible(true);
        int fieldValue = (Integer) field.get(object);
        if (fieldValue > 0 && fieldValue < Integer.MAX_VALUE) {
            value = fieldValue;
        }
    } catch (Exception e) {
    }
    return value;
}
tyronen

tyronen commented on Oct 14, 2015

@tyronen
Contributor

Setting ResizeOptions is indeed the correct solution. The reason frames were skipped is that Android was struggling to scale such a large image in a small view.

At most we could make this automatic some of the time - when the view is specified with an explicit size - but that won't work for match_parent type layouts.

SarthakM9

SarthakM9 commented on Oct 27, 2015

@SarthakM9

Hello,

I got entangled in a similar problem where i was using SimpleDraweeView as a GridView row, to parse some http uri's. All images were > 5mb and were being loaded in memory without getting resized automatically (which was causing Out of Memory Error).

@mikeknoop 's solution works but writing such code in getView() seems an inefficient approach.

So, i created my version of SimpleDraweeView, following are the steps i used:

  1. Copy/Paste SimpleDraweeView in your package and rename it to something else (I renamed it to TweakedDraweeView)

  2. Change the following method as follows:

public void setImageURI(Uri uri, Object callerContext)
{
    ImageRequest imageRequest = ImageRequestBuilder.newBuilderWithSource(uri).setResizeOptions(new ResizeOptions(getLayoutParams().width, getLayoutParams().height)).build();
    DraweeController draweeController = Fresco.newDraweeControllerBuilder().setImageRequest(imageRequest).setOldController(getController()).setAutoPlayAnimations(true).build();
    setController(draweeController);
// exactly same as mikeknoop's
}
  1. Step 2 alone won't work because it would throw an exception saying that the SimpleDraweeView has not been initialized, although you have renamed it, but Fresco still seems to look for SimpleDraweeView.
    The solution for this problem is to add an extra line where you initialized Fresco(probably in Application class)
 TweakedDraweeView.initialize(new PipelineDraweeControllerBuilderSupplier(this));      //since i named my SimpleDraweeView version to TweakedDraweeView

that's all!

saantiaguilera

saantiaguilera commented on Aug 2, 2016

@saantiaguilera

Hi ! I had the same problem and what fixed it was to
setLocalThumbnailPreview(FALSE).

Regarding this issue, can someone from fresco answer me these questions:

  • How does resizing work in case the image is a .png ? Ive seen the docs that it currently only supports jpg, but how does it handle other types (Since i have tested with some really big pngs and i dont see any problem at all, which concerns me more haha)
  • Same goes for resizing but when an image is tiny and the resizing is large (eg. img of 50x50 resized to 200x200) does my app crash or it just ignores this?
  • Resizing works in a separate thread right ? (I really, really, really hope so :P ), what about the postprocessor overridable methods (process(Bitmap) and derivates) ? if other people find this: From documentation: ExecutorSupplier.forBackgroundTasks() -> used for background tasks such as image transcoding, resizing, rotating and post processing. So this four operations are done in a separate job
OlukaDenis

OlukaDenis commented on Aug 30, 2018

@OlukaDenis

I also thave the same issue, my app has images and uses fragments and adapters but it is too slow.
Then shows "The application may be doing too much work on its main thread."

This is how my adpater looks like;
`public class UniversityAdapter extends ArrayAdapter {

public UniversityAdapter(@NonNull Context context, ArrayList<University> wordArrayList) {
    super(context, 0, wordArrayList);
}

@NonNull
@Override
public View getView(int position, View convertView, @NonNull ViewGroup parent) {

    View listViewItem = convertView;

    if (listViewItem == null) {
        listViewItem = LayoutInflater.from(getContext()).inflate(R.layout.university_list_layout, parent, false);
    }

    University currentUniv = getItem(position);

    TextView nameTextView = (TextView) listViewItem.findViewById(R.id.univ_name);
    nameTextView.setText(currentUniv.getName());

    TextView descTextView = (TextView) listViewItem.findViewById(R.id.unive_description);
    descTextView.setText(currentUniv.getDescription());

    TextView websiteTextView = (TextView) listViewItem.findViewById(R.id.univ_website);
    websiteTextView.setText(currentUniv.getWebsite());

    ImageView imageView = (ImageView) listViewItem.findViewById(R.id.univ_image);
    imageView.setImageResource(currentUniv.getImageResourceId());

   return listViewItem;
}

}`

An the fragment looks like this;
`public class UniversityFragment extends android.support.v4.app.Fragment {

public UniversityFragment() {
    // Required empty public constructor
}


@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
                         Bundle savedInstanceState) {
   View rootView = inflater.inflate(R.layout.grid_view_list, container, false);

    ArrayList<University> universities = new ArrayList<University>();
    universities.add(new University( "Ndejje University","Hotel Africana is Uganda\'s Premier luxury Hotel. The hotel has over 14 fully furnished well air-conditioned",
             "www.africana.com", R.drawable.hotelafricana));
    universities.add(new University( "Makerere University","Hotel Africana is Uganda\'s Premier luxury Hotel. The hotel has over 14 fully furnished well air-conditioned",
            "www.africana.com", R.drawable.fadingwest));
    universities.add(new University( "IUEA","Hotel Africana is Uganda\'s Premier luxury Hotel. The hotel has over 14 fully furnished well air-conditioned",
            "www.africana.com", R.drawable.hotelafricana));
    universities.add(new University( "Uganda Christian Univesity","Hotel Africana is Uganda\'s Premier luxury Hotel. The hotel has over 14 fully furnished well air-conditioned",
            "www.africana.com", R.drawable.fadingwest));
    universities.add(new University( "Kyambogo University","Hotel Africana is Uganda\'s Premier luxury Hotel. The hotel has over 14 fully furnished well air-conditioned",
            "www.africana.com", R.drawable.hotelafricana));


    UniversityAdapter universityAdapter = new UniversityAdapter(getActivity(), universities);
    GridView gridView = (GridView) rootView.findViewById(R.id.list);
    gridView.setAdapter(universityAdapter);

   return rootView;
}

}`

Please what could be wrong?

vinit-poojary

vinit-poojary commented on Sep 11, 2018

@vinit-poojary
adkhamjonDev

adkhamjonDev commented on Jun 13, 2021

@adkhamjonDev

need help
<< The application may be doing too much work on its main thread >>
And here is my code
ViewModel->
class YoutubeViewModel(apiService: ApiService):ViewModel() {
val youtubeLiveData=MutableLiveData<Resource>()
private val youtubeRepository=YoutubeRepository(apiService)
init {
getVideos()
}
private fun getVideos() {
viewModelScope.launch {
youtubeLiveData.postValue(Resource.loading(null))
youtubeRepository.getYoutubeData()
.catch {e->
youtubeLiveData.postValue(Resource.error(e.message?:"Error",null))
}.collect {
youtubeLiveData.postValue(Resource.success(it))
}
}
}
@JvmName("getYoutubeLiveData1")
fun getYoutubeLiveData():MutableLiveData<Resource>{
return youtubeLiveData
}
}
And in MainFragment
youtubeViewModel=ViewModelProviders.of(this,ViewModelFactory(ApiClient.apiService))[YoutubeViewModel::class.java]

    youtubeViewModel.getYoutubeLiveData().observe(viewLifecycleOwner,{
        when(it.status){
            Status.ERROR->{
                binding.progress.visibility=View.INVISIBLE
                Toast.makeText(requireContext(), it.message, Toast.LENGTH_SHORT).show()
            }
            Status.LOADING->{
                binding.progress.visibility=View.VISIBLE
            }
            Status.SUCCESS->{
                binding.progress.visibility=View.INVISIBLE
                videoAdapter.submitList(it.data?.items)
                Log.d(TAG, "onCreateView: ${it.data?.items}")
            }
        }
    })
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

      Development

      No branches or pull requests

        Participants

        @mikeknoop@tyronen@avenwu@ohshi000@plamenko

        Issue actions

          The application may be doing too much work on its main thread. · Issue #84 · facebook/fresco