Basically, I am making an API call to get some kind of response back. In this response, there is some field like socialImageUrl that points to a URL string that references an image. There is other data there besides the URL. I am caching that entire response.
So I turn off wifi and LTE, and I checked that I had no internet connection by doing stuff on the browser. I go back into my app and I trigger my LruCache and retrieved the cached response (I know I got it because of logging) and I use that response to render my page.
What I expected: I would see all the data that I cached on the page but anything that used the image URLs will give out some erroneous image or maybe the page doesn't even load at all because there is some error with that image URL seeing as it can't access the internet
What actually happens: I see all of the data I cached and I also see the images
Any clue to what is going on? I'm not doing any image caching as far as I know. I am using react-native's vanilla Image JSX component. I am also on Android.
I was also in this situation ones, sadly React-Native docs are not extensive for now.
The Android implementation of Image component has by default caching enabled for images of low size < 500 KB (the max size is not documented but you can just try images of varied size to check and this size could change in the future).
Note: Even in IOS the image's will be cached by default when using the Image component.
If I had to guess, lru cache does not support caching binary data like an image file.
If you need advanced <Image> performance caching or for the remote image to be permanently stored to local disk for offline app use you can use my higher order component module that upgrades native <Image>
React Native Image Cache HOC
Tl;DR Code Example:
import imageCacheHoc from 'react-native-image-cache-hoc';
const CacheableImage = imageCacheHoc(Image);
export default class App extends Component<{}> {
render() {
return (
<View style={styles.container}>
<Text style={styles.welcome}>Welcome to React Native!</Text>
<CacheableImage style={styles.image} source={{uri: 'https://i.redd.it/rc29s4bz61uz.png'}} />
<CacheableImage style={styles.image} source={{uri: 'https://i.redd.it/hhhim0kc5swz.jpg'}} permanent={true} />
</View>
);
}
}
The first image will be cached until the total local cache grows past 15 MB (by default) then cached images are deleted oldest first until total cache is below 15 MB again.
The second image will be stored to local disk permanently. People use this as a drop in replacement for shipping static image files with your app, but you could use it so that your app displays image content even when user is offline.
If you need to grab all the image files from the network up front (before the <CacheableImage> is rendered by react native) then just use the following CacheableImage static method to pre-fetch the files:
import imageCacheHoc from 'react-native-image-cache-hoc';
const CacheableImage = imageCacheHoc(Image);
CacheableImage.cacheFile('https://i.redd.it/hhhim0kc5swz.jpg', true)
.then( localFileInfo => {
console.log(localFileInfo);
// Console log outputs:
//{
// url: 'https://i.redd.it/rc29s4bz61uz.png',
// cacheType: 'permanent',
// localFilePath: '/this/is/absolute/path/to/file.jpg'
//}
});
Related
I tried to display an image from a local file, so I try to do this
<Image
source={{uri:'../../assets/img_src/BTI.jpg'}}
style={{height:150,width:150}}
/>
my container component seems getting the height and the width but the image doesn't load. So I try this
<Image
source={require('../../assets/img_src/BTI.jpg')}
style={{height:150,width:150}}
/>
It works really well, unfortunately as far as I know I can't use this implementation if i want to manipulate the address file first and store it to a variable.
Example:
const imgSrc = `../../assets/${data.image}`;
where the data.image = 'img_src/BTI.jpg'.
I try to delete all the data on my android simulator in the android studio but the result is still the same. Am I doing something wrong? Is there a better implementation for this?
Thanks a lot :)
Not being able to require() a dynamic path is an intended behaviour of React Native, the packager needs to know paths in advance - see React Native issues
We had this problem in our project, we were storing image URLs on our DB and wanted to concat strings in the app to get the path to the locally stored image. As you've discovered, this doesn't work. You might find our workaround useful.
In our /images folder, we created a new images.js file which simply exported an object whose keys mapped to all our local image paths like so
export const Images = {
image1: require('./image1.png'),
image2: require('./image2.png'),
image3: require('./image3.png'),
};
Now the packager knew all the paths in advance and was happy. Then we added an imageKey to the DB item and used this rather than the absolute path. To grab an image we simply did
import { Images } from '../images/images';
// get your image key e.g. passed down through props
const img = Images[imageKey];
...
render() {
...
<Image source={img} />
...
}
Might not fit your exact use case, but hopefully you might be able to make some use of it.
In a mobile app one or more images (only jpeg/jpg) - either taken with built-in camera or selected from photo library - are displayed before uploading to a web service.
But loading an image into a FMX TImage / TImageControl / TImageViewer and saving it to stream (or file) makes it smaller, about 45%.
Any idea why this happens and is there a way to avoid the reduction in size ?
As asked for this is the simple test code, nothing special to it:
procedure TImageTest.btnTestClick(Sender: TObject);
var
aFile : string;
begin
if not OpenDialog.Execute then
Exit;
// get jpg file name for loading
aFile := OpenDialog.Filename;
// load into TImage, TImageControl or TImageViewer
ImageViewer.Bitmap.LoadFromFile(aFile);
// and save to file for comparison
ImageViewer.Bitmap.SaveToFile(aFile+'_2.jpg');
end;
The ideal solution to avoid losing image quality is by not relying on the visual controls to store your data. Instead, keep a background object containing the raw image file, and only use the visual controls to display this image to the user. When it comes to saving the image, save it from this background object rather than the visual control.
The same concept applies for about all aspects of programming - visual controls are only meant for visual display to the user, not for storing data.
Scenario:
I have a large GIF image which I want to cache the first time user opens the app using Glide - Image Loading and Caching library. After that whenever user opens the app, I want to show the cached version if present. This GIF URL will expire after a given interval. When it expires, I fetch the new GIF URL and display/cache that for future use.
What I tried:
I went through Caching and Cache Invalidation on Glide's github page. I also went though the Google Group thread Ensuring That Images Loaded Only Come From Disk Cache, which shows how to get the image form cache. I also went through How to invalidate Glide cache for some specific images question.
From the links above I see the following code sniplet which shows how to load the image from cache. However this only tries to get the image from cache. If its not present in cache, it doesn't try to get from the network and fails:
Glide.with(TheActivity.this)
.using(new StreamModelLoader<String>() {
#Override
public DataFetcher<InputStream> getResourceFetcher(final String model, int i, int i1) {
return new DataFetcher<InputStream>() {
#Override
public InputStream loadData(Priority priority) throws Exception {
throw new IOException();
}
#Override
public void cleanup() {
}
#Override
public String getId() {
return model;
}
#Override
public void cancel() {
}
};
}
})
.load("http://sampleurl.com/sample.gif")
.diskCacheStrategy(DiskCacheStrategy.SOURCE)
.into(theImageView);
Questions:
Is there a cleaner way to achieve the following: Show the GIF image from the cache if present else download the GIF, cache it for later use and show it in the ImageView.
The caching article above mentions the following:
In practice, the best way to invalidate a cache file is to change
your identifier when the content changes (url, uri, file path etc)
The server sends a different URL to the app when the previous one expires. In this case, I believe the old image will eventually be Garbage Collected? Is there a way to force remove the image from the cache?
On a similar note, is there a way to prevent the Garbage Collection of an image with specific key (to prevent downloading the large file again) and then later instruct to delete the old image from cache when the URL changes?
You don't need a custom ModelLoader to show the GIF from cache if present and fetch it otherwise, that's actually Glide's default behavior. Just using a standard load line should work fine:
Glide.with(TheActivity.this)
.load("http://sampleurl.com/sample.gif")
.diskCacheStrategy(DiskCacheStrategy.SOURCE)
.into(theImageView);
Your code will prevent Glide from downloading the GIF and will only show the GIF if it is already cached, which it sounds like you don't want.
Yes, the old image will eventually be removed. By default Glide uses an LRU cache, so when the cache is full, the least recently used image will be removed. You can easily customize the size of the cache to help this along if you want. See the Configuration wiki page for how to change the cache size.
Unfortunately there isn't any way to influence the contents of the cache directly. You cannot either remove an item explicitly, or force one to be kept. In practice with an appropriate disk cache size you usually don't need to worry about doing either. If you display your image often enough, it won't be evicted. If you try to cache additional items and run out of space in the cache, older items will be evicted automatically to make space.
Glide.with(context)
.load("http://sampleurl.com/sample.gif")
.skipMemoryCache(true)
.into(imageView);
You already noticed that we called .skipMemoryCache(true) to specifically tell Glide to skip the memory cache. This means that Glide will not put the image in the memory cache. It's important to understand, that this only affects the memory cache! Glide will still utilize the disk cache to avoid another network request for the next request to the same image URL.for more read this
Glide Cache & request optimization.
Happy coding!!
FlurryAdNativeAsset secHqImageAsset = adNative.getAsset("secHqImage");
secHqImageAsset.getValue()
The url points to either the local or remote resource, depending on how the ad >space is configured on dev.flurry.com
Caching is enabled for the ad space - default setting: The SDK caches the assets onto the device before it declares the ad ready. The asset’s url in FlurryAdNativeAsset points to the local file (url contains the locator in the form file:// ) where the asset resides.
Caching is not enabled for the ad space: The assets are available via https url. Calling loadAssetIntoView or getAssetView on the FlurryAdNativeAsset object loads the assets directly from the network and does not cache the assets.
https://developer.yahoo.com/flurry/docs/publisher/code/android/
I cannot find anywhere in ad space to disable caching and I want to get the exactly remote link and load the image manually because keep using getAsset("secHqImage").loadAssetIntoView() will cause memory leak.
Is there any way to get the remote url link?
FlurryAdNativeAsset secHqImageAsset = adNative.getAsset("secHqImage");
Now you have the FlurryAdNativeAsset.
secHqImageAsset.getValue()
Should be replaced with:
String urlOfAsset = secHqImageAsset.getValue();
That should give you the URL string from where to get the asset. Once you get it, just use Android provided methods to load the Bitmap of the image.
orI have an app that is consistently downloading large images from a parse back-end. In android you can download the images as streams using their URI's and set a specific dimension size to the bitmap that I want to download. Usually this is done using the BitmapFactory libraries and allows me to download a bitmap that's of a scaled down size, saving my app from long loading times. Is there an equivalency to this method on the IOS platform? This is what I am currently doing, but when I download 15 full sized images I get a large amount of load time:
//where photoQuery is a Parse.com database query that returns the photo as the first object
PFObject *photoObject = [photoQuery getFirstObject];
PFFile *photoFile = (PFFile *)[photoObject objectForKey:#"fullSizedImage"];
UIImage *fullImage = [UIImage imageWithData:photoFile.getData];
Does IOS support something similar to BitmapFactory or this common android design pattern?
PFFile has a method called – getDataInBackgroundWithBlock:. Which can be used like this:
[photoFile getDataInBackgroundWithBlock:^(NSData *data, NSError *error) {
if(!error) {
[imageView setImage:[UIImage imageWithData:data];
}
}
Or you can use another method called - getDataStreamInBackgroundWithBlock:. It is similar as the one above. The difference is the first parameter of the block is a NSInputStream
PFImageView should help you load these images a synchronously. You may try thumbnailing images using Cloud Code.