Android: Memory error because of an ImageView?

2019-07-15 13:48发布

问题:

I have 2 activities Activity A = GameActivity uses an instance of PhotoTask.class which extends AsyncTask and is used to take and save picture. Once the picture is saved, I start Activity B = ShareActivity.

When I take a picture and don't change the orientation, Activity B is called and everything works fine. Yet, if I change the orientation of the screen to take the picture (I pass from portait to landscape) I have errors when I take a picture (Activity B does not start).

I even commented my bitmap images in my code, but still have bitmap errors! What is strange is that if I comment the ImageView5 in my code (which does nothing particular) and keep my bitmap, I have no error. The problem seems to come frome this ImageView but I cant explain it

I added configChanges:orientation in the manifest, recycle my bnitmaps, used a WeakReference of an activity instead of the acitvity itself as a variable in PhotoTask, tried to see the memory leaks with eclipse memory analyzer (Android: Detecting leaks with Eclipse Memory Analyzer) but didnt understand why I had these errors

I really searched a lot and I'm becoming quite desperate so if you could please help me it would be nice

02-04 10:03:04.353: E/AndroidRuntime(17427): FATAL EXCEPTION: main
02-04 10:03:04.353: E/AndroidRuntime(17427): java.lang.RuntimeException: Unable to start activity ComponentInfo{com.example.kersplatt/com.example.kersplatt.ShareActivity}: android.view.InflateException: Binary XML file line #23: Error inflating class <unknown>
02-04 10:03:04.353: E/AndroidRuntime(17427):    at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:1647)
02-04 10:03:04.353: E/AndroidRuntime(17427):    at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:1663)
02-04 10:03:04.353: E/AndroidRuntime(17427):    at android.app.ActivityThread.access$1500(ActivityThread.java:117)
02-04 10:03:04.353: E/AndroidRuntime(17427):    at android.app.ActivityThread$H.handleMessage(ActivityThread.java:931)
02-04 10:03:04.353: E/AndroidRuntime(17427):    at android.os.Handler.dispatchMessage(Handler.java:99)
02-04 10:03:04.353: E/AndroidRuntime(17427):    at android.os.Looper.loop(Looper.java:130)
02-04 10:03:04.353: E/AndroidRuntime(17427):    at android.app.ActivityThread.main(ActivityThread.java:3683)
02-04 10:03:04.353: E/AndroidRuntime(17427):    at java.lang.reflect.Method.invokeNative(Native Method)
02-04 10:03:04.353: E/AndroidRuntime(17427):    at java.lang.reflect.Method.invoke(Method.java:507)
02-04 10:03:04.353: E/AndroidRuntime(17427):    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:875)
02-04 10:03:04.353: E/AndroidRuntime(17427):    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:633)
02-04 10:03:04.353: E/AndroidRuntime(17427):    at dalvik.system.NativeStart.main(Native Method)
02-04 10:03:04.353: E/AndroidRuntime(17427): Caused by: android.view.InflateException: Binary XML file line #23: Error inflating class <unknown>
02-04 10:03:04.353: E/AndroidRuntime(17427):    at android.view.LayoutInflater.createView(LayoutInflater.java:518)
02-04 10:03:04.353: E/AndroidRuntime(17427):    at com.android.internal.policy.impl.PhoneLayoutInflater.onCreateView(PhoneLayoutInflater.java:56)
02-04 10:03:04.353: E/AndroidRuntime(17427):    at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:568)
02-04 10:03:04.353: E/AndroidRuntime(17427):    at android.view.LayoutInflater.rInflate(LayoutInflater.java:623)
02-04 10:03:04.353: E/AndroidRuntime(17427):    at android.view.LayoutInflater.rInflate(LayoutInflater.java:626)
02-04 10:03:04.353: E/AndroidRuntime(17427):    at android.view.LayoutInflater.inflate(LayoutInflater.java:408)
02-04 10:03:04.353: E/AndroidRuntime(17427):    at android.view.LayoutInflater.inflate(LayoutInflater.java:320)
02-04 10:03:04.353: E/AndroidRuntime(17427):    at android.view.LayoutInflater.inflate(LayoutInflater.java:276)
02-04 10:03:04.353: E/AndroidRuntime(17427):    at com.android.internal.policy.impl.PhoneWindow.setContentView(PhoneWindow.java:209)
02-04 10:03:04.353: E/AndroidRuntime(17427):    at android.app.Activity.setContentView(Activity.java:1657)
02-04 10:03:04.353: E/AndroidRuntime(17427):    at com.example.kersplatt.ShareActivity.onCreate(ShareActivity.java:64)
02-04 10:03:04.353: E/AndroidRuntime(17427):    at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1047)
02-04 10:03:04.353: E/AndroidRuntime(17427):    at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:1611)
02-04 10:03:04.353: E/AndroidRuntime(17427):    ... 11 more
02-04 10:03:04.353: E/AndroidRuntime(17427): Caused by: java.lang.reflect.InvocationTargetException
02-04 10:03:04.353: E/AndroidRuntime(17427):    at java.lang.reflect.Constructor.constructNative(Native Method)
02-04 10:03:04.353: E/AndroidRuntime(17427):    at java.lang.reflect.Constructor.newInstance(Constructor.java:415)
02-04 10:03:04.353: E/AndroidRuntime(17427):    at android.view.LayoutInflater.createView(LayoutInflater.java:505)
02-04 10:03:04.353: E/AndroidRuntime(17427):    ... 23 more
02-04 10:03:04.353: E/AndroidRuntime(17427): Caused by: java.lang.OutOfMemoryError: bitmap size exceeds VM budget
02-04 10:03:04.353: E/AndroidRuntime(17427):    at android.graphics.BitmapFactory.nativeDecodeAsset(Native Method)
02-04 10:03:04.353: E/AndroidRuntime(17427):    at android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:460)
02-04 10:03:04.353: E/AndroidRuntime(17427):    at android.graphics.BitmapFactory.decodeResourceStream(BitmapFactory.java:336)
02-04 10:03:04.353: E/AndroidRuntime(17427):    at android.graphics.drawable.Drawable.createFromResourceStream(Drawable.java:697)
02-04 10:03:04.353: E/AndroidRuntime(17427):    at android.content.res.Resources.loadDrawable(Resources.java:1709)
02-04 10:03:04.353: E/AndroidRuntime(17427):    at android.content.res.TypedArray.getDrawable(TypedArray.java:601)
02-04 10:03:04.353: E/AndroidRuntime(17427):    at android.widget.ImageView.<init>(ImageView.java:118)
02-04 10:03:04.353: E/AndroidRuntime(17427):    at android.widget.ImageView.<init>(ImageView.java:108)
02-04 10:03:04.353: E/AndroidRuntime(17427):    ... 26 more

