获取其他app的cache大小和删除其他app的cache

本文介绍了一种通过反射调用 PackageManager 的隐藏方法来获取指定 APP 的缓存大小及删除缓存的方法。由于直接文件操作受限于权限问题,文中详细阐述了如何利用 IPackageStatsObserver 和 IPackageDataObserver 的实现来完成需求。

    public static void getAppSize(Context context, String pkgName, IPackageStatsObserver.Stub observer) {
        try {
            if (observer != null) {
                PackageManager pm = context.getPackageManager();
                Method getPackageSizeInfo = pm.getClass()
                        .getMethod("getPackageSizeInfo", String.class, IPackageStatsObserver.class);
                getPackageSizeInfo.invoke(pm, pkgName, observer);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    static class StatsObserver extends IPackageStatsObserver.Stub {
        @Override
        public void onGetStatsCompleted(PackageStats stats, boolean succeeded) {
            long cacheSize = stats.cacheSize;
            if (Environment.isExternalStorageEmulated()) {
                cacheSize += stats.externalCacheSize;
            }
            Log.d("onGetStatsCompleted",String.valueOf(cacheSize));
        }
    }

    public static void delAppCache(Context context, String pkgName, IPackageDataObserver.Stub observer){
        try{
            if (observer != null) {
                PackageManager pm = context.getPackageManager();
                Method deleteApplicationCacheFiles = pm.getClass()
                        .getMethod("deleteApplicationCacheFiles", String.class, IPackageDataObserver.class);
                deleteApplicationCacheFiles.invoke(pm, pkgName, observer);
            }
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    static class ClearCacheObserver extends IPackageDataObserver.Stub {
        @Override
        public void onRemoveCompleted(String packageName,boolean succeeded) {
            Log.d("onRemoveCompleted",String.format("packageName = %s,succeeded = %b",packageName,succeeded));
        }
    }

      在项目开发过程中遇到了这么一个需求:获取其他app的cache大小和删除其他app的cache。

      app的cache是由data/data/app包名/cache和/sdcard/Android/data/app包名/cache两部分组成。

      大家一定会想到利用如下代码片段实现,但不幸的是,我们没有读写权限。

public static long getAppCacheSize(String packageName){
        File cache = new File(String.format("/data/data/%s/cache",packageName));
        File exCache = new File(String.format("/Android/data/%s/cache",packageName));
        return cache.length()+exCache.length();
}

    public static void deleteAppCache(String packageName){
        File cache = new File(String.format("/data/data/%s/cache",packageName));
        cache.delete();
        File exCache = new File(String.format("/Android/data/%s/cache",packageName));
        exCache.delete();
    }

既然此路不通,我们绕道试试。作为android开发人员我们对android系统的Setting非常熟悉吧,对,

我想说的就是,Setting中可以获取其他app的cache和删除其他app的cache的功能,于是乎,我就去

Setting的源码中找,功夫不负有心人终于找到了,但是,尼玛,它用到的一些方法居然是@hide,

也就是说我们不能直接用,没事我们不是还有发射么,哈哈,具体实现如下,我仿照着写了下。

IPackageStatsObserver.aidl

/*
**
** Copyright 2007, The Android Open Source Project
**
** Licensed under the Apache License, Version 2.0 (the "License");
** you may not use this file except in compliance with the License.
** You may obtain a copy of the License at
**
**     http://www.apache.org/licenses/LICENSE-2.0
**
** Unless required by applicable law or agreed to in writing, software
** distributed under the License is distributed on an "AS IS" BASIS,
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
** See the License for the specific language governing permissions and
** limitations under the License.
*/

package android.content.pm;

import android.content.pm.PackageStats;
/**
 * API for package data change related callbacks from the Package Manager.
 * Some usage scenarios include deletion of cache directory, generate
 * statistics related to code, data, cache usage(TODO)
 * {@hide}
 */
oneway interface IPackageStatsObserver {
    
    void onGetStatsCompleted(in PackageStats pStats, boolean succeeded);
}
IPackageDataObserver.aidl

/*
**
** Copyright 2007, The Android Open Source Project
**
** Licensed under the Apache License, Version 2.0 (the "License");
** you may not use this file except in compliance with the License.
** You may obtain a copy of the License at
**
**     http://www.apache.org/licenses/LICENSE-2.0
**
** Unless required by applicable law or agreed to in writing, software
** distributed under the License is distributed on an "AS IS" BASIS,
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
** See the License for the specific language governing permissions and
** limitations under the License.
*/


package android.content.pm;


/**
 * API for package data change related callbacks from the Package Manager.
 * Some usage scenarios include deletion of cache directory, generate
 * statistics related to code, data, cache usage(TODO)
 * {@hide}
 */
oneway interface IPackageDataObserver {
    void onRemoveCompleted(in String packageName, boolean succeeded);
}

将IPackageStatsObserver.aidl和IPackageDataObserver.aidl置于aidl文件夹下(AndroidStudio),如下图

具体实现代码:

    public static void getAppSize(Context context, String pkgName, IPackageStatsObserver.Stub observer) {
        try {
            if (observer != null) {
                PackageManager pm = context.getPackageManager();
                Method getPackageSizeInfo = pm.getClass()
                        .getMethod("getPackageSizeInfo", String.class, IPackageStatsObserver.class);
                getPackageSizeInfo.invoke(pm, pkgName, observer);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    static class StatsObserver extends IPackageStatsObserver.Stub {
        @Override
        public void onGetStatsCompleted(PackageStats stats, boolean succeeded) {
            long cacheSize = stats.cacheSize;
            if (Environment.isExternalStorageEmulated()) {
                cacheSize += stats.externalCacheSize;
            }
            Log.d("onGetStatsCompleted",String.valueOf(cacheSize));
        }
    }

    public static void delAppCache(Context context, String pkgName, IPackageDataObserver.Stub observer){
        try{
            if (observer != null) {
                PackageManager pm = context.getPackageManager();
                Method deleteApplicationCacheFiles = pm.getClass()
                        .getMethod("deleteApplicationCacheFiles", String.class, IPackageDataObserver.class);
                deleteApplicationCacheFiles.invoke(pm, pkgName, observer);
            }
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    static class ClearCacheObserver extends IPackageDataObserver.Stub {
        @Override
        public void onRemoveCompleted(String packageName,boolean succeeded) {
            Log.d("onRemoveCompleted",String.format("packageName = %s,succeeded = %b",packageName,succeeded));
        }
    }
再加上
<uses-permission android:name="android.permission.GET_PACKAGE_SIZE"/>
<uses-permission android:name="android.permission.DELETE_CACHE_FILES"/>
这两个权限。

      本来以为这么着就可以了,没想到run下发现只能获取cache的大小,但不能清除cache,原因就是android.permission.DELETE_CACHE_FILES这个权限只能

赋予系统应用。好吧,如果知道普通第三方应用清除其他app的cache的方法还望告知,谢谢。


### 删除数据导致APP总计大小变成负值的原因分析 在应用程序中,删除数据后系统计算的软件大小变为负值通常与以下几种情况相关: #### 1. **整数溢出问题** 在某些编程语言中,文件大小可能使用有符号整数类型来表示。当执行删除操作时,如果涉及减法运算且结果超出变量所能表示的范围,可能会导致负值出现。例如,在C语言中,`int`类型的取值范围有限,若文件大小接近最大值时进行删除操作,可能导致计算结果为负值[^1]。 ```c int fileSize = 2147483647; // 假设初始文件大小接近int最大值 fileSize -= 3000000000; // 删除操作导致溢出 printf("%d\n", fileSize); // 输出可能为负值 ``` #### 2. **错误的文件大小更新逻辑** 如果程序在删除数据后未能正确更新文件大小统计信息,可能会导致计算错误。例如,某些情况下,程序可能将已删除的数据仍然计入总大小,或者在处理增量变化时出现逻辑错误[^2]。这可能导致最终计算出的文件大小为负值。 ```python total_size = 100 # 初始总大小 deleted_size = 200 # 删除的数据大小 total_size -= deleted_size # 错误的逻辑导致负值 if total_size < 0: total_size = 0 # 需要修复逻辑以避免负值 ``` #### 3. **存储元数据损坏** 文件系统的元数据(如inode或文件属性)可能因程序错误或外部干扰而损坏。这种损坏可能导致系统在读取文件大小时返回不正确的值,包括负值[^4]。例如,文件系统可能记录了错误的块分配信息,从而导致计算出的文件大小异常。 ```bash ls -l /path/to/file # 可能显示负值大小 ``` #### 4. **缓存机制问题** 某些应用程序为了提高性能,会缓存文件大小信息。然而,如果缓存未及时更新或同步,可能导致显示的文件大小与实际值不符。例如,在删除大量数据后,缓存可能仍保留旧值,从而导致计算结果异常[^3]。 ```python cache_size = 100 # 缓存中的文件大小 actual_size = 50 # 实际文件大小 if cache_size != actual_size: cache_size = actual_size # 需要确保缓存与实际值一致 ``` #### 5. **并发操作冲突** 在多线程或多进程环境中,如果多个操作同时修改文件大小统计信息,可能导致竞争条件(race condition),从而产生不一致的结果。例如,一个线程正在更新文件大小,而另一个线程同时执行删除操作,可能导致最终计算结果为负值[^5]。 ```python import threading total_size = 100 def delete_data(): global total_size total_size -= 150 # 删除操作 def update_size(): global total_size total_size += 50 # 更新操作 t1 = threading.Thread(target=delete_data) t2 = threading.Thread(target=update_size) t1.start() t2.start() t1.join() t2.join() print(total_size) # 可能为负值 ``` --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值