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
Related
In my application I want a image gallery that will show the images that were taken from the Android camera. So, I am trying to copy files from the Android storage to the assets folder to make a image gallery.
For that I have write the following code.
string sourceDriectory="";
string targetDriectory="";
void Start()
{
CopySomething( sourceDriectory, targetDriectory);
}
static void CopySomething(string sourceDriectory, string targetDriectory)
{
UnityEditor.FileUtil.CopyFileOrDirectory(sourceDriectory, targetDriectory);
}
In this code I am not getting how can I will set the sourceDriectory path and the targetDriectory path.
You don't need to copy images only to show them in your app. You can just directly access the image files and display in your app.
Take a look at here for how you can access images from gallery.
I'm using Android Studio. I have my project set to require API version 11. The Emulator is set for Nexus 5 API23 (standard default settings).
I want to have my application write a simple text file to a location where I can pull the text files created onto my computer by plugging in with a USB cable. So it needs to be in the public external storage.
For whatever reason I can't get the code to create a folder for my text files to go into. I have paired my code down to this little nugget in a "Utilities" class I have:
public static boolean createTheDangFolder(Context c, String fileName, String body) {
boolean saved = true;
//File dir = new File(c.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS), "MyCustomFolder");
File dir = new File(Environment.getExternalStorageDirectory(), "MyCustomFolder");
if (!dir.exists()) {
saved = dir.mkdirs();
}
return saved;
}
This function always returns false. If I trade comments on the "File" line it will return true but the commented out line is the app memory and I can't access the files via USB.
I have this line in my Manifest file:
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
I can't for the life of me figure out why it's not working and none of the other questions on the site have given me a solution that works.
They changed the permissions to external storage in KitKat. You can only write to public folders (like downloads) and to your own app's private directory on external storage.
I want to copy a remote image, for example "http://example.com/example.jpg" to the android user phone built gallery...How can I do it?
To that, you should download the image and save it in internal memory.
You can download the image by yourself:
public static Bitmap getBitmap(String url) {
try {
InputStream is = (InputStream) new URL(url).getContent();
Bitmap d = BitmapFactory.decodeStream(is);
is.close();
return d;
} catch (Exception e) {
return null;
}
}
Code from here But you will have memory problems with large images. I strongly recommended you to use a build library like Android Universal Image Loader or Picasso from square
Here you can find an example of how to use the Android DownloadManager to download your file.
The destination path can be determined using the contants defined in the Environment class. Constant DIRECTORY_DCIM points to the parent directory under which all Activities can create a custom folder where they store their images. You could make your own child folder as destination folder
When your image finishes downloading, you will notice that it will not be listed in the default gallery application, this is because Android builds an index with all the media files and is still unaware of your new downloaded image. This index is updated each time you boot your Android device, but since it's a bit unconvienient to reboot your device each time a file is added, you can also codewise inform the indexing service that a new file is created and needs indexing using this piece of code:
Intent intent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
intent.setData(Uri.fromFile(file));
sendBroadcast(intent);
This scanning should also occur after a file has been erased.
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.
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