问题现象:
问题原因: sd卡格式化后,对应手机中的挂载位置发生了变更,而对应的显示数据未进行更新。所以再去原来的位置获取 容量数据时就会获取不到。
来看下代码吧。
此界面的xml文件为:packages/apps/Settings/res/xml/storage_dashboard_fragment.xml
管理类为:packages/apps/Settings/src/com/android/settings/deviceinfo/StorageDashboardFragment.java
显示 已使用容量 的控件管理类为:frameworks/base/packages/SettingsLib/UsageProgressBarPreference/src/com/android/settingslib/widget/UsageProgressBarPreference.java
public void setUsageSummary(CharSequence usageSummary) {
Log.e("xz", "setUsageSummary: usageSummary = " + usageSummary, new Throwable());
if (TextUtils.equals(mUsageSummary, usageSummary)) {
return;
}
mUsageSummary = usageSummary;
notifyChanged();
}
已使用容量取决于 mUsageSummary 的值。
为他赋值的地方在packages/apps/Settings/src/com/android/settings/deviceinfo/storage/StorageUsageProgressBarPreferenceController.java 中。
@Override
public void updateState(Preference preference) {
Log.e("xz", " updateState: ", new Throwable());
if (!mIsUpdateStateFromSelectedStorageEntry) {
// Returns here to avoid jank by unnecessary UI update.
return;
}
mIsUpdateStateFromSelectedStorageEntry = false;
mUsageProgressBarPreference.setUsageSummary(StorageUtils.getStorageSummary(
mContext, R.string.storage_usage_summary, mUsedBytes));
mUsageProgressBarPreference.setTotalSummary(StorageUtils.getStorageSummary(
mContext, R.string.storage_total_summary, mTotalBytes));
mUsageProgressBarPreference.setPercent(mUsedBytes, mTotalBytes);
}
mUsageProgressBarPreference.setUsageSummary(StorageUtils.getStorageSummary(
mContext, R.string.storage_usage_summary, mUsedBytes));
可以看到与mUsedBytes 有关。看看哪里为mUsedBytes赋值
StorageUsageProgressBarPreferenceController.java
private void getStorageStatsAndUpdateUi() {
Log.d(TAG, "xz getStorageStatsAndUpdateUi: ");
// Use cached data for both total size and used size.
if (mStorageEntry != null && mStorageEntry.isMounted() && mStorageEntry.isPrivate()) {
StorageCacheHelper.StorageCache cachedData = mStorageCacheHelper.retrieveCachedSize();
mTotalBytes = cachedData.totalSize;
mUsedBytes = cachedData.totalUsedSize;
mIsUpdateStateFromSelectedStorageEntry = true;
updateState(mUsageProgressBarPreference);
}
Log.d(TAG, "xz getStorageStatsAndUpdateUi 1 : mUsedBytes = " + mUsedBytes + " mTotalBytes = " + mTotalBytes);
// Get the latest data from StorageStatsManager.
ThreadUtils.postOnBackgroundThread(() -> {
try {
if (mStorageEntry == null || !mStorageEntry.isMounted()) {
throw new IOException();
}
Log.d(TAG, "xz getStorageStatsAndUpdateUi: isPrivate = " + mStorageEntry.isPrivate() + " isMounted = " + mStorageEntry.isMounted());
if (mStorageEntry.isPrivate()) {
// StorageStatsManager can only query private storages.
mTotalBytes = mStorageStatsManager.getTotalBytes(mStorageEntry.getFsUuid());
mUsedBytes = mTotalBytes
- mStorageStatsManager.getFreeBytes(mStorageEntry.getFsUuid());
} else {
Log.d(TAG, "xz getStorageStatsAndUpdateUi: mStorageEntry = " + mStorageEntry + " getPath = " + mStorageEntry.getPath());
final File rootFile = mStorageEntry.getPath();
if (rootFile == null) {
Log.d(TAG, "xz Mounted public storage has null root path: " + mStorageEntry);
throw new IOException();
}
mTotalBytes = rootFile.getTotalSpace();
mUsedBytes = mTotalBytes - rootFile.getFreeSpace();
}
Log.d(TAG, "xz getStorageStatsAndUpdateUi 2 : mUsedBytes = " + mUsedBytes + " mTotalBytes = " + mTotalBytes);
} catch (IOException e) {
Log.d(TAG, "xz getStorageStatsAndUpdateUi: e" + e);
// The storage device isn't present.
mTotalBytes = 0;
mUsedBytes = 0;
}
if (mUsageProgressBarPreference == null) {
return;
}
mIsUpdateStateFromSelectedStorageEntry = true;
ThreadUtils.postOnMainThread(() -> updateState(mUsageProgressBarPreference));
});
}
可以注意这句 :
mTotalBytes = rootFile.getTotalSpace();
mUsedBytes = mTotalBytes - rootFile.getFreeSpace();
mStorageEntry.isPrivate() 的判断 是为了区分手机内部存储 和 外部sd 卡
sd卡的 mUsedBytes 就是这里赋值的。
可以看到 数据取决于mStorageEntry.getPath()。
/** Set StorageEntry to display. */
public void setSelectedStorageEntry(StorageEntry storageEntry) {
Log.d(TAG, "xz setSelectedStorageEntry: ");
mStorageEntry = storageEntry;
getStorageStatsAndUpdateUi();
}
而mStorageEntry 是在这个方法中赋值的。再去看看哪里调用的这个方法。
StorageDashboardFragment.java
private void refreshUi() {
Log.d(TAG, "xz refreshUi: mStorageEntries = ");
mStorageSelectionController.setStorageEntries(mStorageEntries);
mStorageSelectionController.setSelectedStorageEntry(mSelectedStorageEntry);
mStorageUsageProgressBarController.setSelectedStorageEntry(mSelectedStorageEntry);
mOptionMenuController.setSelectedStorageEntry(mSelectedStorageEntry);
getActivity().invalidateOptionsMenu();
// To prevent flicker, hides non-current users preference.
// onReceivedSizes will set it visible for private storage.
setNonCurrentUsersVisible(false);
if (!mSelectedStorageEntry.isMounted()) {
// Set null volume to hide category stats.
mPreferenceController.setVolume(null);
return;
}
if (mStorageCacheHelper.hasCachedSizeInfo() && mSelectedStorageEntry.isPrivate()) {
StorageCacheHelper.StorageCache cachedData = mStorageCacheHelper.retrieveCachedSize();
mPreferenceController.setVolume(mSelectedStorageEntry.getVolumeInfo());
mPreferenceController.setUsedSize(cachedData.totalUsedSize);
mPreferenceController.setTotalSize(cachedData.totalSize);
}
if (mSelectedStorageEntry.isPrivate()) {
mStorageInfo = null;
mAppsResult = null;
// Hide the loading spinner if there is cached data.
if (mStorageCacheHelper.hasCachedSizeInfo()) {
//TODO(b/220259287): apply cache mechanism to non-current user
mPreferenceController.onLoadFinished(mAppsResult, mUserId);
} else {
maybeSetLoading(isQuotaSupported());
// To prevent flicker, sets null volume to hide category preferences.
// onReceivedSizes will setVolume with the volume of selected storage.
mPreferenceController.setVolume(null);
}
// Stats data is only available on private volumes.
getLoaderManager().restartLoader(STORAGE_JOB_ID, Bundle.EMPTY, this);
getLoaderManager()
.restartLoader(VOLUME_SIZE_JOB_ID, Bundle.EMPTY, new VolumeSizeCallbacks());
getLoaderManager().restartLoader(ICON_JOB_ID, Bundle.EMPTY, new IconLoaderCallbacks());
} else {
mPreferenceController.setVolume(mSelectedStorageEntry.getVolumeInfo());
}
}
mStorageSelectionController.setSelectedStorageEntry(mSelectedStorageEntry);
这个refresUi 就是界面创建 和 选择查看存储对象时会调用的刷新ui的方法。
其中就会往StorageUsageProgressBarPreferenceController 中赋值 mSelectedStorageEntry
每次在sd卡格式化时 主界面会 onPause 。 格式化完后会 onResume 。
@Override
public void onResume() {
super.onResume();
Log.d(TAG, "onResume: ");
if (mIsLoadedFromCache) {
mIsLoadedFromCache = false;
} else {
mStorageEntries.clear();
mStorageEntries.addAll(
StorageUtils.getAllStorageEntries(getContext(), mStorageManager));
refreshUi();
}
Log.d(TAG, "onResume: mStorageEntries = " + mStorageEntries.toString());
mStorageManager.registerListener(mStorageEventListener);
}
onResume 时会重新从StorageManager 系统服务中 获取 手机中挂载的存储空间。
mStorageEntries 就是 手机中存储的 集合 ,此数据是正确的。
这里会调用 refreshUi() 。 然后去set mSelectedStorageEntry。
mStorageSelectionController.setSelectedStorageEntry(mSelectedStorageEntry);
但是onResume 中并没有去更新mSelectedStorageEntry。所以set的mSelectedStorageEntry 的数据是错误的。还是原来那个旧的挂载位置。导致获取的容量数据也是错误的。
所以解决方案就是在onResume 中 去更新一下mSelectedStorageEntry 的值。
@Override
public void onResume() {
super.onResume();
Log.d(TAG, "onResume: ");
if (mIsLoadedFromCache) {
mIsLoadedFromCache = false;
} else {
mStorageEntries.clear();
mStorageEntries.addAll(
StorageUtils.getAllStorageEntries(getContext(), mStorageManager));
// add by xz start
if (mSelectedStorageEntry != null && mSelectedStorageEntry.isPublic()){
for (StorageEntry storageEntry : mStorageEntries) {
if (storageEntry.isPublic()){
mSelectedStorageEntry = storageEntry;
}
}
}
// add by xz end
refreshUi();
}
Log.d(TAG, "onResume: mStorageEntries = " + mStorageEntries.toString());
mStorageManager.registerListener(mStorageEventListener);
}
谢谢大家的浏览。有空的就点个赞吧。