I have a drawing view, and i am trying to get array of bytes from that view on button click
this is my code:
public void onClick(
drawView.setDrawingCacheEnabled(true);
String imgSaved = MediaStore.Images.Media.insertImage(
getContentResolver(), drawView.getDrawingCache(),
UUID.randomUUID().toString() + ".png", "drawing");
}
}
please help!
If view is ImageView first convert into bitmap then convert into byte array
Convert ImageView to Bitmap
public static Bitmap getImageViewAsBitmap(ImageView imageView) {
imageView.buildDrawingCache();
Bitmap bmap = imageView.getDrawingCache();
return bmap;
}
Convert Bitmap to Byte Array
public byte[] getBitmapAsByteArray(Bitmap bitmap) {
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
bitmap.compress(CompressFormat.PNG, 0, outputStream);
return outputStream.toByteArray();
}
Convert Byte Array to Bitmap
public static Bitmap getByteArrayAsBitmap(byte[] bitmap) {
Bitmap bm = null;
try {
bm = BitmapFactory.decodeStream(new ByteArrayInputStream(bitmap));
} catch (Exception e) {
}
return bm;
}
To call these methods like that:
provide imageview give bitmap
Bitmap m1Bitmap= getImageViewAsBitmap(myimageView);
provide bitmap give byte array
byte[] mbyteArray =getBitmapAsByteArray(mBitmap);
provide byte array give bitmap
Bitmap m2Bitmap= getByteArrayAsBitmap(byte[] bitmap);
I am using universal-image-loader library to load images, but when I call copy() on a loaded bitmap file in some cases I get OutOfMemoryError.
Here is my code:
ImageLoader.getInstance().loadImage(path, new ImageLoadingListener() {
#Override
public void onLoadingStarted(String arg0, View arg1) {
// TODO Auto-generated method stub
}
#Override
public void onLoadingFailed(String arg0, View arg1, FailReason arg2) {
// TODO Auto-generated method stub
}
#Override
public void onLoadingComplete(String arg0, View arg1, Bitmap arg2) {
bm = arg2;
}
#Override
public void onLoadingCancelled(String arg0, View arg1) {
// TODO Auto-generated method stub
}
});
Bitmap bm2= bm.copy(Bitmap.Config.ARGB_8888, true); //where the crash happens
I need the second Bitmap not to be mutable so I can draw on it.
First of all try to find a little time to read good official documentation about bitmaps: Displaying Bitmaps Efficiently
It will give you understanding why and when java.lang.OutofMemoryError happens. And how to avoid it.
What about your question: see this article: Android: convert Immutable Bitmap into Mutable
But from API Level 11 only options.inMutable available to load the
file into a mutable bitmap.
So, if we are building application with API level less than 11, then
we have to find some other alternatives.
One alternative is creating another bitmap by copying the source
bitmap. mBitmap = mBitmap.copy(ARGB_8888 ,true);
But the will throw OutOfMemoryException if the source file is big.
Actually incase if we want to edit an original file, then we will face
this issue. We should be able to load at-least image into memory, but
most we can not allocate another copy into memory.
So, we have to save the decoded bytes into some where and clear
existing bitmap, then create a new mutable bitmap and load back the
saved bytes into bitmap again. Even to copy bytes we cannot create
another ByteBuffer inside the memory. In that case need to use
MappedByteBuffer that will allocate bytes inside a disk file.
Following code would explain clearly:
//this is the file going to use temporally to save the bytes.
File file = new File("/mnt/sdcard/sample/temp.txt");
file.getParentFile().mkdirs();
//Open an RandomAccessFile
/*Make sure you have added uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
into AndroidManifest.xml file*/
RandomAccessFile randomAccessFile = new RandomAccessFile(file, "rw");
// get the width and height of the source bitmap.
int width = bitmap.getWidth();
int height = bitmap.getHeight();
//Copy the byte to the file
//Assume source bitmap loaded using options.inPreferredConfig = Config.ARGB_8888;
FileChannel channel = randomAccessFile.getChannel();
MappedByteBuffer map = channel.map(MapMode.READ_WRITE, 0, width*height*4);
bitmap.copyPixelsToBuffer(map);
//recycle the source bitmap, this will be no longer used.
bitmap.recycle();
//Create a new bitmap to load the bitmap again.
bitmap = Bitmap.createBitmap(width, height, Config.ARGB_8888);
map.position(0);
//load it back from temporary
bitmap.copyPixelsFromBuffer(map);
//close the temporary file and channel , then delete that also
channel.close();
randomAccessFile.close();
And here is sample code.
Be thankful that it happens on your device, and not only on your user's devices.
1) That's something you have to cope with, and react to appropriately. Display error message, or load lower resolution of the bitmap. Your app will run on many kinds of devices, each has different amount of memory.
2) Use important function Bitmap.recycle after each operation which makes your old bitmap redundant. This will immediately free memory for next work without waiting for GC to run, and possible out of memory errors.
You cant do much about the bitmap outofmemory error except ensuring that the bitmap you are copying or displaying is not much large. Fortunately universal imageloader has a feature to compress bitmap by changing the config. So give Bitmap.Config.RGG_565 a try. Its supposed to half the memory footprint of the bitmap. You can also request for large heap size. Another thing you can do is copy the scaled version of the bitmap.
As Illegel Argument said, you need to make sure that the Bitmap is not TOO large. Also, make sure that you are only loading one Bitmap at a time into memory.
You can dynamically scale the bitmap using BitmapFactory
Bitmap b = BitmapFactory.decodeByteArray(imageAsBytes, 0, imageAsBytes.length)
image.setImageBitmap(Bitmap.createScaledBitmap(b, 300, 300, false));
Download this code from the site
http://www.androidhive.info/2012/02/android-custom-listview-with-image-and-text/
extract its ImageLoader , file cache , memory cache classes use them in your bitmap to do things wont create out of memory exception at all and will cache images and increase performance
Use this Code to fullfill your purpose
Make the following classes in your code and in last use imageloader to load url pass it url , imageview and drawable to show incase url is not returning any image
FileCache.java
public class FileCache {
private File cacheDir;
public FileCache(Context context){
//Find the dir to save cached images
if (android.os.Environment.getExternalStorageState().equals(android.os.Environment.MEDIA_MOUNTED))
cacheDir=new File(android.os.Environment.getExternalStorageDirectory(),"LazyList");
else
cacheDir=context.getCacheDir();
if(!cacheDir.exists())
cacheDir.mkdirs();
}
public File getFile(String url){
//I identify images by hashcode. Not a perfect solution, good for the demo.
String filename=String.valueOf(url.hashCode());
//Another possible solution (thanks to grantland)
//String filename = URLEncoder.encode(url);
File f = new File(cacheDir, filename);
return f;
}
public void clear(){
File[] files=cacheDir.listFiles();
if(files==null)
return;
for(File f:files)
f.delete();
}
}
MemoryCache.java
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();
}
}
Utils.java
public class Utils {
public static void CopyStream(InputStream is, OutputStream os)
{
final int buffer_size=1024;
try
{
byte[] bytes=new byte[buffer_size];
for(;;)
{
int count=is.read(bytes, 0, buffer_size);
if(count==-1)
break;
os.write(bytes, 0, count);
}
}
catch(Exception ex){}
}
}
ImageLoader.java
public class ImageLoader {
MemoryCache memoryCache=new MemoryCache();
FileCache fileCache;
private Map<ImageView, String> imageViews=Collections.synchronizedMap(new WeakHashMap<ImageView, String>());
ExecutorService executorService;
public ImageLoader(Context context){
fileCache=new FileCache(context);
executorService=Executors.newFixedThreadPool(5);
}
final int stub_id = R.drawable.no_image;
public void DisplayImage(String url, ImageView imageView)
{
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();
bitmap = decodeFile(f);
return bitmap;
} catch (Exception ex){
ex.printStackTrace();
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;
BitmapFactory.decodeStream(new FileInputStream(f),null,o);
//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;
return BitmapFactory.decodeStream(new FileInputStream(f), null, o2);
} catch (FileNotFoundException e) {}
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() {
if(imageViewReused(photoToLoad))
return;
Bitmap bmp=getBitmap(photoToLoad.url);
memoryCache.put(photoToLoad.url, bmp);
if(imageViewReused(photoToLoad))
return;
BitmapDisplayer bd=new BitmapDisplayer(bmp, photoToLoad);
Activity a=(Activity)photoToLoad.imageView.getContext();
a.runOnUiThread(bd);
}
}
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();
}
}
call this code where you want to cache or download or manage images
imageLoader.DisplayImage(song.get(CustomizedListView.KEY_THUMB_URL), thumb_image);
I'm using the following code to create a resized bitmap, the code works okay, but it's very slow. The problem is that when I try to use this for more than one image the phone freezes until the images are created.
Here is my code:
Bitmap bitmap = null;
File imgFile = new File(my_pic_file);
if(imgFile.exists()) {
bitmap = BitmapFactory.decodeFile(imgFile.getAbsolutePath());
Bitmap resizedBitmap = Bitmap.createScaledBitmap(bitmap, 300, 200, false);
first_image.setImageBitmap(resizedBitmap);
}
The problem is that you are doing the work on the main thread. This means that it will freeze the UI until all the processing is finished. You can fix this by either using a thread or asynchronous task.
private class LoadImageTask extends AsyncTask<String, Void, Bitamp> {
private ImageView mImageView = null;
public LoadImageTask(ImageView imageView) {
mImageView = imageView;
}
protected Bitmap doInBackground(String... file) {
Bitmap bitmap = null;
Bitmap resizedBitmap = null;
File imgFile = new File(file);
if(imgFile.exists()) {
bitmap = BitmapFactory.decodeFile(imgFile.getAbsolutePath());
resizedBitmap = Bitmap.createScaledBitmap(bitmap, 300, 200, false);
}
return resizedBitmap;
}
protected void onPostExecute(Bitmap result) {
if (result != null && mImageView != null) {
mImageView.setImageBitmap(result);
}
}
Then in your code just call
new LoadImageTask(first_image).execute(my_pic_file);
I am unable to fiund how to download images to my widget!
In my widget I load in AsyncTask json with url, title, then I show title in TextView and I need to load images from url.
I tried with this but image load, and now showing
class LoadImages extends AsyncTask<Void, Void, Bitmap>{
#Override
protected Bitmap doInBackground(Void... params) {
Bitmap bitmap = null;
InputStream in = null;
BufferedOutputStream out = null;
try {
in = new BufferedInputStream(new URL("http://mysite/simple.img").openStream());
final ByteArrayOutputStream dataStream = new ByteArrayOutputStream();
out = new BufferedOutputStream(dataStream);
out.flush();
final byte[] data = dataStream.toByteArray();
BitmapFactory.Options options = new BitmapFactory.Options();
//options.inSampleSize = 1;
bitmap = BitmapFactory.decodeByteArray(data, 0, data.length,options);
Log.e("Log", "Yeah");
} catch (IOException e) {
Log.e("Log", "Could not load Bitmap from: " + "mysiteg");
}
return bitmap;
}
}
And on Update I call this
LoadImages load = new LoadImages();
load.execute();
Bitmap bitmap = load.get();
update.setImageViewBitmap(R.id.imageView0, bitmap);
You should override onPostExecute method in LoadImages class, and set bitmap to your ImageView in that method. When LoadImages task is done, it will call onPostExcute method. Considering WeakReference to wrap your ImageView for better performance.
in android I took a picture by the cam and returnded it to my activity:
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == Constatnts.ANSWER_TO_LIFE_UNIVERSE_AND_EVERYTHING && data != null && data.getExtras() != null && data.getExtras().get("data") != null) {
Bitmap snapshot = (Bitmap) data.getExtras().get("data");
String convert = InputOutput.bitmapToString(this, snapshot);
Bitmap back = InputOutput.stringToBitmap(convert);
}
}
When I assign the Bitmap 'snapshot' to an imageview it loosk pretty good an works well. But when I assign the Bitmap 'back" to an imageview it does not change its view. So there must be something wrong in transformation. Here is my code for the tranformation:
public static Bitmap stringToBitmap(String bitmapString) {
byte[] bytes = Base64.decode(bitmapString, Base64.DEFAULT);
Bitmap bitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.length);
return bitmap;
}
public static String bitmapToString(Context context, Bitmap bitmap) {
ByteArrayOutputStream stream = new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.PNG, 100, stream);
bitmap.recycle();
byte[] byteArray = stream.toByteArray();
try {
stream.close();
} catch (IOException e) {
e.printStackTrace();
}
stream.write(byteArray, 0, byteArray.length);
stream = null;
String strBase64 = Base64.encodeToString(byteArray, Base64.URL_SAFE);
return strBase64;
}
Any suggestions what goes wrong here? Thanks!
Here's a code I used once to try this conversion, it should work:
public final static String bitmapToString(Bitmap in){
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
in.compress(Bitmap.CompressFormat.PNG, 100, bytes);
return Base64.encodeToString(bytes.toByteArray(),Base64.DEFAULT);
}
public final static Bitmap stringToBitmap(String in){
byte[] bytes = Base64.decode(in, Base64.DEFAULT);
return BitmapFactory.decodeByteArray(bytes, 0, bytes.length);
}
You might want to add some close() calls to the streams though.