了解Settings-Storage

背景

由于项目需要在产品设置中增加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吻合。

扫描二维码关注公众号,回复: 8503365 查看本文章
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


发布了27 篇原创文章 · 获赞 2 · 访问量 6万+

猜你喜欢

转载自blog.csdn.net/wlia/article/details/42234429