日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 运维知识 > Android >内容正文

Android

Android StorageManager实现原理剖析

發(fā)布時(shí)間:2024/8/1 Android 32 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Android StorageManager实现原理剖析 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

引言

在現(xiàn)在的Android手機(jī)中,EMMC已經(jīng)從32G,64G開始了向128G, 512G的快速轉(zhuǎn)變。
隨著5G時(shí)代的到來,以及近些年Camera的興起,越來越多數(shù)據(jù)將會(huì)在本地進(jìn)行運(yùn)算和存儲(chǔ)。
那么,對(duì)于存儲(chǔ)的管理就會(huì)越來越受人重視。
下圖是一個(gè)AOSP Pixel的Storage截圖,當(dāng)然,這個(gè)界面各個(gè)廠商也是修改的最兇的。

我們這里主要分析的是原生的Storage manager的清理邏輯,以及下方各類型數(shù)據(jù)存儲(chǔ)記錄的規(guī)則。

Storage manager

Storage manager實(shí)現(xiàn)邏輯分析

在點(diǎn)擊Storage manager的界面后,我們可以看到如下的界面:

那么點(diǎn)擊移除照片和視頻,將會(huì)有30天,60天,90天這幾個(gè)選項(xiàng)。
代碼實(shí)現(xiàn)的路徑為src/com/android/settings/deletionhelper/AutomaticStorageManagerSettings.java
在進(jìn)來之后,初始化的方法為:

@Overridepublic View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {View view = super.onCreateView(inflater, container, savedInstanceState);initializeDaysToRetainPreference();initializeSwitchBar();return view;}

可以看到時(shí)進(jìn)行了初始化Perference和SwitchBar的初始化設(shè)置。

private void initializeDaysToRetainPreference() {mDaysToRetain = (DropDownPreference) findPreference(KEY_DAYS);mDaysToRetain.setOnPreferenceChangeListener(this);ContentResolver cr = getContentResolver();int photosDaysToRetain =Settings.Secure.getInt(cr,Settings.Secure.AUTOMATIC_STORAGE_MANAGER_DAYS_TO_RETAIN,Utils.getDefaultStorageManagerDaysToRetain(getResources()));String[] stringValues =getResources().getStringArray(R.array.automatic_storage_management_days_values);mDaysToRetain.setValue(stringValues[daysValueToIndex(photosDaysToRetain, stringValues)]);}

在這邊,將會(huì)從數(shù)據(jù)庫(kù)中取出保存的AUTOMATIC_STORAGE_MANAGER_DAYS_TO_RETAIN的值,并進(jìn)行顯示。

private void initializeSwitchBar() {final SettingsActivity activity = (SettingsActivity) getActivity();mSwitchBar = activity.getSwitchBar();mSwitchBar.setSwitchBarText(R.string.automatic_storage_manager_master_switch_title,R.string.automatic_storage_manager_master_switch_title);mSwitchBar.show();mSwitchController =new AutomaticStorageManagerSwitchBarController(getContext(),mSwitchBar,mMetricsFeatureProvider,mDaysToRetain,getFragmentManager());}

取到的值,將會(huì)通過AutomaticStorageManagerSwitchBarController對(duì)象來進(jìn)行初始化的保存。
而當(dāng)每次用戶操作數(shù)據(jù)有改變的時(shí)候,我們將會(huì)通過監(jiān)聽來獲得:

@Overridepublic boolean onPreferenceChange(Preference preference, Object newValue) {if (KEY_DAYS.equals(preference.getKey())) {Settings.Secure.putInt(getContentResolver(),Settings.Secure.AUTOMATIC_STORAGE_MANAGER_DAYS_TO_RETAIN,Integer.parseInt((String) newValue));}return true;}

這邊其實(shí)就是會(huì)將對(duì)應(yīng)的值寫到數(shù)據(jù)庫(kù)中。那么AUTOMATIC_STORAGE_MANAGER_DAYS_TO_RETAIN這個(gè)變量,就會(huì)非常的重要。
定義如下:

/*** How many days of information for the automatic storage manager to retain on the device.** @hide*/public static final String AUTOMATIC_STORAGE_MANAGER_DAYS_TO_RETAIN ="automatic_storage_manager_days_to_retain";

