Where to save pictures on Android? - java

My application uses quite a lot of pictures that are downloaded from the internet and cached locally on the Android phone. I am wondering, what is the correct way to save those pictures. There are several ways I see, that are not fully satisfying.
Save them on SD Card in a public folder
Uses up space that wont be freed on uninstall
User can see pics in Gallery
Needs a folder on the sdcard root (you can actually see it while browsing your phone)
Save them on SD Card in a non-public folder
Uses up space that wont be freed on uninstall
Secretly uses space on the SD Card
Save them inside the application
Blows up application size far too much
What is the correct way of locally saving the images of my application to not distract the user and leave no garbage anywhere?

Your best solution is to use:
context.getCacheDir()
This directory is private to the application and will be deleted on uninstall, furthermore the system can delete from this directory for you if the device is running short of space.
Note though that the docs say:
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
If you need a lot of space and would rather use the SD card you can call
getExternalCacheDir()
instead. These will also get removed on uninstall, but the system does not monitor the space available in external storage, so won't automatically delete these files if low on space. If using this option you should also check that external storage is available with
Environment.getExternalStorageState()
before attempting to write to it.

You can hide images from the MediaScanner if you put it in a hidden dir (i.e., with a dot prefixed) such as /sdcard/.donotscan/.
Update: As romainguy mentions on twitter this also works if you put a file named .nomedia into the dir.

I think the best way is to use the database.
It does not blow up the application and memory.
The related database is deleted once the application is uninstalled.
Nobody can reach to this files besides your application.
Update: But; If you want to cache only the data, there is a cache manager defined in webkit. CacheManager
I didn't use the package before but the methods seem straight forward to use:
static boolean cacheDisabled()
static boolean endCacheTransaction()
static CacheManager.CacheResult getCacheFile(String url, Map<String, String> headers)
static File getCacheFileBaseDir()
static void saveCacheFile(String url, CacheManager.CacheResult cacheRet)
static boolean startCacheTransaction()
and you can find the usage at Google Gears code
I hope this helps.

If you you don't want to use the CacheManager then use a database or a local (non-SD) file (local files get deleted on a complete uninstall) and register to receive the 'ACTION_DEVICE_STORAGE_LOW' and 'ACTION_DEVICE_STORAGE_OK' broadcast actions. Then you'll know when your application is taking up too much space according to the device and when you need to start deleting pictures. Your application size will still grow, but you will be able to manage the growth and shrinkage.

Just a tip - if you save them on the SD Card, they will be scanned by the MediaScanner and will appear in the users's Gallery (Photos), which you probably don't want. So you probably want to store them on the phone, or somehow tell MediaScanner not to scan them (apparently impossible after a quick Google search.)
Probably best to store a select few in your application's private directory, which will be removed when your application is uninstalled.

Related

Is getExternalFilesDir() private?

I'm trying to store music files users can download with my application, but I don't want them to be publicly visible to 99% of users (I'm excluding rooted devices or special cases)
I'm not worried about the people that go above and beyond to get the audio out by using special tools or a rooted phone.
I am trying to understand the docs but it's not 100% clear to me that getExternalFilesDir() is good to use for that purpose.
Also, the library I'm currently using uses the following line:
Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES).getAbsolutePath()
Is this 100% interchangeable if I just replace it with...
Environment.getExternalFilesDir(Environment.DIRECTORY_MUSIC).getAbsolutePath()
(notice I changed both the directory function, as well as the DIRECTORY type (from PICTURES to MUSIC)
Thanks
Is getExternalFilesDir() private?
No. Any user and any app (with READ_EXTERNAL_STORAGE or WRITE_EXTERNAL_STORAGE) can access external storage.
Is this 100% interchangeable if I just replace it with..
That won't compile due to imbalanced parentheses. Also, the values are different (PICTURES versus MUSIC). Ignoring that, it is "interchangeable" insofar as they will both work and will both be on external storage. However, they are not the same thing. The first one is the user's standard PICTURES directory; the second one is a directory dedicated for pictures that is unique to your app. Also, the former requires WRITE_EXTERNAL_STORAGE on all devices; the latter requires it only for Android 4.3 and below.
I don't want them to be publicly visible to 99% of users (I'm excluding rooted devices or special cases)
Use internal storage (e.g., getFilesDir()).
Use getFilesDir() instead. Thats private app memory.
if you put "." before folder name this folder be hidden
e.g:
String root = Environment.getExternalStorageDirectory().toString();
File myDir = new File(root + "/.myFolderName");

