Image retrieval is slow - java

I'm grabbing thumbnail images from a web server in an AsynTask for each class I create as it's created. For this particular issue, I'm displaying the thumbnail and some text properties in a ListView.
The problem is that it's very slow. I considered saving each image to the device the first time it's needed and then each other time, I could just pull it from a database. I'm not sure if this is ideal or not because the image may or may not be updated on the server. In my opinion, I think it's best to always grab the images on the fly just in case there are new ones to grab.
I'd like some suggestions from all you fine folks about this.
Note: I've also tried getting the images in a ListView adapter but 1) it gets each image each and every time the ListView is scrolled and 2) it's incredibly buggy because as each image downloads, I can watch the images appear in the wrong listview item and then it magically corrects itself after a while. This wasn't at all ideal so that's why I moved the code to the class.
I'd appreciate any advice that's thrown my way.
Here's the getter for the image that lives in the class. This works fine after the image is downloaded the first time but not so well during that initial run.
public Bitmap GetThumbnail_xlarge() throws Exception {
if(_thumbnail_xlarge == null ) {
if( _thumbnailBasePath != null) {
try {new DownloadImageTask(_thumbnail_xlarge).execute(_thumbnailBasePath + "/portrait_xlarge." + _thumbnailExtension); }
catch (Exception e) {} //TODO: The extension could be incorrect. Do nothing for now.
}
else{ throw new Exception("You must set the thumbnail path before getting the image"); }
}
return _thumbnail_xlarge;
}
And, here is the class that does the work. Please note that this is a private class inside of the class where the image property lives:
private class DownloadImageTask extends AsyncTask<String, Void, Bitmap> {
Bitmap bmImage;
public DownloadImageTask(Bitmap bmImage) {
this.bmImage = bmImage;
}
protected Bitmap doInBackground(String... urls) {
String urldisplay = urls[0];
Bitmap mIcon11 = null;
try {
InputStream in = new java.net.URL(urldisplay).openStream();
mIcon11 = BitmapFactory.decodeStream(in);
} catch (Exception e) {
Log.e("Error", e.getMessage());
e.printStackTrace();
}
return mIcon11;
}
protected void onPostExecute(Bitmap result) { _thumbnail_xlarge = result; }
}
I can't think of anything else that might be pertinent at this time. Please ask me for more information if necessary and thanks in advance!

It's incredibly useful to save images locally for later use. This called caching by the way. It's useful, as you are saving resources and you can reduce network requests which are using plenty of them.
If the images are big you can use the WebP format. WebP is an image format which is "lossless and lossy compression for images on the web. WebP lossless images are 26% smaller in size compared to PNGs. WebP lossy images are 25-34% smaller in size compared to JPEG images at equivalent SSIM index."
More info regarding WebP format you can find here: https://developers.google.com/speed/webp/?csw=1
Also if you want to load images smoothly on a ListView you have to use the ViewHolder pattern. More info here: http://developer.android.com/training/improving-layouts/smooth-scrolling.html#ViewHolder

Related

Android, Glide Image Loading Library: NullPointerException, ProviderInfo

