Error inflating class when trying to decodeStream

2019-03-04 06:00发布

问题:

i'm exausted. I'm working on this all day. In my app i have 100 ImageViews, but i'm getting a java.outofmemory error so i decided to decode and resize file, but i can not manage it to work. Can someone take a look in the code and suggest me anything?

MainActivity code: public class MainActivity extends Activity implements OnClickListener {

ImageView display;
int toPhone;
private Context context;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    System.gc();
    context = this;
    toPhone = R.drawable.wal1;
    display = (ImageView) findViewById(R.id.WPdisplay);
    ImageView image1 = (ImageView) findViewById(R.id.WPimg1);
    ImageView image2 = (ImageView) findViewById(R.id.WPimg2);
    ImageView image3 = (ImageView) findViewById(R.id.WPimg3);
    ImageView image4 = (ImageView) findViewById(R.id.WPimg4);
    ImageView image5 = (ImageView) findViewById(R.id.WPimg5);
    Button setWall = (Button) findViewById(R.id.BsetWall);
    image1.setOnClickListener(this);
    image2.setOnClickListener(this);
    image3.setOnClickListener(this);
    image4.setOnClickListener(this);
    image5.setOnClickListener(this);
    setWall.setOnClickListener(this);
    }

@Override
public boolean onCreateOptionsMenu(Menu menu) {
    // Inflate the menu; this adds items to the action bar if it is present.
    getMenuInflater().inflate(R.menu.main, menu);
    return true;
}

public Bitmap decodeAndResizeFile(int resID) {
     try {
            // Decode image size
            BitmapFactory.Options o = new BitmapFactory.Options();
            o.inJustDecodeBounds = true;

i think in the next line i have a problem because it says The value of the local variable bmp is not used

            Bitmap bmp= BitmapFactory.decodeResource(context.getResources(), resID, o);
           // BitmapFactory.decodeStream(new FileInputStream(f), null, o);

            // The new size we want to scale to
            final int REQUIRED_SIZE = 70;

            // Find the correct scale value. It should be the power of 2.
            int width_tmp = o.outWidth, height_tmp = o.outHeight;
            int scale = 1;
            while (true) {
                if (width_tmp / 2 < REQUIRED_SIZE
                        || height_tmp / 2 < REQUIRED_SIZE)
                    break;
                width_tmp /= 2;
                height_tmp /= 2;
                scale *= 2;
            }
            BitmapFactory.Options o2 = new BitmapFactory.Options();
            o2.inSampleSize = scale;
            return BitmapFactory.decodeResource(context.getResources(), resID, o2);

     } finally {

        }
    }

@Override
public void onClick(View v) {
    Bitmap bmpp;
    switch (v.getId()){
    case R.id.WPimg1:
         display.setImageResource(R.drawable.wal1);
         toPhone = R.drawable.wal1;

in the next line i'm trying to decode it(and i'm doing this for each image)

         bmpp = decodeAndResizeFile(toPhone);
         //Bitmap bmpp = decodeAndResizeFile(file);
         break;
    case R.id.WPimg2:
         display.setImageResource(R.drawable.wal2);
         toPhone = R.drawable.wal2;  
         bmpp = decodeAndResizeFile(toPhone);
        display.setImageBitmap(bmpp);
         break;
    case R.id.WPimg3:
         display.setImageResource(R.drawable.wal3);
         toPhone = R.drawable.wal3;
         bmpp = decodeAndResizeFile(toPhone);
         break;
    case R.id.WPimg4:
         display.setImageResource(R.drawable.wal4);
         toPhone = R.drawable.wal4;
         bmpp = decodeAndResizeFile(toPhone);
         break;
    case R.id.WPimg5:
         display.setImageResource(R.drawable.wal5);
         toPhone = R.drawable.wal5;
         bmpp = decodeAndResizeFile(toPhone);
         break;
    case R.id.BsetWall:
         try{
             WallpaperManager.getInstance(getApplicationContext()).setResource(toPhone);
             Toast.makeText(getApplicationContext(), "Wallpaper was set!", Toast.LENGTH_SHORT).show();
         } catch(IOException e) {
             e.printStackTrace();
             Toast.makeText(getApplicationContext(), "No privileges!", Toast.LENGTH_SHORT).show();
         }
         break;
    }
    }
    }

here is my Logcat

