2

I want to load images asynchronously while scrolling the recycler view. For this I used picasso to load image from an url. Also I am using a rounded image view for the image.

Now when I scroll up and down first it takes 10 - 12 seconds to load the images. And when I scroll it gives out of memory error on rounded image view.

contactAdapter:

    public class ContactAdapter extends RecyclerView.Adapter<FeedListRowHolder> {


    private List<FeedItem> feedItemList;

    private Context mContext;

    public ContactAdapter(Context context, List<FeedItem> feedItemList) {
        this.feedItemList = feedItemList;
        this.mContext = context;
    }

    @Override
    public FeedListRowHolder onCreateViewHolder(ViewGroup viewGroup, int i) {

        View v = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.item_layout,null);
        FeedListRowHolder mh = new FeedListRowHolder(v);

        return mh;
    }

    @Override
    public void onBindViewHolder(FeedListRowHolder feedListRowHolder, int i) {
        final FeedItem feedItem = feedItemList.get(i);
        Log.e("Imagename",""+"http://xesoftwares.co.in/contactsapi/profile_images/85368a5bbd6cffba8a3aa202a80563a2.jpg");//+feedItem.getThumbnail());
       /* Picasso.with(mContext).load("http://xesoftwares.co.in/contactsapi/profile_images/85368a5bbd6cffba8a3aa202a80563a2.jpg")
                .error(R.drawable.ic_account_circle_black_24dp)
                .placeholder(R.drawable.ic_account_circle_black_24dp)
                .into(feedListRowHolder.thumbnail)
                ;*/

       Picasso.with(mContext).load("http://xesoftwares.co.in/contactsapi/profile_images/85368a5bbd6cffba8a3aa202a80563a2.jpg")
                .error(R.drawable.ic_account_circle_black_24dp)
                .placeholder(R.drawable.ic_account_circle_black_24dp)
                .into(new Target() {

                    public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom from) {
                        try {
                            String root = Environment.getExternalStorageDirectory().getPath();
                            File myDir = new File(root +"/Contact");

                            if (!myDir.exists()) {
                                myDir.mkdirs();
                            }

                           // String name = new Date().toString();
                            String name = new Date().toString()+".jpg";
                          File  myDir1 = new File(myDir, name);

                            FileOutputStream out = new FileOutputStream(myDir1);
                            bitmap.compress(Bitmap.CompressFormat.JPEG, 100, out);

                            ImageFilePath imageFilePath1=new ImageFilePath(myDir1);
                            ComplexPreferences complexPreferences112 = ComplexPreferences.getComplexPreferences(mContext, "mypref112", Context.MODE_PRIVATE);
                            complexPreferences112.putObject("imageFilePath1", imageFilePath1);
                            Log.e("user2", "" + imageFilePath1);
                            complexPreferences112.commit();
                            out.flush();
                            out.close();
                        } catch(Exception e){
                            // some action
                        }
                    }


                    public void onBitmapFailed(Drawable errorDrawable) {
                    }


                    public void onPrepareLoad(Drawable placeHolderDrawable) {
                    }
                });


       ComplexPreferences complexPreferences112 = ComplexPreferences.getComplexPreferences(mContext, "mypref112", mContext.MODE_PRIVATE);
        ImageFilePath imageFilePath1= complexPreferences112.getObject("imageFilePath1", ImageFilePath.class);
        File myDir1=imageFilePath1.getprofile();

       Picasso.with(mContext).load(myDir1).into(feedListRowHolder.thumbnail);

        feedListRowHolder.title.setText(Html.fromHtml(feedItem.getTitle()));
        //feedListRowHolder.genre.setText(Html.fromHtml(feedItem.getGenre()));

    }

    @Override
    public int getItemCount() {
        return (null != feedItemList ? feedItemList.size() : 0);
    }
}

