1.简介
边学边记录,
2.StorageDashboardFragment.java
在第一篇里没写完,只简单学了 VolumeOptionMenuController和 StorageSelectionPreferenceController,这里继续研究页面里的其他数据
2.1.StorageUsageProgressBarPreferenceController
mStorageUsageProgressBarController.setSelectedStorageEntry(mSelectedStorageEntry);
>setSelectedStorageEntry
选择的存储卡发生变化以后,更新数据
public void setSelectedStorageEntry(StorageEntry storageEntry) {
mStorageEntry = storageEntry;
getStorageStatsAndUpdateUi();
}
>getStorageStatsAndUpdateUi
private void getStorageStatsAndUpdateUi() {
//挂载状态,并且private的(这个好像只有内置卡了?)
if (mStorageEntry != null && mStorageEntry.isMounted() && mStorageEntry.isPrivate()) {
//使用缓存数据刷新总大小以及使用大小
StorageCacheHelper.StorageCache cachedData = mStorageCacheHelper.retrieveCachedSize();
mTotalBytes = cachedData.totalSize;
mUsedBytes = cachedData.totalUsedSize;
mIsUpdateStateFromSelectedStorageEntry = true;
updateState(mUsageProgressBarPreference);
}
// Get the latest data from StorageStatsManager.
ThreadUtils.postOnBackgroundThread(() -> {
try {
if (mStorageEntry == null || !mStorageEntry.isMounted()) {
//非挂载状态抛出异常,catch里把大小弄为0
throw new IOException();
}
//内置卡和外置卡不同的获取大小的方法
if (mStorageEntry.isPrivate()) {
// StorageStatsManager can only query private storages.
mTotalBytes = mStorageStatsManager.getTotalBytes(mStorageEntry.getFsUuid());
mUsedBytes = mTotalBytes
- mStorageStatsManager.getFreeBytes(mStorageEntry.getFsUuid());
} else {
final File rootFile = mStorageEntry.getPath();
if (rootFile == null) {
throw new IOException();
}
mTotalBytes = rootFile.getTotalSpace();
mUsedBytes = mTotalBytes - rootFile.getFreeSpace();
}
} catch (IOException e) {
// The storage device isn't present.
mTotalBytes = 0;
mUsedBytes = 0;
}
if (mUsageProgressBarPreference == null) {
return;
}
mIsUpdateStateFromSelectedStorageEntry = true;
ThreadUtils.postOnMainThread(() -> updateState(mUsageProgressBarPreference));
});
}
>updateState
public void updateState(Preference preference) {
if (!mIsUpdateStateFromSelectedStorageEntry) {
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);
}
2.2.ManageStoragePreferenceController
ManageStoragePreferenceController manageStoragePreferenceController =
use(ManageStoragePreferenceController.class);
manageStoragePreferenceController.setUserId(mUserId);
>setUserId
设置userId,并查询下有没有可以对应action的存储管理器
public void setUserId(int userId) {
mUserId = userId;
mManageStorageDrawable = StorageUtils.getManageStorageIcon(mContext, userId);
}
- getManageStorageIcon 根据action查一下,有没有对应的activity,有的话返回对应的图标
public static Drawable getManageStorageIcon(Context context, int userId) {
ResolveInfo resolveInfo = context.getPackageManager().resolveActivityAsUser(
new Intent(StorageManager.ACTION_MANAGE_STORAGE), 0 /* flags */, userId);
if (resolveInfo == null || resolveInfo.activityInfo == null) {
return null;
}
return Utils.getBadgedIcon(context, resolveInfo.activityInfo.applicationInfo);
}
>getAvailabilityStatus
有图标也就是说有对应的activity处理这个,则可用,否则不可用
public int getAvailabilityStatus() {
return mManageStorageDrawable == null ? CONDITIONALLY_UNAVAILABLE : AVAILABLE;
}
>handlePreferenceTreeClick
public boolean handlePreferenceTreeClick(Preference preference) {
//..
//根据action跳转到对应的页面
final Intent intent = new Intent(StorageManager.ACTION_MANAGE_STORAGE);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
mContext.startActivityAsUser(intent, new UserHandle(mUserId));
return true;
}
2.3.StorageItemPreferenceController
如下图,这个是控制下面一堆选项的
protected List<AbstractPreferenceController> createPreferenceControllers(Context context) {
final List<AbstractPreferenceController> controllers = new ArrayList<>();
final StorageManager sm = context.getSystemService(StorageManager.class);
//那一堆存储列表就是这个控制器
mPreferenceController = new StorageItemPreferenceController(context, this,
null /* volume */, new StorageManagerVolumeProvider(sm), mIsWorkProfile);
controllers.add(mPreferenceController);
//这个是多用户的时候用的,到时候最底部还会多显示其他用户的空间使用情况
mSecondaryUsers = SecondaryUserController.getSecondaryUserControllers(context,
mUserManager, mIsWorkProfile /* isWorkProfileOnly */);
controllers.addAll(mSecondaryUsers);
return controllers;
}
下图就是mSecondaryUsers显示的效果,这个就不研究了
>displayPreference
首先获取我们要控制的所有preference
public void displayPreference(PreferenceScreen screen) {
mScreen = screen;
//这个是上图的控件,外置sdcard用这个
mPublicStoragePreference = screen.findPreference(PUBLIC_STORAGE_KEY);
//剩下的8个是内置卡用的
mImagesPreference = screen.findPreference(IMAGES_KEY);
mVideosPreference = screen.findPreference(VIDEOS_KEY);
mAudioPreference = screen.findPreference(AUDIO_KEY);
mAppsPreference = screen.findPreference(APPS_KEY);
mGamesPreference = screen.findPreference(GAMES_KEY);
mDocumentsAndOtherPreference = screen.findPreference(DOCUMENTS_AND_OTHER_KEY);
mSystemPreference = screen.findPreference(SYSTEM_KEY);
mTrashPreference = screen.findPreference(TRASH_KEY);
}
>handlePreferenceTreeClick
-点击事件,system是显示个对话框,其他的都是跳转到对应的intent
public boolean handlePreferenceTreeClick(Preference preference) {
if (preference.getKey() == null) {
return false;
}
switch (preference.getKey()) {
case PUBLIC_STORAGE_KEY:
launchPublicStorageIntent();
return true;
case IMAGES_KEY:
launchActivityWithUri(mImagesUri);
return true;
case VIDEOS_KEY:
launchActivityWithUri(mVideosUri);
return true;
case AUDIO_KEY:
launchActivityWithUri(mAudioUri);
return true;
case APPS_KEY:
launchAppsIntent();
return true;
case GAMES_KEY:
launchGamesIntent();
return true;
case DOCUMENTS_AND_OTHER_KEY:
launchActivityWithUri(mDocumentsAndOtherUri);
return true;
case SYSTEM_KEY:
final SystemInfoFragment dialog = new SystemInfoFragment();
dialog.setTargetFragment(mFragment, 0);
dialog.show(mFragment.getFragmentManager(), SYSTEM_FRAGMENT_TAG);
return true;
case TRASH_KEY:
launchTrashIntent();
return true;
default:
// Do nothing.
}
return super.handlePreferenceTreeClick(preference);
}
>setVolume
切换查看的存储卡的时候会调用这个
public void setVolume(VolumeInfo volume) {
mVolume = volume;
if (mPublicStoragePreference != null) {
//外置卡那个file选项可见性
mPublicStoragePreference.setVisible(isValidPublicVolume());
}
if (isValidPrivateVolume()) {
//这里是内置卡,preferences的可见性在onLoadFinished方法里设置为true
mIsDocumentsPrefShown = isDocumentsPrefShown();
} else {
//非内置卡,隐藏这堆preferences
setPrivateStorageCategoryPreferencesVisibility(false);
}
}
>onLoadFinished
public void onLoadFinished(@Nullable SparseArray<StorageAsyncLoader.StorageResult> result,
int userId) {
//更新这些preferences的数据
StorageCacheHelper.StorageCache storageCache = getSizeInfo(result, userId);
// Set size info to each preference
mImagesPreference.setStorageSize(storageCache.imagesSize, mTotalSize, animate);
mVideosPreference.setStorageSize(storageCache.videosSize, mTotalSize, animate);
mAudioPreference.setStorageSize(storageCache.audioSize, mTotalSize, animate);
mAppsPreference.setStorageSize(storageCache.allAppsExceptGamesSize, mTotalSize, animate);
mGamesPreference.setStorageSize(storageCache.gamesSize, mTotalSize, animate);
mDocumentsAndOtherPreference.setStorageSize(storageCache.documentsAndOtherSize, mTotalSize,
animate);
mTrashPreference.setStorageSize(storageCache.trashSize, mTotalSize, animate);
if (mSystemPreference != null) {
mSystemPreference.setStorageSize(storageCache.systemSize, mTotalSize, animate);
}
// Cache the size info
if (result != null) {
mStorageCacheHelper.cacheSizeInfo(storageCache);
}
// Sort the preference according to size info in descending order
if (!mIsPreferenceOrderedBySize) {
//按照使用大小排序,移除所有preferences再重新添加
updatePrivateStorageCategoryPreferencesOrder();
mIsPreferenceOrderedBySize = true;
}
//修改可见性
setPrivateStorageCategoryPreferencesVisibility(true);
}
>onEmptyTrashComplete
private void launchTrashIntent() {
final Intent intent = new Intent("android.settings.VIEW_TRASH");
if (mPackageManager.resolveActivityAsUser(intent, 0 /* flags */, mUserId) == null) {
final long trashSize = mTrashPreference.getStorageSize();
if (trashSize > 0) {
//没有找到处理trash的intent,自己处理,弹个对话框,回调就是下边的方法
new EmptyTrashFragment(mFragment, mUserId, trashSize,
this /* onEmptyTrashCompleteListener */).show();
} else {
Toast.makeText(mContext, R.string.storage_trash_dialog_empty_message,
Toast.LENGTH_SHORT).show();
}
} else {
//有处理trash的页面,跳转过去
mContext.startActivityAsUser(intent, new UserHandle(mUserId));
}
}
这个是清空完trash以后,可以看到会重新排序
public void onEmptyTrashComplete() {
if (mTrashPreference == null) {
return;
}
mTrashPreference.setStorageSize(0, mTotalSize, true /* animate */);
updatePrivateStorageCategoryPreferencesOrder();
}
controller都学完了,下边看下Fragment里的一些操作
2.4.内置卡的数据使用
refreshUi方法里
if (mSelectedStorageEntry.isPrivate()) {
mStorageInfo = null;
mAppsResult = null;
if (mStorageCacheHelper.hasCachedSizeInfo()) {
//有缓存数据先使用缓存数据
mPreferenceController.onLoadFinished(mAppsResult, mUserId);
} else {
//支持loading的话就显示个loading
maybeSetLoading(isQuotaSupported());
// 防止出现闪烁的情况,数据先设置为null,后边拿到数据再重新设置
mPreferenceController.setVolume(null);
}
// 获取内置卡的数据使用情况
getLoaderManager().restartLoader(STORAGE_JOB_ID, Bundle.EMPTY, this);
//这个是获取volume的总大小以及使用大小
getLoaderManager()
.restartLoader(VOLUME_SIZE_JOB_ID, Bundle.EMPTY, new VolumeSizeCallbacks());
//这个是获取用户头像的,给那个mSecondaryUsers用的
getLoaderManager().restartLoader(ICON_JOB_ID, Bundle.EMPTY, new IconLoaderCallbacks());
}
2.5.LoaderManager
异步获取数据用的是LoaderManager,简单看下逻辑
getLoaderManager().restartLoader(ICON_JOB_ID, Bundle.EMPTY, new IconLoaderCallbacks());
首先getLoaderManger()是Fragment里自带的方法,其实就是下边这个,现在也推荐这样使用
LoaderManager.getInstance(this)
>LoaderManagerImpl
public static <T extends LifecycleOwner & ViewModelStoreOwner> LoaderManager getInstance(
@NonNull T owner) {
return new LoaderManagerImpl(owner, owner.getViewModelStore());
}
//
LoaderManagerImpl(@NonNull LifecycleOwner lifecycleOwner,
@NonNull ViewModelStore viewModelStore) {
mLifecycleOwner = lifecycleOwner;
mLoaderViewModel = LoaderViewModel.getInstance(viewModelStore);
}
>restartLoader
重新启动一个loader,有对应id的旧的loader,先销毁,再新建一个
public <D> Loader<D> restartLoader(int id, @Nullable Bundle args,
@NonNull LoaderCallbacks<D> callback) {
if (mLoaderViewModel.isCreatingLoader()) {
throw new IllegalStateException("正在创建loader");
}
if (Looper.getMainLooper() != Looper.myLooper()) {
throw new IllegalStateException("必须在主线程调用");
}
LoaderInfo<D> info = mLoaderViewModel.getLoader(id);
Loader<D> priorLoader = null;
if (info != null) {
//有旧的先销毁
priorLoader = info.destroy(false);
}
// And create a new Loader
return createAndInstallLoader(id, args, callback, priorLoader);
}
>createAndInstallLoader
private <D> Loader<D> createAndInstallLoader(int id, @Nullable Bundle args,
@NonNull LoaderCallbacks<D> callback, @Nullable Loader<D> priorLoader) {
LoaderInfo<D> info;
try {
mLoaderViewModel.startCreatingLoader();
//调用callback里的方法获取loader实例对象
Loader<D> loader = callback.onCreateLoader(id, args);
if (loader == null) {
throw new IllegalArgumentException("Object returned from onCreateLoader "
+ "must not be null");
}
if (loader.getClass().isMemberClass()
&& !Modifier.isStatic(loader.getClass().getModifiers())) {
throw new IllegalArgumentException("回调里给的loader不能是一个静态内部类");
}
//实例化一个LoaderInfo对象
info = new LoaderInfo<>(id, args, loader, priorLoader);
//放入map里
mLoaderViewModel.putLoader(id, info);
} finally {
mLoaderViewModel.finishCreatingLoader();
}
return info.setCallback(mLifecycleOwner, callback);
}
2.6.LoaderInfo
可以看到是个LiveData,实现了Loader的监听
public static class LoaderInfo<D> extends MutableLiveData<D>
implements Loader.OnLoadCompleteListener<D> {
LoaderInfo(int id, @Nullable Bundle args, @NonNull Loader<D> loader,
@Nullable Loader<D> priorLoader) {
mId = id;
mArgs = args;
mLoader = loader;
mPriorLoader = priorLoader;
//给loader设置监听
mLoader.registerListener(id, this);
}
>onLoadComplete
可以看到回调里会直接更新这个liveData的数据
public void onLoadComplete(@NonNull Loader<D> loader, @Nullable D data) {
if (Looper.myLooper() == Looper.getMainLooper()) {
setValue(data);
} else {
postValue(data);
}
}
>onActive
protected void onActive() {
mLoader.startLoading();
}
>setCallback
Loader<D> setCallback(@NonNull LifecycleOwner owner,
@NonNull LoaderCallbacks<D> callback) {
LoaderObserver<D> observer = new LoaderObserver<>(mLoader, callback);
//observe是liveData里的方法
//也就是LoaderInfo数据变化的时候调用observer的onChanged方法
observe(owner, observer);
if (mObserver != null) {
removeObserver(mObserver);
}
mLifecycleOwner = owner;
mObserver = observer;
return mLoader;
}
2.7.LoaderObserver
当LoaderObserver数据发生变化的时候,会调用callback里的onLoadFinished
LoaderObserver(@NonNull Loader<D> loader, @NonNull LoaderCallbacks<D> callback) {
mLoader = loader;
mCallback = callback;
}
@Override
public void onChanged(@Nullable D data) {
mCallback.onLoadFinished(mLoader, data);
mDeliveredData = true;
}
2.8 总结下
- LoaderInfo是一个livedata
- Loader在LoaderInfo里注册了一个监听器,回调方法是onLoadComplete,方法里会修改LoaderInfo的值
- setCallback方法里给LoaderInfo注册了观察者LoaderObserver,LoaderInfo数据变化的时候会调用mCallback的onLoadFinished方法
- 注意:Loader必须手动调用forceLoad方法才会开始加载数据,一般放在startLoading方法里
2.9.AsyncTaskLoader
public abstract class AsyncTaskLoader<D> extends Loader<D> {
顾名思义,就是在后台执行任务的
>onForceLoad
可以看到这里实例化了一个LoaderTask
protected void onForceLoad() {
super.onForceLoad();
cancelLoad();
mTask = new LoadTask();
executePendingTask();
}
>executePendingTask
根据条件执行task
void executePendingTask() {
if (mCancellingTask == null && mTask != null) {
if (mTask.waiting) {
mTask.waiting = false;
mHandler.removeCallbacks(mTask);
}
if (mUpdateThrottle > 0) {
long now = SystemClock.uptimeMillis();
if (now < (mLastLoadCompleteTime+mUpdateThrottle)) {
// Not yet time to do another load.
mTask.waiting = true;
mHandler.postAtTime(mTask, mLastLoadCompleteTime+mUpdateThrottle);
return;
}
}
//在executor里执行任务
mTask.executeOnExecutor(mExecutor, (Void[]) null);
}
}
>executeOnExecutor
ModernAsyncTask.java
public final ModernAsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,
Params... params) {
//..
mStatus = Status.RUNNING;
onPreExecute();
mWorker.mParams = params;
//mFuture就是runnable,会执行其run方法,run方法里会执行下边的mWorker里的call方法
exec.execute(mFuture);
return this;
}
>ModernAsyncTask
ModernAsyncTask() {
mWorker = new WorkerRunnable<Params, Result>() {
@Override
public Result call() throws Exception {
mTaskInvoked.set(true);
Result result = null;
try {
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
//最终会调用这个方法
result = doInBackground(mParams);
Binder.flushPendingCommands();
}finally {
postResult(result);
}
return result;
}
};
mFuture = new FutureTask<Result>(mWorker) {
@Override
protected void done() {
try {
final Result result = get();
postResultIfNotInvoked(result);
}
}
};
}
>FutureTask
public interface RunnableFuture<V> extends Runnable, Future<V> {
void run();
}
public class FutureTask<V> implements RunnableFuture<V> {
//这里的callback就是上边的WorkerRunnable
public FutureTask(Callable<V> callable) {
this.callable = callable;
this.state = NEW; // ensure visibility of callable
}
public void run() {
Callable<V> c = callable;
if (c != null && state == NEW) {
V result;
boolean ran;
try {
//调用call方法获取数据
result = c.call();
ran = true;
} catch (Throwable ex) {
result = null;
ran = false;
setException(ex);
}
if (ran)
set(result);
}
}
>LoadTask
AsyncTaskLoader里用到的就是这个对象
final class LoadTask extends ModernAsyncTask<Void, Void, D> implements Runnable {
private final CountDownLatch mDone = new CountDownLatch(1);
@Override
protected D doInBackground(Void... params) {
try {
//可以看到,最终调用的是Loader里的onLoadInBackground
D data = AsyncTaskLoader.this.onLoadInBackground();
return data;
}
}
2.10.实例
这里看下fragment里用到的几种loader
>AsyncLoaderCompat
public class VolumeSizesLoader extends AsyncLoaderCompat<PrivateStorageInfo> {
可以看到在onStartLoading里调用了forceLoad
public abstract class AsyncLoaderCompat<T> extends AsyncTaskLoader<T> {
private T mResult;
public AsyncLoaderCompat(final Context context) {
super(context);
}
@Override
protected void onStartLoading() {
if (mResult != null) {
deliverResult(mResult);
}
if (takeContentChanged() || mResult == null) {
forceLoad();
}
}
>AsyncLoaderCompat
public class UserIconLoader extends AsyncLoaderCompat<SparseArray<Drawable>> {
在onStartLoading里调用了forceLoad
public abstract class AsyncLoaderCompat<T> extends AsyncTaskLoader<T> {
private T mResult;
public AsyncLoaderCompat(final Context context) {
super(context);
}
@Override
protected void onStartLoading() {
if (mResult != null) {
deliverResult(mResult);
}
if (takeContentChanged() || mResult == null) {
forceLoad();
}
}
3.后记
3.1.存储卡大小获取
- 需要api 31
- 获取的字节数,除以1000就行,别除以1024,手机和电脑的计算方式不一样。
public static void testStorage(Context context) {
try {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
File data = Environment.getExternalStorageDirectory();
StorageManager storageManager = context.getSystemService(StorageManager.class);
UUID currentVolumeUUID = storageManager.getStorageVolume(data).getStorageUuid();
StorageStatsManager storageStatsManager = context.getSystemService(StorageStatsManager.class);
long totalByte = storageStatsManager.getTotalBytes(currentVolumeUUID);
long freeByte = storageStatsManager.getFreeBytes(currentVolumeUUID);
System.out.println("log========Show: byte==" + totalByte + "===" + freeByte);
}
} catch (IOException e) {
e.printStackTrace();
}
}
>格式化返回的结果
public static String formatBytes(long sizeBytes) {
final int unit = 1000;
final boolean isNegative = (sizeBytes < 0);
float result = isNegative ? -sizeBytes : sizeBytes;
String suffix = "B";
long mult = 1;
if (result > 900) {
suffix = "KB";
mult = unit;
result = result / unit;
}
if (result > 900) {
suffix = "MB";
mult *= unit;
result = result / unit;
}
if (result > 900) {
suffix = "GB";
mult *= unit;
result = result / unit;
}
if (result > 900) {
suffix = "TB";
mult *= unit;
result = result / unit;
}
if (result > 900) {
suffix = "PB";
mult *= unit;
result = result / unit;
}
final String roundFormat;
if (mult == 1 || result >= 100) {
roundFormat = "%.0f";
} else if (result < 1) {
roundFormat = "%.2f";
} else if (result < 10) {
roundFormat = "%.1f";
} else { // 10 <= result < 100
roundFormat = "%.0f";
}
if (isNegative) {
result = -result;
}
final String roundedString = String.format(roundFormat, result);
return roundedString + suffix;
}