0

I have a code that loads four images into imageview. To avoid OOM issues on big images loading i want to use either Glide/Picasso/Fresco to resize before loading to imageview. Please note that it works on small images but on big images(will be user inputted and hence i cannot control the sizes) it crashes immediately the second large image is selected to imageview on OOM. I understand theseThank you

package com.ny.a4imageswithtexts;

import android.app.ProgressDialog;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
import android.text.TextUtils;
import android.util.Base64;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.Toast;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;

import com.bumptech.glide.Glide;
import com.bumptech.glide.load.DecodeFormat;
import com.bumptech.glide.load.engine.Resource;
import com.bumptech.glide.request.RequestOptions;
import com.bumptech.glide.request.target.CustomTarget;
import com.bumptech.glide.request.transition.Transition;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.ByteArrayOutputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.UnsupportedEncodingException;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;

import javax.net.ssl.HttpsURLConnection;



public class MainActivity extends AppCompatActivity {

    Bitmap bitmap1;
    Bitmap bitmap2;
    Bitmap bitmap3;
    Bitmap bitmap4;

    private int PICK_IMAGE_REQUEST1 = 1;
    private int PICK_IMAGE_REQUEST2 = 2;
    private int PICK_IMAGE_REQUEST3 = 1;
    private int PICK_IMAGE_REQUEST4 = 2;

    boolean check = true;

    Button SelectImageGallery1;
    Button SelectImageGallery2;
    Button SelectImageGallery3;
    Button SelectImageGallery4;

    Button  UploadImageServer;

    ImageView imageView1;
    ImageView imageView2;
    ImageView imageView3;
    ImageView imageView4;

    EditText imageName1;
    EditText imageName2;
    EditText imageName3;
    EditText imageName4;

    ProgressDialog progressDialog ;

    String GetImageNameEditText1;
    String GetImageNameEditText2;
    String GetImageNameEditText3;
    String GetImageNameEditText4;

    String ImageName1 = "image_name1";
    String ImageName2 = "image_name2";
    String ImageName3 = "image_name3";
    String ImageName4 = "image_name4";

    String ImagePath1 = "image_path1";
    String ImagePath2 = "image_path2";
    String ImagePath3 = "image_path3";
    String ImagePath4 = "image_path4";

    String ServerUploadPath ="http://11111111111111111.php";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        imageView1 = findViewById(R.id.imageView1);
        imageView2 = findViewById(R.id.imageView2);
        imageView3 = findViewById(R.id.imageView3);
        imageView4 = findViewById(R.id.imageView4);

        imageName1 = findViewById(R.id.editTextImageName1);
        String strImageName1 = imageName1.getText().toString();
        if(TextUtils.isEmpty(strImageName1))
{imageName1.setError("Image Name Must Be Entered");
           }
        imageName2 = findViewById(R.id.editTextImageName2);
        String strImageName2 = imageName2.getText().toString();
        if(TextUtils.isEmpty(strImageName2))
{imageName2.setError("Image Name Must Be Entered");
            }
        imageName3 = findViewById(R.id.editTextImageName3);
        String strImageName3 = imageName3.getText().toString();
        if(TextUtils.isEmpty(strImageName3))
{imageName3.setError("Image Name Must Be Entered");
              }
        imageName4 = findViewById(R.id.editTextImageName4);
        String strImageName4 = imageName4.getText().toString();
        if(TextUtils.isEmpty(strImageName4))
{imageName4.setError("Image Name Must Be Entered");
              }


        SelectImageGallery1 = findViewById(R.id.buttonSelect1);
        SelectImageGallery2 = findViewById(R.id.buttonSelect2);
        SelectImageGallery3 = findViewById(R.id.buttonSelect3);
        SelectImageGallery4 = findViewById(R.id.buttonSelect4);

        UploadImageServer = findViewById(R.id.buttonUpload);

