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

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

Closed
ultimaters opened this issue Apr 3, 2015 · 25 comments
Closed

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

ultimaters opened this issue Apr 3, 2015 · 25 comments
Labels

Comments

@ultimaters
Copy link

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.

@plamenko
Copy link
Contributor

plamenko commented Apr 3, 2015

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
Copy link
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
Copy link
Contributor

plamenko commented Apr 4, 2015

What's CircleView Adapter?

@mikeknoop
Copy link

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
Copy link
Author

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

@plamenko
Copy link
Contributor

plamenko commented Apr 6, 2015

I'll investigate

@plamenko plamenko self-assigned this Apr 6, 2015
@mikeknoop
Copy link

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
Copy link

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
Copy link
Contributor

plamenko commented Apr 9, 2015

That's really helpful information, thanks!

@plamenko
Copy link
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
Copy link

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.

@tyronen tyronen added the bug label Apr 15, 2015
@plamenko
Copy link
Contributor

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

@plamenko
Copy link
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.

@mikeknoop
Copy link

@plamenko my local images are indeed bigger than the view. They are camera images so likely ~3000x3000px getting scaled to ~150dpi views.

Can Fresco handle this use case?

@plamenko
Copy link
Contributor

@mikeknoop
Copy link

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
Copy link
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
Copy link

avenwu commented Jul 14, 2015

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
Copy link

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
Copy link
Contributor

tyronen commented Oct 14, 2015

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.

@tyronen tyronen closed this as completed Oct 14, 2015
@SarthakM9
Copy link

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
Copy link

saantiaguilera commented Aug 2, 2016

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
Copy link

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
Copy link

need help
vinit-poojary/ViewWallpaper#1

@adkhamjonDev
Copy link

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
Labels
Projects
None yet
Development

No branches or pull requests

12 participants