Just getting into the Glide image loading library for Android. Currently working with some code from here:
https://github.com/bumptech/glide/issues/459
My full project is here is you want to look at it:
https://github.com/mhurwicz/glide02
I'm getting the following exception when I run the app in the emulator in Android Studio:
java.lang.NullPointerException: Attempt to invoke virtual method 'android.content.res.XmlResourceParser android.content.pm.ProviderInfo.loadXmlMetaData(android.content.pm.PackageManager, java.lang.String)' on a null object reference
This is the key statement in MainActivity:
new ShareTask(this).execute("http://thelink");
(thelink is actually goo.gl/gEgYUd -- couldn't leave that in above because stackoverflow doesn't allow URL shorteners. )
Here is my code for the ShareTask class
class ShareTask extends AsyncTask<String, Void, File> {
private final Context context;
public ShareTask(Context context) {
this.context = context;
}
#Override protected File doInBackground(String... params) {
String url = params[0]; // should be easy to extend to share multiple images at once
try {
return Glide
.with(context)
.load(url)
.downloadOnly(Target.SIZE_ORIGINAL, Target.SIZE_ORIGINAL)
.get() // needs to be called on background thread
;
} catch (Exception ex) {
Log.w("SHARE", "Sharing " + url + " failed", ex);
return null;
}
}
#Override protected void onPostExecute(File result) {
if (result == null) { return; }
Uri uri = FileProvider.getUriForFile(context, context.getPackageName(), result);
share(uri); // startActivity probably needs UI thread
}
private void share(Uri result) {
Intent intent = new Intent(Intent.ACTION_SEND);
intent.setType("image/jpeg");
intent.putExtra(Intent.EXTRA_SUBJECT, "Shared image");
intent.putExtra(Intent.EXTRA_TEXT, "Look what I found!");
intent.putExtra(Intent.EXTRA_STREAM, result);
context.startActivity(Intent.createChooser(intent, "Share image"));
}
}
Using debug, it seems I may be running into trouble at the get() statement. For one thing, the width and the height are very large negative numbers. (See the code highlighted in green below.) Then the get() statement returns null. (See the code highlighted in red below.)
Thanks in advance for any help you can provide!
The NPE is coming from FileProvider.getUriForFile because you're passing in the wrong authority. You declared android:authorities="com.example.fileprovider" in the manifest, but you're using the package name at the call. This fails to resolve the info in FileProvider.parsePathStrategy. Match those two strings up and you'll be good to go.
The easiest fix is to use android:authorities="${applicationId}", this leads to 0 hardcoded strings, so you can keep using context.getPackageName().
Regarding your concerns during debug:
Target.SIZE_ORIGINAL is declared to be MIN_VALUE, hence the large number
it's not returning null, IDEA is just confused about where it is in the method, that return null; shouldn't be executed if it fails in the FileProvider code.
doGet(null): null is the timeout here, it's guarded properly in code
I've run the app and weirdly I got a log line saying
W/SHARE: Sharing http://... failed
but not a stack trace, which is weird, because ex cannot be null in a catch!

Downloading an Image file to my Android App from Amazon S3 using transferutility too slow. Can I speed it up?

So here's my problem. Currently I am making an app that has users upload an image which can then be retrieved and viewed by other users. The user is supposed to be able to swipe through these pictures similar to the way it works in the facebook app, but randomly. The problem I'm encountering is that the images are downloading very slowly! It takes about 10 seconds for each image to download. Which is incredibly slow when you want to flip through images every 2-3 seconds or so. Currently I have used the AWS transfer utility to download and upload files to my S3 bucket. An example of my code is below:
TransferObserver observer = transferUtility.download(
"mybucket", /* The bucket to download from */
image, /* The key for the object to download */
iFile /* The file to download the object to */
);
observer.setTransferListener(new TransferListener() {
public void onProgressChanged(int id, long bytesCurrent, long bytesTotal) {
// update progress bar
//progressBar.setProgress(bytesCurrent);
Log.i(TAG, "progress changed");
}
public void onStateChanged(int id, TransferState state) {
Bitmap myBitmap = BitmapFactory.decodeFile(iFile.getAbsolutePath());
//Bitmap fin = rotateBitmap(myBitmap, 90);
//portraitView.setImageBitmap(myBitmap);
bmp = myBitmap;
update();
}
public void onError(int id, Exception ex) {
Log.e("ERROR", ex.getMessage(), ex);
Log.i("ERROR", "189");
Log.i("ERROR", "image is:" + image);
Log.i("ERROR", "iFile is:" + iFile);
update();
}
});
This gets the image file and stores it in iFile, then converts it to a bitmap and displays it on the screen. But it takes 10 seconds or so for onStateChanged() to run.
I have tried this with multiple buckets in multiple US regions (some very close to me, as well as standard.) I've tried activating the transfer acceleration on my buckets but the best download increase I have gotten is about 20% which still isn't going to cut it. Is there any way I can make this run faster? Is it even supposed to run faster?
Should I just use a different place to store my images?

Android AsyncTask sending message to a Handler on a dead thread

