What is my Problem (KISS)?
After showing the 3rd Image in Detail my App crashes cause OutOfMemory
Background information about my App:
I made an offline Supermarket. All products are shown in a ListView.
All Images I use are stored as String (Uri from every Image) in a sqlite Database by SugarORM.
The ListView element got a ArrayAdapter where the Information for each Item in the ListView is set.
Like this:
ImageView imageView = (ImageView) rowView.findViewById(R.id.productimage);
Handler handler = new Handler();
handler.post(new Runnable() {
#Override
public void run() {
imageView.setImageURI(productList.get(position).getPictureUri());
handler.removeCallbacks(this);
}
});
My Intention was, to load the Images in the Background to Click on it, even before the Image is loaded (But it did'nt work for me :/ The Image is Shown, but I cant click on any other View Element while Loading the Image).
The "getPictureUri" gets the URI from the Picture that the Admin has choosen for the product with the following Code:
Intent pickPhoto = new Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
startActivityForResult(pickPhoto,1);
AndroidManifest.xml
android:hardwareAccelerated="false"
android:largeHeap="true"
I extend only from "AppCompatActivity" (Needed for Toolbar). Meaning of this: I don't use "Activity" even once.
Could anyone give me a good advice, how to clear the cached Images (I think this might be the Problem) and besides, maybe one of you got the Time to help me with my "Image Async Loading while you are able to move freely in your app" Problem.
Using setImageUri() is rather intensive on memory, especially when trying to load large bitmaps from a local resource. Instead, use tried and true solutions like Picasso or Glide for any/all image loading tasks. The libraries will work offline, can definitely load images from URIs pointing to a local resource and would most likely solve all your memory issues with image loading.
Related
So I am displaying video files from specific folder when the folder is selected but in the video list I am showing file name with the video thumbnail and that's where the problem starts i am using this way to get the thumb
Bitmap bmThumbnail = ThumbnailUtils.
extractThumbnail(ThumbnailUtils.createVideoThumbnail(info.getFilename(),
MediaStore.Video.Thumbnails.MINI_KIND), 60, 60);
if(bmThumbnail != null) {
imageView.setImageBitmap(bmThumbnail);}
and even if I don't set the bitmap or not the process is taking way too much time to open the new fragment whereas just displaying the name is smooth if I call the following bitmap it takes around 6-7 sec to display the list. The following thing are happening in the adapter as I test the app in adapter then recycler view
so I would like to know what is the best way to do it .
For using image loader i need the url which is not there as i am getting the album art using the file url but it will not produce the album art url directly.
Dealing with images and videos like that takes time. Particularly when dealing with a large group of them. It's unlikely you can speed up the operation but you can make it so your application doesn't have to wait for it by sending it to the background. I recommend Kotlin coroutines if you are up for converting to Kotlin. Otherwise I recommend making a thumbnail work manager
public class VideoThumbnailWorker extends Worker {
}
I've been reading quite a lot on the topic, still not quite clear though. At the moment I'm creating an app, loading an image and a text on one screen. For loading the images I opted for Glide, but where is the most appropriate place to read them from? All of the tutorials I passed pass the image's URL. Isn't it slower when loaded from the net? Thanks a lot!
If you care for apk size then do not put these images static. Instead you can keep these images on server(your or free server) and easily load those images using libraries like Glide or Picasso.
Isn't it slower when loaded from the net?
No. It will download image once and then cache it for future use. So it's very fast.
If you think apk size will doesn't matter for you and user should not face problem due to unavailability of internet then you can keep those images static inside app iteself.
If you want to build an app that uses dynamic images or you want to update your images without updating your application, getting them from the server is better. And in my opition picasso is easy to use and straightforward. Also uses it's own framework caching. But if you think that your images wont change, put them in an asset folder so that they are in app's internal memory. Getting them from the server has it's downsides like you need to use a placeholder images because they won't be retrieved immediately.
You must use caching mechanisms if you want the images always from network. The system I follow is like this: (PS. I use Picasso, fast and reliable):
Picasso.with(this).load(URL).networkPolicy(NetworkPolicy.OFFLINE). //load from cache first time
into(imageView, new Callback() { //Picasso Callback
#Override
public void onSuccess() {
if(isNetworkAvailable()) { // if network available then update the cache for this URL
Picasso.with(MyActivity.this).invalidate(URL);
}
progress.setVisibility(View.GONE); // Progressbar
}
#Override
public void onError() { // Image not loaded, try again one last time
Picasso.with(MyActivity.this).load(URL).into(imageView, new Callback() {
#Override
public void onSuccess() {
progress.setVisibility(View.GONE);
}
#Override
public void onError() {
progress.setVisibility(View.GONE);
}
});
}
});
I'm trying to display an animated GIF picture in Widget, and I have URL for the picture. I know I can use WebView in activity with webView.loadUrl("http://my_url_here.gif"), but WebView is not supported in Android Widget.
I've already had URL for the picture, and I'm finding some ways not using third party libraries.
Does anyone know how to display it in Widget?
Where is no way to show GIF animation on RemoteViews. You can use only widgets that describes in official documentation
A bit late to the party, but I will post this for future references who want to get a widget animated.
I think this is probably what you want to achieve:
https://media.giphy.com/media/Rb7FAFdrgHr51V6zjy/giphy.gif
Fundamentally, GIF files "allow images or frames to be combined, creating basic animations", any GIF is a collection of images with continuous updates/refreshes to achieve the animated effect.
Hence, even though widgets on Android are RemoteViews that only support basic Text and Image, but if you can downsample a GIF into a series of PNGs or Bitmaps, you can achieve the animation effect by creating an async background task that updates the image bitmap or src of the Widget's RemoteView.
There are great answers on SO for converting Gifs into a collection of PNGs:
Using Glide, how can I go over each frame of GifDrawable, as Bitmap?
public class GifPlayer extends AsyncTask<String, Void, List<Bitmap>> {
#Override
protected List<Bitmap> doInBackground(String... params) {
try {
// load Gif images
InputStream in = new java.net.URL(url).openStream();
//... use Glide to convert Gifs into PNGs and save it in your local file system.
List<Bitmap> gifs = ...
} catch (Exception e) {
}
return null;
}
#Override
protected void onPostExecute(List<Bitmap> pngArray) {
while(true) {
for(Bitmap png : pngArray){
views.setImageViewBitmap(R.id.new_widget_image, png);
WidgetManager.updateAppWidget(WidgetID, views);
Thread.sleep(500); // higher lower this number depends on how fast you want to animate
}
}
Not to mention, Android Widgets have a small memory limit, so the above code works, but it will run out of memory for larger GIFs.
In general, RemoteViews.setImageViewBitmap(viewId, bitmap) should only be used for Widgets that don't update frequently.
For frequent updates of Widget such as animating a GIF, always use setImageViewResource(viewId, resId) instead (save frames of a GIF as PNG files identified by consecutive IDs such as gif_1.png, gif_2.png, gif_3.png...)
and replace the above code by:
//...
#Override
protected void onPostExecute(List<Integer> pngArray) {
while(true) {
for(int id : pngArray){
views.setImageViewResource(R.id.new_widget_image, id);
WidgetManager.updateAppWidget(WidgetID, views);
Thread.sleep(500); // higher lower this number depends on how fast you want to animate
}
}
You can use the gifView library :https://github.com/koral--/android-gif-drawable
use this is your .xml:-
<pl.droidsonroids.gif.GifImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:src="#drawable/src_anim"
/>
where src_anim is your loader gif file
You can use a load of different image loading libraries for this, but I recommend Fresco. They have great built-in GIF support detailed on this page which looks like the following in code:
Uri uri = Uri.parse("http://my_url_here.gif");
DraweeController controller = Fresco.newDraweeControllerBuilder()
.setUri(uri)
.setAutoPlayAnimations(true)
. // other setters
.build();
mSimpleDraweeView.setController(controller);
update
I never really understand where this need to not use libraries stems from, but you could just manually copy across all the code classes to your app then! The code in these libraries is battle tested and will work much more reliably than most code you decide to write alone.
That being said, there's a useful gist I've used in the past that may be what you're looking for.
I am using Picasso to handle image loading and caching in my Android Udacity project and I am noticing the caching is not working as I'd expect:
As you can see on the left fragment, the image has already loaded in an earlier thread. Now, with the same URL link, I am asking Picasso to place that image in the fragment on the right.
Here is the code which generates the grid view on the left fragment (and occurs first):
https://github.com/esend7881/udacity-android-popmovie/blob/a9a1b9a19a37594bb5edd736b7ec59229fb5905a/app/src/main/java/com/ericsender/android_nanodegree/popmovie/adapters/GridViewAdapter.java#L71
String load = String.format(sImgUrl, sImgSize, movie.poster_path);
Picasso.with(mContext.getApplicationContext())
.load(load)
.placeholder(R.drawable.abc_btn_rating_star_on_mtrl_alpha)
.error(R.drawable.abc_btn_rating_star_off_mtrl_alpha)
.resize(550, 775)
.into(viewHolder.imageView);
And then here is the code which runs in the right fragment:
https://github.com/esend7881/udacity-android-popmovie/blob/a9a1b9a19a37594bb5edd736b7ec59229fb5905a/app/src/main/java/com/ericsender/android_nanodegree/popmovie/fragments/MovieDetailsFragment.java#L308
Picasso.with(getActivity().getApplicationContext())
.load(String.format(sImgUrl, sImgSize, mMovieObj.poster_path))
.error(R.drawable.blank)
.fit()// .resize(366, 516)
.into(mMovieThumb, new com.squareup.picasso.Callback() {
#Override
public void onSuccess() {
Utils.log(sw.toString());
Utils.hideViewSafe(mMovieThumbProgress);
}
#Override
public void onError() {
Utils.log(sw.toString());
Utils.hideViewSafe(mMovieThumbProgress);
}
});
I am using the same application context in each as well as the load text:
String.format(sImgUrl, sImgSize, mMovieObj.poster_path))
and
getActivity().getApplicationContext()
So, I would think Picasso ought to detect when the exact same URL load link appears in the same context within a short period of time from each other and Picasso would then load the exact same image back into the app.
If this is not how Picasso caching works, then how does it?
As a comment mentioned, I'd guess this is affected by the size of the image being different in both fragments.
I'd recommend using https://github.com/facebook/fresco instead of picasso. It's more efficient, especially with different sizes. You can also directly access cached files if required https://github.com/facebook/fresco/issues/80
It's probably related to the HTTP headers received when getting the image that do not allow caching, as Picasso relies on an HTTP component to do the caching.
Try uploading your image on imgur, try hardcoding that path and see if it works. If that's the case, you'll have to find a workaround on how to get the image from the movie database.
Care: No code here, only text and some questions about bitmap caching
I'm currently developing an App which is almost finished. The only thing left, that I would like to do is caching images. Because, at the moment, when the user opens the app the app downloads images from a server. Those images are not static, that means they can change every minute/hour/day. I don't know when they change, because it's a list of images gathered by the amount of twitter shares, facebook likes etc. That means, when a picture has 100 likes and 100 tweets it is place 1. But when another picture gets more likes and tweets it gets rank 1 and the other one will be placed as rank 2. This isn't exactly my app, but just so you understand the principle.
Now I looked into Bitmap caching so the user doesn't have to download the same images over and over. The question I do have is how do I do it? I mean, i Understand HOW to cache bitmaps.
I looked into this documentation article: http://developer.android.com/training/displaying-bitmaps/cache-bitmap.html
But, the problem is, how do I know if the Bitmap already got downloaded and has been cached or if I have to download it again? Don't I have to download the image first to check if I have this particular image already in my system?
I thought about getting the URL of the image, then convert it into a hash. And then, save the files to the cache with the hash as filename. Then, when the image URL comes it will be checked wether the image is available in the cache or not. If it is it will be loaded if not it will be downloaded. Would that the way to go be?
Or am I misunderstanding bitmap caching and it does it from its own already?
my best advice on those cases is: Do not try to re-invent the wheel.
Image loading/caching is a very complex task in Android and a lot of good developers already did that. Just re-use their work.
My personal preference is Picasso http://square.github.io/picasso/
to load stuff with it is one very simple line of code:
Picasso.with(context).load(url).into(imgView);
it's that simple!
It does both RAM and disk cache, handles all threading issues and use the excellent network layer okHttp.
edit:
and to get access directly to the Bitmap you can:
Picasso.with(context).load(url).into(new Target() {
void onBitmapLoaded(Bitmap bitmap, LoadedFrom from){
// this will be called on the UI thread after load finishes
}
void onBitmapFailed(Drawable errorDrawable){
}
void onPrepareLoad(Drawable placeHolderDrawable){
}
});
Check this library:
http://code.google.com/p/android-query/wiki/ImageLoading
It does caching automagically
example
//fetch a remote resource in raw bitmap
String url = "http://www.vikispot.com/z/images/vikispot/android-w.png";
aq.ajax(url, Bitmap.class, new AjaxCallback<Bitmap>() {
#Override
public void callback(String url, Bitmap object, AjaxStatus status) {
}
});.
http://code.google.com/p/android-query/wiki/AsyncAPI
You can try https://github.com/thest1/LazyList
the project code was designed for listviews, but still, its purpose is to download images from URLs in the backgroud so the user doesn't have to hold on the whole downloading time.
you take these JAVA classes : FileCache, ImageLoader, MemoryCache, import them into your project,
for downloading an image you just call imageLoader.DisplayImage(URL,ImageView);
the best part is that it takes care of the cache itself so you don't have to worry about that
hope this helps