2015-09-27 6 views
1

Я просто пытаюсь изменить фоновое изображение приложения, в нем ничего нет, и я просто хотел изменить фон основной активности на изображение. Он отображается правильно в предварительном просмотре, но когда я пришел, чтобы запустить его либо на эмуляторе, либо на моем физическом устройстве, я получаю эту ошибку.Настройка фона фона вызывает OutOfMemoryError

Бросив OutOfMemoryError «Не удался не выделить 604786188 байт распределения с 4194208 свободными байтами и 230MB до ООГО»

Изображения меньше 1.5MB поэтому у меня нет подсказки, почему это не будет работать из памяти или пытаться выделить так много.

все, что я сделал, было открытым пустым проектом и изменить фон относительной компоновки.

Странно, если я запустил приложение на эмуляторе nexus 4, он отлично работает, но связь 5 даже в той же версии на андроиде вызывает эту ошибку. не

EDIT

больше не становится выше ошибки, а не следующая ошибка происходит на запуске приложения, независимо от используемого эмулятора.

FATAL EXCEPTION: main 
    Process: com.jacksteel.comp4, PID: 2164 
    java.lang.RuntimeException: Unable to instantiate activity ComponentInfo{com.jacksteel.comp4/com.jacksteel.comp4.MainActivity}: java.lang.NullPointerException: Attempt to invoke virtual method 'android.view.View android.view.Window.findViewById(int)' on a null object reference 
      at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2236) 
      at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2390) 
      at android.app.ActivityThread.access$800(ActivityThread.java:151) 
      at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1303) 
      at android.os.Handler.dispatchMessage(Handler.java:102) 
      at android.os.Looper.loop(Looper.java:135) 
      at android.app.ActivityThread.main(ActivityThread.java:5257) 
      at java.lang.reflect.Method.invoke(Native Method) 
      at java.lang.reflect.Method.invoke(Method.java:372) 
      at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:903) 
      at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:698) 
    Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'android.view.View android.view.Window.findViewById(int)' on a null object reference 
      at android.app.Activity.findViewById(Activity.java:2072) 
      at com.jacksteel.comp4.MainActivity.<init>(MainActivity.java:20) 
      at java.lang.reflect.Constructor.newInstance(Native Method) 
      at java.lang.Class.newInstance(Class.java:1606) 
      at android.app.Instrumentation.newActivity(Instrumentation.java:1066) 
      at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2226) 
            at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2390) 
            at android.app.ActivityThread.access$800(ActivityThread.java:151) 
            at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1303) 
            at android.os.Handler.dispatchMessage(Handler.java:102) 
            at android.os.Looper.loop(Looper.java:135) 
            at android.app.ActivityThread.main(ActivityThread.java:5257) 
            at java.lang.reflect.Method.invoke(Native Method) 
            at java.lang.reflect.Method.invoke(Method.java:372) 
            at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:903) 
            at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:698) 

EDIT 2 Исходный код

package com.jacksteel.comp4; 

import android.content.res.Resources; 
import android.graphics.Bitmap; 
import android.graphics.BitmapFactory; 
import android.graphics.drawable.BitmapDrawable; 
import android.support.v7.app.AppCompatActivity; 
import android.os.Bundle; 
import android.view.Menu; 
import android.view.MenuItem; 
import android.widget.RelativeLayout; 


public class MainActivity extends AppCompatActivity { 
    private RelativeLayout Bg = (RelativeLayout) findViewById(R.id.MainBg); 
    @Override 
    protected void onCreate(Bundle savedInstanceState) { 
     super.onCreate(savedInstanceState); 
     setContentView(R.layout.activity_main); 

     Bitmap bitmap = decodeSampledBitmapFromResource(getResources(),R.drawable.redboxes,Bg.getWidth(), Bg.getHeight()); 
     Resources res = getResources(); 
     BitmapDrawable backgroundDrawable = new BitmapDrawable(res, bitmap); 
     Bg.setBackgroundDrawable(backgroundDrawable); 
    } 


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

