如何實現手機緩存清理的功能

android手機上的殺毒軟件或者是手機管家等類似應用都會有垃圾清理的功能, 那麼清理的緩存文件是什麼? 怎麼去找出應用的緩存文件並將他們清理, 在這裏我將介紹如何實現手機垃圾文件清理的功能。

  1. 緩存文件是什麼,緩存文件的目錄在那裏?
    我們知道方法:
    getFilesDir() 的路徑是 /data/data/包名/files/
    getCacheDir()的路徑是 /data/data/包名/cache/
    緩存文件路徑就是getCacheDir()的路徑,在此路徑下的文件爲緩存文件,當系統內存不夠,系統會去釋放這個路徑下的文件。

先看一下緩存清理demo的結果:

2 界面編寫
(1)主界面包括一個title, 一個進度條,一個線性佈局保存掃描結果(也可以是listview這裏爲了簡便), 一個清理按鈕
佈局代碼:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#999999"
    android:orientation="vertical"
    tools:context=".MainActivity" >

    <TextView
        android:layout_width="match_parent"
        android:layout_height="40dp"
        android:gravity="center"
        android:text="緩存清理"
        android:textColor="#ffffff"
        android:textSize="22sp" />


    <FrameLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content" >

        <ProgressBar
            android:id="@+id/pb"
            style="@android:style/Widget.ProgressBar.Horizontal"
            android:layout_width="match_parent"
            android:layout_height="wrap_content" />

        <TextView
            android:id="@+id/scan_state"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="掃描狀態"
            android:textColor="#ffffff" />
    </FrameLayout>

    <LinearLayout
        android:layout_weight="1000"
        android:id="@+id/ll_container"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_marginTop="5dp"
        android:orientation="vertical" >
    </LinearLayout>
    <Button 
        android:id="@+id/clear_caches"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:visibility="gone"
        android:text="一鍵清除"/>
</LinearLayout>

(2)掃描到的緩存item佈局文件:
包含兩個textview展示應用名稱與緩存大小,一個view作爲分割線

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content" >

    <TextView
        android:id="@+id/app_name"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textColor="#ffffff"
        android:textSize="20sp" />

    <TextView
        android:id="@+id/cache_size"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@id/app_name"
        android:textColor="#ffffff"
        android:textSize="16sp" />

    <View
        android:layout_width="match_parent"
        android:layout_height="1dp"
        android:layout_below="@id/cache_size"
        android:background="#88000000" />

</RelativeLayout>

3 功能邏輯實現代碼

public class MainActivity extends Activity implements OnClickListener{