--- decoder->decode returned false
Shutting down VM
threadid=1: thread exiting with uncaught exception (group=0xb3adbba8)
FATAL EXCEPTION: main
Process: app.technozed.testwall, PID: 1250
java.lang.RuntimeException: Unable to start activity ComponentInfo{app.technozed.testwall/app.technozed.testwall.MainActivity}: android.view.InflateException: Binary XML file line #304: Error inflating class <unknown>
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2195)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2245)
at android.app.ActivityThread.access$800(ActivityThread.java:135)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1196)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:136)
at android.app.ActivityThread.main(ActivityThread.java:5017)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:515)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:779)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:595)
at dalvik.system.NativeStart.main(Native Method)
Caused by: android.view.InflateException: Binary XML file line #304: Error inflating class <unknown>
at android.view.LayoutInflater.createView(LayoutInflater.java:620)
at com.android.internal.policy.impl.PhoneLayoutInflater.onCreateView(PhoneLayoutInflater.java:56)
at android.view.LayoutInflater.onCreateView(LayoutInflater.java:669)
at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:694)
at android.view.LayoutInflater.rInflate(LayoutInflater.java:755)
at android.view.LayoutInflater.rInflate(LayoutInflater.java:758)
at android.view.LayoutInflater.rInflate(LayoutInflater.java:758)
at android.view.LayoutInflater.inflate(LayoutInflater.java:492)
at android.view.LayoutInflater.inflate(LayoutInflater.java:397)
at android.view.LayoutInflater.inflate(LayoutInflater.java:353)
at com.android.internal.policy.impl.PhoneWindow.setContentView(PhoneWindow.java:290)
at android.app.Activity.setContentView(Activity.java:1929)
at app.technozed.testwall.MainActivity.onCreate(MainActivity.java:28)
at android.app.Activity.performCreate(Activity.java:5231)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1087)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2159)
... 11 more
Caused by: java.lang.reflect.InvocationTargetException
at java.lang.reflect.Constructor.constructNative(Native Method)
at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
at android.view.LayoutInflater.createView(LayoutInflater.java:594)
... 26 more
Caused by: java.lang.OutOfMemoryError
at android.graphics.BitmapFactory.nativeDecodeAsset(Native Method)
at android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:587)
at android.graphics.BitmapFactory.decodeResourceStream(BitmapFactory.java:422)
at android.graphics.drawable.Drawable.createFromResourceStream(Drawable.java:840)
at android.content.res.Resources.loadDrawable(Resources.java:2110)
at android.content.res.TypedArray.getDrawable(TypedArray.java:602)
at android.widget.ImageView.<init>(ImageView.java:129)
at android.widget.ImageView.<init>(ImageView.java:119)
... 29 more

回答1:

Where you have the warning 'The value of the local variable bmp is not used' in decodeAndResizeFile(), you can remove the 'Bitmap bmp =' part and leave it as:

BitmapFactory.decodeResource(context.getResources(), resID, o);

This is because this call is with o.inJustDecodeBounds = true. When that is the case, only the dimensions are returned in object 'o' and a null bitmap is returned (so there's no point storing it).

In your switch statement, change each case to be like the following:

case R.id.WPimg1:
    toPhone = R.drawable.wal1;
    bmpp = decodeAndResizeFile(toPhone);
    display.setImageBitmap(bmpp);
    break;

So here, you decode the resource into a scaled down bitmap and then assign that bitmap to the ImageView. You don't want or need to do this:

display.setImageResource(R.drawable.wal1);

...since you're risking an OutOfMemoryException and you go on to set 'display' with the scaled bitmap anyway. Having made those changes, your code works for me. I can see the wallpaper preview images and then set the wallpaper (don't forget the SET_WALLPAPER permission in the manifest).

That doesn't however explain your logcat which appears to be a problem in onCreate() inflating R.layout.activity_main. Towards the end of your logcat, it shows an OutOfMemoryError again. I can re-create this error if I assign a large jpg to an ImageView in the XML (an OOM results and the layout therefore cannot be inflated). You cannot scale down drawables 'on-the-fly' when they are assigned in XML, so create physically smaller drawables for use within the XML.

EDIT:

When you click on an image, 'display' gets a new bitmap and the old one is eligible for GC. Here is one approach for releasing the memory used by the old one right away, without waiting for GC to kick in:

Move this from onClick() to being a class variable instead:

Bitmap bmpp;  //Reference to the same bitmap that 'display' is using

Then in onClick():

Bitmap old_bm = null;  //Reference to hold the old bitmap
switch (v.getId()){
case R.id.WPimg1:
    old_bm = bmpp;  //Set to the bitmap currently being used by 'display'
    toPhone = R.drawable.wal1;
    bmpp = decodeAndResizeFile(toPhone);   //'display' now has a new bitmap
    display.setImageBitmap(bmpp);
    if (old_bm != null)
    {
        old_bm.recycle();  //Recyle the old bitmap
        old_bm = null;     //Clear the reference to allow GC to clean up fully
    }
    break;