roundedImageView :

   public  class RoundedImageView extends ImageView {
    public RoundedImageView(Context ctx, AttributeSet attrs) {
            super(ctx, attrs);
        }


        @Override
        protected void onDraw(Canvas canvas) {

            Drawable drawable = getDrawable();

            if (drawable == null) {
                return;
            }

            if (getWidth() == 0 || getHeight() == 0) {
                return;
            }
            Bitmap b =  ((BitmapDrawable)drawable).getBitmap() ;
           if(b!=null) {
            Bitmap bitmap = b.copy(Config.ARGB_4444, true); //error line 39                          

               int w = getWidth() / 2;


               Bitmap roundBitmap = getRoundedCroppedBitmap(bitmap, w);
               canvas.drawBitmap(roundBitmap, 0, 0, null);
           }

        }

        public static Bitmap getRoundedCroppedBitmap(Bitmap bitmap, int radius) {
            Bitmap finalBitmap;
            if(bitmap.getWidth() != radius || bitmap.getHeight() != radius)
                finalBitmap = Bitmap.createScaledBitmap(bitmap, radius, radius, false);
            else
                finalBitmap = bitmap;
            Bitmap output = Bitmap.createBitmap(finalBitmap.getWidth(),
                    finalBitmap.getHeight(), Config.ARGB_4444);
            Canvas canvas = new Canvas(output);

            final Paint paint = new Paint();
            final Rect rect = new Rect(0, 0, finalBitmap.getWidth(), finalBitmap.getHeight());

            paint.setAntiAlias(true);
            paint.setFilterBitmap(true);
            paint.setDither(true);
            canvas.drawARGB(0, 0, 0, 0);
            paint.setColor(Color.parseColor("#BAB399"));
            canvas.drawCircle(finalBitmap.getWidth() / 2+0.7f, finalBitmap.getHeight() / 2+0.7f,
                    finalBitmap.getWidth() / 2+0.1f, paint);
            paint.setXfermode(new PorterDuffXfermode(Mode.SRC_IN));
            canvas.drawBitmap(finalBitmap, rect, rect, paint);


            return output;
        }

}

Error:

       java.lang.OutOfMemoryError
                                                               at android.graphics.Bitmap.nativeCopy(Native Method)
                                                               at android.graphics.Bitmap.copy(Bitmap.java:556)
                                                               at com.xesc.contacts.utils.RoundedImageView.onDraw(RoundedImageView.java:39)

How can I solve this out of memory error? I tried to use .fit() in picasso but it gives illegalSateException. Also it takes at least 10 seconds to load the image how can I reduce the time of loading the image?

Thank you..

EDIT : The code after .into() dose not work, it directly jumps on complexShared preferences. And as complex sharedPreferences remains null it is throwing an null pointer exception.

This happened when I uninstalled the app and run again. Before it was running well.

Sid
  • 2,792
  • 9
  • 55
  • 111
  • size of your Image must be large compared to dimensions you have given to your ImageView. either increase your dimensions or reduce size of your image on server. – V-rund Puro-hit Sep 20 '16 at 04:19
  • what about resize? have you tried resize function in picasso if not please give it a try – George Thomas Sep 20 '16 at 04:27
  • how can I resize? Could you please show? @George Thomas – Sid Sep 20 '16 at 04:28
  • just add .resize(100,100) with your picasso call.. replace 100 * 100 with your required image size. You could also call .onlyScaleDown() on picasso to instruct it to only resize the image if the actual image size is greater than width and height specified in the resize method. so small image wont get resized – George Thomas Sep 20 '16 at 04:32
  • .onlyScaleDown() is not found in picasso. @GeorgeThomas – Sid Sep 20 '16 at 05:26
  • its not running the code of .into() after I uninstalled the app and run again. Can you tell me why is it happening? @GeorgeThomas – Sid Sep 20 '16 at 07:07
  • i would suggest instead your rounded imageView use https://github.com/hdodenhof/CircleImageView and give a try! just for confirmation – George Thomas Sep 20 '16 at 07:13
  • no it dose not help for null pointer I am getting. @GeorgeThomas – Sid Sep 20 '16 at 07:33

3 Answers3

1

Your code may be going to download full image into memory, in memory you dont require to full image it may be closer to your image view,

in picaso fit() handler this, It uses inSampleSize on BitmapFactory.Options to downsample the image as it is being loaded into memory, But its depend on your image view size, It may be help you. You may be use like :

