I wrote a little piece of code that download image from internet and cache them into cache dir.
It runs in a secondary thread.
{
String hash = md5(urlString);
File f = new File(m_cacheDir, hash);
if (f.exists())
{
Drawable d = Drawable.createFromPath(f.getAbsolutePath());
return d;
}
try {
InputStream is = download(urlString);
Drawable drawable = Drawable.createFromStream(is, "src");
if (drawable != null)
{
FileOutputStream out = new FileOutputStream(f);
Bitmap bitmap = ((BitmapDrawable)drawable).getBitmap();
bitmap.compress(CompressFormat.JPEG, 90, out);
}
return drawable;
} catch (Throwable e) { }
return null;
}
I use this code to load picture inside a ListView item, and it works fine. If I remove the first if (where i load image from disk) it runs smoothly (and download picture every time!). If I keep it, when you scroll listview you feel some lags during picture's loading from disk, why?
To answer the question "why", I experienced this with lots of gc() messages in my logcat. Android allocates the memory before decoding the file from disk which could cause garbage collection, which is painful for the performance in all threads. Probably the same happens when you encode your jpeg as well.
For decoding part you can try reuse existing bitmap if you have one to let Android decode image in-place. Please have a look at the following snippet:
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = inSampleSize;
options.inJustDecodeBounds = false;
options.inMutable = true;
if (oldBitmap != null) {
options.inBitmap = oldBitmap;
}
return BitmapFactory.decodeFile(f.getAbsolutePath(), options);
http://lucasr.org/2012/04/05/performance-tips-for-androids-listview/
Do in background.( use AsyncTask to load images.)
Related
Working on Android fashion app and downloading various images from AWS Cloudfront, then storing them locally on internal storage.
The same image looks different if I show it from my app or from the gallery app. It's not about display, I'm sure of this because I tested with the Photoshop color picker.
I guess it may depend on compression but I have maximum value (100). Also, I tried to write directly array byte to my memory and decode the array without any compression.
Look this image to better understand:
I know that Android handles different color profile and I've tried many of those, with no effects.
Also, I use Glide library to load the images and I set no cache and ARGB8888 color profile.
Then I tried to use Picasso, but nothing changed.
That's how I download and save the images:
#Override
protected Bitmap doInBackground(String... strings) {
try {
return BitmapFactory.decodeStream((InputStream)new URL(strings[0]).getContent());
} catch (IOException e) {
e.printStackTrace();
return null;
}
}
#Override
protected void onPostExecute(Bitmap bitmap) {
super.onPostExecute(bitmap);
if (bitmap != null) {
try {
String filename = colorImage.getUniqueId() + "_zoom.jpg";
// I tried this
File file = new File(getContext().getFilesDir(), filename);
FileOutputStream fos = new FileOutputStream(file, false);
// Writing the bitmap to the output stream
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, fos);
// But also this
// File file = new File(getContext().getFilesDir(), filename);
// FileOutputStream fos = new FileOutputStream(file, false);
// FileUtils.writeByteArrayToFile(file, array);
fos.close();
} catch (Exception e) {
Log.e("mylog", "saveInternalStorageError(): " + e.getMessage());
}
}
}
That's how I show the image (after I read all issues referenced here https://github.com/bumptech/glide/issues/515):
Glide.with(this)
.load(imageUriF)
.asBitmap()
.encoder(new BitmapEncoder(Bitmap.CompressFormat.JPEG, 100))
.diskCacheStrategy(DiskCacheStrategy.NONE)
.into(firstImage);
I also tried to show the image without Glide with same results
Bitmap myBitmap = BitmapFactory.decodeFile(imgFile.getAbsolutePath());
img.setImageBitmap(myBitmap);
And Picasso too, with same results.
Have you got any idea about this issue?
I'm stuck trying to load an image placed in assets folder with OpenCV 3.0 in Android. I've read a lot of answers here, but I can't figure out what I'm doing wrong.
"my image.jpg" is place directly in the assets folder created by Android Studio.
This is the code I'm using. I've checked and the library has been loaded correctly.
Mat imgOr = Imgcodecs.imread("file:///android_asset/myimage.jpg");
int height = imgOr.height();
int width = imgOr.width();
String h = Integer.toString(height);
String w = Integer.toString(width);
if (imgOr.dataAddr() == 0) {
// If dataAddr() is different from zero, the image has been loaded
// correctly
Log.d(TAG, "WRONG UPLOAD");
}
Log.d(h, "height");
Log.d(w, "width");
When I try to run my app, this is what I get:
08-21 18:13:32.084 23501-23501/com.example.android D/MyActivity: WRONG UPLOAD
08-21 18:13:32.085 23501-23501/com.example.android D/0: height
08-21 18:13:32.085 23501-23501/com.example.android D/0: width
It seems like the image has no dimensions. I guess because it has not been loaded correctly. I'va also tried to load it placing it in the drawable folder, but it doesn't work anyway and I'd prefer to use the assets one.
Anyone can please help me and tell me how to find the right path of the image?
Thanks
Problem: imread needs absolute path and your assets are inside a apk, and the underlying c++ classes cannot read from there.
Option 1: load image into Mat without using imread from drawable folder.
InputStream stream = null;
Uri uri = Uri.parse("android.resource://com.example.aaaaa.circulos/drawable/bbb_2");
try {
stream = getContentResolver().openInputStream(uri);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
BitmapFactory.Options bmpFactoryOptions = new BitmapFactory.Options();
bmpFactoryOptions.inPreferredConfig = Bitmap.Config.ARGB_8888;
Bitmap bmp = BitmapFactory.decodeStream(stream, null, bmpFactoryOptions);
Mat ImageMat = new Mat();
Utils.bitmapToMat(bmp, ImageMat);
Option 2: copy image to cache and load from absolute path.
File file = new File(context.getCacheDir() + "/" + filename);
if (!file.exists())
try {
InputStream is = context.getAssets().open(filename);
int size = is.available();
byte[] buffer = new byte[size];
is.read(buffer);
is.close();
FileOutputStream fos = new FileOutputStream(file);
fos.write(buffer);
fos.close();
} catch (Exception e) {
throw new RuntimeException(e);
}
if (file.exists()) {
image = cvLoadImage(file.getAbsolutePath(), type);
}
I need my App to take a photo using the camera, show it in my Activities ImageView, and then sent it to a server using an HttpClient. So far, so good. Unfortunately, I stumbled upon the well described MemoryOutOfBoundsException. So I want to compress my image using JPG or PNG.
Now - after some excessive googling - do I get this right:
a) The camera will always output an uncompressed Bitmap, which is directly written to the file system. I.e. like this:
Intent cameraIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
this.imageTempFile = new File(android.os.Environment.getExternalStorageDirectory(), "myTempFileName"); // write the camera output to a tmpFile
cameraIntent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(this.imageTempFile)); // link the tmpFile to a member for convenience later on
startActivityForResult(cameraIntent, CAMERA_REQUEST);
So there is no way to resize / compress it right away?
b) If I want to display an image in a ImageView, I need to pass a Bitmap to it using ImageView.setImageBitmap(Bitmap bm). So showing the result is extremely memory consuming...!?
c) If I want to alter the Bitmap (resize / compress), I need to read it from this file into memory using a BitmapFactory
d) Now I can resize the Bitmap using Bitmap.createScaledBitmap()
e) But if I want to compress the image, I need to write it back to the file system using an OutputStream via Bitmap.compress()...
f) So for the task of taking an image, resizing it, showing it, and then sending it to a server, I need to actually write it to a file, then read it, then write it to a file again, then read it - and then send it? WTF? I there no easier way?
PS: Here's my code for b) to e):
// c) read the Bitmap from file
Bitmap bitmap;
BitmapFactory.Options bitmapOptions = new BitmapFactory.Options();
bitmap = BitmapFactory.decodeFile(this.imageTempFile.getAbsolutePath(), bitmapOptions);
// d) do some resizing
bitmap = Bitmap.createScaledBitmap(bitmap, (int) mywidth, (int) myheight, true);
// e) compress
OutputStream out = new ByteArrayOutputStream(50);
this.imageTempFile.delete();
File file = new File(this.imageTempFile.getAbsolutePath());
try {
out = new FileOutputStream(file);
bitmap.compress(Bitmap.CompressFormat.JPEG, 85, out);
out.flush();
out.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
// NOW we could read it again from the file to send it afterwards...
Bitmap newBitmap = BitmapFactory.decodeFile(this.imageTempFile.getAbsolutePath(), bitmapOptions);
i try the example i gotten from here. The example shows how to display a image that is larger then the screen and the screen acting like a window, allowing user to look at the images via scrolling. However, what i want to achieved is similar just that, instead of one single image, i tried to combining 18 smaller images (171X205) into one single image. I am able to did that but to save loading time on downloading of images from the server, i cached the images. Heres the problem, i cant seems to display the images out on screen even though the images are indeed cached. Thus, i tried something else by fetching image from the drawable folder but still the same issue arise. Does anyone have any idea how to go about solving this problem?
A snippets of code to load images from cache:
for(int i =0; i<18; i++)
File cacheMap = new File(context.getCacheDir(), smallMapImageNames.get(i).toString());
if(cacheMap.exists()){
//retrieved from cached
try {
FileInputStream fis = new FileInputStream(cacheMap);
Bitmap bitmap = BitmapFactory.decodeStream(fis)
puzzle.add(bitmap);
}catch(...){}
}else{
Drawable smallMap = LoadImageFromWebOperations(mapPiecesURL.get(i).toString());
if(i==0){
height1 = smallMap.getIntrinsicHeight();
width1 = smallMap.getIntrinsicWidth();
}
if (smallMap instanceof BitmapDrawable) {
Bitmap bitmap = ((BitmapDrawable)smallMap).getBitmap();
FileOutputStream fos = null;
try {
cacheMap.createNewFile();
fos = new FileOutputStream(cacheMap);
bitmap.compress(CompressFormat.JPEG, 100, fos);
fos.flush();
fos.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
puzzle.add(bitmap);
}
}
}
The function where it retrieved images from the server
private Drawable LoadImageFromWebOperations(String url) {
// TODO Auto-generated method stub
try
{
InputStream is = (InputStream) new URL(url).getContent();
Drawable d = Drawable.createFromStream(is, "src name");
return d;
}catch (Exception e) {
System.out.println("Exc="+e);
return null;
}
}
I am drawing onto a canvas and so no ImageView is use to display the images.
For all of my lazy image loading I use Prime.
Is there a way to lazyload an image from the internet into a remoteview.
remoteView.setImageViewBitmap(R.id.image, UrlUtils.loadBitmap(bitmapUrl));
I use this function but it is blocking my widget during a small time.
public static Bitmap loadBitmap(String url) {
Bitmap bitmap = null;
InputStream in = null;
BufferedOutputStream out = null;
try {
in = new BufferedInputStream(getPageInputStream(url));
final ByteArrayOutputStream dataStream = new ByteArrayOutputStream();
out = new BufferedOutputStream(dataStream);
Utils.copy(in, out);
out.flush();
final byte[] data = dataStream.toByteArray();
BitmapFactory.Options options = new BitmapFactory.Options();
// options.inSampleSize = 1;
bitmap = BitmapFactory.decodeByteArray(data, 0, data.length,
options);
in.close();
out.close();
} catch (IOException e) {
e.printStackTrace();
}
return bitmap;
}
Thanks
Absolutely
Draw your widget the usual way, all the textual parts, etc. beside image
Create a service which will load image. Here's good tutorial that includes how to create and call service from the appwidget
After updating your widget call the service. Pass widget ID and image URL
Load image from cache or remotely in your service and update your widget again. Voila, you have it now
Try android-query lib for lazyload loading.
https://code.google.com/p/android-query/#Image_Loading