I am trying to make a program that should work something like a catalog - I have a JSON array that I loop through and parse the JSON objects into a string containing the image link, and two strings for descriptions for each object. Now, when downloading the images from the internet with the link, I run into a problem at the same image every time, image number 93. I checked the link, and it's working, just the same as the others.
This happens:
W/MessageQueue: Handler (android.os.Handler) {f95f6fe} sending message to a Handler on a dead thread
java.lang.IllegalStateException: Handler (android.os.Handler) {f95f6fe} sending message to a Handler on a dead thread
at android.os.MessageQueue.enqueueMessage(MessageQueue.java:543)
at android.os.Handler.enqueueMessage(Handler.java:643)
at android.os.Handler.sendMessageAtTime(Handler.java:612)
at android.os.Handler.sendMessageDelayed(Handler.java:582)
at android.os.Handler.post(Handler.java:338)
at android.os.ResultReceiver$MyResultReceiver.send(ResultReceiver.java:57)
at com.android.internal.os.IResultReceiver$Stub.onTransact(IResultReceiver.java:58)
at android.os.Binder.execTransact(Binder.java:565)
This is how my AsyncTask looks:
private class DownloadImageTask extends AsyncTask<String, Void, Bitmap> {
ImageView bmImage;
public DownloadImageTask(ImageView bmImage) {
this.bmImage = bmImage;
}
protected Bitmap doInBackground(String... urls) {
String urldisplay = urls[0];
Bitmap mIcon11 = null;
try {
InputStream in = new java.net.URL(urldisplay).openStream();
mIcon11 = BitmapFactory.decodeStream(in);
} catch (Exception e) {
e.printStackTrace();
}
return mIcon11;
}
protected void onPostExecute(Bitmap result) {
bmImage.setImageBitmap(result);
}
}
This is how I call the AsyncTask:
new DownloadImageTask(imgProductPicture).execute(pictureLink);
layoutTest.addView(NewImgView);
In my code I make sure that no more than 5 of these are called at a time, and they all load without issue until reaching the object indexed 92. The object itself is fine, but the app shuts off at this point always. Does anyone have an idea why? I've tried downloading less images at a time (one by one) and it still fails at the same point. I'd appreciate any help.
AsyncTask uses handler of the main thread, to callback onPostExecute(). If the main thread is dead when to callback, system throws the exception. To avoid this, you have to keep the main thread alive until all the work completes.
I solved the problem by creating a new handler/runnable every time I called the DownloadImageTask. Thanks to all who have tried to help.

Saving a .jpg to byte[] android

This shouldn't be that hard, but I cannot figure this out. I need to save an image, on my end only, and build it dynamically so all my users will view these images. The tutorials on Parse.com are very helpful, but not in the case of images. I need detailed explanations or helpful links. Thanks for looking.
This is all I have so far as far as saving an image. I am properly getting the file in my Data Browser, but if I try to view it, it only shows my string "beatdown.jpg" not the actual jpg.
....
private void saveImage() {
// TODO Auto-generated method stub
InputStream header = new FileInputStream("beatdown.jpg");
byte[] head = IOUtils.toByteArray(header);
ParseFile file = new ParseFile(head);
try{
file.save();
} catch (ParseException e) {
e.printStackTrace();
}
ParseObject displayImage = new ParseObject("displayImage");
displayImage.put("header", file);
try{
displayImage.save();
} catch (ParseException e1){
e1.printStackTrace();
}
}
I understand I am trying to get the string of "beatdown.jpg" to bytes in the code above, and it is not handling it as a .jpg. But I don't know how to make it a .jpg.
EDIT: I added commons-io. But when I run the code (see the above updated code), it won't register on anything on parse.com. I am getting this in my logcat;
Service com.android.exchange.ExchangeService has leaked ServiceConnection com.android.emailcommon.service.ServiceProxy$ProxyConnection#40cf2498 that was originally bound here
The key elements are:
File f = new File("pathToFile");
FileInputStream fis = new FileInputStream(f);
byte[] bytes = new byte[f.length()];
fis.read(bytes);
Of course there's exception handling and the like to do, but this should be enough to give you the general idea.

Simple way to add contact photo to ImageView?