.fit().centerCrop()
Dhaval Solanki
  • 4,589
  • 1
  • 23
  • 39
  • its not running the code of .into() after I uninstalled the app and run again. Can you tell me why is it happening? @Dhavalkumar Solanki – Sid Sep 20 '16 at 07:07
0

First check ::if downloaded image has large stream it will cause outofmemory: so first do this

           Picasso.with(mContext).load("http://xesoftwares.co.in                                   /contactsapi/profile_images/85368a5bbd6cffba8a3aa202a80563a2.jpg")
            .error(R.drawable.ic_account_circle_black_24dp)
            .placeholder(R.drawable.ic_account_circle_black_24dp)
             .resize(width, height) //you height width

next check:: while compressing into bitmap :: put compression in try catch as risky code :Let me know if it helps :)

Edit: Do this solution for null pointer

  ImageFilePath imageFilePath1=new ImageFilePath(myDir1);

   ComplexPreferences complexPreferences112 = ComplexPreferences.getComplexPreferences(mContext, "mypref112", Context.MODE_PRIVATE);
           if(imageFilePath1!=null)
         complexPreferences112.putObject("imageFilePath1", imageFilePath1);
DevKRos
  • 422
  • 2
  • 15
  • its not running the code of .into() after I uninstalled the app and run again. Can you tell me why is it happening? @KanishR – Sid Sep 20 '16 at 07:07
  • could you please elaborate : it's not running as in : execution not reachin this line or getting exception if any exception haapens plese add it ... :::: probably your view is not ready for loading .. can you add .error and .placeholder before it – DevKRos Sep 20 '16 at 07:12
  • its giving null pointer exception when I am getting the object from sharedPreferences not while putting the object in shared preferences. @KanishR – Sid Sep 20 '16 at 07:32
  • @Sid did you implement the above code checking if image path1 is not null ? shared prefrence has data in map whenevr u install the app the shared prefrence is deleted .. allso when you clear data of app .. i am not actually getting at what point you are stuck since in edited question it's not null pointer . Please do add code part of my edited answer and let us see if it works – DevKRos Sep 20 '16 at 07:47
  • the problem is onBitmapLoaded method is not getting execute. I tried your solution dose not work. Because the problem is method is not getting exceute. @KanishR – Sid Sep 20 '16 at 07:52
0

Looking at your code, you are not recycling the bitmaps, so an OutOfMemory error is expected, you need to recycle all your bitmaps once you don't use them, so you prolly have to keep references to them. Look here for more a deeper insight on recycle bitmaps:

Android: Bitmap recycle() how does it work?

In your case, the onDraw() function from your ImageView, you create 2 bitmaps, one with copy and one with cropped:

First, you don't need to create a bitmap, here you have how to read the image size without creating the actual bitmap, so if the image is big you will get a crash:

android: get image dimensions without opening it

public static Bitmap lessResolution (String filePath, int width, int height) {
    int reqHeight = height;
    int reqWidth = width;
    BitmapFactory.Options options = new BitmapFactory.Options();    

    // First decode with inJustDecodeBounds=true to check dimensions
    options.inJustDecodeBounds = true;
    BitmapFactory.decodeFile(filePath, options);

    // Calculate inSampleSize
    options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);

    // Decode bitmap with inSampleSize set
    options.inJustDecodeBounds = false;        

    return BitmapFactory.decodeFile(filePath, options); 
}

private static int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) {

    final int height = options.outHeight;
    final int width = options.outWidth;
    int inSampleSize = 1;

    if (height > reqHeight || width > reqWidth) {
        // Calculate ratios of height and width to requested height and width
        final int heightRatio = Math.round((float) height / (float) reqHeight);
        final int widthRatio = Math.round((float) width / (float) reqWidth);

        // Choose the smallest ratio as inSampleSize value, this will guarantee
        // a final image with both dimensions larger than or equal to the
        // requested height and width.
        inSampleSize = heightRatio < widthRatio ? heightRatio : widthRatio;
    }
    return inSampleSize;
}

And after you get your smaller bitmap to load, keep a reference to it and you have to recycle once it is not needed, so you need to overwrite the onDetacheFromWindow function and use bitmap.recycle() there

Community
  • 1
  • 1
Tazz
  • 781
  • 1
  • 8
  • 23