Android手机的多样性给了用户很多选择,但也造成了开发的难度。举例来说,连象Storage这样的最基本的功能也会有意想不到的复杂度。
每个Android手机都有一个Primary external storage, 用来存储程序数据,以及和其他程序共享文件。许多程序都需要这个空间来存储文件。比如Camera用它来存储照片,Gmail用它来存储下载的文件。
造成问题的是不同手机用不同的硬件来提供这个存储空间。有的用板上的Flash,有的用SdCard. 问题就出在SdCard上,SdCard是可以取掉的。有个时候是故意取掉的,有个时候是无意的。一次,我同事说我们程序出问题,我刚开始看,SdCard在的呀。最后把手机打开,竟然是因为卡没有完全插进去。从外面是看不出来的。
当然用户是永远没错的,Dev需要处理没有SdCard的情形。我们程序里面带有一个apk文件,运行的时候需要调用PackageInstallerActivity来安装这个apk文件。但因为这是两个不一样的进程,所以我们是把apk文件拷贝到外部存储空间上,这样PackageInstallerActivity才能访问到它。
当我的同事SdCard没有全部插进去的时候,这种方式就不能工作了。因为存储空间不存在了。
花了好几个小时研究怎样共享内部的文件,一直没找到什么好方法。Android的Sandbox方式还真是让共享造成很多麻烦。都开始想做一个ContentProvider了,当然是有点小题大做的味道,但也没有其他的好办法。
直到在Context中看到这个。
http://developer.android.com/reference/android/content/Context.html#openFileOutput(java.lang.String, int)
public static final int MODE_WORLD_READABLE
Since: API Level 1
File creation mode: allow all other applications to have read access to the created file.
基本上,这可以允许在程序的Sandbox里创建文件的时候,允许其他程序访问。
接下来就简单了。把需要共享的文件从Resource里拷贝到程序的私有存储空间,并且赋予足够的权限,然后就可以用文件路径来访问了。
主要代码如下
/**
* Get the shared file path. The shared file resides in app private storage but it's make world readable.
* In the first time, the shared file is copied from raw resource.
* @param ctx Context
* @return The shared file object.
*/
private File getSharedFilePath(Context ctx) {
File cacheFile = ctx.getFileStreamPath(CACHE_APK);
if (!cacheFile.exists()) {
Log.i(TAG, "Copy the file to internal storage for the first time.");
OutputStream os = null;
InputStream is = null;
try {
is = ctx.getResources().openRawResource(R.raw.nytimes);
os = ctx.openFileOutput(CACHE_APK, Context.MODE_WORLD_READABLE);
IOUtils.copy(is, os);
} catch (FileNotFoundException e) {
Log.i(TAG, "Shouldn't happen:" + e);
} catch (IOException e) {
Log.i(TAG, "Failed to copy:" + e);
} finally {
IOUtils.closeSilently(os);
IOUtils.closeSilently(is);
}
}
return cacheFile;
}