ImageView showing black/white background for transparent base64 string image - java

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.

Related

ImageView's picture does not set with AsyncTask

Here in my code I'm setting a picture into an ImageView in my MyTask class which is using AsyncTask, but as I run my code just one or two ImageViews has pictures and the others are empty showing the default picture.
Is there any problems with the thread?
Here is my code:
#Override
public View getView(final int position, View convertView, ViewGroup parent) {
Log.e("sara" , "this part takes time");
LayoutInflater inflater = getLayoutInflater();
convertView = getLayoutInflater().inflate(R.layout.gallery_gridsq, parent, false);
iv = (ImageView) convertView.findViewById(R.id.icon);
file = new File(Uri.parse(getItem(position).toString()).getPath());
new myTask().execute();
return convertView;
}
private class myTask extends AsyncTask <Void , Void ,Bitmap> {
#Override
protected Bitmap doInBackground(Void... params) {
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
try {
BitmapFactory.decodeStream(new FileInputStream(file), null, options);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
options.inJustDecodeBounds = false;
options.inSampleSize = 2;
try {
bmp = BitmapFactory.decodeStream(new FileInputStream(file), null, options);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
return bmp;
}
#Override
protected void onPostExecute(Bitmap aVoid) {
iv.setImageBitmap(aVoid);
}
}
This is because you are overriding the iv(imageview) object every time when getView() method is called and because MyTask is an asynch task in which doInBackground() is called asynchronously on background thread and onPostExecute() called on main thread after that. So the onPostExecute will only update the current imageview. You have to set the bitmap on every image view of grid. This solution should work for you:
#Override
public View getView(final int position, View convertView, ViewGroup parent) {
Log.e("sara" , "this part takes time");
LayoutInflater inflater = getLayoutInflater();
convertView = getLayoutInflater().inflate(R.layout.gallery_gridsq, parent, false);
ImageView iv = (ImageView) convertView.findViewById(R.id.icon);
File file = new File(Uri.parse(getItem(position).toString()).getPath());
new myTask(iv, file).execute();
return convertView;
}
private class MyTask extends AsyncTask <Void , Void ,Bitmap> {
Imageview iv;
File file;
public void MyTask(Imageview iv, File file)
{
this.iv=iv;
this.file= file;
}
#Override
protected Bitmap doInBackground(Void... params) {
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
try {
BitmapFactory.decodeStream(new FileInputStream(file), null, options);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
options.inJustDecodeBounds = false;
options.inSampleSize = 2;
try {
bmp = BitmapFactory.decodeStream(new FileInputStream(file), null, options);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
return bmp;
}
#Override
protected void onPostExecute(Bitmap aVoid) {
iv.setImageBitmap(aVoid);
}
}
i consider that you are try to load an image using a url
you can user image loader reference (such as universal image loader) which is best for loading images
here is the code you can have
first of download the universal image loader library from the below
https://github.com/nostra13/Android-Universal-Image-Loader
You have to add this method your application level class so you don't need to write multiple times
public static void initImageLoader(Context context) {
// This configuration tuning is custom. You can tune every option, you
// may tune some of them,
// or you can create default configuration by
// ImageLoaderConfiguration.createDefault(this);
// method.
ImageLoaderConfiguration.Builder config = new ImageLoaderConfiguration.Builder(
context);
config.threadPriority(Thread.NORM_PRIORITY - 2);
config.denyCacheImageMultipleSizesInMemory();
config.diskCacheFileNameGenerator(new Md5FileNameGenerator());
config.diskCacheSize(50 * 1024 * 1024); // 50 MiB
config.tasksProcessingOrder(QueueProcessingType.LIFO);
config.writeDebugLogs(); // Remove for release app
// Initialize ImageLoader with configuration.
ImageLoader.getInstance().init(config.build());
}
Then in Adapter class you have to just add the below code to load image
// For Image Display options - Failed , Success and Error
// Add this is in your adapter constructor
DisplayImageOptions userimgoptions = new DisplayImageOptions.Builder()
.showImageOnLoading(R.drawable.profile_pic_no_border)
.showImageForEmptyUri(R.drawable.profile_pic_no_border)
.showImageOnFail(R.drawable.profile_pic_no_border).cacheInMemory(true)
.cacheOnDisk(true).bitmapConfig(Bitmap.Config.RGB_565).build();
// in you getview method just use this signle line code to load image
ImageLoader.getInstance().displayImage(modelFeeds.getUserAvatarOriginal(), myHolder.img_profilepicture, userimgoptions);
i hope this will help you
Set the Imageview using Glide Library
Step1: Include the Dependencies in your build.gradle file
compile 'com.github.bumptech.glide:glide:3.7.0'
Step2: Include the Glide in Java file
String fileName = "1.jpg";
String completePath = Environment.getExternalStorageDirectory() + "/" +
fileName;
File file = new File(completePath);
Uri imageUri = Uri.fromFile(file);
Glide.with(this)
.load(imageUri)
.placeholder(R.drawable.no_media)
.into(imgView);

Using BitMaps to get images from Instagram API

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).

The right way to load a PDF into an ImageView

I'm looking for the proper way to load a PDF File into an ImageView.
I use the BitmapWorkerTask class from Android.
I have a button which allow me to choose the File I want to upload into the ImageView. When I click on this file, the process begin.
My issue is that my PDF is perfectly load 3 time 5. And I don't understand why it's not working the other time.
ImageView map;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
map = (ImageView)findViewById(R.id.pdf);
}
/**
* Use this to load a pdf file from your assets and render it to a Bitmap.
*
* #param context
* current context.
* #param filePath
* of the pdf file in the assets.
* #return a bitmap.
*/
#Nullable
public static Bitmap renderToBitmap(Context context, String filePath) {
Bitmap bi = null;
InputStream inStream = null;
try {
inStream = new FileInputStream(filePath);
bi = renderToBitmap(context, inStream);
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
inStream.close();
} catch (IOException e) {
// do nothing because the stream has already been closed
}
}
return bi;
}
/**
* Use this to render a pdf file given as InputStream to a Bitmap.
*
* #param context
* current context.
* #param inStream
* the inputStream of the pdf file.
* #return a bitmap.
* #see https://github.com/jblough/Android-Pdf-Viewer-Library/
*/
#Nullable
public static Bitmap renderToBitmap(Context context, InputStream inStream) {
Bitmap bi = null;
try {
byte[] decode = IOUtils.toByteArray(inStream);
ByteBuffer buf = ByteBuffer.wrap(decode);
PDFPage mPdfPage = new PDFFile(buf).getPage(0);
float width = mPdfPage.getWidth();
float height = mPdfPage.getHeight();
RectF clip = null;
bi = mPdfPage.getImage((int) (width), (int) (height), clip, true,
true);
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
inStream.close();
} catch (IOException e) {
// do nothing because the stream has already been closed
}
}
return bi;
}
private void renderMap() {
String mapFilePath = Environment.getExternalStorageDirectory().toString()+"/Android/data/com.empower.data/"+mapFileName;
BitmapWorkerTask task = new BitmapWorkerTask(map, mapFilePath, MainActivity.this);
task.loadBitmap(R.id.pdf, map, mapFilePath);
map.setVisibility(View.VISIBLE);
map.invalidate();
}
And my BitmapWorkerTask.java
public class BitmapWorkerTask extends AsyncTask<Integer, Void, Bitmap> {
private final WeakReference<ImageView> imageViewReference;
private int data = 0;
private String mapFilePath1;
private Context context;
private RelativeLayout loadingPanel;
public BitmapWorkerTask(ImageView imageView, String mapFilePath2, Context context) {
// Use a WeakReference to ensure the ImageView can be garbage collected
this.context = context;
mapFilePath1 = mapFilePath2;
imageViewReference = new WeakReference<ImageView>(imageView);
}
// Decode image in background.
#Override
protected Bitmap doInBackground(Integer... params) {
data = params[0];
Bitmap bm = MainActivity.renderToBitmap(context , mapFilePath1);
return bm;
}
#Override
protected void onPreExecute() {
loadingPanel = (RelativeLayout)((Activity) context).findViewById(R.id.loadingPanel);
loadingPanel.setVisibility(View.VISIBLE);
}
// Once complete, see if ImageView is still around and set bitmap.
#Override
protected void onPostExecute(final Bitmap bitmap) {
if (imageViewReference != null && bitmap != null) {
final ImageView imageView = imageViewReference.get();
if (imageView != null) {
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
imageView.setImageBitmap(bitmap);
loadingPanel.setVisibility(View.GONE);
}
}, 500);
}
}
}
public void loadBitmap(int resId, ImageView imageView, String mapFilePath1) {
BitmapWorkerTask task = new BitmapWorkerTask(imageView, mapFilePath1, context);
task.execute(resId);
}
}

OutOfMemoryError bitmaps android

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

Unable to load multiple images in a RemoteViewsFactory

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

Categories

Resources