背景
由于项目需要在产品设置中增加storage功能,故对系统原生实现进行学习
settings_header
packages/apps/Settings/src/com/android/settings/Settings.java
loadHeadersFromResource(R.xml.settings_headers, headers);
通过查看settings_headers.xml,可以知道选项对应的Fragment。
此处以查看Storage为例,可以看到对应Memory.java
<header android:id="@+id/storage_settings" android:fragment="com.android.settings.deviceinfo.Memory" android:icon="@drawable/ic_settings_storage" android:title="@string/storage_settings" />
原生log
Memory.TAG = "MemorySettings"
打印该Log的手机没有插入外置SD Card.
Log提供了Volume, path等信息
D/MemorySettings( 5148): Volume: Phone storage ,state is: mounted ,emulated is: false, path is: /storage/sdcard0 D/MemorySettings( 5148): Volume: SD card ,state is: removed ,emulated is: false, path is: /storage/sdcard1 D/MemorySettings( 5148): default path group length = 2 D/MemorySettings( 5148): Not [sd + swap] support, add internal storage D/MemorySettings( 5148): Not [sd + swap] support, add non-emulated category, volume : /storage/sdcard0 D/MemorySettings( 5148): Not [sd + swap] support, add non-emulated category, volume : /storage/sdcard1 D/MemorySettings( 5148): onResume D/MemorySettings( 5148): dynamicUpdateUnmountDlg() D/MemorySettings( 5148): Path /storage/sdcard0 volume state is mounted D/MemorySettings( 5148): Path /storage/sdcard1 volume state is removed D/MemorySettings( 5148): current status is MTP D/MemorySettings( 5148): Dynamic Update Install Location in OnResume() D/MemorySettings( 5148): dynamicShowDefaultWriteCategory() D/MemorySettings( 5148): external storage path= /storage/sdcard1 D/MemorySettings( 5148): Path /storage/sdcard0 volume state is mounted D/MemorySettings( 5148): set the pref phone storage order D/MemorySettings( 5148): Path /storage/sdcard1 volume state is removed D/MemorySettings( 5148): set the pref sd card order D/MemorySettings( 5148): numberOfWriteDisk : 1 D/MemorySettings( 5148): get default path/storage/sdcard0 D/MemorySettings( 5148): isHwUsbDisconnected :false ,set mIsUsbCableInserted true D/MemorySettings( 5148): pluginType :2 , set mIsUsbCableInserted true D/MemorySettings( 5148): onPrepareOptionsMenu, mIsUsbCableInserted: true D/MemorySettings( 5148): onPrepareOptionsMenu, mIsUsbCableInserted: true D/MemorySettings( 5148): pluginType :2 , set mIsUsbCableInserted true D/MemorySettings( 5148): onPrepareOptionsMenu, mIsUsbCableInserted: true D/MemorySettings( 5148): pluginType :2 , set mIsUsbCableInserted true D/MemorySettings( 5148): onPrepareOptionsMenu, mIsUsbCableInserted: true D/MemorySettings( 5148): pluginType :2 , set mIsUsbCableInserted true D/MemorySettings( 5148): onPrepareOptionsMenu, mIsUsbCableInserted: true
device_info_memory.xml
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android" android:title="@string/storage_settings_title"> <PreferenceScreen android:key="apk_installer" android:title="@string/akp_installer_settings_title" android:summary="@string/akp_installer_settings_summary"> </PreferenceScreen> <ListPreference android:key="app_install_location" android:title="@string/app_install_location_title" android:summary="@string/app_install_location_summary" android:persistent="false" android:entries="@array/app_install_location_entries" android:entryValues="@array/app_install_location_values"/> <PreferenceCategory android:key="memory_select" android:title="@string/select_memory" /> </PreferenceScreen>
preference的组织方式有PreferenceScreen和PreferenceCategory,PreferenceCategory是带层次组织关系而PreferenceScreen就是最平白和基础的方式。
PreferenceCategory可以将几个组建组合在一起,并加上标题。
本人理解PreferenceScreen,PreferenceCategory就是Preference的ViewGroup,类似LinearLayout和RelativeLayout的关系。参考http://blog.csdn.net/flowingflying/article/details/6671548
ListPreference在原生Camera中使用过。算有了解。
private void addCategory(StorageVolumePreferenceCategory category)
device_info_memory.xml本身并没有太多的选项,可以断定是由代码动态加入的view(preference),由此在代码里搜索包含add的方法。
将addCategory()中的代码注释,得到如下效果,可见判断正确。
该方法是private,在代码中查找使用位置,只在onCreate()中有。
if (!(FeatureOption.MTK_SHARED_SDCARD && FeatureOption.MTK_2SDCARD_SWAP)) {
Xlog.d(TAG, "Not [sd + swap] support, add internal storage");
addCategory(StorageVolumePreferenceCategory.buildForInternal(context));
}
for (StorageVolume volume : storageVolumes) { if (FeatureOption.MTK_SHARED_SDCARD && FeatureOption.MTK_2SDCARD_SWAP) { Xlog.d(TAG, "[sd + swap] support, add emulated category"); addCategory(StorageVolumePreferenceCategory.buildForPhysical(context, volume)); } else if (!volume.isEmulated()) { Xlog.d(TAG, "Not [sd + swap] support, add non-emulated category, volume : " + volume.getPath()); addCategory(StorageVolumePreferenceCategory.buildForPhysical(context, volume)); } }
与Log吻合。
D/MemorySettings( 5148): Not [sd + swap] support, add internal storage D/MemorySettings( 5148): Not [sd + swap] support, add non-emulated category, volume : /storage/sdcard0 D/MemorySettings( 5148): Not [sd + swap] support, add non-emulated category, volume : /storage/sdcard1
而调用的函数buildForInternal,buildForPhysical只是创建StorageVolumePreferenceCategory对象时向构造函数传递了不同的volume参数。
/** * Build category to summarize internal storage, including any emulated * {@link StorageVolume}. */ public static StorageVolumePreferenceCategory buildForInternal(Context context) { return new StorageVolumePreferenceCategory(context, null); }
/** * Build category to summarize specific physical {@link StorageVolume}. */ public static StorageVolumePreferenceCategory buildForPhysical( Context context, StorageVolume volume) { return new StorageVolumePreferenceCategory(context, volume); }
StorageVolumePreferenceCategory
addCategory的参数是StorageVolumePreferenceCategory,而函数内部也是add和init方法,猜测每个选项的内容应该是在StorageVolumePreferenceCategory。
private void addCategory(StorageVolumePreferenceCategory category) { mCategories.add(category); getPreferenceScreen().addPreference(category); category.init(); }
查看Log,能够看到已经打出了total size信息
D/StorageVolumePreferenceCategory( 9124): onResume D/StorageVolumePreferenceCategory( 9124): updatePreferencesFromState, state is mounted D/StorageVolumePreferenceCategory( 9124): onResume D/StorageVolumePreferenceCategory( 9124): updatePreferencesFromState, state is removed D/StorageVolumePreferenceCategory( 9124): onUsbStateChanged D/StorageVolumePreferenceCategory( 9124): onUsbStateChanged D/StorageVolumePreferenceCategory( 9124): updatePreferencesFromState, state is mounted D/StorageVolumePreferenceCategory( 9124): onUsbStateChanged D/StorageVolumePreferenceCategory( 9124): updatePreferencesFromState, state is removed D/StorageVolumePreferenceCategory( 9124): null : total size is 5279711232, avail size is 4743716864 D/StorageVolumePreferenceCategory( 9124): Phone storage : total size is 9325838336, avail size is 8014692352 D/StorageVolumePreferenceCategory( 9124): updatePreferencesFromState, state is mounted D/StorageVolumePreferenceCategory( 9124): SD card : total size is 0, avail size is 0 D/StorageVolumePreferenceCategory( 9124): updatePreferencesFromState, state is removed D/StorageVolumePreferenceCategory( 9124): updateDetails, SD card, isPrimary is false D/StorageVolumePreferenceCategory( 9124): updateDetails, mVolume is null D/StorageVolumePreferenceCategory( 9124): measureMedia is false D/StorageVolumePreferenceCategory( 9124): updateDetails, Phone storage, isPrimary is true D/StorageVolumePreferenceCategory( 9124): measureMedia is true D/StorageVolumePreferenceCategory( 9124): mDcim size is Pictures, videos 33.38MB D/StorageVolumePreferenceCategory( 9124): music size is 458752 D/StorageVolumePreferenceCategory( 9124): downloads size is 65536
public void updateApproximate(long totalSize, long availSize)
根据Log找到函数updateApproximate
approximate
英 [ə'prɒksɪmət] 美 [əˈprɑksəmɪt]
:大约; 近似的; 约计; 使近似
添加打印formatSize,与界面数值一致
D/StorageVolumePreferenceCategory(10244): Phone storage : total size is 9325838336, avail size is 8028520448 V/StorageVolumePreferenceCategory(10244): formatSize(totalSize):8.69GB V/StorageVolumePreferenceCategory(10244): formatSize(availSize):7.48GB D/StorageVolumePreferenceCategory(10244): updatePreferencesFromState, state is mounted D/StorageVolumePreferenceCategory(10244): SD card : total size is 0, avail size is 0 V/StorageVolumePreferenceCategory(10244): formatSize(totalSize):0.00B V/StorageVolumePreferenceCategory(10244): formatSize(availSize):0.00B D/StorageVolumePreferenceCategory(10244): updatePreferencesFromState, state is removed D/StorageVolumePreferenceCategory(10244): null : total size is 5279711232, avail size is 4743725056 V/StorageVolumePreferenceCategory(10244): formatSize(totalSize):4.92GB V/StorageVolumePreferenceCategory(10244): formatSize(availSize):4.42GB
数据源
updateApproximate已经解决了界面的问题,调用updateApproximate则是数据源从何而来?
消息机制表明数据源通知界面更新是通过进程间通信。
private Handler mUpdateHandler = new Handler() { @Override public void handleMessage(Message msg) { switch (msg.what) { case MSG_UI_UPDATE_APPROXIMATE: final long[] size = (long[]) msg.obj; updateApproximate(size[0], size[1]); break; case MSG_UI_UPDATE_DETAILS: final MeasurementDetails details = (MeasurementDetails) msg.obj; updateDetails(details); break; } } };
而代码在中有MeasurementReceiver mReceiver,可以理解整个流程是观察者模式。
private MeasurementReceiver mReceiver = new MeasurementReceiver() { @Override public void updateApproximate(StorageMeasurement meas, long totalSize, long availSize) { mUpdateHandler.obtainMessage(MSG_UI_UPDATE_APPROXIMATE, new long[] { totalSize, availSize }).sendToTarget(); }
@Override public void updateDetails(StorageMeasurement meas, MeasurementDetails details) { mUpdateHandler.obtainMessage(MSG_UI_UPDATE_DETAILS, details).sendToTarget(); } };
StorageMeasurement.java
MeasurementReceiver的定义在StorageMeasurement.java
StorageMeasurement没有继承其他类,也没有实现接口,提供的public member只有如下几个。此类高内聚低耦合,感觉可以直接使用该类,移植方便。
public static StorageMeasurement getInstance(Context context, StorageVolume volume) public void cleanUp() public void invalidate() public void measure() public void setReceiver(MeasurementReceiver receiver)
从设计模式来看,采用单例的一种情况是该类向其他类提供服务,而搜索getInstance()引用的位置,查看对public方法的调用,可以了解到如何使用该类。
Erase
Erase选项和点击进入的界面
通过前面的内容可以知道,磁盘选项是通过addCategory添加,但是addCategory的log,调用位置和参数,代码中搜索erase都不能找到erase相关信息。
可能是erase依赖addCategory的结果,但是在addCategory()之后的代码也看不到任何erase相关信息。
不能理清逻辑,则通过搜索的办法找线索
grep -rHni erase packages/apps/Settings/src/com/android/settings/deviceinfo/ 找不到包含erase的行
grep -rHni erase packages/apps/Settings/src/com/android/settings/ 能找到包含erase的行
将包含erase的行的输出保存到临时文件1,用vim处理只包含类名。然后查找deviceinfo目录下使用了这些类名的文件(之前搜索erase的类都在deviceInfo目录)。
结果是只有StorageVolumePreferenceCategory用到了MediaFormat
$ cat 1 | uniq > 2 $ a=`cat 2` $ for i in $a; do echo "grep $i"; grep -rHni $i packages/apps/Settings/src/com/android/settings/deviceinfo/; done grep PrivacySettings grep ApnEditor grep MediaFormat packages/apps/Settings/src/com/android/settings/deviceinfo/StorageVolumePreferenceCategory.java:728: com.android.settings.MediaFormat.class); grep MasterClearConfirm grep AdvancedMasterClearConfirm grep applications/ManageApplications grep MasterClear grep CredentialStorage grep ChartNetworkSeriesView
在MediaFormat中查找onClick(),有两处,打log验证,OK!
小结
至此,对HiCam需要的Storage了解完成了第一阶段调研。
从UI入手,但是不纠结于UI的实现,用UI基本的设计思想来理清思路。
从代码中不能直接找到Erase的逻辑时,能够合理而不盲目的使用搜索的办法。
磁盘的知识
volume
http://en.wikipedia.org/wiki/Volume_(computing)
http://vbird.dic.ksu.edu.tw/linux_basic/0420quota_3.php