2013-11-20 7 views
14

私のデータベースにイメージを保存します。また、イメージとタイトルがすでにデータベースに入っているかどうか確認したいと思います。そうであれば、データベースに追加されません。これは私のクラスです。sqlite dbにイメージを格納する際のjava.lang.OutOfMemoryError

観光

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(); 

} 


} 

それは、エミュレータ上で正常に動作しますが、私は私のS4でのテストを試してみましたが、その後、このクラスに行くの3回の試行の後に、それは停止を余儀なくされました。私はUSBデバッグでそれを試して、logcatはjava.lang.outofmemoryerrorを示した。 logcatは、私のcontactimageアダプターのエラーを指摘しました。

ContactImageAdapter

public class ContactImageAdapter extends ArrayAdapter<Contact>{ 
Context context; 
    int layoutResourceId; 
    // BcardImage data[] = null; 
    ArrayList<Contact> data=new ArrayList<Contact>(); 
    public ContactImageAdapter(Context context, int layoutResourceId, ArrayList<Contact> data) { 
     super(context, layoutResourceId, data); 
     this.layoutResourceId = layoutResourceId; 
     this.context = context; 
     this.data = data; 
    } 

    @Override 
    public View getView(int position, View convertView, ViewGroup parent) { 
     View row = convertView; 
     ImageHolder holder = null; 

     if(row == null) 
     { 
      LayoutInflater inflater = ((Activity)context).getLayoutInflater(); 
      row = inflater.inflate(layoutResourceId, parent, false); 

      holder = new ImageHolder(); 
      holder.txtTitle = (TextView)row.findViewById(R.id.txtTitle); 
      holder.imgIcon = (ImageView)row.findViewById(R.id.imgIcon); 
      row.setTag(holder); 
     } 
     else 
     { 
      holder = (ImageHolder)row.getTag(); 
     } 

     Contact picture = data.get(position); 
     holder.txtTitle.setText(picture._name); 
     //convert byte to bitmap take from contact class 

     byte[] outImage=picture._image; 
     ByteArrayInputStream imageStream = new ByteArrayInputStream(outImage); 
     Bitmap theImage = BitmapFactory.decodeStream(imageStream); 
     holder.imgIcon.setImageBitmap(theImage); 
     return row; 

    } 

    static class ImageHolder 
    { 
     ImageView imgIcon; 
     TextView txtTitle; 
    } 
} 

そして、私は画像を管理し、それらを保存するにはほとんど(ほとんどない)の知識を持って、このラインにBitmap theImage = BitmapFactory.decodeStream(imageStream);

を指摘しました。私はandroid:largeHeapも有効にしますが、複数の試行で強制的にクローズします。誰かがこの問題を解決するのを手伝ってくれることを願っています。少なくとも、sqlite dbにテキストと画像を保存する方法を教えてください。どうもありがとう!

+5

一般に、アプリケーションディレクトリ(外部)にファイルを保存し、必要に応じてデータベース内のファイルパスを維持することをおすすめします。複雑な操作 –

+0

http://developer.androidの指示に従ってください。com/training/displaying-bitmaps/load-bitmap.html –

+0

画像の大きさはどれくらいですか?おそらく、保存する前にサイズを変更して圧縮する必要があります。アマチュアの写真家としては、ほとんどの場合、同じ画像で何度もやってはいけないと仮定して、ほとんどの場合70%という品質に目立った影響を与えずに、少なくとも80%までJPEGを圧縮できることがわかります。あなたは現在、100%の品質比率を持っています。サイズを変更して圧縮すると、必要なサイズとそれに必要なメモリ容量に大きな違いが生じます。 –

答えて

10