Android clears my application's folder files

I'm working on an application that downloads many files to the SD card.For a device that doesn't have SD Card. It uses the internal storage.
Whenever the storage goes lower than 500Mb, the operating system clears the folder i'm using to store files these files. Did anybody have a problem like this?.
I think that this problem is related to memory swap for the internal storage(When it gets lower than 500MB, it clears the application folder).
Your help is appreciated.
The documentation can help you out here
If you'd like to cache some data, rather than store it persistently,
you should use getCacheDir() to open a File that represents the
internal directory where your application should save temporary cache
files.
When the device is low on internal storage space, Android may delete
these cache files to recover space. However, you should not rely on
the system to clean up these files for you. 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.
The documentation does not say what hard limit is considered while trying to recover space. Whether there is enough space on the internal storage is also not under your control. Repopulate the cache if you find that it is missing and try to maintain a low overhead on the cached files.
As an example I wrote an app a while back that downloads image and sound snippets and caches them. If they are available in the cache I return them. If not I attempt to download them again and put a placeholder image where the actual data is supposed to reside.
Another solution is to store your data in a directory that is not considered a cache by the android OS. However you run the risk of overpopulating the user's disk with data and your data will not be removed when the application has been uninstalled.
In which directory you are saving your downloaded files?
Please take a look at:
http://developer.android.com/reference/android/os/Environment.html
and
http://developer.android.com/training/basics/data-storage/files.html
Also keep in mind the definition of "ExternalStorage":
"Note: don't be confused by the word "external" here. This directory can better be thought as media/shared storage. It is a filesystem that can hold a relatively large amount of data and that is shared across all applications (does not enforce permissions). Traditionally this is an SD card, but it may also be implemented as built-in storage in a device that is distinct from the protected internal storage and can be mounted as a filesystem on a computer."

Android, Not sure if I should store to the internal storage or external storage

I have an app out there on the market already The app is not a game just an informative app with lots of pictures and stats and I received a comment from someone stating that the size of the app being 20Mb is being stored on the "internal storage which isn't fair and that some of it should be stored on the SD card."
Now I am still trying to learn everything about android coding but is this a big deal so that I should move all those images to the external storage like the SD card? Is 20Mb to the internal storage too much or what number in Mb should I look out for to know when to store to an external? I looked at the storage documentation here and it states that the user can modify when they enable USB mass storage to transfer files on a computer, so does this pose a risk to my app breaking if the user modifies/removes/changes anything?
You are looking for installLocation. Just change one thing in the manifest and let users choose where the APK is installed.
Note: By default, your application will be installed on the internal storage and cannot be installed on the external storage unless you define this attribute to be either "auto" or "preferExternal".
This is no different to a computer having two hard drives, a boot disk and a second disk.
Obvious if you can store using the second disk/external storage, you are generally better off as the internal storage has many uses and usually fills up first.
Generally speaking it is best to leave it to the user to decide but I would store on the external storage by default.
For an older phones it is a big deal, believe me. The best option would be to let user choose, which storage he wants to use, in some sort of configuration dialog.
If the app requires those files then do not put them in external storage as that will allow the user to remove them.
If they can be removed/altered then you should put them in external storage.
You could also do a mix of the two so that the size could be reduced without sacrificing the necessary components.

application size issue