    private ProgressBar pb; //掃描進度條
    private TextView scan_state; //顯示掃描的狀態
    private LinearLayout ll_container; //保存顯示掃描結果
    private PackageManager pm;   //包管理
    private Button cleanCaches;  //清理手機緩存

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        pb = (ProgressBar) findViewById(R.id.pb);
        scan_state = (TextView) findViewById(R.id.scan_state);
        ll_container = (LinearLayout) findViewById(R.id.ll_container);
        cleanCaches = (Button) findViewById(R.id.clear_caches);
        cleanCaches.setOnClickListener(this);
        scanCaches();
    }

    //掃描手機裏面所有的應用程序的緩存
    private void scanCaches() {
        pm = getPackageManager();
        //因爲掃描緩存需要時間,開啓線程來掃描
        new Thread(){
            public void run() {
                Method getPackageSizeInfoMethod = null;
                //getPackageSizeInfo方法在sdk中隱藏了,使用反射機制來獲取此方法
                //1.先獲取PackageManager提供的所有方法
                Method[] methods = PackageManager.class.getMethods();
                //2.遍歷所有方法找到getPackageSizeInfo方法
                for (Method method : methods) {
                    if("getPackageSizeInfo".equals(method.getName())) {
                        getPackageSizeInfoMethod = method;
                        break;
                    }
                }
                //獲取所有安裝的包信息
                List<PackageInfo> packageInfos = pm.getInstalledPackages(0);
                //設置進度條大小
                pb.setMax(packageInfos.size());
                int progress = 0;
                //遍歷所有包,獲取緩存信息
                for (PackageInfo info : packageInfos) {
                    try {
                        getPackageSizeInfoMethod.invoke(pm, info.packageName,
                            new MyDataObserver());
                        //防止掃描過快
                        Thread.sleep(100);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                    progress++;
                    pb.setProgress(progress);
                }
                //掃描完畢,更新顯示文本內容
                runOnUiThread(new Runnable() {

                    @Override
                    public void run() {
                        scan_state.setText("掃描完畢...");
                        //掃描完成,顯示清理按鈕
                        cleanCaches.setVisibility(View.VISIBLE);
                    }
                });
            };
        }.start();
    }

    private class MyDataObserver extends IPackageStatsObserver.Stub {

        @Override
        public void onGetStatsCompleted(PackageStats pStats, boolean succeeded)
                throws RemoteException {
            final long cache = pStats.cacheSize;
            final String packname = pStats.packageName;
            final ApplicationInfo appInfo;
            try {
                appInfo = pm.getApplicationInfo(packname, 0);

                runOnUiThread(new Runnable() {

                    @Override
                    public void run() {
                        scan_state.setText("正在掃描:" + appInfo.loadLabel(pm));
                        if (cache > 0) {
                            View view = View.inflate(getApplicationContext(), R.layout.cache_info_layout, null);
                            TextView tv_cache = (TextView) view.findViewById(R.id.cache_size);
                            tv_cache.setText("緩存大小:" + Formatter.formatFileSize(getApplicationContext(), cache));
                            TextView tv_name = (TextView) view.findViewById(R.id.app_name);
                            tv_name.setText(appInfo.loadLabel(pm));
                            ll_container.addView(view, 0);
                        }
                    }
                });
            } catch (Exception e) {
                e.printStackTrace();
            }

        }

    }

    private class MyPackDataObserver extends IPackageDataObserver.Stub {

        @Override
        public void onRemoveCompleted(String packageName, boolean succeeded)
                throws RemoteException {
            runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    Toast.makeText(getApplicationContext(), "緩存刪除成功", Toast.LENGTH_SHORT).show();
                    ll_container.removeAllViews();
                    scan_state.setText("緩存清理成功");
                }
            });
        }

    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
        case R.id.clear_caches:
            CleanAllCache();
            break;
        default:
            break;
        }
    }

    //清除所有緩存
    private void CleanAllCache() {
        Method[] methods = PackageManager.class.getMethods();
        for (Method method : methods) {
            if("freeStorageAndNotify".equals(method.getName())) {
                    try {
                        method.invoke(pm, Integer.MAX_VALUE, new MyPackDataObserver());
                    } catch (InvocationTargetException e) {
                        // TODO Auto-generated catch block
                        e.getCause().printStackTrace();
                    } catch (Exception e) {
                        e.printStackTrace();
                    }

                return;
            }
        }
    }

}

代碼中的註釋已經很詳細,但是這裏需要說明一下,關於freeStorageAndNotify等方法的獲得是使用反射機制,因爲這些方法在PackageManager中是隱藏的,我們通過獲取所有的method然後通過名字匹配,獲得方法再通過invoke方法去使用獲得的方法。具體關於獲得所有應用的緩存與清理緩存的步驟方法可以參考android系統源碼中的setting模塊。
當你copy上面代碼到你的工程中,肯定會報錯,因爲IPackageStatsObserver.Stub和IPackageDataObserver.Stub找不到,因爲這兩個類是aidl,因爲讀取與清理緩存需要讀取其他應用程序,需要進程間數據通信。
你需要下載IPackageDataObserver.aidl, IPackageStatsObserver.aidl和PackageStats.aidl這三個文件(可以下載demo的源碼,源碼中包含這三個文件),在你的工程中創建一個名爲android.content.pm的package,將這三個文件copy到這個包下,如圖:

然後它會自動在gen目錄下生成對應的java文件:

這個時候上面的代碼就不會出現錯誤了,關於aidl這裏不去介紹,不懂大家可以google一下。

注意: 應用程序需要下面兩個權限:

    <uses-permission android:name="android.permission.GET_PACKAGE_SIZE"/>
    <uses-permission android:name="android.permission.CLEAR_APP_CACHE"/>

將其加到清單文件AndroidManifest.xml中。

4 關於緩存清理的功能原理
緩存清理利用了android的一個bug, 調用freeStorageAndNotify的方法,向系統請求說需要一塊很大的內存這裏傳入的值是Integer.MAX_VALUE, 系統內存不夠會去自動去將應用的緩存清理來滿足內存的請求, 這樣就實現了緩存清理的功能。
至此實現了手機緩存清理的功能,如有任何疑問可以留言。
源碼下載地址:
http://download.csdn.net/detail/lbcab/9517254

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章