FOR YOUR INFORMATION, PIECE OF MY CODE

public class ShareActivity extends Activity implements OnClickListener{
    public static final String APP_ID = "**********";
    Facebook facebook = new Facebook(APP_ID);
    AsyncFacebookRunner mAsyncRunner = new AsyncFacebookRunner(facebook);
    String FILENAME = "AndroidSSO_data";
    SharedPreferences mPrefs;
    Handler mHandler;
    File file;
    byte[] bytes;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        Log.w("SHAREACTIVITY","SECOND");
        super.onCreate(savedInstanceState);
        setContentView(R.layout.share);
        file = (File) this.getIntent().getExtras().get("PICTURE_TAKEN");
        Bitmap bitmap = BitmapFactory.decodeFile(file.getAbsolutePath());
        Bitmap mutableBitmap = bitmap.copy(Bitmap.Config.ARGB_8888, true);
        **ImageView image5 = (ImageView) findViewById(R.id.imageView5);**
        **image5.setImageBitmap(mutableBitmap);**
        bitmap.recycle();
        mutableBitmap.recycle();

    }

CLASS PHOTOTASK

public class PhotoTask extends AsyncTask<Boolean, Void, Void> {
    private Camera camera;
    private SurfaceView surfaceCamera;
    private boolean isPreview=false;
    private SurfaceHolder holder;
    private WeakReference<GameActivity> weakgameactivity;
    GameActivity gameactivity;
    private File output;

    public PhotoTask(Camera camera, SurfaceView surfaceCamera,boolean isPreview, SurfaceHolder holder, GameActivity gameactivity) {
        super();
        this.camera = camera;
        this.surfaceCamera = surfaceCamera;
        this.isPreview = isPreview;
        this.holder = holder;
        this.weakgameactivity = new WeakReference<GameActivity>(gameactivity);
        this.gameactivity=this.weakgameactivity.get();
    }