I'm building a dictionary application and I have a problem right now. My application's is 16MB and when I install it on a phone, Database files copies to the data folder and in the manage apps section I see that my application size is 32MB (my app+data folder).
I don't cheat user, I want to say, my app is 16MB, but when user install it , it become 32MB. Why? this is a negative point and I want to solve it. I want my app uses only 16MB in users phone. just that
How I can fix this? I have to read and write in assets folder directly or there is other solution? this is a problem in low storage size phones. how I can fix this?
I am not sure how your database is structured in terms of whether it is a pre-loaded database wherein you just include you .db file with all the data OR is it something where in you push all your DB content with the app and then at the time of app installation you actually install all you data in the DB.
In case of the latter situation you double the size of your app because you already have data content (in files) which you want to use to populate your database (say 16 mb in this case). Then you use these files to actually create your DB file (which is 16mb again) and this doubles the size of the app.
So what you could do is pre-populate your DB content in a .db file and then just use this file directly as the Db file in your app (this will keep it to 16mb). Follow this tutorial :
http://www.reigndesign.com/blog/using-your-own-sqlite-database-in-android-applications/
Hope this helps.
Not sure I fully understand your situation.
Do you have a roughly 16MB dictionary, that is packaged inside your app as string constants in your code or some resource file or something (to make it 16MB) and then, when your app installs or first launches, you also write this dictionary into your app's database?
If so, then now you have 2 copies of your dictionary around to make it 32MB.
To solve this, either keep only one copy in your app, or download the dictionary from somewhere to get it into your database rather than storing it as a constant in your app.

When caching images and data should you use Internal or External Storage?

I've read this Android save app settings/data in Internal/External Storage but I don't think i've really got an answer.
I'm developing an app where I should store some "cache" data, like a dozen of images and some strings (json).
Initially I was storing all that on the sdcard, (external storage) but later i thought that this could be deprecated by the SDCard deprecation in most recent devices.
After reading a bit, I understood that external storage is not only sdcard, but "a removable storage media (such as an SD card) or an internal (non-removable) storage" so it should not be deprecated but...it's shared space, and there is not ownership over the files stored there, so the first problem was that I was unable to delete them when the app was deleted.
So I changed to the Internal Storage, to avoid having the files/images "public" and also having them removed after app deletion.
Is this the recommended approach?
On devices older with low internal storage but with a lot of space in the SDcard is this a good approach?
My application supports from 1.6 to 4.0 (so far) so I have a lot of legacy devices... and I must have the app working (well) on all.
Looking forward for some interesting answers!
It depends on the type of data you are wanting to store.
You mention it's cached data so myassumption is that it should not matter if for some reason it all disappears. This leads me to believe that you should be using the getCacheDir(). In this case the system will remove files if the cache becomes too big so devices with low internal storage shouldn't present a problem (although it is recommended to manage this your self anyway), it's relatively secure and it will be managed by the app so if there is an uninstall it will be removed.
getExternalCacheDir() was introduced in 2.2 so isn't any use to you unless you would like to detect the version and switch between the 2 caching directories getExternalCacheDir() doesn't provide security so data could be accessed by anyway with access to the SD card. The only reason I could think you might want to do this is because of size of cache you desire but from your description the data doesn't seem excessive.
UPDATED from comment:
although this is a specific case where it's cache...but I don't want
it to be deleted whenever the system wants. It's the kind of cache
that I need the to app decide when to purge. What is the main concern
of storing in on "normal" internal storage without being on the cache
dir?
If you get to the stage where the system is cleaning up internal cached data because storage is so low then you should probably leave it to clean up this sort of app data. By using standard internal data storage you are bypassing this safe guard which would probably create more unpleasant problems than having app data deleted.
If your data is of high importance then I would suggest trying to identify specific data that is more important and managing that separately. If this data you identify needs to be secure then internal storage using files or a db (depending on the data type) seems like your only real option but you would have to be wary of this data building up.
UPDATED from comment
What do you think about using SharedPreferences to save string data?
Is there a limit on SharedPreference string saved size? Is it a (good)
possibility?
I have used shared preferences to store relatively big json strings in the past with no problem, I find it's simpler than using the databases for primitive data types (and strings) where there are limited values to save. However when you have images or lot's of values, management becomes more complex. Also you will have the same problem as you would with standard internal storage in terms of storage space.
I would keep images on the external storage, probably in a "hidden" folder (adding a dot at the beggining of the folder's name: .folder) and also a the Media Scanner "avoider" (.nomedia), because, as you pointed, you want the application to work in old devices, and precisely those devices don't have to much internal memory.
You can also add an option in your application to remove that folder, so in case the user wants to uninstall, he can do that before.
I would use internal storage for cache. This will take away the chance of a user being able to access the files used in your app. I would go for internal

Categories