Many image files in res/drawable, How to access in Android? - android

I have to make a dedicated image viewer app for Android 2.x.
There are too many jpeg image files: about 2000~ jpegs, over 100MB.
I want access the image files with their file names,
but I couldn't find such an example.
By the way, is it okay to put many image files in /res/drawable folder?
I heard that the android application cannot be installed on sdcard and
the program repository is very small so 100MB app cannot be installed generally.
I found some examples which download the large data files on sdcard online,
but I cannot run a web server to host the data files,
and I must upload the fully packaged program on Android Market. (Should I build one apk file?)
What are the best practices for managing too many resource images (or something) in Android?

I think you are going to have a hard time convincing users to install a program that is 100 MB into the internal memory of their phones. It would be much better to sideload the images onto the SD card. There are a number of fairly cheap file hosting services available such as Amazon S3.
Also, you should consider allowing the users to download the images in small groups instead of in one large chunk.

The G1 has 256MB of internal storage for applications. Even on the Nexus One there's only 512MB so I think it's unlikely that anyone would want a single application taking up such a high proportion of this storage, so creating a 100MB+ .apk file isn't going to be practical.
You are right that stock android phones cannot run applications from the SD Card. (There are custom firmwares that allow this, but this isn't going to help you as only a small minority of users run these.)
You say that you cannot run a webserver, but unfortunately, I think that's your only real option here. You could dowload the images as needed and cache them on the SD Card if you had them on a webserver somewhere. Configuring a webserver to serve a whole of images is pretty straightforward, although you may need to do some work to stop people looking at the images using a web browser rather than your app if you're charging for it.

ImageView iv = new ImageView(context);
iv.setImageResource(R.drawable.icon);

To access a large array of images sitting in a directory on the SD card:
ImageView iv= ...
int imageIndex=0; //can access all image files by imageIndex, 0=first entry
List<String> ImageList=FindImages();
if(ImageList!=null && ImageList.size()>=currentIndex){
iv.setImageDrawable(Drawable.createFromPath(ImageList.get(imageIndex)));
}
//put your image files on SD in DIRECTORY
private List<String> FindImages() {
final List<String> tFileList = new ArrayList<String>();
Resources resources = getResources();
// may use array of valid image file extensions
//String[] imageTypes = ...
FilenameFilter[] filter = new FilenameFilter[imageTypes.length];
int i = 0;
/* can use string array of image types:
for (final String type : imageTypes) {
filter[i] = new FilenameFilter() {
public boolean accept(File dir, String name) {
return name.endsWith("." + type);
}
};
i++;
}*/
filter[i] = new FilenameFilter() {
public boolean accept(File dir, String name) {
return name.endsWith(".png");
}
};
FileUtils fileUtils = new FileUtils();
File[] allMatchingFiles = fileUtils.listFilesAsArray(
new File(DIRECTORY), filter, -1);
for (File f : allMatchingFiles) {
tFileList.add(f.getAbsolutePath());
}
return tFileList;
}

Haven't tried it myself, but looks like this could work:
public BitmapDrawable(Resources res, String filepath);
https://developer.android.com/reference/android/graphics/drawable/BitmapDrawable.html#BitmapDrawable%28android.content.res.Resources,%20java.lang.String%29

Related

Upcoming changes to Android External Storage Permissions

Using Visual Studio 2019
Future changes to Android OS appear to place restrictions on using external storage. My App currently stores a database and 300+ images on the SD card. I have successfully moved the sqlite database to internal storage and am now wondering where I can place my image library. Can the user install the images in a public Pictures folder? I currently use the following to manage my images:
public static class ImageAssetManager
{
public static Dictionary<string, Drawable> cache = new Dictionary<string, Drawable>();
public static Drawable Get(string url)
{
string imageurl;
imageurl = Vars.DataLoc + "Images/" + url;
if (!cache.ContainsKey(imageurl))
{
Drawable drawable;
drawable = Drawable.CreateFromPath(imageurl);
cache.Add(imageurl, drawable);
}
return cache[imageurl];
}
}
Vars.DataLoc is the root path to the external storage, and url is the name of the requested image.
Where can I have the user store the images and how do I modify the above routine to work with the new storage location? I could theoretically store them in the Assets folder (like I do with the database) and have the the system write them out to LocalApplicationData, but the number and names of the files are not static.
I have to support devices as old as KitKat, if possible. Since this App is usually used where there is no internet connection, all files must be local.
Thanks,
Dan

How to manage external storage independently of the phone

I have phone (B15 CAT) with a sd card slot. When i insert a sdcard in this phone and asking for the external storage directory with :
Environment.getExternalStorageDirectory()
it always return an space on sdcard0 which is the internal memory. This memory is too small for my need.
By listing /mnt i found a mount point named /sdcard2 which is the "real" scard.
Unfortunately sdcard2 doesn't seems to be a standard and some other brand will use some other name...
Knowing that getExternalStorageDirectory() seems working as expected on phone with no sdcard slot , like nexus 4, how should i handle external storage to be sure to write on the sdcard (big space available) and not on internal memory ?
I have tried something like this :
File mnt = new File("/mnt");
File[] liste = mnt.listFiles();
boolean hassd2 = false;
for(File mount : liste) {
if(folder.getName().equals("sdcard2") {
hassd2 = true;
break;
}
}
String path = "";
if(hassd2) {
path = "/sdcard2/my/folder/"
} else {
File p = new File(Environment.getExternalStorageDirectory().getAbsolutePath()+"/my/folder/");
path = p.toString();
}
It's working but only with this specific phone and others one with no sdcard slot ...
I also had the problem with the build in functions of Android in case of multiple 'external' storages mounted. I parsed the mounted directories directly from the f_stab file.
This link should give you the code you needed.
After having the mount points you could try to calculate the available space in order to decide if it is enough for your operation.

Why is AssetManger.list() so slow?

I'm trying to populate a ListView with a mixture of files stored on the SDcard AND stored as assets in the APK. Using TraceView, I can see that the performance of AssetManager.list() is poor in comparison to File.listFiles(), even though I'm using a filename filter for the SDcard.
Here is a simple method that returns all the png files from a folder on the SDcard:
// The folder on SDcard may contain files other than png, so filter them out
private File[] getMatchingFiles(File path) {
File[] flFiles = path.listFiles(new FilenameFilter() {
public boolean accept(File dir, String name) {
name = name.toLowerCase();
return name.endsWith(".png");
}
});
return flFiles;
}
I invoke that method here and it takes about 12ms to retrieve 16 files:
final String state = Environment.getExternalStorageState();
if (Environment.MEDIA_MOUNTED.equals(state)||Environment.MEDIA_SHARED.equals(state)) {
File path = Environment.getExternalStoragePublicDirectory(getResources().getString(R.string.path_dir));
if (path.exists()){
File[] files = getMatchingFiles(path);
...
Whereas the am.list method takes 49ms to retrieve just the names of about 6 files!
// Get all filenames from specific Asset Folder and store them in String array
AssetManager am = getAssets();
String path = getResources().getString(R.string.path_dir);
String[] fileNames = am.list(path);
...
Can anyone explain why the performance would be so poor? Is the performance proportional to the number of assets stored in the APK? I'm aware that assets are compressed, but I'm only fetching the names of the assets, which I thought would be stored in a table somewhere.
Coverdriven's comment "stored in a table somewhere" inspired me to solve my own problem which I've been putting off for a while.
This doesn't answer the OP but does offer a different approach and it handles subfolders which CommonsWare's solution doesn't unless you go recursive (which of course is another possible solution). It's specifically aimed at apps which have a large number of assets in subfolders.
I added an ANT pre-build target to run this command (I'm on Windows)
dir assets /b /s /A-d > res\raw\assetfiles
This creates a recursive (/s), barebones (/b) listing of all files, excluding directory entries (/A-d) in my assets folder.
I then created this class to statically load the contents of assetfiles into a hashmap, the key of which is the filename and the value the full path
public class AssetFiles {
// create a hashmap of all files referenced in res/raw/assetfiles
/*map of all the contents of assets located in the subfolder with the name specified in FILES_ROOT
the key is the filename without path, the value is the full path relative to FILES_ROOT
includes the root, e.g. harmonics_data/subfolder/file.extension - this can be passed
directly to AssetManager.open()*/
public static HashMap<String, String> assetFiles = new HashMap<String, String>();
public static final String FILES_ROOT = "harmonics_data";
static {
String line;
String filename;
String path;
try {
BufferedReader reader = new BufferedReader(new InputStreamReader(TidesPlannerApplication.getContext().getResources().openRawResource(R.raw.assetfiles)));
while ((line = reader.readLine()) != null) {
// NB backlash (note the escape) is specific to Windows
filename = line.substring(line.lastIndexOf("\\")+1);
path = line.substring(line.lastIndexOf(FILES_ROOT)).replaceAll("\\\\","/");;
assetFiles.put(filename, path);
}
} catch (IOException e) {
e.printStackTrace();
}
}
public static boolean exists(String filename){
return assetFiles.containsKey(filename);
}
public static String getFilename(String filename){
if (exists(filename)){
return assetFiles.get(filename);
} else {
return "";
}
}
}
To use it, I simply call AssetFiles.getFilename(filename) which returns the full path which I can pass to AssetManager.open(). Much much faster!
NB. I haven't finished this class and it's not hardened yet so you'll need to add appropriate exception catches and actions. It's also quite specific to my app in that all of my assets are in subfolders which are in turn located in a subfolder of the assets folder (see FILES_ROOT) but easy to adapt to your situation.
Note also the need to replace backslashes, since Windows generates the assetfiles listing, with forward slashes. You could eliminate this on OSX and *nix platforms.
Can anyone explain why the performance would be so poor?
Reading the contents of a ZIP archive (the APK where the assets are located) is slower than reading the contents of a directory on the filesystem, apparently. In the abstract, this is not especially surprising, as I suspect that this would be true for all major operating systems.
Read in that list() data once, then save it somewhere else for faster access (e.g., database), particularly in a form that is optimized for future lookups (e.g., where a simple database query could give you what you want, vs. having to load and "recursively search it" again).
If you have a deep tree of directories in the assets you can detect firstly if an item is file or directory and then call .list() on it (really accelerates the walking through the tree). This is my solution I've discovered for this:
try {
AssetFileDescriptor desc = getAssets().openFd(path); // Always throws exception: for directories and for files
desc.close(); // Never executes
} catch (Exception e) {
exception_message = e.toString();
}
if (exception_message.endsWith(path)) { // Exception for directory and for file has different message
// Directory
} else {
// File
}
You can approach APK package as it's a ZIP file and read all the entries using Java's builtin ZipFile. It will give you all the file names with their full paths. Perhaps it shouldn't be hard to find which directories you have.
So far this is the fastest approach I've tested.
credit goes to #obastemur's commit on jxcore-android-basics sample project

When to clear the cache dir in Android?

I have an application that displays pictures from the internet (showcase for designer work). I start caching my content in the internal cache directory, but the app content could take about 150 MB in cache size. And what android docs says :
You should always maintain the cache files yourself and stay within a
reasonable limit of space consumed, such as 1MB. When the user
uninstalls your application, these files are removed.
So I took a look at the Currents app (Galaxy Nexus) and the cache size for the application is 110 MB. But what's weird is that applications like Google Currents & Google Maps cache the content in something called (USB Storage Data) :
So what is this 'USB Storage Data' that the previous application uses. And if you implement caching in your application, Do you loop over all your application files in cache to get the size every time you need to insert something and then compare and clear it? Or do you keep caching the content until Android decides its time to clean some application cache directory ?
I'm really interested to know what is the flow of managing cache in Android, or at least what other applications do with large content to cache.
Before I get to your question, here's a brief explanation of the two storage types:
Cache
This is an app-specific directory on the filesystem. The intent for this directory is store temporary data your application may need to keep around between sessions, but may not be vital to keep them forever. You typically access this directory with Context.getCacheDir(). This will show up as "Cache" on your app settings.
Files
Like the cache directory, your app also has an app-specific directory for holding files. Files in this directory will exist until the app explicitly deletes them or the app is uninstalled. You typically access this directory with Context.getFilesDir(). This can show up as various things on the app info screen, but in your screenshot this is "USB Storage Data".
NOTE: If you want to explicitly place on external media (typically SD card), you can use Context.getExternalFilesDir(String type).
The Difference
Both directories are specific only to your application (other apps do not have access). One of the differences between the cache and files directory is that if the system gets low on storage, the first place it is going to free resources is from your cache directory. The system will not clear any data from the files directory. Another difference is that the cache directory can typically be cleared manually from the app info screen. The files directory typically can as well, but clearing the files directory will also clear the cache directory.
Which one do I use?
It depends on how vital that data is compared to the lifetime of your app. If you only need data for one session and you doubt you'll ever need to use that data again, then don't use either. Just keep it in memory until you don't need it. If you suspect you'll need to reuse the data between multiple sessions, but you don't have to keep a hard copy, use the cache directory. If you must have this data no matter what, or if it's rather large data that needs persistent storage, use the files directory. Here's some examples I can think of:
Cache - A recently opened email
Once opened, cache the data so when the user wants to read that email again, it loads instantly rather using the network again to retrieve the same data. I don't need to keep this forever, because eventually the user will be finished with the email.
Files - An attachment downloaded from an email
This is an action by the user who is saying "I want to keep this data so I can pull it back up whenever I need it." Therefore, put it in files directory as I don't ever want to delete this file until the user wants it deleted.
When should I clear the cache directory?
From the Context.getCacheDir() javadocs:
Note: you should not rely on the system deleting these files for you;
you should always have a reasonable maximum, such as 1 MB, for the
amount of space you consume with cache files, and prune those files
when exceeding that space.
It uses the example of 1 MB, but that may or may not be reasonable for your app. Regardless, you need to set a hard maximum. The reason for this simply comes down to designing a responsible app. So when should you check? I would recommend checking every time you want to put something in the cache directory. Here's a very simple cache manager:
public class CacheManager {
private static final long MAX_SIZE = 5242880L; // 5MB
private CacheManager() {
}
public static void cacheData(Context context, byte[] data, String name) throws IOException {
File cacheDir = context.getCacheDir();
long size = getDirSize(cacheDir);
long newSize = data.length + size;
if (newSize > MAX_SIZE) {
cleanDir(cacheDir, newSize - MAX_SIZE);
}
File file = new File(cacheDir, name);
FileOutputStream os = new FileOutputStream(file);
try {
os.write(data);
}
finally {
os.flush();
os.close();
}
}
public static byte[] retrieveData(Context context, String name) throws IOException {
File cacheDir = context.getCacheDir();
File file = new File(cacheDir, name);
if (!file.exists()) {
// Data doesn't exist
return null;
}
byte[] data = new byte[(int) file.length()];
FileInputStream is = new FileInputStream(file);
try {
is.read(data);
}
finally {
is.close();
}
return data;
}
private static void cleanDir(File dir, long bytes) {
long bytesDeleted = 0;
File[] files = dir.listFiles();
for (File file : files) {
bytesDeleted += file.length();
file.delete();
if (bytesDeleted >= bytes) {
break;
}
}
}
private static long getDirSize(File dir) {
long size = 0;
File[] files = dir.listFiles();
for (File file : files) {
if (file.isFile()) {
size += file.length();
}
}
return size;
}
}
Of course, this could be an expensive operation, so you should plan on caching on a background thread.
Also, this could be as complicated as you need it to be. In my example, I'm assuming all cached files are placed at the root of the cache directory, so I don't check for potential sub-directories. The routine for deleting files can also become more sophisticated, such as deleting files by oldest access date.
One thing to keep in mind when deciding to cache data is that you need to always plan for the case that your cached data no longer exists. Always have a routine in place to retrieve data by external means when your cache doesn't have it in storage. Likewise, always check your cache before retrieve data externally. The purpose of the cache is to cut down on network activity, long processes, and provide a responsive UI in your app. So use it responsibly :)
i thing best way to clearing app cache when activity finish so that every time cache clear when new activity call.
put this code in onDestroy() for clear app cache
#Override
protected void onDestroy() {
super.onDestroy();
try {
trimCache(this);
// Toast.makeText(this,"onDestroy " ,Toast.LENGTH_LONG).show();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public static void trimCache(Context context) {
try {
File dir = context.getCacheDir();
if (dir != null && dir.isDirectory()) {
deleteDir(dir);
}
} catch (Exception e) {
// TODO: handle exception
}
}
public static boolean deleteDir(File dir) {
if (dir != null && dir.isDirectory()) {
String[] children = dir.list();
for (int i = 0; i < children.length; i++) {
boolean success = deleteDir(new File(dir, children[i]));
if (!success) {
return false;
}
}
}
// The directory is now empty so delete it
return dir.delete();
}
I think the idea behind the cache is to write anything you want on it and Android will manage its size if it gets too high.
You should keep in mind that you can write files to the cache, but always checks if the file is still saved when trying to access it. And let android manage th cache.
Depends on the type of application:
Some applications only use single sessions and don't need to remember any data, so you can clear the cache when you want (some apps even do this automatically in their onStop activity)
Most application keep your data because they remember your settings, the account you have used to log in,... In this case, it's best to only clear the cache when you don't use the application a lot.
Also:
So i took a look at Chrome app (Galaxy Nexus) and the cache size for the application is 110 MB. But what wired is that applications like Google current & Google maps cache the content in something called (USB Storage Data) :
AFAIK, Usb storage data has a different use from cache: the storage is to store program specific information (like maps for a GPS app), the cache is used to store user specific information (like logins)
In case of google maps: I assume they store map data in the usb storage, and keep your settings and search history in the cache ==> map data is application specific, settings and search history are user specific
According to the documentation the system will clear the cache when the device is low on internal storage. Since API8 you have getExternalCacheDir() method that i think useful since i read you can have around 150MB of data but the drawback of the external cache it's that you will have to clean your cache directory yourself if it's get too big.

load Image from specific folder on the sdcard?

I'm attempting to create a gallery/gridview that is loaded with images from a specific folder that resides on an SDCard. The path to the folder is known, ("mnt/sdcard/iWallet/Images") , but in the examples I've seen online I am unsure how or where to specify the path to the pictures folder I want to load images from. I have read through dozens of tutorials, even the HelloGridView tutorial at developer.android.com but those tutorials do not teach me what i am seeking.
Every tutorial I have read so far has either:
A) called the images as a Drawable from the /res folder and put them into an array to be loaded, not using the SDCard at all.
B) Accessed all pictures on the SDCard using the MediaStore but not specifying how to set the path to the folder I want to display images form
or
C) Suggested using BitmapFactory, which I haven't the slightest clue how to use.
If I'm going about this in the wrong way, please let me know and direct me toward the proper method to do what I'm trying to do.
my target android sdk version 1.6...
thanks..
You can directly create Bitmaps from decodeFile (String pathName) that will give you Bitmap object that can be set on ImageView
Update: Below is sudo code with minor errors modify it to suit your needs
File path = new File(Environment.getExternalStorageDirectory(),"iWallet/Images");
if(path.exists())
{
String[] fileNames = path.list();
}
for(int i = 0; i < fileNames .length; i++)
{
Bitmap mBitmap = BitmapFactory.decodeFile(path.getPath()+"/"+ fileNames[i]);
///Now set this bitmap on imageview
}
Actually, you are wrong to mention fixed path to access SD-card directory, because in some device it is /mnt/sdcard and in other /sdcard.
so to access root directory of sd-card, use the getExternalStorageDirectory(), it gives you actual path of root directory.
This function will resturn all the files from specific folder you need to pass path till ur folder
public static List getFilesFromDir(File aStartingDir)
{
List result = new ArrayList();
File[] filesAndDirs = aStartingDir.listFiles();
List filesDirs = Arrays.asList(filesAndDirs);
Iterator filesIter = filesDirs.iterator();
File file = null;
while ( filesIter.hasNext() ) {
file = (File)filesIter.next();
result.add(file); //always add, even if directory
if (!file.isFile()) {
//must be a directory
//recursive call!
List deeperList = getFileListing(file);
result.addAll(deeperList);
}
}
Collections.sort(result);
return result;
}
BitmapDrawable d = new BitmapDrawable(getResources(), path+".jpg"); // path is ur resultant //image
img.setImageDrawable(d);
Hope it help u...
You can access your directory using File java class, then iterate through all the files in there, create a bitmap for each file using Bitmapfactory.decodeFile() then add the bitmaps to your gallery.

Categories

Resources