那么定義出來的天數(shù)是怎么檢測(cè)的呢?這就從一個(gè)廣播說起:

<!-- Automatic storage management tasks. --><serviceandroid:name=".automatic.AutomaticStorageManagementJobService"android:label="@string/automatic_storage_manager_service_label"android:permission="android.permission.BIND_JOB_SERVICE"android:enabled="@bool/enable_automatic_storage_management"android:exported="false"/><receiver android:name=".automatic.AutomaticStorageBroadcastReceiver"android:enabled="@bool/enable_automatic_storage_management"><intent-filter><action android:name="android.intent.action.BOOT_COMPLETED" /></intent-filter></receiver>

因?yàn)槲覀兦懊嬖O(shè)置的是天數(shù),所以這邊我們將會(huì)起一個(gè)receiver來接收boot_complete的廣播。

@Overridepublic void onReceive(Context context, Intent intent) {// Automatic deletion serviceJobScheduler jobScheduler =(JobScheduler) context.getSystemService(Context.JOB_SCHEDULER_SERVICE);ComponentName component = new ComponentName(context,AutomaticStorageManagementJobService.class);long periodicOverride = SystemProperties.getLong(DEBUG_PERIOD_FLAG, PERIOD);JobInfo job = new JobInfo.Builder(AUTOMATIC_STORAGE_JOB_ID, component).setRequiresCharging(true).setRequiresDeviceIdle(true).setPeriodic(periodicOverride).build();jobScheduler.schedule(job);}

這邊可以看到,其實(shí)就是根據(jù)boot complete的時(shí)間,設(shè)置了jobscheduler的service去定時(shí)執(zhí)行AutomaticStorageManagementJobService。
實(shí)現(xiàn)的文件為AutomaticStorageManagementJobService.java
在這邊我們關(guān)注的是onStartJob的方法:

@Overridepublic boolean onStartJob(JobParameters args) {// We need to double-check the preconditions here because they are not enforced for a// periodic job.if (!preconditionsFulfilled()) {// By telling the system to re-schedule the job, it will attempt to execute again at a// later idle window -- possibly one where we are charging.jobFinished(args, true);return false;}mProvider = FeatureFactory.getFactory(this).getStorageManagementJobProvider();if (maybeDisableDueToPolicy(mProvider, this, getClock())) {jobFinished(args, false);return false;}if (!volumeNeedsManagement()) {Log.i(TAG, "Skipping automatic storage management.");Settings.Secure.putLong(getContentResolver(),Settings.Secure.AUTOMATIC_STORAGE_MANAGER_LAST_RUN,System.currentTimeMillis());jobFinished(args, false);return false;}if (!Utils.isStorageManagerEnabled(getApplicationContext())) {Intent maybeShowNotificationIntent =new Intent(NotificationController.INTENT_ACTION_SHOW_NOTIFICATION);maybeShowNotificationIntent.setClass(getApplicationContext(),NotificationController.class);getApplicationContext().sendBroadcast(maybeShowNotificationIntent);jobFinished(args, false);return false;}if (mProvider != null) {return mProvider.onStartJob(this, args, getDaysToRetain());}jobFinished(args, false);return false;}

當(dāng)系統(tǒng)在低存儲(chǔ)的模式下,并且打開了automatic storage management的功能,那么才會(huì)最后去執(zhí)行mProvider的onStartJob的工作。
mProvider在AOSP中的實(shí)現(xiàn)就基本上終止了,pixel會(huì)使用文件管理器替換掉這個(gè)功能。
而華為,小米,Oppo,Vivo等廠商也是使用不同的定制化的apk去overlay storageManager。
這里需要注意,這個(gè)其實(shí)可以被overlay,override掉,所以也方便了各大廠商在這邊的定制。

各種類型計(jì)算方案實(shí)現(xiàn)

計(jì)算這邊我們分為兩部分,第一部分是我們點(diǎn)擊Settings的Perference后,進(jìn)行的Activity跳轉(zhuǎn)。

@Overridepublic boolean handlePreferenceTreeClick(Preference preference) {if (preference == null) {return false;}Intent intent = null;if (preference.getKey() == null) {return false;}switch (preference.getKey()) {case PHOTO_KEY:intent = getPhotosIntent();break;case AUDIO_KEY:intent = getAudioIntent();break;case GAME_KEY:intent = getGamesIntent();break;case MOVIES_KEY:intent = getMoviesIntent();break;case OTHER_APPS_KEY:// Because we are likely constructed with a null volume, this is theoretically// possible.if (mVolume == null) {break;}intent = getAppsIntent();break;case FILES_KEY:intent = getFilesIntent();FeatureFactory.getFactory(mContext).getMetricsFeatureProvider().action(mContext, SettingsEnums.STORAGE_FILES);break;case SYSTEM_KEY:final SystemInfoFragment dialog = new SystemInfoFragment();dialog.setTargetFragment(mFragment, 0);dialog.show(mFragment.getFragmentManager(), SYSTEM_FRAGMENT_TAG);return true;}if (intent != null) {intent.putExtra(Intent.EXTRA_USER_ID, mUserId);Utils.launchIntent(mFragment, intent);return true;}return super.handlePreferenceTreeClick(preference);}

其實(shí)在這邊,就可以很容易的定義不同的perference以及將會(huì)出發(fā)的intent。
以點(diǎn)擊Photo為例:

private Intent getPhotosIntent() {Bundle args = getWorkAnnotatedBundle(2);args.putString(ManageApplications.EXTRA_CLASSNAME, Settings.PhotosStorageActivity.class.getName());args.putInt(ManageApplications.EXTRA_STORAGE_TYPE,ManageApplications.STORAGE_TYPE_PHOTOS_VIDEOS);return new SubSettingLauncher(mContext).setDestination(ManageApplications.class.getName()).setTitleRes(R.string.storage_photos_videos).setArguments(args).setSourceMetricsCategory(mMetricsFeatureProvider.getMetricsCategory(mFragment)).toIntent();}

這里就會(huì)丁志偉相應(yīng)跳轉(zhuǎn)的activity了。


計(jì)算的方式如下,仍然以Photo的類型來進(jìn)行說明:
這里其實(shí)是會(huì)有一個(gè)多用戶的概念:

public void onLoadFinished(SparseArray<StorageAsyncLoader.AppsStorageResult> result,int userId) {final StorageAsyncLoader.AppsStorageResult data = result.get(userId);final StorageAsyncLoader.AppsStorageResult profileData = result.get(Utils.getManagedProfileId(mContext.getSystemService(UserManager.class), userId));mPhotoPreference.setStorageSize(getPhotosSize(data, profileData), mTotalSize);mAudioPreference.setStorageSize(getAudioSize(data, profileData), mTotalSize);mGamePreference.setStorageSize(getGamesSize(data, profileData), mTotalSize);mMoviesPreference.setStorageSize(getMoviesSize(data, profileData), mTotalSize);mAppPreference.setStorageSize(getAppsSize(data, profileData), mTotalSize);mFilePreference.setStorageSize(getFilesSize(data, profileData), mTotalSize);if (mSystemPreference != null) {// Everything else that hasn't already been attributed is tracked as// belonging to system.long attributedSize = 0;for (int i = 0; i < result.size(); i++) {final StorageAsyncLoader.AppsStorageResult otherData = result.valueAt(i);attributedSize +=otherData.gamesSize+ otherData.musicAppsSize+ otherData.videoAppsSize+ otherData.photosAppsSize+ otherData.otherAppsSize;attributedSize += otherData.externalStats.totalBytes- otherData.externalStats.appBytes;}final long systemSize = Math.max(TrafficStats.GB_IN_BYTES, mUsedBytes - attributedSize);mSystemPreference.setStorageSize(systemSize, mTotalSize);}}

這里其實(shí)就是拿兩個(gè)data:

final StorageAsyncLoader.AppsStorageResult data = result.get(userId);final StorageAsyncLoader.AppsStorageResult profileData = result.get(Utils.getManagedProfileId(mContext.getSystemService(UserManager.class), userId));

然后進(jìn)行后續(xù)photo,games等內(nèi)容的傳遞。

private long getPhotosSize(StorageAsyncLoader.AppsStorageResult data,StorageAsyncLoader.AppsStorageResult profileData) {if (profileData != null) {return data.photosAppsSize + data.externalStats.imageBytes+ data.externalStats.videoBytes+ profileData.photosAppsSize + profileData.externalStats.imageBytes+ profileData.externalStats.videoBytes;} else {return data.photosAppsSize + data.externalStats.imageBytes+ data.externalStats.videoBytes;}}

在photo中,其實(shí)只是對(duì)data的photo的size進(jìn)行了相加,

public static class AppsStorageResult {public long gamesSize;public long musicAppsSize;public long photosAppsSize;public long videoAppsSize;public long otherAppsSize;public long cacheSize;public StorageStatsSource.ExternalStorageStats externalStats;}

因?yàn)樵贏ppsStorageResult類中,已經(jīng)對(duì)其進(jìn)行了計(jì)算。
這里就要提一下StorageAsyncLoader的實(shí)現(xiàn)了,在函數(shù)初始化后,將會(huì)對(duì)app進(jìn)行l(wèi)oadapp的操作。

@Overridepublic SparseArray<AppsStorageResult> loadInBackground() {return loadApps();}private SparseArray<AppsStorageResult> loadApps() {mSeenPackages = new ArraySet<>();SparseArray<AppsStorageResult> result = new SparseArray<>();List<UserInfo> infos = mUserManager.getUsers();// Sort the users by user id ascending.Collections.sort(infos,new Comparator<UserInfo>() {@Overridepublic int compare(UserInfo userInfo, UserInfo otherUser) {return Integer.compare(userInfo.id, otherUser.id);}});for (int i = 0, userCount = infos.size(); i < userCount; i++) {UserInfo info = infos.get(i);result.put(info.id, getStorageResultForUser(info.id));}return result;}

這邊會(huì)去調(diào)用getStorageResultForUser進(jìn)行統(tǒng)計(jì),然后put到result中。

private AppsStorageResult getStorageResultForUser(int userId) {Log.d(TAG, "Loading apps");List<ApplicationInfo> applicationInfos =mPackageManager.getInstalledApplicationsAsUser(0, userId);AppsStorageResult result = new AppsStorageResult();UserHandle myUser = UserHandle.of(userId);for (int i = 0, size = applicationInfos.size(); i < size; i++) {ApplicationInfo app = applicationInfos.get(i);StorageStatsSource.AppStorageStats stats;try {stats = mStatsManager.getStatsForPackage(mUuid, app.packageName, myUser);} catch (NameNotFoundException | IOException e) {// This may happen if the package was removed during our calculation.Log.w(TAG, "App unexpectedly not found", e);continue;}final long dataSize = stats.getDataBytes();final long cacheQuota = mStatsManager.getCacheQuotaBytes(mUuid, app.uid);final long cacheBytes = stats.getCacheBytes();long blamedSize = dataSize;// Technically, we could overages as freeable on the storage settings screen.// If the app is using more cache than its quota, we would accidentally subtract the// overage from the system size (because it shows up as unused) during our attribution.// Thus, we cap the attribution at the quota size.if (cacheQuota < cacheBytes) {blamedSize = blamedSize - cacheBytes + cacheQuota;}// This isn't quite right because it slams the first user by user id with the whole code// size, but this ensures that we count all apps seen once.if (!mSeenPackages.contains(app.packageName)) {blamedSize += stats.getCodeBytes();mSeenPackages.add(app.packageName);}switch (app.category) {case CATEGORY_GAME:result.gamesSize += blamedSize;break;case CATEGORY_AUDIO:result.musicAppsSize += blamedSize;break;case CATEGORY_VIDEO:result.videoAppsSize += blamedSize;break;case CATEGORY_IMAGE:result.photosAppsSize += blamedSize;break;default:// The deprecated game flag does not set the category.if ((app.flags & ApplicationInfo.FLAG_IS_GAME) != 0) {result.gamesSize += blamedSize;break;}result.otherAppsSize += blamedSize;break;}}Log.d(TAG, "Loading external stats");try {result.externalStats = mStatsManager.getExternalStorageStats(mUuid,UserHandle.of(userId));} catch (IOException e) {Log.w(TAG, e);}Log.d(TAG, "Obtaining result completed");return result;}

針對(duì)不同APP的category來進(jìn)行類別的劃分,并且進(jìn)行size的計(jì)算。

總結(jié)

以上是生活随笔為你收集整理的Android StorageManager实现原理剖析的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。