    PictureCallback myPictureCallback_JPG = new PictureCallback(){

        @Override
        public void onPictureTaken(byte[] data, Camera camera) {
            Log.w("GAMEACTIVITY","FIRST");
//          ImageView image5 = (ImageView) gameactivity.findViewById(R.id.imageView6);
            File imagesFolder = new File(Environment.getExternalStorageDirectory(), "/KersplattFolder");

            imagesFolder.mkdirs(); 
            SimpleDateFormat dateFormat = new SimpleDateFormat("yyyymmddhhmmss");
            String date = dateFormat.format(new Date());
            String fileName = "Kersplatt_" + date + ".jpg";
            output = new File(imagesFolder, fileName);
            ImageView view = (ImageView) gameactivity.findViewById(R.id.imageView6);
            view.setDrawingCacheEnabled(true);
            Bitmap b = view.getDrawingCache();
            FileOutputStream fos = null;
            try {
                fos = new FileOutputStream(output);
            } catch (FileNotFoundException e1) {
                // TODO Auto-generated catch block
                e1.printStackTrace();
            }
            b.compress(CompressFormat.JPEG, 95, fos);
            try {
            fos.write(data);
            fos.close();
            } catch (FileNotFoundException e) {
                    e.printStackTrace();
            }
              catch (IOException e) {
                    e.printStackTrace();
            }
              camera.stopPreview();
              Log.w("GAMEACTIVITY","INTENT");
              b.recycle();
              Intent intent = new Intent(gameactivity.getApplicationContext(),ShareActivity.class);
              // Sending the picture taken to ShareActivity
              intent.putExtra("PICTURE_TAKEN", output);
              gameactivity.startActivity(intent);
        }

    };

    @Override
    protected Void doInBackground(Boolean... params) {
         camera.takePicture(null,null, myPictureCallback_JPG);
            Log.w("GAMEACTIVITY","TAKEPICTURE");
            return null;
    }


}

My imageview5

   <ImageView
         android:id="@+id/imageView5"
         android:layout_width="85dp"
         android:layout_height="100dp"
         android:layout_marginLeft="8dp"
         android:layout_marginTop="20dp"
         android:adjustViewBounds="true"
         android:src="@drawable/diviseur" />

回答1:

After you take picture you need to use Image resize to resize the image pass it Image and the new Width and height that is your desired. If trace the heap on clcik DDMS and then at evice on device list and debug the code you will see the size of the Image increasing exra ordinary take look at Loading large bitmap Efficiently from the Official Documents

after reading you can give try to this following code and your problem will be solved

 /** getResizedBitmap method is used to Resized the Image according to custom width and height 
  * @param image
  * @param newHeight (new desired height)
  * @param newWidth (new desired Width)
  * @return image (new resized image)
  * */
public static Bitmap getResizedBitmap(Bitmap image, int newHeight, int newWidth) {
    int width = image.getWidth();
    int height = image.getHeight();
    float scaleWidth = ((float) newWidth) / width;
    float scaleHeight = ((float) newHeight) / height;
    // create a matrix for the manipulation
    Matrix matrix = new Matrix();
    // resize the bit map
    matrix.postScale(scaleWidth, scaleHeight);
    // recreate the new Bitmap
    Bitmap resizedBitmap = Bitmap.createBitmap(image, 0, 0, width, height,
            matrix, false);
    return resizedBitmap;
}


回答2:

As Usman Kurd says, you load the (full) image in memory, and create a copy of that in memory. This uses a vast amount of memory. The documentation he refers to states:

BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeResource(getResources(), R.id.myimage, options);
int imageHeight = options.outHeight;
int imageWidth = options.outWidth;
String imageType = options.outMimeType;

Your out of memory occurs on either of these lines, not on setting the image in the imageview

Bitmap bitmap = BitmapFactory.decodeFile(file.getAbsolutePath());
Bitmap mutableBitmap = bitmap.copy(Bitmap.Config.ARGB_8888, true);

If you use this android won't load the entire bitmap into memory but a scaled down version. Read the documentation and your error will definitely go away.

Above code will only decode the bounds. The documentation explains how to use this. See the chapter named "Load a Scaled Down Version into Memory" how to implement this properly