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;
}