Android load image from assets

41 Post

Game Programming, Android and Retro Games

Android: Loading files from the Assets and Raw folders

Posted by Dimitri | May 24th, 2011 | Filed under Programming

This tutorial will explain how to load files from the res/raw and the Assets folder using a String to specify the file name. Yes, there are a lot of tutorials on this subject, but they all use the automatically generated integer IDs from the R class as inputs and not many of them even mention the possibility of loading files from the Assets folder. As a result of depending on the ID, the file reference must be known beforehand.

Instead, the code featured in this post will explain how to find the reference to the file and then load it at runtime based solely on its name. This means that the reference ID and the file don’t even have to exist in the first place, and can be acquired at run time.

Before looking into the code, it’s important to highlight the main differences between the raw folder and the Assets folder. Since raw is a subfolder of Resources (res), Android will automatically generate an ID for any file located inside it. This ID is then stored an the R class that will act as a reference to a file, meaning it can be easily accessed from other Android classes and methods and even in Android XML files. Using the automatically generated ID is the fastest way to have access to a file in Android.

The Assets folder is an “appendix” directory. The R class does not generate IDs for the files placed there, so its less compatible with some Android classes and methods. Also, it’s much slower to access a file inside it, since you will need to get a handle to it based on a String. There is also a 1MB size limit for files placed inside the Assets folder, however some operations are more easily done by placing files in this folder, like copying a database file to the system’s memory. There’s no (easy) way to create an Android XML reference to files inside the Assets folder.

The first thing the code does is to create two private variables. A Resources object that will act as a handle to the app’s resources and a String, that will be used to output the content of the files to LogCat (lines 16 and 18).

Now let’s jump straight to line 60 where the LoadFile() method is defined. It returns a String and takes two parameters: the first one is the file name and the second is a boolean, that signals the method whether it should load from the res/raw or the Assets folder.

After that, the method creates a InputStream object (line 63). Streams are like handles to read files into buffers (Input Stream) and to write files from these buffers (Output stream).

Line 65 checks if the loadFromRawFolder parameter is true. Case it is, the code at lines 68 and 70 is executed. The former dynamically loads resources from the raw folder. The getIdentifier() method from the resources object returns a resource ID based on the path. This parameter is composed by:

Читайте также:  Как поставить заводские настройки андроида

package name:type of resource/file name

Package name is self explanatory; type of resource can be one of the following: raw, drawable, string. File name, in this example, is the fileName parameter, an it’s being concatenated to create one single String. Since all information that this method requires is being passed on the first parameter the other two can be null.

Finally, line 70 feeds this ID to the openRawResource() method, which will return a InputStream from a file located at the res/raw folder.

At the else block, the Assets folder is being opened, by first calling the getAssets() method to return a handle to the Assets folder and right after that, the open() method is called, passing the fileName as the parameter. All that is done to also return the InputStream, although this time, for a file at the Assets folder (line 75).

Now that we have the InputStream, we can create the buffer to read the file into. That’s accomplished by line 79, that creates a byte array that has exactly the same length as iS (the InputStream object). The file is read into the buffer at the next line (line 81).

With the file loaded into the buffer, it’s time to prepare a OutputStream to write the buffer contents into. First, a object of this type is created at line 83. Then, the write() method is called passing the buffer as the parameter (line 85). With that, a handle to the file’s content was created.

There’s nothing left to do with these two streams, so they are closed at lines 87 and 88. Finally, the return for this method is set, by converting the oS object to a String (line 91).

At last, the LoadFile() method is called at line 33 (don’t forget to omit the file extension) and at line 47 (don’t forget to include the file extension). Both method calls are surrounded by a try/catch block since they can throw an exception.

The returned String from the method calls are stored at the output variable. It’s then later used to print the contents of the loaded files into LogCat’s console (lines 35 and 49).

