应用管理详情
用户在感觉手机空间被APP占用较大时候,会查看APP的应用占用磁盘详情,可以在设置的应用管理找到当前APP然后查看。
- 应用大小:安装APK解压后占用磁盘空间
- 缓存:指
data/data/{package-name}/cache
和sdcard/Android/{package-name}/cache
占用磁盘大小 - 用户数据:指
data/data/{package-name}/
和sdcard/Android/{package-name}/
减去缓存磁盘占用后的大小,可能不同厂商定制后的计算有所不同。
管理储存空间
app安装后要恢复到安装时候的状态有2种方式,卸载重新安装或者打开设置找到APP的应用管理-> 清除数据 -> 清除全部数据/清除缓存。而在日常使用中发现有个特别的情况,在TG和bilibili的应用在选择清除数据时候没有清除全部数据的选项,而是管理空间(在piexl7Pro 上还是clear data)点击后会打开APP自己的一个缓存管理定制页面,在这里可以自由的清理一些开发者允许的数据。
定制存储空间管理
其实在google官方文档中有专门对这个功能的介绍,只是平常开发中并没有产品对此有特别需求,我们就很少知道了。 developer.android.com/training/da…
让我们跟着文档学习下如何定制一个存储空间管理的页面吧。
- 创建并在Manifest中注册一个普通Activity或者是你APP中已经存在的管理和缓存清理的Activity,不需要添加任何特殊属性。当您的 activity 将 android:exported 设置为 false 时,仍然可以调用。
<application ... android:name=".app.DemoApplication" android:theme="@style/AppTheme"> ... <activity android:name=".screen.ManageSpaceActivity" android:exported="false"/>
- 在application的标签中添加android:manageSpaceActivity并声明上面的Activity路径
<application android:name=".app.DemoApplication" ... android:manageSpaceActivity=".screen.ManageSpaceActivity" android:theme="@style/AppTheme">
- 安装APP在设置中找到应用详情,点击清理数据 -> 管理空间
用Compose + MVI 快速实现存储空间管理页面
1. 使用Compose编写UI,并且提供一个按钮去清理空间, PieChart源码查看后面github link
class ManageSpaceActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
MVIHiltTheme {
ManageSpaceScreen()
}
}
}
}
@OptIn(ExperimentalMaterial3Api::class)
@Preview
@Composable
fun ManageSpaceScreen() {
val scaffoldState = remember { SnackbarHostState() }
val viewModel = viewModel<ManageSpaceViewModel>()
SideEffect {
viewModel.sendAction(ManageSpaceAction.LoadSpaceData)
}
val lifecycleOwner = LocalLifecycleOwner.current
LaunchedEffect(viewModel.effect) {
lifecycleOwner.lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) {
viewModel.effect.collect {
if (it is ManageSpaceEffect.ShowToast) {
scaffoldState.showSnackbar(it.content)
}
}
}
}
Scaffold(modifier = Modifier.fillMaxSize(), snackbarHost = { SnackbarHost(scaffoldState) }, floatingActionButton = {
if (viewModel.state.value is ManageSpaceState.StorageSpace) {
FloatingActionButton(onClick = {
scaffoldState.currentSnackbarData?.dismiss()
viewModel.sendAction(ManageSpaceAction.ClearCache)
}) {
Icon(Icons.Outlined.Delete, contentDescription = "")
}
}
}) {
when (val spaceState = viewModel.state.value) {
ManageSpaceState.Loading -> CircularProgressIndicator()
is ManageSpaceState.StorageSpace -> Box(
modifier = Modifier
.padding(it)
.fillMaxSize(), contentAlignment = Alignment.Center
) {
val point = listOf(
spaceState.apkBytes.toFloat(), spaceState.dataBytes.toFloat(), spaceState.cacheBytes.toFloat()
)
val labels = listOf(
"应用大小:${spaceState.apkSize}", "用户数据:${spaceState.dataSize}", "缓存:${spaceState.cacheSize}"
)
val color = listOf(Color.Magenta, Color.Green, Color.Gray)
PieChart(color, point, labels)
}
}
}
}
2. 定义MVI中的model和Intent
sealed class ManageSpaceAction {
object LoadSpaceData : ManageSpaceAction()
object ClearCache : ManageSpaceAction()
}
sealed class ManageSpaceState {
object Loading : ManageSpaceState()
data class StorageSpace(
val apkBytes: Long,
val dataBytes: Long,
val cacheBytes: Long,
val apkSize: String,
val dataSize: String,
val cacheSize: String,
) : ManageSpaceState()
}
sealed class ManageSpaceEffect {
data class ShowToast(val content: String) : ManageSpaceEffect()
}
3. 给ViewModel添加一个repository来获取数据,并且管理M和I
class ManageSpaceViewModel constructor(
val context: Application,
) : AndroidViewModel(context) {
private val repository: ManageSpaceRepository = ManageSpaceRepository(context)
private val _viewState: MutableState<ManageSpaceState> = mutableStateOf(ManageSpaceState.Loading)
val state: State<ManageSpaceState> = _viewState
private val _effect = MutableSharedFlow<ManageSpaceEffect>()
val effect: SharedFlow<ManageSpaceEffect> by lazy { _effect.asSharedFlow() }
fun sendAction(action: ManageSpaceAction) {
when (action) {
ManageSpaceAction.LoadSpaceData -> {
viewModelScope.launch {
withContext(Dispatchers.IO) {
repository.getAppSize()
}.onSuccess {
_viewState.value = it
}.onFailure {
_effect.tryEmit(ManageSpaceEffect.ShowToast("free space load error"))
}
}
}
ManageSpaceAction.ClearCache -> {
viewModelScope.launch {
_viewState.value = (ManageSpaceState.Loading)
withContext(Dispatchers.IO) {
repository.clearCache()
repository.getAppSize()
}.onSuccess {
_viewState.value = it
}.onFailure {
_effect.tryEmit(ManageSpaceEffect.ShowToast("space clear error"))
}
}
}
}
}
}
4. repository通过StorageStatsManager来获取APP的所有信息和清理缓存
class ManageSpaceRepository(private val context: Context) {
private val filesDir: File
get() = context.filesDir
fun getAppSize() = kotlin.runCatching {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val storageStatsManager = context.getSystemService(Context.STORAGE_STATS_SERVICE) as StorageStatsManager
val uid = context.packageManager.getApplicationInfo(context.packageName, PackageManager.GET_META_DATA).uid
val storageStats = storageStatsManager.queryStatsForUid(StorageManager.UUID_DEFAULT, uid)
ManageSpaceState.StorageSpace(
dataBytes = storageStats.dataBytes,
cacheBytes = storageStats.cacheBytes,
apkBytes = storageStats.appBytes,
dataSize = Formatter.formatFileSize(context, storageStats.dataBytes),
cacheSize = Formatter.formatFileSize(context, storageStats.cacheBytes),
apkSize = Formatter.formatFileSize(context, storageStats.appBytes),
)
} else {
TODO()
}
}
fun clearCache() = kotlin.runCatching {
val time = measureTimeMillis {
filesDir.deleteRecursively()
}
println(2500 - time)
}
}
5. 运行查看下
Github
作者:forJrking
链接:https://juejin.cn/post/7299667259902623754
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。