I'm having problems getting and setting a contact's image as a view's background, surprisingly there are few examples on how to do it. I'm trying to build something similar to the People app which displays big contact photos.
This is what I'm doing right now:
Uri uri = ContentUris.withAppendedId(ContactsContract.Contacts.CONTENT_URI, Long.valueOf(id));
InputStream input = ContactsContract.Contacts.openContactPhotoInputStream(context.getContentResolver(), uri);
Bitmap bm = BitmapFactory.decodeStream(input);
Drawable d = new BitmapDrawable(bm);
button.setBackgroundDrawable(drawable);
This works however the URI it uses gets a thumbnail picture, so even if there is a big photo images looks very bad when scaled to fit the imageView. I know another method to get the URI that actually gets a big photo which is:
final Uri imageUri = Uri.parse(cur.getString(cur.getColumnIndex(ContactsContract.Contacts.PHOTO_URI)));
However I haven't managed to get it to the imageView, maybe the code above can be adapted to use the second uri. If you know how to use the second uri or if there is an easier way to get the contact image than through the URI please tell me. Any info will be thanked.
Good job in getting the URI. You're almost there. First of all consider using PHOTO_THUMBNAIL_URI instead of PHOTO_URI, as it may be what you need in terms of size.
Edit : FYI, PHOTO_THUMBNAIL_URI is available starting API 11. You can still use it conditionally.
If you want to use an external library, 'Android Universal Image Loader' is definitely what you are looking for, as starting from it's 1.7.1 version from a few days ago, it added support for content schemes and it is pretty smart, memory wise. It also has a lot of customization options.
Edit: this lib is already dead. Use Fresco instead.
If you'd rather be nicer to your final bundle size and write the code yourself,
You need to get and decode the input stream of that content; This should be done on a background thread. Check out this connivence method; You initialise it with your image view and the uri you got and start it when you want to load the ImageView.
private class ContactThumbnailTask extends AsyncTask<Void, Void, Bitmap> {
private WeakReference<ImageView> imageViewWeakReference;
private Uri uri;
private String path;
private Context context;
public ContactThumbnailTask(ImageView imageView, Uri uri, Context context) {
this.uri = uri;
this.imageViewWeakReference = new WeakReference<ImageView>(imageView);
this.path = (String)imageViewWeakReference.get().getTag(); // to make sure we don't put the wrong image on callback
this.context = context;
}
#Override
protected Bitmap doInBackground(Void... params) {
InputStream is = null;
try {
is = context.getContentResolver().openInputStream(uri);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
Bitmap image = null;
if (null!= is)
image= BitmapFactory.decodeStream(is);
return image;
}
#Override
protected void onPostExecute(Bitmap bitmap) {
if (imageViewWeakReference != null && imageViewWeakReference.get() != null && ((String)imageViewWeakReference.get().getTag()).equals(path) && null != bitmap)
imageViewWeakReference.get().setImageBitmap(bitmap);
}
}
It is suggestion. First know one thing.
When you set a contacts image. First Android show the Cropping activity for that image. Like
****carefully see above image. Android crop the image as square shape. And store the square shape image as a Blob in contacts.(It is not individual image. It is blob.)
You get a square shape image for your image view from your coding. so top and bottom show black color only. Because your mobile in rectangle shape.****
If you want to show full screen image. Please set a big image to contacts through programmatically. Lot of examples available in internet.
All the best for your try. If you have any doubts. Please provide comments.
You can try using SmartImageView: http://loopj.com/android-smart-image-view/ extends imageview and also loads images asynchronously.
Use an external library to do that for you. Or browse the code and make something similar your own way.
Here's one I use on several of my own apps: UrlImageViewHelper
The code would go like this:
Uri uri = ContentUris.withAppendedId(ContactsContract.Contacts.CONTENT_URI, Long.valueOf(id));
UrlImageViewHelper.setUrlDrawable(button, uri.toString(), R.drawable.dummy_contact_photo);
You can easily set the contact photo to the image view by using the following method.
public String getImageUriString(String phoneNumber)
{
ContentResolver resolver = context.getContentResolver();
Cursor names = resolver.query(
Uri.withAppendedPath(ContactsContract.PhoneLookup.CONTENT_FILTER_URI, Uri.encode(phoneNumber)),
null, null, null, null);
names.moveToFirst();
String name = "";
if(!names.isAfterLast())
{
name = names.getString(names.getColumnIndex(ContactsContract.PhoneLookup.PHOTO_URI));
}
else
{
name = null;
}
names.close();
return name;
}
public void setImageView(ImageView contactPhoto) {
String photoUriString = di.getImageUriString(contactNumber);
if(photoUriString != null) {
Uri photoUri = Uri.parse(photoUriString);
contactPhoto.setImageURI(photoUri);
} else {
contactPhoto.setImageResource(R.drawable.icon_contact);
}
}
From your class, set the image view with the uri getting from the above method.
To be able to do that you have to just add one last parameter preferHighres = true:
openContactPhotoInputStream (ContentResolver cr, Uri contactUri, boolean preferHighres)
If preferHighres is true and the contact has a higher resolution photo available, it is returned. If false, this function always tries to get the thumbnail
Uri uri = ContentUris.withAppendedId(ContactsContract.Contacts.CONTENT_URI, Long.valueOf(id));
InputStream input = ContactsContract.Contacts.openContactPhotoInputStream(context.getContentResolver(), uri, true);
All the images probably would have differnet sizes. In order to resize them I use next code:
Bitmap bm = BitmapFactory.decodeStream(input);
bm = Bitmap.createScaledBitmap(photo, contactImageWidth, contactImageheight, false);
Drawable d = new BitmapDrawable(getContext(), bm);
button.setBackgroundDrawable(d);

Categories