あなたは(それが大きいと仮定して)画像全体の複数の場所を持っているが、メモリに保持します:

  1. Contactオブジェクトは、それを持っています。読み込まれたすべてのイメージは、インスタンスレベルの変数であるimageArryにあります。

    public class Attractions extends ListActivity { 
        DataBaseHandler db = new DataBaseHandler(this); 
        ArrayList<Contact> imageArry = new ArrayList<Contact>(); 
    
  2. ContactImageAdapter.getView方法であなたは、ホルダーオブジェクトにBMPなどの画像の別のコピーを作成し、メソッドの外にそれを渡します。 したがって、ある時点では、それらのすべてを保持するのに十分なメモリがありません。また、私は確かにdecodeStreamが実行するためにいくつかのより多くのメモリを必要とします。 getViewで作成されたそれぞれの新しい所有者は、GCによって清掃されるとき、それは予測できないすべての後

。 通常、このような状況では、あるメソッドで新しいオブジェクトが作成され、呼び出し元のメソッドに渡されると、そのオブジェクトはFull GCによってのみ収集されます。

"Software Sainath"と言っているように、データベースに画像を保存しないでください。 そしてそれらもメモリに保存しないでください。

P.S.その後、ビューに外部イメージファイルへのリンクを提供します。また、ビューをロードする時間も節約できます。画像はキャッシュに保存され、ユーザーが少なくとも1度取得した場合、ネットワークを再び通過しません。

私は画像が頻繁に自分自身を変更することはありませんね。連絡先の別のイメージは別のファイルになります...

5

私は幾分似たような問題に対する答えを書いていましたが、hereはあなたが確認できるリンクです。問題は、データベースにイメージを保存するというアプローチにあります。これを行うべきではありません。代わりに、イメージをファイルとして電話機のメモリに書き込み、さらにそれを使用します。

3
  1. イメージをSqliteデータベースに保存しないと、データベースに3つまたは5つのイメージが保存された後、メモリ不足エラーが発生します。これはベストプラクティスではなく、sqliteの行内のフィールドに割り当てられる最大メモリは3MB未満です。これに注意してください。

  2. イメージをデータベースに保存する代わりに、アプリケーションフォルダ内にイメージを保存し、データベースへのパスを保存します。

  3. あなたのイメージをそのままイメージ・アダプターにロードしています。たとえば、イメージが1280x720解像度で2MBのサイズであれば、メモリヒープと同じ領域が使用されます。

イメージを縮小して、このようにアダプタイメージビューにビットマップとして読み込むことができます。

イメージをビットマップとして読み込む前に、その高さと幅を取得してください。

//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; 

Imageを縮小するには、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(); 

あなたはこのlinkをビットマップとメモリビューについてもう少し理解したい場合。

ビットマップのリスケールを自分で処理したくない場合は、同じことをするGlideまたはPicassoライブラリを使用することができます。

picassoを使用する場合は、ピカソを使用して画像を読み込むための記事を作成しました。これは、リストビューで画像を読み込む際に役立ちます。

http://codex2android.blogspot.in/2015/11/picasso-android-example.html

1

ネットワーク

import java.lang.ref.SoftReference; 
 
import java.util.Collections; 
 
import java.util.HashMap; 
 
import java.util.Map; 
 
import android.graphics.Bitmap; 
 
    
 
public class MemoryCache { 
 
    private Map<String, SoftReference<Bitmap>> cache=Collections.synchronizedMap(new HashMap<String, SoftReference<Bitmap>>()); 
 
    
 
    public Bitmap get(String id){ 
 
     if(!cache.containsKey(id)) 
 
      return null; 
 
     SoftReference<Bitmap> ref=cache.get(id); 
 
     return ref.get(); 
 
    } 
 
    
 
    public void put(String id, Bitmap bitmap){ 
 
     cache.put(id, new SoftReference<Bitmap>(bitmap)); 
 
    } 
 
    
 
    public void clear() { 
 
     cache.clear(); 
 
    } 
 
}

1
  1. からイメージをロード中にSqliteをへのイメージを保管しないでください迅速なガベージコレクション対象と参照型を使用していることを確認してくださいデータベース。それはベストプラクティスではありません。
  2. イメージをデータベースに保存するのではなく、イメージをストレージに保存しますが、プライベートにしておきたい場合は、アプリケーションフォルダ内に保管し、データベースへのパスを保存します。
  3. http://square.github.io/picasso/またはhttps://github.com/bumptech/glideのようなよく知られているライブラリの1つを使用すると、メモリの問題やいくつかのクールなトランジション効果が得られます。 Glideを使用することをお勧めします。メモリ制限の少ないデバイスではうまく機能するためです。
関連する問題