Does it store the data in memory or in the disk?
Asked this because I am planning to convert my parse file into an image thumbnail in which i set it to my list items. If it's in memory there would be no need to decode it in a background thread and since the file is in memory it would be garbage collected say when the activity is closed right?
ParseFile imageFile = event.getThumbnailFile();
final ParseImageView imageView = (ParseImageView) convertView.findViewById(R.id.event_thumbnail);
imageView.setPlaceholder(getResources().getDrawable(R.drawable.ic_report_problem_black_48dp));
imageFile.getDataInBackground(new GetDataCallback() {
#Override
public void done(byte[] bytes, ParseException e) {
Bitmap bitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.length); // will replace this to convert bigger images into small ones
imageView.setImageBitmap(bitmap);
}
});
It seems that if the getDataInBackground is successful it apparently stores the the ParseFile in a disk cache. Subsequent calls to the method updates the disk cache when the file is updated on the cloud.
Related
I'm downloading some text and images from the server using AsyncTask. I'm storing the texts and images in sq-lite database. I'm storing in the form of byte array.
In post Execute method of AsyncTask I store the convert the images in byte array and store them in database using for loop.
Problem is when I display data in List View from database sometimes it displays the correct images but some times it displays wrong images. I'm using a handler to delay the information into List View by 8 seconds. I've no idea why sometimes it shows the correct images and sometimes it shows wrong images.
What I'm doing wrong? How can I display the correct image every time. Moreover Can anyone help me how can I avoid the log cat message :
"Skipped XXX frames. The application is doing to much work in main thread."
I'm using fragments. As the application starts it will get data from server and which includes URL to Image. I'm using that URL to download images. Storing both the texts and images in database and showing it in List View in onPostExecute method.
Below are the codes I've used:
// convert from bitmap to byte array
public static byte[] getBytes(Bitmap bitmap) {
ByteArrayOutputStream byteArrayOutputStream =
new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.PNG, 0, byteArrayOutputStream);
return byteArrayOutputStream.toByteArray();
}
In adapter class where I'm extending CursorAdapter class. I'm getting byte array from sq-lite database and converting it in image and display it to List View.
Code to convert from byte array to bitmap is below:
private static Bitmap getImage(byte[] image) {
return BitmapFactory.decodeByteArray(image, 0, image.length);
}
I'm setting it to list View item like this:
byte [] image = cursor.getBlob(imageColumnIndex);
if(image == null)
{
}else{
Bitmap bitmap = getImage(image);
imageView.setImageBitmap(bitmap);
}
I'm workin with Volley API in my WebServices, and then I'll write the data in with SQLITE.
Webservices that comes with a JSON with many itens, each of then has data and a image, and I need to keep this image in my cache to display offline in a ListView and later on a detail screen. In the future the user will clean these itens and clear the imagens of them too.
Well, how can I save these images in my local DB and link with each Item that I have from the JSON?
I will be, in 90% of the time, offline. I will only stay online for synchronizing and download the updated server items.
For dealing with images in Android the benchmark is to use Picasso library. It takes care of:
Handling ImageView recycling and download cancelation in an adapter;
Complex image transformations with minimal memory use;
Automatic memory and disk caching.
Besides that, if you're going to display images in lists and need animation to enhance your UI, I strongly recommend changing from ListView to RecyclerView. It is not recommended to store the images in the DB, you would lose time converting this from/to blobs (check here and here). Said that, what I suggest is:
Use Picasso to load the images from the URL provided in the JSON;
Implement a Custom target to handle the image file downloaded by Picasso;
Save the image in a folder inside your app directory;
Use RecyclerView to display the images; (optional)
If you need a project example where those things are done, you can check this. In this project I follow the approache I've described above. You can download the app from the store and see how it will download the images.
Quicky-guide:
To use Picasso add this to you module's gradle file: compile 'com.squareup.picasso:picasso:2.5.2'
Create a class that implements import com.squareup.picasso.Callback;:
public class ImageWarehouse implements Callback {
private static final String TAG = "ImageWarehouse";
private String mDirectory;
private String mFileName;
private ImageView mContainer;
#Inject
App mApplication;
public ImageWarehouse(String fileName, ImageView container, String directory) {
this.mFileName = fileName;
this.mContainer = container;
this.mDirectory = directory;
this.getStorageDir();
}
#Override
public void onSuccess() {
if (this.isExternalStorageWritable()) {
final Bitmap bitmap = ((BitmapDrawable) this.mContainer.getDrawable()).getBitmap();
new AsyncTask<Void, Void, File>() {
#Override
protected File doInBackground(Void... params) {
File file = null;
try {
file = new File(ImageWarehouse.this.getStorageDir().getPath().concat("/").concat(ImageWarehouse.this.mFileName.concat(Constants.MEDIA_EXTENSION)));
file.createNewFile();
FileOutputStream ostream = new FileOutputStream(file);
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, ostream);
ostream.close();
} catch (Exception e) {
Log.e(TAG, "External Storage is not available");
}
return file;
}
}.execute();
} else {
Log.e(TAG, "External Storage is not available");
}
}
#Override
public void onError() {
}
public boolean isExternalStorageWritable() {
String state = Environment.getExternalStorageState();
if (Environment.MEDIA_MOUNTED.equals(state)) {
return true;
}
return false;
}
public File getStorageDir() {
File file = new File(Environment.getExternalStorageDirectory(), Constants.MEDIA_DIRECTORY.concat(this.mDirectory));
if (!file.mkdirs()) {
}
return file;
}
}
Call Picasso in order to display the image in the layout and save it to the specified path:
Picasso
.load(URL)
.fit()
.centerCrop()
.into(viewHolder.cover,
new ImageWarehouse(
name,
viewHolder.cover,
Constants.MEDIA_CHARACTER
)
);
You can user Android-Universal-Image-Loader it is also cache image in memory. it is show from cache when next time same url used in imageloader for get image
see below link for complete source code of Universal Image Loader Example.
Android - Universal Image Loader
You can save your images in Sqlite as blob and can retrieve it when required. Create table as
create table sometable(id integer primary key autoincrement,photo BLOB);
And save the image as
//convert image into byte[]
ByteArrayOutputStream baos = new ByteArrayOutputStream();
Bitmap bitmap = ((BitmapDrawable)getResources().getDrawable(R.drawable.common)).getBitmap();
bitmap.compress(Bitmap.CompressFormat.PNG, 100, baos);
byte[] photo = baos.toByteArray();
//And now store this image
ContentValues initialValues = new ContentValues();
initialValues.put("photo",photo);
return db.insert("sometable", null, initialValues);
And retrieve image as
Cursor cur=your query;
while(cur.moveToNext())
{
byte[] photo=cur.getBlob(index of blob cloumn);
}
I'm a little bit confused about the caching procedure when the images are stored into a custom ArrayAdapter....
As far as I know you can use LruCache only on Activities, so how to cache images in an adapter?
A Cache is just a data structure that keeps track of Objects. You can create your own cache, then save, for example, bitmaps using URLs as keys. For example, take this Object:
public static Map<String, Object> cache = new HashMap<String, Object>();
This is your cache. You can now save images by their urls. For example, say you get a bitmap from http://www.example.com/img.png. A simple method like this will get the cached image if it exists, or get a new one if it does not:
public Bitmap getImage(String url)
{
synchronized(cache) {
Object o = cache.get(url);
if (o != null)
return (Bitmap) o;
//here, get the bitmap from the URL using whatever method you want, then save it and return it:
Bitmap bmp = getBitmapForURL(url);
cache.put(url, bmp);
return bmp;
}
}
So you just call:
myImageView.setImageBitmap(getImage("http://www.example.com/img.png"));
Store only image URIs in adapter not the images.
I am downloading images from server into the ListView , now to perform this task i am using ImageDownloader example code. so far its working fine.
But i want to save images of ListView in a SD card but i am confused when to store the images as images are being downloaded Asynchronously and because of ViewHolder pattern its little tough for me to judge.
Once i stored it in a SD card next time i want to read it from memory only.
ImageDownload is storing bitmap in cache and fetching it from there once it gets downloaded.But the problem is its behavior is not predictable.
Sometimes it downloads from server and sometimes from cache.
so can anyone help me in finding what is the proper place to store the images in sd card once.
Modify your ImageDownloader class to save the image like this :
add a parameter to download method like :
download(String url, ImageView imageView, Boolean saveData)
make a global variable saveData in yout ID class :
private Boolean saveData;
and store in it the value given as parameter in download dmethod:
this.saveData = saveData;
and the BitmapDownloaderTask's onPostExecute method should look like this :
#Override
protected void onPostExecute(Bitmap bitmap) {
if (isCancelled()) {
bitmap = null;
}
addBitmapToCache(url, bitmap);
if (saveData == true) {
try {
FileOutputStream out = new FileOutputStream(path);
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, out);
} catch (Exception e) {
e.printStackTrace();
}
}
if (imageViewReference != null) {
ImageView imageView = imageViewReference.get();
BitmapDownloaderTask bitmapDownloaderTask = getBitmapDownloaderTask(imageView);
// Change bitmap only if this process is still associated with it
if (this == bitmapDownloaderTask) {
imageView.setImageBitmap(bitmap);
}
}
}
where path is the path were you want to save the image .
and next time before you want to load the image you have to see if it is already downloaded and load it from the path otherwise call ImageDownloader.
that's it! enjoy!
There's a 2MB limit on the server where I need to upload an image.
I'm using this method to downsample a Bitmap Strange out of memory issue while loading an image to a Bitmap object
within this method
public InputStream getPhotoStream(int imageSizeBytes) throws IOException {
int targetLength = 1500;
ByteArrayOutputStream photoStream;
byte[] photo;
Bitmap pic;
final int MAX_QUALITY = 100;
int actualSize = -1;
do {
photo = null;
pic = null;
photoStream = null;
//this calls the downsampling method
pic = getPhoto(targetLength);
photoStream = new ByteArrayOutputStream();
pic.compress(CompressFormat.JPEG, MAX_QUALITY, photoStream);
photo = photoStream.toByteArray();
actualSize = photo.length;
targetLength /= 2;
} while (actualSize > imageSizeBytes);
return new ByteArrayInputStream(photo);
}
This throws OutOfMemoryError on the second iteration. How could I downsample the image below a certain size limit?
I think the problem is happening because you are compressing the image to a in memory representation, you need to free up that memory before you try to compress again.
You need call close() in the photoStream before trying again in order to free up resources.
Also toByteArray() makes a copy of the stream in memory that you have to free up later, why don't you just use the photoStream.size() to check for the file size?
I can post some code if you need.
Instead of this:
pic = null;
Do this:
if (pic!=null)
pic.recycle();
pic = null
If you simply set the bitmap object to null the memory it occupied isn't released immediately. In the second case you are explicitly telling the OS you are done with the bitmap and its okay to release its memory.
Also consider using a compression quality of 90 instead of 100, I believe that will reduce the resulting file size quite a bit.