Android設定裡面預設儲存器選項(default write disk)的實現
原生的Android設定裡面沒有預設儲存器的選項,但是MTK偏偏加上了這個功能,可能MTK覺得這個比較有用吧,所以,他們在原生的基礎上面做了修改,加上了這個功能。但是高通平臺沒有這個功能,相對MTK來說,高通比較嚴謹一點,不會隨隨便便加上一些功能,但是MTK平臺優化了很多東西(有有點也有缺點),開發者這可能是很多山寨手機選擇MTK平臺的原因吧。
我給“預設儲存器”做了一個簡單的定義:一些內建應用的資料存放位置。當然,如果其他應用知道這個介面(不是標準介面,所以只有開發者本人知道,或者MTK的人),也可以使用該功能。
之前接到一個在高通平臺上的手機裡面加上預設儲存器的選項,即如果選擇本地記憶體,一些內建應用的資料將儲存到本地記憶體裡面,如果選擇SD卡儲存,則這些應用的資料將會儲存到SD卡里面。借鑑MTK平臺的實現方法,整理其思路是:通過SystemProperties介面往系統記憶體裡面寫入儲存路徑(set),然後在一些指定應用裡面儲存資料的時候判斷當前預設儲存器,使用get方法讀取這個屬性。為什麼使用SystemProperties,因為其設定的值全域性都可以使用,即不同應用都可以通過共享記憶體取出其值,解決了程序間通訊的問題。
1:我們先在framework層加上一些自定義的介面檔案。我加的位置是 ....../frameworks/base/core/java/android/os/storage/,新增的檔名StorageManagerEx.java,內容如下:
package android.os.storage; import android.util.Log; import com.android.internal.R; import android.os.SystemProperties; import java.io.File; import android.os.Environment; public class StorageManagerEx { » private static final String TAG ="StorageManagerEx"; » private static final String PROP_SD_DEFAULT_PATH = "persist.sys.sd.defaultpath"; » » private static final String STORAGE_PATH_SD1 ="/storage/sdcard0"; » private static final String STORAGE_PATH_SD2 ="/storage/sdcard1"; » private static final String STORAGE_PATH_SD1_ICS = "/mnt/sdcard"; » private static final String STORAGE_PATH_SD2_ICS = "/mnt/sdcard2"; » » private static final String STORAGE_PATH_SHARE_SD = "/storage/emulated/0"; » » /** » * Return default path for writing. » * @return default path. » * @hide » * @internal » */ public static String getDefaultPath(){ » String path = STORAGE_PATH_SD1; » try { » » path = SystemProperties.get(PROP_SD_DEFAULT_PATH); » » Log.i(TAG,"default path = "+path); » } catch (IllegalArgumentException e) { » » Log.e(TAG,"IllegalArgumentException when get default path:"+e); » } » //Property will be empty when first boot should set to default » if(path.equals("")){ » » Log.i(TAG,"getDefaultPath empty! set to default"); » » //Here may be some problems.refer to MTK:only share sd and no swap,internal path is » » //"/storage/emulated/0",i don't know "share" and "no swap",so i skip. » » // if (FeatureOption.MTK_SHARED_SDCARD && !FeatureOption.MTK_2SDCARD_SW AP) {» » » » // setDefaultPath(STORAGE_PATH_SHARE_SD); » » // path = STORAGE_PATH_SHARE_SD; » » // } else { » » // setDefaultPath(STORAGE_PATH_SD1); » » // path = STORAGE_PATH_SD1; » » // } » » setDefaultPath(STORAGE_PATH_SD1); » » path = STORAGE_PATH_SD1; » } » » //Here we don't care multi user which will be used by google,ignore and go on. » » //MOTA upgrade from ICS to JB,update defaultPath to JB. » if(path.equals(STORAGE_PATH_SD1_ICS)){ » » path = STORAGE_PATH_SD1; » » setDefaultPath(path); » }else if(path.equals(STORAGE_PATH_SD2_ICS)){ » » path = STORAGE_PATH_SD2; » » setDefaultPath(path); » } » » Log.i(TAG,"getDefaultPath path="+path); » return path; } /** * set default path for Appto storage data * this only can used by settings. * * @param path * @hide * @internal */ public static void setDefaultPath(String path){ » Log.i(TAG,"setDefaultPath path = " + path); » if (path == null) { » » Log.e(TAG,"setDefaultPath error! path = null"); » » return; » } » » try { » » SystemProperties.set(PROP_SD_DEFAULT_PATH,path); » } catch (IllegalArgumentException e) { » » Log.e(TAG,"IllegalArgumentException when setDefaultPath:" + e); » } } /** * Returns external SD card path.» /sdcard2 * @hide * @internal */ // public static String getExternalStoragePath(){» //method 1: //» String sdCardDir = null; // android.os.storage.StorageManager storageManager = // (android.os.storage.StorageManager) context // .getSystemService(Context.STORAGE_SERVICE); // StorageVolume[] volumes = storageManager.getVolumeList(); // for (int i = 0; i < volumes.length; i++) { // if (volumes[i].isRemovable() && volumes[i].allowMassStorage()) { // sdCardDir = volumes[i].getPath(); // break; // } // } // return sdCardDir; // } public static String getExternalStoragePath(){» //method 2: String sdCardDir = null; String state = Environment.getStorageState(new File(STORAGE_PATH_SD2)); if(state.equals(Environment.MEDIA_MOUNTED)){ » sdCardDir = STORAGE_PATH_SD2; }else if(state.equals(Environment.MEDIA_UNMOUNTED)){ » sdCardDir = Environment.MEDIA_UNMOUNTED; }else{ » sdCardDir = STORAGE_PATH_SD2; } return sdCardDir; } /** * Returns internal storage path. * @return * @hide * @internal */ public static String getInternalStoragePath(){ » String sdCardDir = null; » String state = Environment.getStorageState(new File(STORAGE_PATH_SHARE_SD)); » » if(state.equals(Environment.MEDIA_MOUNTED)){ » » sdCardDir = STORAGE_PATH_SD1; » }else if(state.equals(Environment.MEDIA_UNMOUNTED)){ » » sdCardDir = Environment.MEDIA_UNMOUNTED; » }else{ » » sdCardDir = STORAGE_PATH_SD1; » } » return sdCardDir; } 1 }
定義的主要作用是往系統記憶體的屬性裡面新增屬性presist.sys.sd.default.我們可以進入adb裡面檢視和修改一些屬性1:adb shell 2:getprop + 鍵(或不加) tip:我們也可以setprop + 鍵 +值
2:在設定裡面新增預設儲存器的選項:
在Settings的工程裡面的.....res/xml/device_info_memory.xml的檔案裡面新增
然後在PreferenceCategory裡面新增RadioButtonPreference (此處省略)......<span style="white-space:pre"> </span><PreferenceCategory android:key="memory_select" android:title="@string/select_memory" /> ......
在Memroy.java檔案裡面新增
private void initDefaultWriteDisk(){
» mDefaultWriteCategory = (PreferenceCategory)findPreference(DEFAULT_WRITE_CATEGORY_KEY);
List<RadioButtonPreference> defaultWritePreference = new ArrayList<RadioButtonPreference>();
StorageVolume[] volumes = mStorageManager.getVolumeList();
List<StorageVolume> storageVolumesEx = initVolumeList(volumes);
for (int i = 0; i < storageVolumesEx.size(); i++) {
» StorageVolume volumn = storageVolumesEx.get(i);
» RadioButtonPreference avalibileDisk = new RadioButtonPreference(getActivity());
» String path = volumn.getPath();» //volumn path
» android.util.Log.i("haiyong.liu","volumn.path="+path);
» String state = Environment.getStorageState(new File(path));
»
» avalibileDisk.setKey(path);
» avalibileDisk.setTitle(volumn.getDescription(getActivity()));
» avalibileDisk.setPath(path);
» avalibileDisk.setOnPreferenceChangeListener(this);
» defaultWritePreference.add(avalibileDisk);
» android.util.Log.e("haiyong.liu","Environment="+state);
» if(state.equals(Environment.MEDIA_UNMOUNTED)||state.equals(Environment.MEDIA_REMOVED
)){
» »
» }else {
» » mDefaultWriteCategory.addPreference(avalibileDisk);
» » if(path.equals(StorageManagerEx.getDefaultPath())){
» » » avalibileDisk.setChecked(true);
» » » mDeafultWritePathPref = avalibileDisk;
» » }else{
» » » avalibileDisk.setChecked(false);
» » }
» }
» » }
}
private List<StorageVolume> initVolumeList(StorageVolume[] volumes){
» List<StorageVolume> storageVolumes = new ArrayList<StorageVolume>();
» /*if(UserManager.supportsMultipleUsers()){» //return the current user's volumn lis
t if supported,otherwise ignore.
» » StorageVolume[] sv = mStorageManager.getVolumeListAsUser();
» » for (int i = 0; i < sv.length; i++) {
if (!"not_present".equals(mStorageManager.getVolumeState(volumes[i].getPath()))) {
storageVolumes.add(volumes[i]);
}
}
» » return storageVolumes;
» }*/
»
for (int i = 0; i < volumes.length; i++) {
if (!"not_present".equals(mStorageManager.getVolumeState(volumes[i].getPath()))) {
storageVolumes.add(volumes[i]);
}
}
return storageVolumes;
}
@Override
public boolean onPreferenceChange(Preference preference, Object newValue) {
if (preference != null && preference instanceof RadioButtonPreference) {
if (mDeafultWritePathPref != null) {
mDeafultWritePathPref.setChecked(false);
}
StorageManagerEx.setDefaultPath(preference.getKey());
mDeafultWritePathPref = (RadioButtonPreference) preference;
return true;
}
return false;
}
然後在oncreate方法裡面呼叫
initDefaultWriteDisk()
這個方法,這個方法放在Memory.java的onCreate(...)方法裡面,另外,StorageManagerEx.setDefaultPath(),這個方法只可以在settings這個apk裡面使用,即只有settings這個apk可以寫入。
3:找到對應需要新增該功能的工程,我們需要新增的是Camrea2,藍芽,下載器,錄音這幾個Apk,找到這幾個原始碼儲存資料的地方,將儲存位置改為StorageManagerEx.getDefaultPath()
tip:SystemProperties寫入資料時候需要許可權。在Manifests.xml檔案加上android:sharedUserId="android.uid.system",在Android.mk中,將LOCAL_CERTIFICATE := XXX修改成LOCAL_CERTIFICATE :=platform。