在sqlite db中存储图像时发生java.lang.OutOfMemoryError

在sqlite db中存储图像时发生java.lang.OutOfMemoryError,java,android,sqlite,bitmap,out-of-memory,Java,Android,Sqlite,Bitmap,Out Of Memory,我想在我的数据库中存储图像。我还想检查一下,如果图像和标题已经在数据库中。如果是这样,它将不会将它们添加到数据库中。这是我的课 景点 public class Attractions extends ListActivity { DataBaseHandler db = new DataBaseHandler(this); ArrayList<Contact> imageArry = new ArrayList<Contact>(); List<Contact>

我想在我的数据库中存储图像。我还想检查一下,如果图像和标题已经在数据库中。如果是这样,它将不会将它们添加到数据库中。这是我的课

景点

public class Attractions extends ListActivity {
DataBaseHandler db = new DataBaseHandler(this);
ArrayList<Contact> imageArry = new ArrayList<Contact>();
List<Contact> contacts;
ContactImageAdapter adapter;
int ctr, loaded; 
int [] landmarkImages={R.drawable.oblation,R.drawable.eastwood,R.drawable.ecopark,R.drawable.circle};
String []landmarkDetails = { "Oblation", "Eastwood", "Ecopark", "QC Circle"};

/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_attractions);
    ctr  = db.checkContact(landmarkDetails[loaded]);



    // get image from drawable

    /**
     * CRUD Operations
     * */
    // Inserting Contacts
    Log.d("Insert: ", "Inserting ..");


    for(loaded=0; loaded <landmarkDetails.length;loaded++){

        Bitmap image = BitmapFactory.decodeResource(getResources(),
                landmarkImages[loaded]);


        // convert bitmap to byte
        ByteArrayOutputStream stream = new ByteArrayOutputStream();
        image.compress(Bitmap.CompressFormat.JPEG, 100, stream);
        byte imageInByte[] = stream.toByteArray();
        Log.d("Going to load images", "Image "+ loaded);

        Log.d("Goind to load objects", "loading");

        if(ctr == 0){
            Log.d("Nothing Loaded", "Loading Now");
            db.addContact(new Contact(landmarkDetails[loaded], imageInByte));}
            Log.d(landmarkDetails[loaded], "Loaded!");
            image.recycle();
    }
    loadFromDb();


}

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


    return true;
}

public void loadFromDb(){
    // Reading all contacts from database
            contacts = db.getAllContacts();
            for (Contact cn : contacts) {
                String log = "ID:" + cn.getID() + " Name: " + cn.getName()
                        + " ,Image: " + cn.getImage();

                // Writing Contacts to log
                Log.d("Result: ", log);
                //add contacts data in arrayList
                imageArry.add(cn);

            }
            adapter = new ContactImageAdapter(this, R.layout.screen_list,
                    imageArry);
            ListView dataList = (ListView) findViewById(android.R.id.list);
            dataList.setAdapter(adapter);

}

public void onPause(){
    super.onPause();
}

public void onResume(){
    super.onResume();

}


}
并指向这一行
位图theImage=BitmapFactory.decodeStream(imageStream)


我对管理图像和存储图像几乎一无所知。我还启用了
android:largeHeap
,但仍然会强制关闭多次尝试。我希望有人能帮助我解决这个问题,或者至少向我展示一种将文本和图像存储到sqlite db的不同方式。非常感谢

