Every time when my app tries to create a directory on SD card, it throws runtime exception.
The error is
java.lang.RuntimeException: Could not create library directory /storage/sdcard0/Seafile/logan676#163.com (cloud.seafile.com)/personal folder
So my question is if it is allowed to create directories which contains space.
And I implemented the code like below.
if (path != null) {
// Has record in database
repoDir = new File(path);
if (!repoDir.exists()) {
if (!repoDir.mkdirs()) {
throw new RuntimeException("Could not create library directory " + path);
}
}
return path;
}
I have to create such kind of directories because I deleted them when user clear caches.
/**
* Deletes cache directory under a specific account<br>
* remember to clear cache from database after called this method
*
* #param dirPath
* #throws IOException
*/
public static void clearCache(String dirPath) throws IOException {
// clear all cached files inside of the directory, the directory itself included
File cacheDir = new File(dirPath);
FileUtils.deleteDirectory(cacheDir);
}
This clearCache method will delete the root directory and its sub-directories.
So does it have anything to do with the runtime exceptions above.
Related
I know this question has been answered before, in so many ways. But I am still missing something.
From this site, I found (I think) the ideal way to copy entire Assets folder to external storage -
/**
* Copy the asset at the specified path to this app's data directory. If the
* asset is a directory, its contents are also copied.
*
* #param path
* Path to asset, relative to app's assets directory.
*/
private void copyAsset(String path) {
AssetManager manager = getAssets();
// If we have a directory, we make it and recurse. If a file, we copy its
// contents.
try {
String[] contents = manager.list(path);
// The documentation suggests that list throws an IOException, but doesn't
// say under what conditions. It'd be nice if it did so when the path was
// to a file. That doesn't appear to be the case. If the returned array is
// null or has 0 length, we assume the path is to a file. This means empty
// directories will get turned into files.
if (contents == null || contents.length == 0)
throw new IOException();
// Make the directory.
File dir = new File(getExternalFilesDir(null), path);
dir.mkdirs();
// Recurse on the contents.
for (String entry : contents) {
copyAsset(path + "/" + entry);
}
} catch (IOException e) {
copyFileAsset(path);
}
}
/**
* Copy the asset file specified by path to app's data directory. Assumes
* parent directories have already been created.
*
* #param path
* Path to asset, relative to app's assets directory.
*/
private void copyFileAsset(String path) {
File file = new File(getExternalFilesDir(null), path);
try {
InputStream in = getAssets().open(path);
OutputStream out = new FileOutputStream(file);
byte[] buffer = new byte[1024];
int read = in.read(buffer);
while (read != -1) {
out.write(buffer, 0, read);
read = in.read(buffer);
}
out.close();
in.close();
} catch (IOException e) {
Log.e(e);
}
}
It works fine (copy both the folder and its contents including subfolders), if I use it as following -
copyAsset("Folder_1");
But, if for e.g. my Assets Folder contains -
Assets
|- Folder_1
| |- Subfolder_1
| |- File_1
|- Folder_2
| |- Subfolder_2
| |- File_2
|- Folder_3
| |- Subfolder_3
| |- File_3
I can only pass one folder and its contents at a time in copyAsset(), e.g. copyAsset("Folder_1") or copyAsset("Folder_2") etc.
What path should I pass in copyAsset(), so that the entire Assets folder get copied by calling the function only once. Or, is there any better way to do this?
At present, I am forced to move all 3 Folders (Folder_1, Folder_2 & Folder_3) into one Parent Folder and then calling copyAsset("Parent_Folder").
I tried passing null (copyAsset()) and empty string (copyAsset("")), but both failed.
This code works perfectly until you clear cache from Device's Settings/App/your app menu.
/**
* #param context te get packageName & {#code cacheDir} from internal storage
* #return file for {#code cacheDir} either SD card or internal storage. Or {#code null} if cacheDir doesn't exist
*/
public static File getCacheDir(Context context) throws IOException {
File cacheDir;
if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
String cacheDirPath = getApplicationCacheDirPath(context.getPackageName());
cacheDir = new File(Environment.getExternalStorageDirectory() + File.separator + cacheDirPath);
} else {
cacheDir = context.getCacheDir();
}
if (cacheDir != null && !cacheDir.exists()) {
if (!cacheDir.mkdirs()) {
throw new IOException("Can't use cacheDir");
}
}
return cacheDir;
}
I've tried to delete this file and create it again, as a workaround, to release lock by some other(system) app, and that failed also. Only device's reboot do solve that problem. Any help would be appreciated. Thank you!
So I made the grave error of storing a bunch of .txt files in the Assets folder on Android only to discover that it is read-only and I need to be able to write to some of them, delete others and add new ones when needed.
I know I can store it internally or externally on the SD card.
If I store it internally, where do I place all of my files?
Is externally a better idea here?
Thanks
EDIT
It isnt a major problem if they are visible to the user
Here is a sample code i use for getting the storage directory:
/**
* Check if external storage is built-in or removable.
*
* #return True if external storage is removable (like an SD card), false
* otherwise.
*/
#TargetApi(9)
public static boolean isExternalStorageRemovable() {
if (hasGingerbread()) {
return Environment.isExternalStorageRemovable();
}
return true;
}
/**
* Get the external app cache directory.
*
* #param context The context to use
* #return The external cache dir
*/
#TargetApi(8)
public static File getExternalCacheDir(Context context) {
if (hasFroyo()) {
return context.getExternalCacheDir();
}
// Before Froyo we need to construct the external cache dir ourselves
final String cacheDir = "/Android/data/" + context.getPackageName() + "/cache/";
return new File(Environment.getExternalStorageDirectory().getPath() + cacheDir);
}
/**
* Get a usable cache directory (external if available, internal otherwise).
*
* #param context The context to use
* #param uniqueName A unique directory name to append to the cache dir
* #return The cache dir
*/
public static File getDiskCacheDir(Context context, String uniqueName) {
// Check if media is mounted or storage is built-in, if so, try and use external cache dir
// otherwise use internal cache dir
final String cachePath =
Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState()) ||
!isExternalStorageRemovable() ? getExternalCacheDir(context).getPath() :
context.getCacheDir().getPath();
return new File(cachePath + File.separator + uniqueName);
}
And you got yourself a path to place your files....
And now to place them upon install, you should copy them on first run from your assets to that folder, or create them and populate them depending on your needs....and no there is no other way to have files shipped with your app, they can come in assets, you can create them from your app, or download them from some server.
Edit1: the folder will be in Android/data/your_package_name/cache+anything you want...
and the two functions used for gingerbread and froyo:
public static boolean hasFroyo() {
// Can use static final constants like FROYO, declared in later versions
// of the OS since they are inlined at compile time. This is guaranteed behavior.
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.FROYO;
}
public static boolean hasGingerbread() {
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD;
}
I am trying to load a plugin implementation of an interface from a jar file which is in the /assets directory of my .apk file. The only way I've been able to get this to work is by extracting the jar file to private external storage and then passing that file to the DexClassLoader.
That works, but why should the jar have to exist in two places (the .apk and private external storage)? The DexClassLoader has to have a file path as its argument.
Is there a way to give it a direct path to the file that is in the /assets folder so that I don't have to use up external storage for an extra copy of what's already present?
Here are the relevant code snippets:
// somewhere in my main Activity ...
final File aExtractedDexFile = new File(getDir("dex", Context.MODE_PRIVATE),
LIBRARY_DEX_JAR);
extractDexTo(aExtractedDexFile);
loadLibraryProvider(aExtractedDexFile);
and
/** Extract the jar file that contains the implementation class.dex and place in private storage */
private void extractDexTo(File tJarInternalStoragePath) {
BufferedInputStream aJarInputStream = null;
OutputStream aDexOutputStream = null;
try {
aJarInputStream = new BufferedInputStream(getAssets().open(LIBRARY_DEX_JAR));
aJarOutputStream = new BufferedOutputStream(new FileOutputStream(tJarInternalStoragePath));
byte[] buf = new byte[BUF_SIZE];
int len;
while ((len = aJarInputStream.read(buf, 0, BUF_SIZE)) > 0)
{
aJarOutputStream.write(buf, 0, len);
}
aJarOutputStream.close();
aJarInputStream.close();
} catch (IOException e) {
if (aDexOutputStream != null) {
try {
aJarOutputStream.close();
} catch (IOException ioe) {
ioe.printStackTrace();
}
}
if (aJarInputStream != null) {
try {
aJarInputStream.close();
} catch (IOException ioe) {
ioe.printStackTrace();
}
}
}
}
and
/** Use DexClassLoader to load the classes from LibraryProvider */
private void loadLibraryProvider(File tFile) {
// Internal storage where the DexClassLoader writes the optimized dex file to.
final File aOptimizedDexOutputPath = getDir("outdex", Context.MODE_PRIVATE);
// Initialize the class loader with the secondary dex file.
DexClassLoader cl = new DexClassLoader(tFile.getAbsolutePath(),
aOptimizedDexOutputPath.getAbsolutePath(),
null,
getClassLoader());
Class<?> aLibProviderClazz = null;
try {
// Load the library class from the class loader.
aLibProviderClazz = cl.loadClass(LIBRARY_PROVIDER_CLASS);
sLibraryProvider = (LibraryInterface) aLibProviderClazz.newInstance();
} catch (Exception exception) {
// Handle exception gracefully here.
exception.printStackTrace();
}
}
Is there a way to give it a direct path to the file that is in the /assets folder so that I don't have to use up external storage for an extra copy of what's already present?
The answer is No. I suppose you follow this blog posted by official source implementing your code. if there is a better way of doing things, the bloger should recommend it in his blog.
Reason why you need optimizedDirectory is explained in the API:
This class loader requires an application-private, writable directory to cache optimized classes.
Also note that assets directory is not writable in apk, so it can't be done with purely assets directory.
Reason why you need copy jar file is a little bit subtle, mentioned in the blog:
First, it has to be copied to a storage location whose path can be supplied to the class loader.
Everything (folders/files) embedded within apk archive is not exposable (or interpretable) to the underlying file system at runtime. In another word, dexPath required in both DexClassLoader and PathClassLoader's constructor need a solid path string like /data/data/com.example/dex/common-lib.jar that represents the file in file system.
I have created two string one with the name SDCARD which means "sdcard" and RECYCLE_BIN_ROOT which stands for a folder with name of Recycle in Sdcard. But i am getting problem with this.
I have created code for copying files from sdcard ( for eg images in DICM ) to recycle bin, but result is zero.I have created recycle folder using file explorer option in eclipse also.
Please tell me some solution.
public class C
{
public static String SDCARD = "/sdcard";
public static String RECYCLE_BIN_ROOT = SDCARD+"/.Recycle";
}
java.io.File f = new java.io.File(RECYCLE_BIN_ROOT);
if (!f.exists()) {
if (f.mkDirs()) {
Log.v("", "File created successfully...");
} else {
Log.v("", "File cannot created successfully...");
// You can throw exception
}
}