I am caching my images in android and not sure how to reuse bitmaps as android suggest here:
https://developer.android.com/training/displaying-bitmaps/manage-memory.html
here is my code
final int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);
// Use 1/8th of the available memory for this memory cache.
final int cacheSize = maxMemory / 8;
this.imageCache= new LruCache<String, Bitmap>(cacheSize){
#Override
protected int sizeOf(String key, Bitmap bitmap) {
// The cache size will be measured in kilobytes rather than
// number of items.
return bitmap.getByteCount() / 1024;
}
};
this.m_adapter = new ImageScreenAdapter(this, R.layout.imagelist_item, items, imageCache);
setListAdapter(this.m_adapter);
this is the method I use to download my bitmaps
private Bitmap downloadBitmap(String url, ProgressBar progress, int position) {
final AndroidHttpClient client = AndroidHttpClient.newInstance("Android");
final HttpGet getRequest = new HttpGet(url);
try {
HttpResponse response = client.execute(getRequest);
final int statusCode = response.getStatusLine().getStatusCode();
if (statusCode != HttpStatus.SC_OK) {
Log.w("ImageDownloader", "Error " + statusCode
+ " while retrieving bitmap from " + url);
if(progress!=null)
{
RemoveImageResults(position);
return null;
}
return BitmapFactory.decodeResource(getResources(), R.drawable.missingpic);
}
final HttpEntity entity = response.getEntity();
if (entity != null) {
InputStream inputStream = null;
try {
inputStream = entity.getContent();
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = false;
options.inDither = true;
final Bitmap bitmap = BitmapFactory.decodeStream(inputStream,null, options);
return bitmap;
} finally {
if (inputStream != null) {
inputStream.close();
}
entity.consumeContent();
}
}
} catch (Exception e) {
// Could provide a more explicit error message for IOException or
// IllegalStateException
getRequest.abort();
//Log.w("ImageDownloader", "Error while retrieving bitmap from " + url);
} finally {
if (client != null) {
client.close();
}
}
return null;
}
and in my AsyncTask
#Override
protected void onPostExecute(Bitmap result) {
final Bitmap Image=result;
if(Image!=null)
imageCache.put(imageUrl, Image);
myActivity.runOnUiThread(new Runnable() {
public void run() {
imageView.setImageBitmap(Image);
imageView.setVisibility(View.VISIBLE);
}
});
}
private Bitmap download_Image(String url) {
return downloadBitmap(url, progress, position);
}
But this could run out of memory if it gets up to 1000 images in my list adapter, so how do I reuse the bitmaps or recycle the unused ones? I am targeting android 3.0 or better and as android suggest I could use a Set> mReusableBitmaps; but I don't follow how to implement this.
The best way to handle this issue is to implement some kind of lazy image loading where you have weak referenced bitmaps that can be recycled by the system easier. There is a ton of samples online and there is a very popular open source library on github that does all this for you. They even have callbacks that you can use to display a progress bar while your image loads and get rid of it when the image is done downloading. You can find it here : https://github.com/nostra13/Android-Universal-Image-Loader
Related
I have build an app. I use bitmaps to get images from Instagram API and stora them in a GridView.
But I've a problem, when I use bitmaps and memory cache.
My code is:
public void DisplayImage(String urrl, ImageView imageView) {
String url = urrl.replaceAll(" ", "%20");
imageViews.put(imageView, url);
Bitmap bitmap = memoryCache.get(url);
if (bitmap != null)
imageView.setImageBitmap(bitmap);
else {
queuePhoto(url, imageView);
imageView.setImageResource(stub_id);
}
}
The variable bitmap returns null, from method memoryCache.get(url):
public Bitmap get(String id){
try{
if(!cache.containsKey(id)) {
return null; }else {
return cache.get(id); }
}catch(NullPointerException ex){
ex.printStackTrace();
return null;
}
}
I tried some solutions, but with no success.
I saw other solutions for similar problems, but neither can solve mine.
I'm new to bitmaps, and this kind of stuff.
EDIT
public class MemoryCache {
private static final String TAG = "MemoryCache";
private Map<String, Bitmap> cache= Collections.synchronizedMap(
new LinkedHashMap<String, Bitmap>(10,1.5f,true));//Last argument true for LRU ordering
private long size=0;//current allocated size
private long limit=1000000;//max memory in bytes
public MemoryCache(){
//use 25% of available heap size
setLimit(Runtime.getRuntime().maxMemory()/4);
}
public void setLimit(long new_limit){
limit=new_limit;
Log.i(TAG, "MemoryCache will use up to "+limit/1024./1024.+"MB");
}
public Bitmap get(String id){
try{
if(!cache.containsKey(id)) {
return null; }else {
//NullPointerException sometimes happen here http://code.google.com/p/osmdroid/issues/detail?id=78
return cache.get(id); }
}catch(NullPointerException ex){
ex.printStackTrace();
return null;
}
}
public void put(String id, Bitmap bitmap){
try{
if(cache.containsKey(id))
size-=getSizeInBytes(cache.get(id));
cache.put(id, bitmap);
size+=getSizeInBytes(bitmap);
checkSize();
}catch(Throwable th){
th.printStackTrace();
}
}
private void checkSize() {
Log.i(TAG, "cache size="+size+" length="+cache.size());
if(size>limit){
Iterator<Map.Entry<String, Bitmap>> iter=cache.entrySet().iterator();//least recently accessed item will be the first one iterated
while(iter.hasNext()){
Map.Entry<String, Bitmap> entry=iter.next();
size-=getSizeInBytes(entry.getValue());
iter.remove();
if(size<=limit)
break;
}
Log.i(TAG, "Clean cache. New size "+cache.size());
}
}
public void clear() {
try{
//NullPointerException sometimes happen here http://code.google.com/p/osmdroid/issues/detail?id=78
cache.clear();
size=0;
}catch(NullPointerException ex){
ex.printStackTrace();
}
}
long getSizeInBytes(Bitmap bitmap) {
if(bitmap==null)
return 0;
return bitmap.getRowBytes() * bitmap.getHeight();
}
}
ImageLoader:
public class ImageLoader {
MemoryCache memoryCache = new MemoryCache();
FileCache fileCache;
private Map<ImageView, String> imageViews = Collections
.synchronizedMap(new WeakHashMap<ImageView, String>());
ExecutorService executorService;
Handler handler = new Handler();// handler to display images in UI thread
public ImageLoader(Context context) {
fileCache = new FileCache(context);
executorService = Executors.newFixedThreadPool(5);
}
final int stub_id = R.drawable.crayon;
public void DisplayImage(String urrl, ImageView imageView) {
String url = urrl.replaceAll(" ", "%20");
imageViews.put(imageView, url);
Bitmap bitmap = memoryCache.get(url);
if (bitmap != null)
imageView.setImageBitmap(bitmap);
else {
queuePhoto(url, imageView);
imageView.setImageResource(stub_id);
}
}
private void queuePhoto(String url, ImageView imageView) {
PhotoToLoad p = new PhotoToLoad(url, imageView);
executorService.submit(new PhotosLoader(p));
}
private Bitmap getBitmap(String url) {
File f = fileCache.getFile(url);
// from SD cache
Bitmap b = decodeFile(f);
if (b != null)
return b;
// from web
try {
Bitmap bitmap = null;
URL imageUrl = new URL(url);
HttpURLConnection conn = (HttpURLConnection) imageUrl
.openConnection();
conn.setConnectTimeout(30000);
conn.setReadTimeout(30000);
conn.setInstanceFollowRedirects(true);
InputStream is = conn.getInputStream();
OutputStream os = new FileOutputStream(f);
Utils.CopyStream(is, os);
os.close();
conn.disconnect();
bitmap = decodeFile(f);
return bitmap;
} catch (Throwable ex) {
ex.printStackTrace();
if (ex instanceof OutOfMemoryError)
memoryCache.clear();
return null;
}
}
// decodes image and scales it to reduce memory consumption
private Bitmap decodeFile(File f) {
try {
// decode image size
BitmapFactory.Options o = new BitmapFactory.Options();
o.inJustDecodeBounds = true;
FileInputStream stream1 = new FileInputStream(f);
BitmapFactory.decodeStream(stream1, null, o);
stream1.close();
// Find the correct scale value. It should be the power of 2.
final int REQUIRED_SIZE = 70;
int width_tmp = o.outWidth, height_tmp = o.outHeight;
int scale = 1;
while (true) {
if (width_tmp / 2 < REQUIRED_SIZE
|| height_tmp / 2 < REQUIRED_SIZE)
break;
width_tmp /= 2;
height_tmp /= 2;
scale *= 2;
}
// decode with inSampleSize
BitmapFactory.Options o2 = new BitmapFactory.Options();
o2.inSampleSize = scale;
FileInputStream stream2 = new FileInputStream(f);
Bitmap bitmap = BitmapFactory.decodeStream(stream2, null, o2);
stream2.close();
return bitmap;
} catch (FileNotFoundException e) {
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
// Task for the queue
private class PhotoToLoad {
public String url;
public ImageView imageView;
public PhotoToLoad(String u, ImageView i) {
url = u;
imageView = i;
}
}
class PhotosLoader implements Runnable {
PhotoToLoad photoToLoad;
PhotosLoader(PhotoToLoad photoToLoad) {
this.photoToLoad = photoToLoad;
}
#Override
public void run() {
try {
if (imageViewReused(photoToLoad))
return;
Bitmap bmp = getBitmap(photoToLoad.url);
memoryCache.put(photoToLoad.url, bmp);
if (imageViewReused(photoToLoad))
return;
BitmapDisplayer bd = new BitmapDisplayer(bmp, photoToLoad);
handler.post(bd);
} catch (Throwable th) {
th.printStackTrace();
}
}
}
boolean imageViewReused(PhotoToLoad photoToLoad) {
String tag = imageViews.get(photoToLoad.imageView);
if (tag == null || !tag.equals(photoToLoad.url))
return true;
return false;
}
// Used to display bitmap in the UI thread
class BitmapDisplayer implements Runnable {
Bitmap bitmap;
PhotoToLoad photoToLoad;
public BitmapDisplayer(Bitmap b, PhotoToLoad p) {
bitmap = b;
photoToLoad = p;
}
public void run() {
if (imageViewReused(photoToLoad))
return;
if (bitmap != null)
photoToLoad.imageView.setImageBitmap(bitmap);
else
photoToLoad.imageView.setImageResource(stub_id);
}
}
public void clearCache() {
memoryCache.clear();
fileCache.clear();
}
}
SOLUTION
In class MyGridView, i used Glide like Shmuel send on link!
View view = inflater.inflate(R.layout.medialist_inflate, null);
imgView = (ImageView) view.findViewById(R.id.ivImage);
Glide.with(context).load(imageThumbList.get(position)).into(imgView);
return view;
For loading images from a web url the best practice approach is the use the Glide image loading library.
https://github.com/bumptech/glide
It is an open source image loading library that is recommend and supported by Google.
The api looks like this -
Glide
.with(context)
.load(url)
.centerCrop()
.placeholder(R.drawable.loading_spinner)
.crossFade()
.into(myImageView);
See how easy that is :D
It automatically handles caching the images (both in memory and to disk).
Im using DisplayingBitmap.zip downloaded from developers android site to load images asynchronously into the imageview. Im receiving base64 strings from webservice. so modified the code to convert base64 to bitmap in ImageFetcher.class from (DisplayingBitmaps) instead of downloading image from url.
NOTE: Im receiving gif images in the form of base64 string.
Converting base64 to Bitmap
public Bitmap convertBase64ToImage(String mBase64String) {
Bitmap bitmap = null;
try {
String imageDataBytes = mBase64String.substring(mBase64String.indexOf(",") + 1);
InputStream stream = new ByteArrayInputStream(Base64.decode(imageDataBytes.getBytes(), Base64.DEFAULT));
bitmap = BitmapFactory.decodeStream(stream);
} catch (Exception e) {
e.printStackTrace();
}
return bitmap;
}
}
Converting base64 bitmap to file inorder to obtain decoded resized bitmap using processBitmap method in ImageFetcher.class(DisplayingBitmaps):
/**
* The main process method, which will be called by the ImageWorker in the AsyncTask background
* thread.
*
* #param data The data to load the bitmap, in this case, a regular http URL
* #return The downloaded and resized bitmap
*/
private Bitmap processBitmap(String data, String imageID) {
if (BuildConfig.DEBUG) {
PrintLog.error(TAG, "processBitmap --- imageID " + imageID);
}
Bitmap bitmap = null;
bitmap = convertBase64ToImage(data);
if (bitmap != null) {
File f = null;
try {
//create a file to write bitmap data
f = new File(mContext.getFilesDir(), imageID);
f.createNewFile();
//Convert bitmap to byte array
ByteArrayOutputStream bos = new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.PNG, 100/*ignored for PNG*/, bos);
byte[] bitmapdata = bos.toByteArray();
//write the bytes in file
FileOutputStream fos = new FileOutputStream(f);
fos.write(bitmapdata);
fos.flush();
fos.close();
} catch (Exception e) {
e.printStackTrace();
}
bitmap = decodeSampledBitmapFromFile(f.getAbsolutePath(), mImageWidth, mImageHeight, getImageCache());
}
return bitmap;
}
#Override
protected Bitmap processBitmap(Object data, String imageID) {
return processBitmap(String.valueOf(data), imageID);
}
decodeSampledBitmapFromFile method from ImageResizer.class
public static Bitmap decodeSampledBitmapFromFile(String filename, int reqWidth, int reqHeight, ImageCache cache) {
// First decode with inJustDecodeBounds=true to check dimensions
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeFile(filename, options);
// Calculate inSampleSize
options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
// If we're running on Honeycomb or newer, try to use inBitmap
if (DeviceUtils.hasHoneycomb()) {
addInBitmapOptions(options, cache);
}
// Decode bitmap with inSampleSize set
options.inJustDecodeBounds = false;
return BitmapFactory.decodeFile(filename, options);
}
Implementing ImageFetcher.class(DisplayingBitmaps.zip) in my class
private static final String IMAGE_CACHE_DIR = "clubsCategoryIcons";
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ImageCache.ImageCacheParams cacheParams = new ImageCache.ImageCacheParams(getActivity(), IMAGE_CACHE_DIR);
cacheParams.setMemCacheSizePercent(0.25f); // Set memory cache to 10% of app memory
// The ImageFetcher takes care of loading images into our ImageView children asynchronously
mImageFetcher = new ImageFetcher(getActivity(), getResources().getDimensionPixelSize(R.dimen.image_icon_size));
mImageFetcher.setLoadingImage(R.drawable.settings_clubs);
mImageFetcher.addImageCache(getActivity().getSupportFragmentManager(), cacheParams);
}
passing this mImageFetcher object to adapter class to load images asynchronously for each item.
ClubsCategoryAdapter clubsAdapter = new ClubsCategoryAdapter(getActivity(), new ArrayList<ClubsCategoryParser.ClubsCategory>(), mImageFetcher);
recyclerView.setAdapter(clubsAdapter);
ClubsCategoryAdapter.class
public class ClubsCategoryAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private ImageFetcher mImageFetcher;
public ClubsCategoryAdapter(Context context, ArrayList<ClubsCategoryParser.ClubsCategory> clubsCategoryList, ImageFetcher mImageFetcher ) {
this.context = context;
this.clubsCategoryList = clubsCategoryList;
this.mImageFetcher = mImageFetcher;
}
#Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, final int position) {
final ViewHolder viewHolder = (ViewHolder) holder;
final ClubsCategoryParser.ClubsCategory singleItem = clubsCategoryList.get(position);
if (!TextUtils.isNullOrEmpty(singleItem.image_url)) {
mImageFetcher.loadImage(singleItem.image_url, String.valueOf(singleItem.ID), viewHolder.imgCategoryIcon);
}
loadImage method in ImageWorker.class(DisplayingBitmaps)
public void loadImage(Object data, String imageID, ImageView imageView) {
if (data == null) {
return;
}
BitmapDrawable value = null;
if (mImageCache != null) {
value = mImageCache.getBitmapFromMemCache(imageID);
}
if (value != null) {
// Bitmap found in memory cache
imageView.setImageDrawable(value);
} else if (cancelPotentialWork(data, imageView)) {
//BEGIN_INCLUDE(execute_background_task)
final BitmapWorkerTask task = new BitmapWorkerTask(data, imageID, imageView);
final AsyncDrawable asyncDrawable = new AsyncDrawable(mResources, mLoadingBitmap, task);
imageView.setImageDrawable(asyncDrawable);
// NOTE: This uses a custom version of AsyncTask that has been pulled from the
// framework and slightly modified. Refer to the docs at the top of the class
// for more info on what was changed.
task.executeOnExecutor(AsyncTask.DUAL_THREAD_EXECUTOR);
//END_INCLUDE(execute_background_task)
}
}
actual asynctask to process the image asynchronously
/**
* The actual AsyncTask that will asynchronously process the image.
*/
private class BitmapWorkerTask extends AsyncTask<Void, Void, BitmapDrawable> {
private Object mData;
private String imageID;
private final WeakReference<ImageView> imageViewReference;
public BitmapWorkerTask(Object data, String imageID, ImageView imageView) {
mData = data;
this.imageID = imageID;
imageViewReference = new WeakReference<ImageView>(imageView);
}
/**
* Background processing.
*/
#Override
protected BitmapDrawable doInBackground(Void... params) {
//BEGIN_INCLUDE(load_bitmap_in_background)
if (BuildConfig.DEBUG) {
PrintLog.error(TAG, "doInBackground - starting work");
}
final String dataString = String.valueOf(mData);
Bitmap bitmap = null;
BitmapDrawable drawable = null;
// Wait here if work is paused and the task is not cancelled
synchronized (mPauseWorkLock) {
while (mPauseWork && !isCancelled()) {
try {
Log.e("pauseWork", "iswaiting -------------");
mPauseWorkLock.wait();
} catch (InterruptedException e) {
}
}
}
Log.e("pauseWork", "iswaiting end -------------");
// If the image cache is available and this task has not been cancelled by another
// thread and the ImageView that was originally bound to this task is still bound back
// to this task and our "exit early" flag is not set then try and fetch the bitmap from
// the cache
if (mImageCache != null && !isCancelled() && getAttachedImageView() != null && !mExitTasksEarly) {
bitmap = mImageCache.getBitmapFromDiskCache(imageID);
}
// If the bitmap was not found in the cache and this task has not been cancelled by
// another thread and the ImageView that was originally bound to this task is still
// bound back to this task and our "exit early" flag is not set, then call the main
// process method (as implemented by a subclass)
if (bitmap == null && !isCancelled() && getAttachedImageView() != null && !mExitTasksEarly) {
bitmap = processBitmap(mData, imageID);
}
// If the bitmap was processed and the image cache is available, then add the processed
// bitmap to the cache for future use. Note we don't check if the task was cancelled
// here, if it was, and the thread is still running, we may as well add the processed
// bitmap to our cache as it might be used again in the future
if (bitmap != null) {
if (DeviceUtils.hasHoneycomb()) {
// Running on Honeycomb or newer, so wrap in a standard BitmapDrawable
drawable = new BitmapDrawable(mResources, bitmap);
} else {
// Running on Gingerbread or older, so wrap in a RecyclingBitmapDrawable
// which will recycle automagically
drawable = new RecyclingBitmapDrawable(mResources, bitmap);
}
if (mImageCache != null) {
mImageCache.addBitmapToCache(imageID, drawable);
}
}
if (BuildConfig.DEBUG) {
PrintLog.error(TAG, "doInBackground - finished work");
}
return drawable;
//END_INCLUDE(load_bitmap_in_background)
}
/**
* Once the image is processed, associates it to the imageView
*/
#Override
protected void onPostExecute(BitmapDrawable value) {
//BEGIN_INCLUDE(complete_background_work)
// if cancel was called on this task or the "exit early" flag is set then we're done
if (isCancelled() || mExitTasksEarly) {
value = null;
}
final ImageView imageView = getAttachedImageView();
if (value != null && imageView != null) {
if (BuildConfig.DEBUG) {
PrintLog.error(TAG, "onPostExecute - setting bitmap");
}
setImageDrawable(imageView, value);
}
//END_INCLUDE(complete_background_work)
}
#Override
protected void onCancelled(BitmapDrawable value) {
super.onCancelled(value);
synchronized (mPauseWorkLock) {
mPauseWorkLock.notifyAll();
}
}
/**
* Returns the ImageView associated with this task as long as the ImageView's task still
* points to this task as well. Returns null otherwise.
*/
private ImageView getAttachedImageView() {
final ImageView imageView = imageViewReference.get();
final BitmapWorkerTask bitmapWorkerTask = getBitmapWorkerTask(imageView);
if (this == bitmapWorkerTask) {
return imageView;
}
return null;
}
}
Images are showing fine for the first time when installing the app, But after killing the app and loads the same page shows the image with the black/white background.
I have tried many examples and articles.. But nothing helped. I don't know why its coming with the black/white background after killing/exiting the app.
Your app is showing the image fine for the first time and displaying black background after re-openning app because "DisplayingBitmap" app caches the images to the filesystem using JPEG format. As you know JPEG doesn't support transparent mode.
Please open ImageCache class and look at the #68 line:
private static final CompressFormat DEFAULT_COMPRESS_FORMAT = CompressFormat.JPEG;
To avoid black background I changed this value to PNG format:
private static final CompressFormat DEFAULT_COMPRESS_FORMAT = CompressFormat.PNG;
UPDATE:
Also, you can set compressFormat to JPEG:
ImageCache.ImageCacheParams cacheParams = new ImageCache.ImageCacheParams(getActivity(), IMAGE_CACHE_DIR);
cacheParams.setMemCacheSizePercent(0.25f);
cacheParams.compressFormat = Bitmap.CompressFormat.JPEG;
it works for me and hope it helps you.
I'm using asynctask to download image to listview with custom cursoradapter. The images are downloaded to the first item in the list and when I scroll down to the second and so on. It's not synchronize and it's not the right picture. What am I doing wrong?
I cant upload pictures so here is my code:
class DownloadItemdPoster extends AsyncTask {
#Override
protected Bitmap doInBackground(String... params) {
// get the address from the params:
String address = params[0];
HttpURLConnection connection = null;
InputStream stream = null;
ByteArrayOutputStream outputStream = null;
// the bitmap will go here:
Bitmap b = null;
try {
// build the URL:
URL url = new URL(address);
// open a connection:
connection = (HttpURLConnection) url.openConnection();
// check the connection response code:
if (connection.getResponseCode() != HttpURLConnection.HTTP_OK) {
// not good..
return null;
}
// the input stream:
stream = connection.getInputStream();
// get the length:
int length = connection.getContentLength();
// tell the progress dialog the length:
// this CAN (!!) be modified outside the UI thread !!!
// progressDialog.setMax(length);
// a stream to hold the read bytes.
// (like the StringBuilder we used before)
outputStream = new ByteArrayOutputStream();
// a byte buffer for reading the stream in 1024 bytes chunks:
byte[] buffer = new byte[1024];
int totalBytesRead = 0;
int bytesRead = 0;
// read the bytes from the stream
while ((bytesRead = stream.read(buffer, 0, buffer.length)) != -1) {
totalBytesRead += bytesRead;
outputStream.write(buffer, 0, bytesRead);
// notify the UI thread on the progress so far:
// publishProgress(totalBytesRead);
Log.d("TAG", "progress: " + totalBytesRead + " / " + length);
}
// flush the output stream - write all the pending bytes in its
// internal buffer.
outputStream.flush();
// get a byte array out of the outputStream
// theses are the bitmap bytes
byte[] imageBytes = outputStream.toByteArray();
// use the BitmapFactory to convert it to a bitmap
b = BitmapFactory.decodeByteArray(imageBytes, 0, length);
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (connection != null) {
// close connection:
connection.disconnect();
}
if (outputStream != null) {
try {
// close output stream:
outputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return b;
}
#Override
protected void onPostExecute(Bitmap result) {
ImageView itemPoster = (ImageView) findViewById(R.id.imageViewItemPoster);
if (result == null) {
// no image loaded - display the default image
itemPoster.setBackgroundResource(R.drawable.defaultitembackground);
Toast.makeText(MainActivity.this, "Error Loading Image",
Toast.LENGTH_LONG).show();
} else {
// set the showactivitybackground to the poster:
itemPoster.setImageBitmap(result);
}
};
}
public class MovieAdapter extends CursorAdapter {
public MovieAdapter(Context context, Cursor c) {
super(context, c);
}
#Override
public View newView(Context context, Cursor cursor, ViewGroup parent) {
return getLayoutInflater().inflate(R.layout.movies_item_layout,
parent, false);
}
#Override
public void bindView(View view, Context context, Cursor cursor) {
String movieName = cursor.getString(cursor
.getColumnIndex(DbConstants.MOVIE_NAME));
String movieGenre = cursor.getString(cursor
.getColumnIndex(DbConstants.MOVIE_GENRE));
String itemPoster = cursor.getString(cursor
.getColumnIndexOrThrow(DbConstants.MOVIE_POSTER));
String movieRuntime = cursor.getString(cursor
.getColumnIndex(DbConstants.MOVIE_RUNTIME));
String movieRating = cursor.getString(cursor
.getColumnIndex(DbConstants.MOVIE_RATING));
String movieReleasedYear = cursor.getString(cursor
.getColumnIndex(DbConstants.MOVIE_YEAR_RELEASD));
TextView name = (TextView) view
.findViewById(R.id.textViewMovieName);
TextView genre = (TextView) view
.findViewById(R.id.textViewMovieGenre);
TextView runtime = (TextView) view
.findViewById(R.id.textViewMovieRuntime);
TextView rating = (TextView) view
.findViewById(R.id.textViewMovieRating);
TextView year = (TextView) view
.findViewById(R.id.textViewMovieYear);
ImageView setItemPoster = (ImageView) view
.findViewById(R.id.imageViewItemPoster);
setItemPoster.setTag(1);
name.setText(movieName);
genre.setText("*" + movieGenre);
runtime.setText("*" + movieRuntime);
rating.setText("*" + "R:" + " " + movieRating);
year.setText("*" + movieReleasedYear);
if (setItemPoster != null) {
new DownloadItemdPoster().execute(itemPoster);
adapter.notifyDataSetChanged();
}
You can use Glide library to load image from server.Example :
Glide.with(mContext).load(url).into(myImageView);
You must have to use image loader lib(picasso , volley , universal loader lib) rather than make aysntask to load image from server.
UniversalLoaderLib
Example:-
Initiate below code in your adapter or activity once.
DisplayImageOptions options = new DisplayImageOptions.Builder().showImageOnLoading(R.drawable.loading_icon).showImageForEmptyUri(R.drawable.ic_error_transparent).showImageOnFail(R.drawable.ic_error_transparent)
.cacheOnDisc(true).bitmapConfig(Bitmap.Config.RGB_565)/*.considerExifParams(true)*/.build();
ImageLoader.getInstance().init(ImageLoaderConfiguration.createDefault(activity.getBaseContext()));
Use below code where you want to load image from server.
ImageLoader.getInstance().displayImage(urladdress, imageview, options);
I am showing an image in webview and add onclick listener on download button. When user clicks on the download button it get the address of webview (which in this case is www.realearnpak.com/test/1.jpg). Image is shown in webview, but download image function does not work.
Please help me I am new thanks in advance. I tried alternative functions to download image from url but the do not appear to work.
public class MainActivity extends Activity {
public void DownloadImageFromPath(String path){
InputStream in =null;
Bitmap bmp=null;
int responseCode = -1;
try{
URL url = new URL(path);//"http://192.xx.xx.xx/mypath/img1.jpg
HttpURLConnection con = (HttpURLConnection)url.openConnection();
con.setDoInput(true);
con.connect();
responseCode = con.getResponseCode();
if(responseCode == HttpURLConnection.HTTP_OK)
{
//download
in = con.getInputStream();
bmp = BitmapFactory.decodeStream(in);
in.close();
}
}
catch(Exception ex){
Log.e("Exception",ex.toString());
}
}
#SuppressWarnings("deprecation")
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final WebView mWebView= (WebView) findViewById(R.id.webView1);
mWebView.setWebViewClient(new WebViewClient() {
#Override
public void onReceivedError(WebView view, int errorCode,
String description, String failingUrl) {
// Handle the error
}
#Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
view.loadUrl(url);
return true;
}
});
mWebView.loadUrl("http://www.realearnpak.com/test/1.jpg");
final AlertDialog alertDialog = new AlertDialog.Builder(this).create();
alertDialog.setTitle("Reset...");
alertDialog.setButton("OK", new DialogInterface.OnClickListener() {
public void onClick(final DialogInterface dialog, final int which) {
// here you can add functions
}
});
final Button button = (Button) findViewById(R.id.button1);
button.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
// Perform action on click
String webUrl = mWebView.getUrl();
//alertDialog.setMessage(webUrl);
// alertDialog.show();
DownloadImageFromPath(webUrl);
}
});
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
}
Here is one part of a code you can adapt for you need (but you would need to put it in a asynctask or the like) :
// Defines a handle for the byte download stream
InputStream inputStream = null;
FileOutputStream outputStream = null;
HttpEntity entity = null;
// Downloads the image and catches IO errors
try {
// Opens an HTTP connection to the image's URL
final String imageURL = URLEncoder.encode("http://my-path/to-my/beautiful-picture.jp", "UTF-8").replaceAll("\\+", "%20");
HttpGet httpRequest;
try {
httpRequest = new HttpGet(imageURL);
} catch (Exception e) {
Log.w(TAG, e);
throw new InterruptedException();
}
HttpResponse response = new DefaultHttpClient().execute(httpRequest);
final int statusCode = response.getStatusLine().getStatusCode();
if (statusCode != HttpStatus.SC_OK) {
Log.w(TAG, "Error " + statusCode + " while retrieving bitmap from " + imageURL);
throw new InterruptedException();
}
// Gets the input stream containing the image
entity = response.getEntity();
inputStream = new BufferedHttpEntity(entity).getContent();
bitmap = BitmapFactory.decodeStream(new FlushedInputStream(inputStream));
if (bitmap == null) {
Log.w(TAG, "Error while decoding bitmap from " + imageURL);
throw new IllegalStateException();
}
outputStream = getContext().openFileOutput("Beautiful-picture.jpg", Context.MODE_PRIVATE);
boolean ok = bitmap.compress(Bitmap.CompressFormat.JPEG, 100, outputStream);
if (!ok) {
Log.w(TAG, "Error while compressing bitmap from " + imageURL);
throw new IllegalStateException();
}
// If an IO error occurs, returns immediately
} catch (IOException e) {
e.printStackTrace();
return;
/*
* If the input stream is still open, close it
*/
} finally {
try {
if (null != inputStream) {
inputStream.close();
}
if (null != outputStream) {
outputStream.close();
}
if (entity != null) {
entity.consumeContent();
}
} catch (Exception e) {
e.printStackTrace();
}
}
For the FlushedInputStream, I let you look by yourself :p
There is a great library for this (actually there are a few). I would suggest you use one of them, as this is a common activity (downloading an image from the Web) and this is NOT something that makes sense to do yourself.
I use Picasso (it is a common favorite), Universal Image Downloader is another option.
I prefer volley depends oon what you need it for.
http://www.bignerdranch.com/blog/solving-the-android-image-loading-problem-volley-vs-picasso/
I'm creating a simple Android widget that fetches and displays some movie covers from a website. It's simple GridView that displays the images. The widget works fine but the moment I begin scrolling, I get and OutOfMemoryError and Android kills my process.
Here's the code of my RemoteViewsFactory. I can't seem to understand how to resolve this. The images I'm using are properly sized so I don't waste memory in Android. They are perfectly sized. As you can see I'm using a very small LRU cache and in my manifest, I've even enabled android:largeHeap="true". I've racked my head on this issue for a full two days and I'm wondering if what I'm trying to do is even possible?
public class SlideFactory implements RemoteViewsFactory {
private JSONArray jsoMovies = new JSONArray();
private Context ctxContext;
private Integer intInstance;
private RemoteViews remView;
private LruCache<String, Bitmap> lruCache;
private static String strCouch = "http://192.168.1.110:5984/movies/";
public SlideFactory(Context ctxContext, Intent ittIntent) {
this.ctxContext = ctxContext;
this.intInstance = ittIntent.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, AppWidgetManager.INVALID_APPWIDGET_ID);
this.lruCache = new LruCache<String, Bitmap>(4 * 1024 * 1024);
final Integer intMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);
final Integer intBuffer = intMemory / 8;
lruCache = new LruCache<String, Bitmap>(intBuffer) {
#Override
protected int sizeOf(String strDigest, Bitmap bmpCover) {
return bmpCover.getByteCount() / 1024;
}
};
}
public RemoteViews getViewAt(int intPosition) {
if (intPosition <= getCount()) {
try {
String strDocid = this.jsoMovies.getJSONObject(intPosition).getString("id");
String strDigest = this.jsoMovies.getJSONObject(intPosition).getJSONObject("value").getJSONObject("_attachments").getJSONObject("thumb.jpg").getString("digest");
String strTitle = this.jsoMovies.getJSONObject(intPosition).getJSONObject("value").getString("title");
Bitmap bmpThumb = this.lruCache.get(strDigest);
if (bmpThumb == null) {
String strUrl = strCouch + strDocid + "/thumb.jpg";
System.out.println("Fetching" + intPosition);
bmpThumb = new ImageFetcher().execute(strUrl).get();
this.lruCache.put(strDigest, bmpThumb);
}
remView.setImageViewBitmap(R.id.movie_cover, bmpThumb);
remView.setTextViewText(R.id.movie_title, strTitle);
} catch (Exception e) {
e.printStackTrace();
}
return remView;
}
return null;
}
public void onCreate() {
return;
}
public void onDestroy() {
jsoMovies = null;
}
public int getCount() {
return 20;
}
public RemoteViews getLoadingView() {
return null;//new RemoteViews(this.ctxContext.getPackageName(), R.layout.loading);
}
public int getViewTypeCount() {
return 1;
}
public long getItemId(int intPosition) {
return intPosition;
}
public boolean hasStableIds() {
return true;
}
public void onDataSetChanged() {
this.remView = new RemoteViews(this.ctxContext.getPackageName(), R.layout.slide);
try {
DefaultHttpClient dhcNetwork = new DefaultHttpClient();
String strUrl = strCouch + "_design/application/_view/language?" + URLEncoder.encode("descending=true&startkey=[\"hi\", {}]&attachments=true");
HttpGet getMovies = new HttpGet(strUrl);
HttpResponse resMovies = dhcNetwork.execute(getMovies);
Integer intMovies = resMovies.getStatusLine().getStatusCode();
if (intMovies != HttpStatus.SC_OK) {
throw new HttpResponseException(intMovies, "Server responded with an error");
}
String strMovies = EntityUtils.toString(resMovies.getEntity(), "UTF-8");
this.jsoMovies = new JSONObject(strMovies).getJSONArray("rows");
} catch (Exception e) {
Log.e("SlideFactory", "Unknown error encountered", e);
}
}
}
Here's the source of the AsyncTask that fetches the images:
public class ImageFetcher extends AsyncTask<String, Void, Bitmap> {
#Override
protected Bitmap doInBackground(String... strUrl) {
Bitmap bmpThumb = null;
try {
URL urlThumb = new URL(strUrl[0]);
HttpURLConnection hucConnection = (HttpURLConnection) urlThumb.openConnection();
InputStream istThumb = hucConnection.getInputStream();
bmpThumb = BitmapFactory.decodeStream(istThumb);
istThumb.close();
hucConnection.disconnect();
} catch (Exception e) {
e.printStackTrace();
}
return bmpThumb;
}
}
I had similar bitter experience and after lots of digging I found that setImageViewBitmap copies all bitmap into new instance, so taking double memory.
Consider changing following line into either static resource or something else !
remView.setImageViewBitmap(R.id.movie_cover, bmpThumb);
This takes lots of memory and pings garbage collector to clean memory, but so slow that your app can't use the freed memory in time.
My workaround is using LRUCache to store widget's bitmaps:
First, init as usual:
protected void initializeCache()
{
final int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);
// Use 1/10th of the available memory for this memory cache.
final int cacheSize = maxMemory / 10;
bitmapLruCache = new LruCache<String, Bitmap>(cacheSize)
{
#Override
protected int sizeOf(String key, Bitmap bitmap) {
// The cache size will be measured in kilobytes rather than
// number of items.
return bitmap.getByteCount() / 1024;
}
};
}
Then reuse bitmaps:
void updateImageView(RemoteViews views, int resourceId, final String imageUrl)
{
Bitmap bitmap = bitmapLruCache.get(imageUrl);
if (bitmap == null)
{
bitmap = // get bitmap from web with your loader
bitmapLruCache.put(imageUrl, bitmap);
}
views.setImageViewBitmap(resourceId, bitmap);
}
With this code widget does not crash my app now.
More info here