您有多个位置可以将整个图像(假设它很大)保存在内存中:

  • 联系人对象有它。所有加载的映像都位于实例级变量imageArry中

    public class Attractions extends ListActivity {
        DataBaseHandler db = new DataBaseHandler(this);
        ArrayList<Contact> imageArry = new ArrayList<Contact>();
    
    公共类活动{
    DataBaseHandler db=新的DataBaseHandler(此);
    ArrayList imageArry=新的ArrayList();
    
  • ContactImageAdapter.getView
    方法中,您在holder对象中创建另一个图像副本作为BMP,并将其从方法中传递出去。 因此,在某些情况下,您没有足够的内存来保存所有这些文件。此外,我确信
    decodeStream
    需要更多的内存来执行

  • 毕竟,GC将清理在
    getView
    中创建的每个新保持架的时间是不可预测的。 通常在这种情况下,当对象在某个方法中创建为新对象,然后传递回调用方法时,该对象将仅由完整GC收集

    所以,正如“软件Sainath”所说,不要在数据库中存储图像… 也不要把它们留在记忆中

    请注意,然后向视图提供指向外部图像文件的链接。这也将节省加载视图的时间。图像将在缓存中,如果用户至少获得一次,它将不会再次通过网络


    我猜那里的图像不会经常更改。联系人的另一个图像将是另一个文件…

    我不久前写了一个关于类似问题的答案,是您可以检查的链接。问题在于如何将图像保存到数据库中,您不应该这样做。相反,将图像作为文件写入在手机内存中添加,并进一步使用

  • 最终不要将图像存储到Sqlite数据库中,在将三到五个图像保存到数据库后,您将遇到内存不足错误。这不是最佳做法,Sqlite中为一行中的字段分配的最大内存小于3mb,请注意这一点

  • 不要将图像保存到数据库,而是将图像保存在应用程序文件夹中,保存到数据库的路径

  • 您正在按原样将图像加载到图像适配器。假设您的图像分辨率为1280x720,大小为2mb,它将占用内存堆中相同的空间

  • 您可以缩小图像并将其作为位图加载到适配器ImageView,如下所示

    在将图像加载为位图之前,请获取图像的高度和宽度

    //Code read the image and give you image height and width.it won't load your bitmap. 
    
    
      BitmapFactory.Options option = new BitmapFactory.Options();
                    option.inJustDecodeBounds = true;
                    BitmapFactory.decodeFile(your_image_url,option);
                    int image_original_width = option.outHeight;
                    int image_original_height = option.outWidth;
    
    现在要缩小图像,你必须知道ImageView的宽度和高度。这是因为我们要缩小与像素完美匹配的ImageView的图像

    int image_view_width = image_view.getWidht();
     int image_view_height = image_view.getHeight();
     int new_width;
     int new_height;
    
    float scaled_width;
     if(image_original_width>image_view_width)
    {     //if the image_view width is lesser than original_image_width ,you have to scaled down the image.  
         scale_value =(float)image_original_width/(float)image_view_width; 
         new_width = image_original_width/scaled_value;
         new_height = image_orignal_height/scale_value
    }
    else
    {
      // use the image_view width and height as sacling value widht and height;
          new_width = image_view_width;
          new_height = image_view_height;
    }
    
    现在缩小位图并按如下方式加载

      // this will load a bitmap with 1/4 the size of the original one.
       // this to lower your bitmap memory occupation in heap. 
       BitmapFactory.Options option = new BitmapFactory.Options();
       option.inSampleSize = 4;
       Bitmap current_bitmap = BitmapFactory.decodeFile(image_url,option);
       Bitmap scaled_bitmap =      Bitmap.createScaledBitmap(current_bitmap,new_width,new_height,true);
    holder.imgIcon.setImageBitmap(scaled_bitmap);
    //release memory occupied by current_bitmap in heap, as we are no longer using it. 
    current_bitmap.recycle();
    
    如果您想了解更多关于位图和内存的信息,请查看此视图

    如果您不想自己处理位图的重缩放,您可以使用或库来完成同样的操作

    我写了一篇关于使用毕加索在listview中加载图像的文章,这将帮助您开始使用毕加索


    从网络加载图像时,请确保使用快速垃圾收集合格引用类型

    import java.lang.ref.SoftReference;
    导入java.util.Collections;
    导入java.util.HashMap;
    导入java.util.Map;
    导入android.graphics.Bitmap;
    公共类内存缓存{
    私有映射缓存=Collections.synchronizedMap(新的HashMap());
    公共位图获取(字符串id){
    如果(!cache.containsKey(id))
    返回null;
    SoftReference ref=cache.get(id);
    返回ref.get();
    }
    公共void put(字符串id、位图){
    cache.put(id,新的软引用(位图));
    }
    公共空间清除(){
    cache.clear();
    }
    }
  • 不要将图像存储到Sqlite数据库。这不是最佳做法
  • 不要将图像保存到数据库中,而是将图像保存在存储器中,但如果您想将其保持为私有,则将其保存在应用程序文件夹中,并保存到数据库的路径
  • 使用一个著名的库,比如or,它们为解决内存问题和一些很酷的过渡效果提供了很大的帮助。 我建议使用Glide,因为它在内存限制较低的设备上工作得很好

  • 通常建议将文件存储在应用程序目录中(外部)必要时在数据库中维护文件路径从数据库中插入图像和检索图像是一项复杂的操作按照图像大小的说明进行操作?也许你需要调整图像大小并在存储之前对其进行压缩。作为一名业余摄影师,我知道你可以将jpg压缩到至少80%,而不会对qua造成明显影响在大多数情况下也是70%(假设您没有在同一张图像上多次这样做)。您当前有100%的质量比。如果您调整大小和压缩,这可能会对所需大小和内存产生很大影响。请检查所有具有多少mb的图像。如果图像更大,则会出现错误。无论您使用的是SQLite、Oracle还是MS SQL Server,将图像或大斑点添加到任何数据库都是一个糟糕的主意这将在数据库的所有表中导致巨大的性能损失。
      // this will load a bitmap with 1/4 the size of the original one.
       // this to lower your bitmap memory occupation in heap. 
       BitmapFactory.Options option = new BitmapFactory.Options();
       option.inSampleSize = 4;
       Bitmap current_bitmap = BitmapFactory.decodeFile(image_url,option);
       Bitmap scaled_bitmap =      Bitmap.createScaledBitmap(current_bitmap,new_width,new_height,true);
    holder.imgIcon.setImageBitmap(scaled_bitmap);
    //release memory occupied by current_bitmap in heap, as we are no longer using it. 
    current_bitmap.recycle();