    @Override 
    public boolean onOptionsItemSelected(MenuItem item) { 
     // Handle action bar item clicks here. The action bar will 
     // automatically handle clicks on the Home/Up button, so long 
     // as you specify a parent activity in AndroidManifest.xml. 
     int id = item.getItemId(); 

     //noinspection SimplifiableIfStatement 
     if (id == R.id.action_settings) { 
      return true; 
     } 

     return super.onOptionsItemSelected(item); 
    } 

    public static int calculateInSampleSize(
      BitmapFactory.Options options, int reqWidth, int reqHeight) { 
     // Raw height and width of image 
     final int height = options.outHeight; 
     final int width = options.outWidth; 
     int inSampleSize = 1; 

     if (height > reqHeight || width > reqWidth) { 

      final int halfHeight = height/2; 
      final int halfWidth = width/2; 

      // Calculate the largest inSampleSize value that is a power of 2 and keeps both 
      // height and width larger than the requested height and width. 
      while ((halfHeight/inSampleSize) > reqHeight 
        && (halfWidth/inSampleSize) > reqWidth) { 
       inSampleSize *= 2; 
      } 
     } 

     return inSampleSize; 
    } 
    public static Bitmap decodeSampledBitmapFromResource(Resources res, int resId,int reqWidth, int reqHeight) { 

     // First decode with inJustDecodeBounds=true to check dimensions 
     final BitmapFactory.Options options = new BitmapFactory.Options(); 
     options.inJustDecodeBounds = true; 
     BitmapFactory.decodeResource(res, resId, options); 

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

     // Decode bitmap with inSampleSize set 
     options.inJustDecodeBounds = false; 
     return BitmapFactory.decodeResource(res, resId, options); 
    } 
} 

EDIT 3 Update

Обновленный Исходный код

@Override 
protected void onCreate(Bundle savedInstanceState) { 
    super.onCreate(savedInstanceState); 
    setContentView(R.layout.activity_main); 
    final RelativeLayout Bg = (RelativeLayout) findViewById(R.id.MainBg); 

    Bg.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { 
     @Override 
     public void onGlobalLayout() { 
      Bg.getViewTreeObserver().removeGlobalOnLayoutListener(this); 
      Bitmap bitmap = decodeSampledBitmapFromResource(getResources(), R.drawable.redboxes, Bg.getWidth(), Bg.getHeight()); 
      Resources res = getResources(); 
      BitmapDrawable backgroundDrawable = new BitmapDrawable(res, bitmap); 
      Bg.setBackgroundDrawable(backgroundDrawable); 
     } 
    }); 

} 

Обновлено Ошибка

FATAL EXCEPTION: main 
    Process: com.jacksteel.comp4, PID: 1872 
    java.lang.OutOfMemoryError: Failed to allocate a 107347980 byte allocation with 1048576 free bytes and 63MB until OOM 
      at dalvik.system.VMRuntime.newNonMovableArray(Native Method) 
      at android.graphics.BitmapFactory.nativeDecodeAsset(Native Method) 
      at android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:609) 
      at android.graphics.BitmapFactory.decodeResourceStream(BitmapFactory.java:444) 
      at android.graphics.BitmapFactory.decodeResource(BitmapFactory.java:467) 
      at com.jacksteel.comp4.MainActivity.decodeSampledBitmapFromResource(MainActivity.java:93) 
      at com.jacksteel.comp4.MainActivity$1.onGlobalLayout(MainActivity.java:27) 
      at android.view.ViewTreeObserver.dispatchOnGlobalLayout(ViewTreeObserver.java:912) 
      at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:1881) 
      at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1061) 
      at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:5885) 
      at android.view.Choreographer$CallbackRecord.run(Choreographer.java:767) 
      at android.view.Choreographer.doCallbacks(Choreographer.java:580) 
      at android.view.Choreographer.doFrame(Choreographer.java:550) 
      at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:753) 
      at android.os.Handler.handleCallback(Handler.java:739) 
      at android.os.Handler.dispatchMessage(Handler.java:95) 
      at android.os.Looper.loop(Looper.java:135) 
      at android.app.ActivityThread.main(ActivityThread.java:5257) 
      at java.lang.reflect.Method.invoke(Native Method) 
      at java.lang.reflect.Method.invoke(Method.java:372) 
      at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:903) 
      at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:698) 
