清除手机缓存

工程目录如下:

 

需要的aidl文件:上面三个文件对应的文件内容(包名和内容必须一致)

package android.content.pm;

/**
  API包数据变化相关的软件包管理器的回调。
  *一些使用场景包括删除缓存目录,生成
  *统计相关代码、数据缓存的使用(TODO)
* {@hide} 代表在源码中是隐藏的
*/ oneway interface IPackageDataObserver { void onRemoveCompleted(in String packageName, boolean succeeded); }
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);
}
package android.content.pm;

parcelable PackageStats;

主活动MainActivity.java

/*
清理缓存(getCacheDir();//data/data/包名/cache目录):
IPackageStatsObserver.aidl与PackageStats.aidl用来获取缓存信息
IPackageDataObserver.aidl用来清理缓存
*/
public class MainActivity extends Activity {
    
    private PackageManager pm;
    private TextView appInfo;//显示获取到的软件缓存的大小
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        appInfo = (TextView) findViewById(R.id.appInfo);
        
        getFilesDir();//data/data/包名/files/
        getCacheDir();//data/data/包名/cache目录
        scanCache();//扫描缓存
        findViewById(R.id.btn).setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                //一键清除所有缓存的单击事件
                clearAllCache();
            }
        });
    }

    /**
     * 扫描手机里所有应用程序的缓存信息
     * 安卓源码中是通过pm.getPackageSizeInfo()方法得到缓存大小的,但是这个方法被隐藏了,
     * 我们不能直接调用这个方法,所以只能通过反射了
     * 需要的权限:<uses-permission android:name="android.permission.GET_PACKAGE_SIZE"/>
     */
    private void scanCache() {
        pm = getPackageManager();
        new Thread(){
            public void run() {
                Method getPackageSizeInfoMethod = null ;
                //获得这个类的所有方法
                Method[] methods = PackageManager.class.getMethods();
                for (Method method : methods) {
                    //getPackageSizeInfo这个方法可以获取缓存大小(谷歌将这个方法隐藏了,不能直接调用)
                    if("getPackageSizeInfo".equals(method.getName())){
                        getPackageSizeInfoMethod=method;//得到这个方法
                    }
                }
                //获取所有程序的包名
                List<PackageInfo> packInfos = pm.getInstalledPackages(0);
                for (PackageInfo info : packInfos) {
                    try {
                        //使用通过反射得到的方法来获取缓存信息. 会执行MyDataObserver类中的onGetStatsCompleted方法
                        getPackageSizeInfoMethod.invoke(pm, info.packageName,new MyDataObserver());
                        Thread.sleep(300);//稍微睡下,这样才能看到界面上显示的扫描信息
                        
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            };
        }.start();
    }

    /**
     * 继承远程接口
     */
    private class MyDataObserver extends IPackageStatsObserver.Stub{

        @Override
        public void onGetStatsCompleted(PackageStats pStats, boolean succeeded)
                throws RemoteException {
            try {
                //注意:该方法是在子线程中运行的
                final long cache = pStats.cacheSize;//缓存的大小
                long code = pStats.codeSize;//该软件代码的大小
                long data = pStats.dataSize;//数据的大小
                String packname = pStats.packageName;//程序包名
                final ApplicationInfo appInfo = pm.getApplicationInfo(packname, 0);
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        String appName = appInfo.loadLabel(pm).toString();//通过包名获取软件名
                        MainActivity.this.appInfo.setText("正在扫描:"+appName);
                        if(cache > 0){//有些软件是没有缓存的
                            /* 
                                  这里可以在布局文件中创建一个布局,然后在这里创建一个布局(程序名,缓存大小,清除按钮)添加到这个布局中,
                              ....让需要清理缓存的程序和缓存的大小显示出来,并为按钮设置单击事件
                             */
                            System.out.println(appName+"缓存的大小:"+Formatter.formatFileSize(getApplicationContext(), cache));
                            //清理缓存,这里我直接清理所有缓存,其实可以在上面添加的布局中为清除按钮设置单击事件,在单击事件中进行清除缓存
                            //clearCache(appInfo.packageName);
                        }
                    }
                });
            } catch (NameNotFoundException e) {
                e.printStackTrace();
            }
        }
    }
    
    /**
     * 通过包名清除缓存,清除缓存也需要利用反射才能得到清除缓存的方法
     * 需要的权限:<uses-permission android:name="android.permission.DELETE_CACHE_FILES"/>
     * 但是很可惜,这个权限只有系统的app才能注册,所以不能直接去清除缓存了,如果想点击按钮单独清理某个程序的缓存,
     * 就只能跳转到系统清理软件缓存的页面了.
     * 但是,我们可以实现一键全部清除,这个也是利用安卓系统的bug,我们发送一个广播告诉安卓系统,我们需要200G的内存,
     * 安卓系统收到通知,发现现在可用的内存空间不足,所以就会去清除所有的缓存了,这样也就达到了我们的目的
     */
    private void clearCache(String packageName) {
        try {
            //通过反射指定方法名和参数得到清理缓存的方法
            Method method = PackageManager.class.getMethod("deleteApplicationCacheFiles", 
                    String.class,IPackageDataObserver.class);
            //执行方法清除缓存,清楚后会执行MypackDataObserver类里的onRemoveCompleted方法
            method.invoke(pm, packageName,new MypackDataObserver());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    
    /**
     * 清除全部缓存,需要的权限:
     * <uses-permission android:name="android.permission.CLEAR_APP_CACHE"/>
     */
    public void clearAllCache(){
        try {
            Method[] methods = PackageManager.class.getMethods();
            for (Method method : methods) {
                if("freeStorageAndNotify".equals(method.getName())){
                    //告诉系统,我需要Integer.MAX_VALUE这么大的内存空间,系统现在可用空间小于这么多就会去清除缓存
                    method.invoke(pm, Long.MAX_VALUE,new MypackDataObserver());
                    return;
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    
    /**
     * 清除缓存的远程接口服务类
     */
    private class MypackDataObserver extends IPackageDataObserver.Stub{

        @Override
        public void onRemoveCompleted(String packageName, boolean succeeded)
                throws RemoteException {
                //String appName = pm.getApplicationInfo(packageName, 0).loadLabel(pm).toString();
                if(succeeded){
                    System.out.println(packageName+"的缓存清除成功");
                }else{
                    System.out.println(packageName+"的缓存清除失败");
                }
        }
    }
    
}

可以只有清除缓存,利用代码跳转到这个界面

效果图:

 

posted @ 2016-09-09 18:18  ts-android  阅读(1622)  评论(0编辑  收藏  举报