        SelectImageGallery1.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Intent intent = new Intent();
                intent.setType("image/*");
                intent.setAction(Intent.ACTION_GET_CONTENT);
                startActivityForResult(Intent.createChooser(intent,
"Select Image1 From Gallery"), 1);

            }
        });

        SelectImageGallery2.setOnClickListener(new View.OnClickListener() {
              @Override
                public void onClick(View view) {
                    Intent intent = new Intent();
                    intent.setType("image/*");
                    intent.setAction(Intent.ACTION_GET_CONTENT);

startActivityForResult(Intent.createChooser(intent, "Select Image4
From Gallery"), 2);
                }
          });

        SelectImageGallery3.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Intent intent = new Intent();
                intent.setType("image/*");
                intent.setAction(Intent.ACTION_GET_CONTENT);
                startActivityForResult(Intent.createChooser(intent,
"Select Image3 From Gallery"), 3);

            }
        });

        SelectImageGallery4.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Intent intent = new Intent();
                intent.setType("image/*");
                intent.setAction(Intent.ACTION_GET_CONTENT);
                startActivityForResult(Intent.createChooser(intent,
"Select Image4 From Gallery"), 4);

            }
        });

        UploadImageServer.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                GetImageNameEditText1 = imageName1.getText().toString();
                GetImageNameEditText2 = imageName2.getText().toString();
                GetImageNameEditText3 = imageName3.getText().toString();
                GetImageNameEditText4 = imageName4.getText().toString();
               ImageUploadToServerFunction();
            }
        });

    }

    @Override
    protected void onActivityResult(int RC, int RQC, Intent I) {
        super.onActivityResult(RC, RQC, I);
        if (RC == 1 && RQC == RESULT_OK && I != null && I.getData() != null) {
            Uri uri = I.getData();
            RequestOptions options = new RequestOptions()
                    .format(DecodeFormat.PREFER_RGB_565)
                    .placeholder(R.drawable.ic_launcher_background)
                    .error(R.drawable.ic_launcher_background);

            Glide.with(this)
                    .setDefaultRequestOptions(options)
                    .load(uri)
                    .centerInside()
                    .into(new CustomTarget<Drawable>(512, 512) {
                        @Override
                        public void onResourceReady(@NonNull Drawable
bitmap1, @Nullable Transition<? super Drawable> transition) {
                            imageView1.setImageDrawable(bitmap1);
                        }
                        @Override
                        public void onLoadCleared(@Nullable Drawable
placeholder) {
                        }
                    });
        }
        if (RC == 2 && RQC == RESULT_OK && I != null && I.getData() != null) {
            Uri uri = I.getData();
            RequestOptions options = new RequestOptions()
                    .format(DecodeFormat.PREFER_RGB_565)
                    .placeholder(R.drawable.ic_launcher_background)
                    .error(R.drawable.ic_launcher_background);

            Glide.with(this)
                    .setDefaultRequestOptions(options)
                    .load(uri)
                    .centerInside()
                    .into(new CustomTarget<Drawable>(512, 512) {
                        @Override
                        public void onResourceReady(@NonNull Drawable
bitmap2, @Nullable Transition<? super Drawable> transition) {
                            imageView2.setImageDrawable(bitmap2);
                        }
                        @Override
                        public void onLoadCleared(@Nullable Drawable
placeholder) {
                        }
                    });
        }
        if (RC == 3 && RQC == RESULT_OK && I != null && I.getData() != null) {
            Uri uri = I.getData();
            RequestOptions options = new RequestOptions()
                    .format(DecodeFormat.PREFER_RGB_565)
                    .placeholder(R.drawable.ic_launcher_background)
                    .error(R.drawable.ic_launcher_background);

            Glide.with(this)
                    .setDefaultRequestOptions(options)
                    .load(uri)
                    .centerInside()
                    .into(new CustomTarget<Drawable>(512, 512) {
                        @Override
                        public void onResourceReady(@NonNull Drawable
bitmap3, @Nullable Transition<? super Drawable> transition) {
                            imageView3.setImageDrawable(bitmap3);
                        }
                        @Override
                        public void onLoadCleared(@Nullable Drawable
placeholder) {
                        }
                    });
        }
        if (RC == 4 && RQC == RESULT_OK && I != null && I.getData() != null) {
            Uri uri = I.getData();
            RequestOptions options = new RequestOptions()
                    .format(DecodeFormat.PREFER_RGB_565)
                    .placeholder(R.drawable.ic_launcher_background)
                    .error(R.drawable.ic_launcher_background);

            Glide.with(this)
                    .setDefaultRequestOptions(options)
                    .load(uri)
                    .centerInside()
                    .into(new CustomTarget<Drawable>(512, 512) {
                        @Override
                        public void onResourceReady(@NonNull Drawable
bitmap4, @Nullable Transition<? super Drawable> transition) {
                            imageView4.setImageDrawable(bitmap4);
                        }
                        @Override
                        public void onLoadCleared(@Nullable Drawable
placeholder) {
                        }
                    });
        }
    }
    public String getStringImage1(Bitmap bitmap1){
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        bitmap1.compress(Bitmap.CompressFormat.JPEG, 80, baos);
        byte[] imageBytes = baos.toByteArray();
        String encodedImage1 = Base64.encodeToString(imageBytes,
Base64.DEFAULT);
        return encodedImage1;
    }

    public String getStringImage2(Bitmap bitmap2){
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        bitmap2.compress(Bitmap.CompressFormat.JPEG, 80, baos);
        byte[] imageBytes = baos.toByteArray();
        String encodedImage2 = Base64.encodeToString(imageBytes,
Base64.DEFAULT);
        return encodedImage2;
    }
    public String getStringImage3(Bitmap bitmap3){
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        bitmap3.compress(Bitmap.CompressFormat.JPEG, 80, baos);
        byte[] imageBytes = baos.toByteArray();
        String encodedImage3 = Base64.encodeToString(imageBytes,
Base64.DEFAULT);
        return encodedImage3;
    }

    public String getStringImage4(Bitmap bitmap4){
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        bitmap4.compress(Bitmap.CompressFormat.JPEG, 80, baos);
        byte[] imageBytes = baos.toByteArray();
        String encodedImage4 = Base64.encodeToString(imageBytes,
Base64.DEFAULT);
        return encodedImage4;
    }


    public void ImageUploadToServerFunction(){
        final String imageName1 = GetImageNameEditText1.trim();
        final String imageName2 = GetImageNameEditText2.trim();
        final String imageName3 = GetImageNameEditText3.trim();
        final String imageName4 = GetImageNameEditText4.trim();
        final String imageView1 = getStringImage1(bitmap1);
        final String imageView2 = getStringImage2(bitmap2);
        final String imageView3 = getStringImage3(bitmap3);
        final String imageView4 = getStringImage4(bitmap4);


        class AsyncTaskUploadClass extends AsyncTask<Void,Void,String> {

            @Override
            protected void onPreExecute() {
                super.onPreExecute();
                progressDialog =
ProgressDialog.show(MainActivity.this,"Image is Uploading","Please
Wait",false,false);
            }

            @Override
            protected void onPostExecute(String string1) {

                super.onPostExecute(string1);

                // Dismiss the progress dialog after done uploading.
                progressDialog.dismiss();

                // Printing uploading success message coming from
server on android app.

Toast.makeText(MainActivity.this,string1,Toast.LENGTH_LONG).show();

                // Setting image as transparent after done uploading.
                ImageView cleared1 = findViewById(R.id.imageView1);
                cleared1.setImageResource(android.R.color.transparent);

                ImageView cleared2 = findViewById(R.id.imageView2);
                cleared2.setImageResource(android.R.color.transparent);

                ImageView cleared3 = findViewById(R.id.imageView3);
                cleared3.setImageResource(android.R.color.transparent);

                ImageView cleared4 = findViewById(R.id.imageView4);
                cleared4.setImageResource(android.R.color.transparent);


            }
            @Override
            protected String doInBackground(Void... params) {
                ImageProcessClass imageProcessClass = new ImageProcessClass();
                HashMap<String,String> HashMapParams = new
HashMap<String,String>();
                HashMapParams.put(ImageName1, imageName1);
                HashMapParams.put(ImageName2, imageName2);
                HashMapParams.put(ImageName3, imageName3);
                HashMapParams.put(ImageName4, imageName4);
                HashMapParams.put(ImagePath1, imageView1);
                HashMapParams.put(ImagePath2, imageView2);
                HashMapParams.put(ImagePath3, imageView3);
                HashMapParams.put(ImagePath4, imageView4);

                String FinalData =
imageProcessClass.ImageHttpRequest(ServerUploadPath, HashMapParams);
                return FinalData;
            }
        }
        AsyncTaskUploadClass AsyncTaskUploadClassOBJ = new
AsyncTaskUploadClass();
        AsyncTaskUploadClassOBJ.execute();
    }

    public class ImageProcessClass{
        public String ImageHttpRequest(String
requestURL,HashMap<String, String> PData) {
            StringBuilder stringBuilder = new StringBuilder();
            try {
                URL url;
                HttpURLConnection httpURLConnectionObject ;
                OutputStream OutPutStream;
                BufferedWriter bufferedWriterObject ;
                BufferedReader bufferedReaderObject ;
                int RC ;
                url = new URL(requestURL);
                httpURLConnectionObject = (HttpURLConnection)
url.openConnection();
                httpURLConnectionObject.setReadTimeout(19000);
                httpURLConnectionObject.setConnectTimeout(19000);
                httpURLConnectionObject.setRequestMethod("POST");
                httpURLConnectionObject.setDoInput(true);
                httpURLConnectionObject.setDoOutput(true);
                OutPutStream = httpURLConnectionObject.getOutputStream();
                bufferedWriterObject = new BufferedWriter(
                        new OutputStreamWriter(OutPutStream, "UTF8"));
                bufferedWriterObject.write(bufferedWriterDataFN(PData));
                bufferedWriterObject.flush();
                bufferedWriterObject.close();
                OutPutStream.close();
                RC = httpURLConnectionObject.getResponseCode();
                if (RC == HttpsURLConnection.HTTP_OK) {
                    bufferedReaderObject = new BufferedReader(new
InputStreamReader(httpURLConnectionObject.getInputStream()));
                    stringBuilder = new StringBuilder();
                    String RC2;
                    while ((RC2 = bufferedReaderObject.readLine()) != null){
                        stringBuilder.append(RC2);
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
            return stringBuilder.toString();
        }

        private String bufferedWriterDataFN(HashMap<String, String>
HashMapParams) throws UnsupportedEncodingException {
            StringBuilder stringBuilderObject;
            stringBuilderObject = new StringBuilder();
            for (Map.Entry<String, String> KEY : HashMapParams.entrySet()) {
                if (check)
                    check = false;
                else
                    stringBuilderObject.append("&");

stringBuilderObject.append(URLEncoder.encode(KEY.getKey(), "UTF-8"));
                stringBuilderObject.append("=");

stringBuilderObject.append(URLEncoder.encode(KEY.getValue(),
"UTF-8"));
            }

            return stringBuilderObject.toString();
        }

    }

}


'''
NyP
  • 499
  • 4
  • 18

3 Answers3

0

I faced the same issue in my application even in the presence of Glide and Picasso. I solved it by specifying the height and width of the desired view. So, the application doesn't exceed the memory limit of the app and mobile. Here is a piece of code from my app.

val options = RequestOptions()
    .format(DecodeFormat.PREFER_RGB_565)
    .placeholder(R.drawable.ic_placeholder)
    .error(R.drawable.ic_error)

Glide.with(this)
     .setDefaultRequestOptions(options)
     .load(campaign!!.imageURL)
     .centerInside()
     .into(object : CustomTarget<Drawable>(width, height) {
         override fun onResourceReady(resource: Drawable, transition: Transition<in Drawable>?) {
              imagePort.setImageDrawable(resource)
         }

        override fun onLoadCleared(placeholder: Drawable?) {

        }
     })

Changes for your code: I executed your code, It will is the working copy using glide. Just don't forget to add the glide dependencies in your gradle file

implementation 'com.github.bumptech.glide:glide:4.11.0'
annotationProcessor 'com.github.bumptech.glide:compiler:4.11.0'

Changes in the onActivityResult function of yours...

protected void onActivityResult(int RC, int RQC, Intent I) {
        super.onActivityResult(RC, RQC, I);
        if (RC == 1 && RQC == RESULT_OK && I != null && I.getData() != null) {
            Uri uri = I.getData();
            RequestOptions options = new RequestOptions()
                    .format(DecodeFormat.PREFER_RGB_565)
                    .placeholder(R.drawable.ic_launcher_background)
                    .error(R.drawable.ic_launcher_background);

            Glide.with(this)
                    .setDefaultRequestOptions(options)
                    .load(uri)
                    .centerInside()
                    .into(new CustomTarget<Drawable>(1000, 500) {
                        @Override
                        public void onResourceReady(@NonNull Drawable resource, @Nullable Transition<? super Drawable> transition) {
                            imageView1.setImageDrawable(resource);
                        }

                        @Override
                        public void onLoadCleared(@Nullable Drawable placeholder) {

                        }
                    });
        }
Attiq ur Rehman
  • 475
  • 1
  • 6
  • 21
  • Thanks @Attiq. Let me try it and revert – NyP Jan 15 '20 at 09:21
  • how do i implement your code in my code here. Thats where the resize matters most. `Uri uri = I.getData(); try { bitmap1 = MediaStore.Images.Media.getBitmap(getContentResolver(), uri); //bitmap1 = MediaStore.Images.Media.getBitmap(getContentResolver(), filePath); imageView1.setImageBitmap(bitmap1); } catch(IOException e) { e.printStackTrace(); }` – NyP Jan 15 '20 at 09:36
  • I am using the Glide library. Instead of handling everything by myself, dedicate the responsibility to the Glide library. Create Uri of the image path. Uri imageUri = Uri.fromFile( new File( filePath())); Glide.with(this).setDefaultRequestOptions(options).load( imageUri).into( .... – Attiq ur Rehman Jan 15 '20 at 09:41
  • Welcome, If your issue is resolved then I recommend you to accept my answer. – Attiq ur Rehman Jan 15 '20 at 10:09
  • the issue is not solved sir. i am unable to implement your code. I have attached the full activity – NyP Jan 15 '20 at 10:20
  • Check the updated answer. I made changes to your code. It's working fine at my device. Just set the height and width of your desired view. – Attiq ur Rehman Jan 15 '20 at 11:01
  • Thanks so much @Attiq, it hasn't worked fully. i have tried with small images but i am i am now getting error `E/AndroidRuntime: FATAL EXCEPTION: main java.lang.NullPointerException` at line 185 `ImageUploadToServerFunction();` , at line 281 `bitmap1.compress(Bitmap.CompressFormat.JPEG, 80, baos);` and at line 216 `final String imageView1 = getStringImage1(bitmap1);` – NyP Jan 15 '20 at 12:53
  • It looks like you have resolved the OOM error but stuck in a new one. Post a new question with log, I will be happy to help you – Attiq ur Rehman Jan 16 '20 at 02:26
  • Thank you @Attiq. Though i still have the NullPointer since i can't call back the image in imageview i am getting error `E/AndroidRuntime: FATAL EXCEPTION: main java.lang.NullPointerException ` at line 185 `ImageUploadToServerFunction(); ` , at line 281 `bitmap1.compress(Bitmap.CompressFormat.JPEG, 80, baos); ` and at line 216 `final String imageView1 = getStringImage1(bitmap1); ` . I impelemented your code in my full code as in my post edit above. – NyP Jan 16 '20 at 10:38
  • I had to change from drawable to Bitmap – NyP Jan 17 '20 at 09:31
  • Bitmap bitmap = Glide.with(getContext()).asBitmap().load(mUri).apply(RequestOptions.centerCropTransform()).submit(width, height).get(); – Attiq ur Rehman Jan 17 '20 at 09:48
0

1- To implement Glide https://github.com/bumptech/glide (that I prefer)

2- To implement Picasso https://square.github.io/picasso/#download (also a strong library)

They handle images professionally. They have tools to avoid OOM caused by images. you should follow tutorials to implement and use them.

But OOM is not only because of the images. Whole application must be observed.

https://stackoverflow.com/a/58358334/11982611 this post can help for this.

Edit: To make resizing with Glide

Glide.with(mCtxt).load( image uri or url or drawable )
                     .error( image )
                .override(320,180)
                .centerCrop()
                .into(  imageview );
Eren Tüfekçi
  • 2,463
  • 3
  • 16
  • 35
  • Thanks @Eren, i have tried those but they dont resize images before loading to imageview. – NyP Jan 15 '20 at 09:33
0

You should create a bitmap with the desired size. Typically, stay below the openGL limit (usually 2048px on the largest side)

Then, use a Matrix to transform the original bitmap to the target.

Matrix matrix=new Matrix();
matrix.postScale(0.001f,0.001f); // whatever scale you want to use
// recreate the new Bitmap
Bitmap resizedBitmap = Bitmap.createBitmap(bitmapSource, 0, newWidth, newHeight, matrix, true);

Please note that if your images come from files, there are different mechanisms to decode them with a resize, avoiding to open the full size image (with the problems of memory and performance), see BitmapFactory.Options

caketuzz
  • 126
  • 4