And that’s about it! The method that was declared in the Activity can be easily adapted to load and return anything from these folders, not just a String. Additionally, it can be used to dynamically to acquire a reference, and load files at run time.

Downloads

Want to see more posts like this more often? Please donate by clicking at the button below:

Don’t feel like donating? Alternatively, you can support the site by purchasing the FULL version of the Arrows Live Wallpaper app at the Google Play Store™:

Источник

Loading images from assets #155

Comments

vpratfr commented Sep 24, 2014

I have switched my current project from Picasso to Glide. However, I cannot manage to load an image from the application assets. Is that even possible?

I have also tried the load(String) method without success and I don’t see a load method taking for instance an InputStream or a FileDescriptor I could have obtained with the AssetManager class.

That exact same code was working in Picasso.

Glide is working properly in the rest of the app and loading nicely remote images.

The text was updated successfully, but these errors were encountered:

sjudd commented Sep 24, 2014

Thanks for reporting this, in theory the Uri is sufficient. What failure do you see (if you don’t see any exception you can turn on logging using adb shell setprop log.tag.GenericRequest, see https://github.com/bumptech/glide/wiki/Debugging-and-Error-Handling)?

If using the AssetManager is a requirement you can always define a custom ModelLoader that uses the AssetManager to return an InputStream and pass it in to your request with the using() syntax:

Читайте также:  Удаляются иконки с рабочего стола андроид

If we need to use the AssetManager to parse these urls rather than just relying on a ContentResolver, the best fix for the library would be to modify the existing UriLoader and adding another case if we’re given an asset Uri.

vpratfr commented Sep 24, 2014

I get this exception thrown:

However the file does exist in the asset folder. By using a custom loader/fetcher, I manage to load those files properly. Here are the corresponding classes: https://gist.github.com/vpratfr/5e61fd452a27e964ea63

Then I am loading the assets with:

So something is wrong with the default loading, I don’t know if that’s a bug on your side or on mine.

One problem remains : inside a GridView (probably in a ListView too), I cannot see the image without scrolling the view out of sight and getting it back on screen. Does it have something to do with caching? Or with my custom loader/fetcher?

sjudd commented Sep 25, 2014

Thanks for the code and report I’m looking into asset manager uris.

I’m not sure where to point you to with regards to the GridView issue. Glide’s Flickr sample app uses grid views without a problem. The only problem I see in your code is that you don’t need to use a weak reference for a Context in your DataFetcher. Instead just pass in the application Context (context.getApplicationContext()). That said, I don’t think using a weak reference would cause the image to fail to load.

Do you see a debug log when the image fails to load the first time? What width and height do your views have?

TWiStErRob commented Sep 25, 2014

As it should be loaded

As @vpratfr wants to load it

it gives the following messages:

java.io.FileNotFoundException: /android_asset/test.jpg: open failed: ENOENT (No such file or directory)
resolveUri failed on bad bitmap uri: file:///android_asset/test.jpg

Why Picasso works

we want the following to work (which is roughly equivalent to the above setImageURI version):

java.io.FileNotFoundException: No such file or directory

Solution

Add if to UriLoader for checking «root folder» android_asset and implement an AssetFetcher like @vpratfr did; however I think relativePath.replace(«file:///android_asset/», «»); should be outside of the class in UriLoader , because there is no such thing as an «asset Uri» in the Android framework, assets are referenced by relative path to the assets folder.

Other comments

As for the GridView , it really depends on how you declared the ImageView in XML and what methods you call on them. Aside @sjudd’s suggestions you can try adding a .listener() and log/debug whether the Bitmap arrives at the target when it’s not shown.

I agree that these kind of Uris should be supported for ease of use, but why don’t you put your images into raw or drawable-nodpi , or even simpler: use drawables?

vpratfr commented Sep 25, 2014

Currently doing some other tasks, will try to debug the GridView in the afternoon. Thanks for having had a look at it.

I think too the right thing to do would be to add an additional if statement in the UriLoader class to detect assets and use an appropriate Fetcher.

I have not directly implemented it in the library and submitted a PR because I still have not taken the time to understand the Model things and would not want to break something (and honestly don’t have much because my client is pushing). From the code I sent I think that should be an easy fix though.

I agree that these kind of Uris should be supported for ease of use, but why don’t you put your images into raw or drawable-nodpi, or even simpler: use drawables?

Because the file name is dynamically determined. Those are flags and I get the country list from a webservice. The flag name is taken from the ISO code (for instance FR.png).

Читайте также:  Android java widget example

If I was using a resource, that name would get compiled to an int (R.raw.FR) and I would need some reflection magic (or an ugly switch statement) to link the ISO code to the flag resource.

Hence the asset is required in my case.

TWiStErRob commented Sep 25, 2014

If I was using a resource, that name would get compiled to an int (R.raw.FR) and I would need some reflection magic (or an ugly switch statement) to link the ISO code to the flag resource.

. or you could use the method provided by the framework:

I’m doing the same, I have a DB table for categories and the icon name is stored there (I’m loading SVG files from raw this way via Glide). Also «ugly switch» usually converts nicely to lookup Map .

Or if you really want to pass Uri around:

This even works if you put it in XML ( android:src ), because ContentResolver /Android framework supports this. I think the above uses the same method I laid out above, but also involves parsing the Uri first.

vpratfr commented Sep 25, 2014

Ok. Back with some tests. I have implemented SVG support (using your sample as a base) and I am now using the method you describe (SVGs in the raw resource folder and some low-res versions in the drawable resource folder).

More info about the GridView bug:
1.
I am using com.makeramen.RoundedImageView to host the image. That is a subclasse of ImageView.

SVGs get properly loaded in that view. Drawables however are only loaded when the GridView row is out of sight and back (using an adapter and following the ViewHolder pattern).
1.
If I switch to Picasso, drawables are properly loaded in that RoundedImageView. Apart from the Glide.with. code changed to the similar Picasso.with. code, all the rest is the same.

So my conclusion : Picasso is doing something different than Glide when it comes to setting a drawable.

My first guess : it may be related to an issue logged in the RoundedImageView repo if you are using the setImageURI method and Picasso is using another method (like directly calling setImageResource). This would explain too why SVGs get shown properly as they are not using the setImageURI method but probably setImageDrawable instead.

sjudd commented Sep 25, 2014

What api level are you using? Not 4.0.4 by any chance?

If you use Glide.with(xxx).load(xxx).asBitmap() does it work as you expect?

Glide uses setImageDrawable, not setImageUri. Using asBitmap() will cause it to use setImageBitmap instead.

Thanks for looking more into this!

vpratfr commented Sep 25, 2014

Testing on Android 4.4.4 (Galaxy Note 2 with Slimkat ROM)

vpratfr commented Sep 25, 2014

It works when using asBistmap

sjudd commented Sep 26, 2014

Any chance you could add a bit of code or a sample project demonstrating the issue? Even just the xml you’re using to specify your grid and the items in it would be helpful.

As I mentioned earlier I know the drawables can work in a GridView because the Flickr sample works fine, but clearly something is a bit different here.

Thanks for the follow up.

sjudd commented Sep 28, 2014

I spent a little while looking in to this, it seems like RoundedImageView doesn’t handle TransitionDrawables quite right. Removing the crossfade using dontAnimate() or using a normal fade in solved the problem. Using asBitmap() will also work because Glide doesn’t allow you to cross fade bitmaps.

I think the ideal solution is to follow the pattern the author setup for Picasso and create your own custom Glide transformation: https://github.com/vinc3m1/RoundedImageView/blob/master/roundedimageview/src/main/java/com/makeramen/RoundedTransformationBuilder.java.

I’ll leave this open to track the asset manager uris, but otherwise everything seems WAI from Glide’s point of view.

Источник

Оцените статью