+0

Это очень распространенная проблема. См. [Этот ответ] (http://stackoverflow.com/a/10127787/4191629). Я рекомендую использовать библиотеку загрузки изображений, такую ​​как [Picasso] (http://square.github.io/picasso/) или [Glide] (https://github.com/bumptech/glide), которая позаботится о изменение размера и кэширование памяти для вас. – maciekjanusz

+0

«Изображение меньше 1,5 МБ» - это сжатый размер на диске. Это мало влияет на объем памяти, необходимый для несжатого изображения. Каково разрешение этого изображения? И в каком каталоге ресурсов (или каталогах) у вас есть версии этого изображения? – CommonsWare

+0

Не могли бы вы разместить полный исходный код своего макета и активности? – mixel

ответ

4

UPDATE

Вы должны поместить изображение, которое будет загружаться с BitmapFactory.decodeResource() в drawable-nodpi каталоге, чтобы избежать масштабирования от плотности, используемой в имени drawable-<density> каталога для вашей плотности экрана устройства.

В вашем случае вы гнали изображение в drawable-hdpi директории и устройство имеет xxhdpi экран так, в BitmapFactory.decodeResource() с inSampleSize=2 изображений были 2x расширены (от hdpi до xxhdpi) и 2x масштабируется вниз (по inSampleSize), остающемуся в оригинальном размере и вызывающее приложении авария OutOfMemoryError.

оригинальный ответ

1.5MB является размер сжатого изображения. Когда вы загружаете его в ImageView или фон какого-либо другого вида, то Bitmap неявно создается. В Bitmap каждый пиксель изображения занимает 4 байта. Поэтому, если ваше изображение имеет разрешение 4096x4096 пикселей, оно занимает 64 мегабайта в памяти, хотя изображение может быть заполнено одним цветом и занимает всего несколько килобайт, сжатых до png или jpeg.

Вы должны определить реальный размер вашего зрения и загружать масштабируется изображение:

view.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { 
     @Override 
     public void onGlobalLayout() { 
      view.getViewTreeObserver().removeGlobalOnLayoutListener(this); 
      Bitmap bitmap = ImageUtils.decodeSampledBitmapFromFile(backgroundImageFile, view.getWidth(), view.getHeight()); 
      view.setBackground(new BitmapDrawable(getResources(), bitmap)); 
     } 
    }); 

Там мой метод утилита загружайте масштабируется Bitmap от File:

public class ImageUtils { 
    public static Bitmap decodeSampledBitmapFromFile(File file, int reqWidth, int reqHeight) { 

     // First decode with inJustDecodeBounds=true to check dimensions 
     final BitmapFactory.Options options = new BitmapFactory.Options(); 
     options.inJustDecodeBounds = true; 
     BitmapFactory.decodeFile(file.getAbsolutePath(), options); 

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

     // Decode bitmap with inSampleSize set 
     options.inJustDecodeBounds = false; 
     return BitmapFactory.decodeFile(file.getAbsolutePath(), options); 
    } 

    public static int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) { 
     // Raw height and width of image 
     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.floor((float) height/(float) reqHeight); 
      final int widthRatio = Math.floor((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; 
    } 
} 

Вы можете использовать другие BitmapFactory методы для декодировать Bitmap из массива потоков, ресурсов и байтов.

+0

с использованием .setBackground дает следующее: 'Для вызова требуется API-уровень 16 (текущий мин - 8): android.view.View # setBackground', и приложение все еще падает при запуске – Jack

+0

Это потому, что это новый API. Вместо этого вы должны использовать 'View.setBackgroundDrawable()'. – mixel

Смежные вопросы