我想将图像存储在我的数据库中。另外,我想检查图像和标题是否已存在于数据库中。如果是这样,它将不会将它们添加到数据库中。这是我的班级。
景点
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将错误指向contactimageadapter。
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的不同方式。非常感谢!
答案 0 :(得分:10)
你有多个地方整个图像(假设它很大)保留在内存中:
联系对象有它。所有加载的图像都在imageArry中,它是实例级变量。
public class Attractions extends ListActivity {
DataBaseHandler db = new DataBaseHandler(this);
ArrayList<Contact> imageArry = new ArrayList<Contact>();
在ContactImageAdapter.getView
方法中,您在holder对象中创建另一个图像副本作为BMP,并将其传递出方法。
所以,在某些时候你没有足够的内存来保留所有内容。此外,我确信decodeStream
需要更多内存才能执行。
毕竟,当getView
中创建的每个新持有人都被GC清理时,无法预测。
通常对于这种情况,当对象在某些方法中创建为new时,然后传递回调用方法,该对象将仅由Full GC收集。
所以,作为&#34;软件Sainath&#34;说,不要将图像存储在数据库中...... 并且不要将它们留在记忆中。
P.S。然后向视图提供指向外部图像文件的链接。这也将节省加载视图的时间。图像将位于缓存中,如果用户至少获得一次,则不会再次通过网络。
我猜想那里的图像不会经常改变它们自己。联系人的另一张图片将是另一个文件...
答案 1 :(得分:6)
我之前写了一个类似问题的答案,here是您可以检查的链接。问题在于将图像保存到数据库中的方法,您不应该这样做。而是将图像作为文件写在手机存储器上并进一步使用。
答案 2 :(得分:3)
最终不要将Image存储到Sqlite数据库,在将三或五个图像保存到数据库后,您将遇到内存不足错误。这不是最好的做法,在sqlite中为一行中的字段分配的最大内存小于3mb,请注意这一点。
不是将图像保存到数据库,而是将图像保留在应用程序文件夹中,保存数据库的路径。
您正在将图像加载到图像适配器。我们假设您的图像分辨率为1280x720,大小为2mb,它将在您的内存堆中占用相同的空间。
您可以按比例缩放图像并将其作为位图加载到适配器ImageView中。
在将图像加载为Bitmap之前,请获取高度和宽度。
//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();
如果您想了解更多关于位图和内存的信息,请查看link。
如果您不想自己处理重新缩放位图。您可以使用Glide或Picasso库来执行相同操作。
我写了一篇关于使用Picasso在listview中加载图片的文章,如果你想使用毕加索,它将帮助你开始。
http://codex2android.blogspot.in/2015/11/picasso-android-example.html
答案 3 :(得分: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();
}
}
答案 4 :(得分:1)