I have a fairly large list of image URL's that I'm using to load up a ViewPager using Picasso. I need to be able to provide sharing capabilities for these images via an intent (ultimately sharing via ShareActionProvider). From what I've read, Picasso isn't really built to handle this sort of thing out of the box, though it provides all the necessary tools to do so.
My plan before research was to create a simple LruCache which uses the url as the key and bitmap value. This caching would occur in onBitmapLoaded via Picasso's Target interface. Whenever I want to share an image, I'll check the cache for the bitmap. If it's not there, I'll fetch with Picasso. Now that I have a cached bitmap regardless, I'll write to a file (...this part doesn't seem right, though I have to write to a file to get a uri, right?) and add the file uri to the intent.
However I see that with the Picasso.Builder I can set (and retain a reference to) my own cache - https://stackoverflow.com/a/18552559/413254. This means I could do away with the custom Target and confusion with properly implementing hashCode and equals methods to ensure accurate recycling, retrieval, etc.
My question is, how does Picasso use this cache? What are the keys? Is there a way to get a bitmap Uri without writing it to disk?
If you want to use ShareActionProvider to share the image on the current page you don't have to keep your own cache of images. But to be able to share it to others, the image should be in the shared file system on the device.
It would be better if you use image loading libraries with custom disk cache support like Universal Image Loader
If you want to use Picasso (which is a good decision).
You either have to save a copy of the image on every page change which is nor a good option.
Or you can give a custom network handler to Picasso and set a custom cache implementation to it. I would suggest using OkHttp with custom caching which stores files in the format you desire. When you do that, you have to have a function that converts image URLs to file path on a device.
In every page change, if you have a Fragment inside your ViewPager, put the ShareActionProvider into your Fragments.
Get the reference of the ShareActionProvider inside onCreateOptionsMenu. And then set the Intent with the file path you get.
Intent shareIntent = new Intent(Intent.ACTION_SEND);
Uri phototUri = Uri.parse(Utils.getFilePath(imageUrl));
shareIntent.setData(phototUri);
shareIntent.setType("image/png");
shareIntent.putExtra(Intent.EXTRA_STREAM, phototUri);
mShareActionProvider.setShareIntent(intent);
Edit:
The other option I would prefer is to ditch ShareActionProvider and use a normal menu item for this. The problem with ShareActionProvideris that you have to make the share Intent ready for user to share before hand. You have to make it ready even the user won't share it.
But when you have a normal button, it is much easier because you only make the operation when the user clicks the share button. In that case you can simply request the image one more time from Picasso with a Target object and write the Bitmap you got to a file in the shared external file system and share it.
Related
I know this is a very frequently asked questions and there are multiple answers. My question is "which is better?"
I've an activity where I capture an image using camera. I need to pass this image to another activity.
One way is to create Bitmap and pass it in putExtra since Bitmap is a Parcelable. This fails when the image size is too big.
I found 2 solutions. This answer uses MemoryCache to save and retrieve the image. Many answers (this, this and this) recommend to save the image to storage and then pass the path to new activity and read the image there.
Which is a better method in this case? (In terms of speed and memory)
It is safer in any case work with path or link than pass it as it is. It works not only with images but with most types of data. At the same time while working with path you can easy make if/else checks and handling some unexpected scenarios. Also pass the path is much faster.
I have the user's profile pic across multiple activities in my app. Once, they change their profile image, I want to make sure that all my Glide instance's cache are cleared. That way when they navigate around the app, they can see their updated profile pic.
Currently I'm using this method: Glide.get(activity).clearDiskCache(); and that only clears the Glide cache for that activity and not across my app.
Hope someone has a quick solution, where I don't need to call the .signature() function for each glide instance in each of my activites. Or clear each glide cache in each activity.
Try
Glide.get(context).clearMemory();
OR
Glide.get(context).clearDiskCache();
Note: clearMemory() must be called on the main thread. clearDiskCache() must be called on a background thread. You can't call both at once on the same thread.
I went through this whole windmill trying Signatures and clearing caches, and to be honest - none of those options work particularly well and they're usually slow.
Glide's first recommended solution is bar far superior, although it can sometimes take a bit more time to rework your code. I eventually lost my marbles and made the necessary changes to my code. It was well worth it.
Solution: Change the image name of the image when the user uploads a new image. Get the file name and use that. Once the image URL has changed, Glide understands you have changed the image and will update the Cache accordingly.
I'm using Picasso to download images in my app. My understanding is that it uses a http client (HttpResponseCache or OkHttpClient) to cache these images on disk.
Without knowing much about either of these libraries, is it possible to mark certain images as permanent? In other words, I would like to download an image and guarantee that it will be available offline.
Thinking about it, I couldn't really have the disk cache go over a certain size, so I guess what I really need is to remove the TTL on the image url and allow the cache to remove images in a first in first out scenario.
In that case, can I control which image will be deleted first (by using a timestamp based on accessed, rather than downloaded)?
Update
Based on the answer from this SO question:
Android + Picasso: changing URL cache expiration
So this answers the first part of the question - we can control the TTL through the server.
After talking to a colleague, he pointed out that the http client should take into account how frequently an image is accessed (in addition to the TTL). So hopefully I don't have to worry about this either.
That leaves me with one question. I know which images I don't need anymore, can I remove an image from the disk cache?
You can iterate the elements in OkHttp's disk cache, and call Iterator.remove() to get rid of the ones you don't want.
http://square.github.io/okhttp/javadoc/com/squareup/okhttp/Cache.html#urls--
You'd have to extend Picasso's default cache and create a custom Picasso instance to use it:
Extend LruCache
Override the void set(String key, Bitmap bitmap) method to do what you describe (adding timestamp, etc.). Check out the original source code here.
Make sure that the original trimToSize method is never called by set (and clearKeyUri for that matter), and write your own to check for the timestamp etc to get the behavior you describe
Create a custom Picasso instance with your custom cache like this:
Picasso picasso = new Picasso.Builder(context).memoryCache(cache).build(); Picasso.setSingletonInstance(picasso);
Where cache is an instance of your custom LruCache class
when you want to store the images on the disk you should use the okhttpdownloader
OkHttpClient client = new OkHttpClient.Builder()
.cache(new Cache(getCacheDir(), Integer.MAX_VALUE))
.build();
Picasso build = new Picasso.Builder(this)
.downloader(new OkHttp3Downloader(client))
.build();
Picasso.setSingletonInstance(build);
I want to know how long data can pass though intent. If i am passing parcelabel(as i am passing bitmap) more than 500x500 size it is give java transection binding fail.
If it's small (thumbnail) then it should be fine, if it is big you can end up with failed binder transaction errors. To avoid it you should pass a URI to location where it is saved. You can put it on sdcard if you have permission or in cache folder if not. If you are sending intent to some other process, then you should put bitmap on sdcard to make it readable by other processes. Last solution is to write Content Provider, this is usefull when you are sharing bitmap files, and want to for example attach bitmap with email.
When I use ListView the getView() method is called many times. Every time when the getView() is called i load the image with Asyc task. I mean every time i reset the image which is annoying.
How to understand when to load the image?
You should cache loaded images, by storing i.e. on SD card, so once you got a copy there, no need to download it again. There's lot of ready-to-use classes that can do the job for you, like:
http://greendroid.cyrilmottier.com/reference/greendroid/widget/AsyncImageView.html
you must must have two flags.
One which says if you've already loaded the image, if true you do nothing.
One which says if you're currently loading the image, if true you do nothing.
The members will also help you on maintaining the state of the image.
Your code should look something like this:
private boolean isLoading = false;
private boolean hasLoaded = false;
if(!hasLoaded){
if(!isLoading){
isLoading = true;
//do async load
//on positive completition callback set hasLoaded to true
//on negative completition callback set isLoading to false
}
}
One of the best solution is to create image cache using the WeakReference. This way you can keep images in memory and only need load from server when they are not in memory. In this method the image would be removed from the memory when system encounter low memory situation. So your current activity would always keep the hard reference to the bitmap's required and the image cache would keep the weak reference to the bitmap's.
below reference links will help you
http://developer.android.com/training/displaying-bitmaps/cache-bitmap.html
http://www.codeproject.com/Articles/35152/WeakReferences-as-a-Good-Caching-Mechanism
the Volley library (made by google) has a very intuitive class for an imageView that can have a url , called "NetworkImageView" .
you should check it out and watch the video, since they show that it's quite annoying to do it using asyncTask (plus the asyncTask is known to have a limit of tasks, about 255 or so) .
for setting the url, just use setImageUrl .
it has some useful methods for the phases of loading too: setDefaultImageResId , setErrorImageResId.
it's also supposed to have built in caching mechanism of some sort, but i haven't read much about it, so you might want to check out their samples.
this will remove the need to use asyncTasks for the listView's items.
one of my questions regarding the volley includes a sample code , here .
You can add a caching layer and optionally preloading the images. A good strategy for caching Images (Bitmap objects to be exact) is to use a strategy called LRU or least recently used.
Android support library has a class called LruCache that implements this strategy. So for example, when you download/load the image for the first time, you stick it into the cache. later, you can first check if it's already in cache and load it from there.
For preloading, A good rule of thumb is to preload the previous ten and the next ten items.