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

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

手机卫士-11

發(fā)布時間:2024/1/1 编程问答 32 豆豆
生活随笔 收集整理的這篇文章主要介紹了 手机卫士-11 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

手機衛(wèi)士-11

課1

看門狗WatchDogService程序優(yōu)化

程序鎖不斷打開關(guān)閉打開關(guān)閉,有時還是有界面沒及時切換過來 有一瞬間還看見程序的界面,隱私還是保護得不夠好 原因是看門狗里WatchDogService.java里死循環(huán),整個死循環(huán)的周期有一定的事件,所以會產(chǎn)生多次打開程序鎖而界面沒切換過來 那是因為應(yīng)用程序還不夠優(yōu)化

//該標志符用來控制是否不斷刷新 flag = true; new Thread() {//其實該service所做的事件就是創(chuàng)建一個死循環(huán),不斷查看tempStopPacknames集合里的數(shù)據(jù)和新打開的app,從而進行比較操作public void run() {//如果為true就進入死循環(huán)while (flag) {// 獲取用戶的正在運行的應(yīng)用程序任務(wù)棧的列表,最近使用的在集合的最前面List<RunningTaskInfo> taskinfos = am.getRunningTasks(100);String packname = taskinfos.get(0).topActivity.getPackageName();System.out.println(packname);//對比獲得的最新加載的app的名字是否存在于加鎖app數(shù)據(jù)庫中:當在加鎖數(shù)據(jù)庫中找到該packname,那么再比較該packname的app有沒有通過密碼校驗if (dao.find(packname)) {//有通過密碼校驗// 檢查主人是否發(fā)過臨時停止保護的指令if (tempStopPacknames.contains(packname)) {// 什么事情都不做,即不再彈出程序鎖界面//沒有通過密碼校驗} else {// 這個應(yīng)用程序需要被鎖定// 跳出來看門狗,讓用戶輸入密碼。Intent intent = new Intent(WatchDogService.this,WatchDogEnterPwdActivity.class);//給該WatchDogEnterPwdActivity.class設(shè)置一個開啟任務(wù)的模式intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);//用意圖開啟界面,并加上應(yīng)用的信息intent.putExtra("packname", packname);startActivity(intent);}}try {Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}}}; }.start();

我們給WatchDogService.java里死循環(huán)里加個計算器來計算循環(huán)一個的時間。


這是在效率比較高的模擬器結(jié)果 換成一個arm的模擬器來部署一下應(yīng)用


看看其效率如何


  • 如何優(yōu)化?

處理細節(jié): 在查詢數(shù)據(jù)庫的過程中不斷打開數(shù)據(jù)庫然后close,效率低,我們改造下ApplockDao.java,

ApplockDao.java

/*** 查詢?nèi)康囊i定的應(yīng)用程序* @return 全部鎖定應(yīng)用程序包名的集合*/ public List<String> findAll(){List<String> packnames = new ArrayList<String>();SQLiteDatabase db = helper.getWritableDatabase();Cursor cursor = db.query("lockinfo", null, null, null, null, null, null);while(cursor.moveToNext()){String packname = cursor.getString(cursor.getColumnIndex("packname"));packnames.add(packname);}cursor.close();db.close();//返回一個叫packnames的集合return packnames; }

把所有的數(shù)據(jù)一次取出來,放在內(nèi)存中,然后讓看門狗去查看內(nèi)存,這樣效率大大改善。 經(jīng)過以上的改善,

得到如下的改善?

但是這樣的死循環(huán)非常耗電,占據(jù)的cpu資源大多,怎么解決?

搜搜地圖(相當費電,后來版本來個重大改變后,在鎖屏后gps的定位會占時關(guān)閉掉,這樣的改變可可以省不少的電量)--->騰訊地圖

需求:那么我們可以考慮,我們能否鎖屏后我們的看門狗是否可以停掉,打開鎖后看門狗會打開。

把看門狗中死循環(huán)的代碼抽取到子線程中去跑。 把線程封裝成一個方法

startDogThread();

然后讓死循環(huán)加入一個成員變量標記flag,在自定義廣播接受者出接收開關(guān)屏幕的廣播事件處理

else if (Intent.ACTION_SCREEN_OFF.equals(action)) {tempStopPacknames.clear();// 臨時停止看門狗flag = false; } else if (Intent.ACTION_SCREEN_ON.equals(action)) {// 開啟看門狗if (!flag) {startDogThread();} }receiver = new InnerReceiver(); IntentFilter filter = new IntentFilter(); filter.addAction("com.itheima.doggoaway"); filter.addAction(Intent.ACTION_SCREEN_OFF); filter.addAction(Intent.ACTION_SCREEN_ON); registerReceiver(receiver, filter);

這樣又大大減低了電量的消耗

優(yōu)化程序后的小bug,上鎖效果消失了?

在提高看門狗效率的同時還有bug:我們改造了ApplockDao的findAll時,這時看門狗只看集合內(nèi)存里保存的應(yīng)用集合,這樣如果當操作一個應(yīng)用上鎖之后,再去鎖上別的應(yīng)用時這個后被鎖上的app的上鎖效果就失效了,因為之前看門狗里的死循環(huán)是檢查private List tempStopPacknames;集合,現(xiàn)在是看新定義的private List lockPacknames;集合,以實現(xiàn)緩存的效果,當在點擊listview里的加鎖和解鎖的按鈕時,它們操作的是數(shù)據(jù)庫dao的add和delete的方法,而方法是繼續(xù)調(diào)用數(shù)據(jù)庫的操作,看門狗根本不知道數(shù)據(jù)庫也發(fā)生了變化,那么如果我們怎么解決?

改造ApplockDao的add和delete,發(fā)送自定義廣播,當我們進行上鎖的動畫操作時,會對數(shù)據(jù)庫進行操作,那么我們讓ApplockDao的add和delete加上自定義廣播,讓看門狗接收這自定義的廣播,這就可以讓看門狗知道需要對數(shù)據(jù)庫進行增刪的操作了。在看門狗里這樣接收廣播

ApplockDao.java

/*** 添加一條鎖定的應(yīng)用程序* @param packname 包名*/ public void add(String packname){SQLiteDatabase db = helper.getWritableDatabase();ContentValues values = new ContentValues();values.put("packname", packname);db.insert("lockinfo", null, values);db.close();//通知看門狗更新鎖定的應(yīng)用程序集合Intent intent = new Intent();intent.setAction("com.itheima.mobilesafe.notifydogadd");intent.putExtra("packname", packname);context.sendBroadcast(intent); }/*** 刪除一條鎖定的應(yīng)用程序* @param packname 包名*/ public void delete(String packname){SQLiteDatabase db = helper.getWritableDatabase();db.delete("lockinfo", "packname=?", new String[]{packname});db.close();//通知看門狗更新鎖定的包名集合Intent intent = new Intent();intent.setAction("com.itheima.mobilesafe.notifydogdelete");intent.putExtra("packname", packname);context.sendBroadcast(intent); }

WatchDogService.java

else if("com.itheima.mobilesafe.notifydogadd".equals(action)){lockPacknames.add(intent.getStringExtra("packname")); }else if("com.itheima.mobilesafe.notifydogdelete".equals(action)){lockPacknames.remove(intent.getStringExtra("packname")); }receiver = new InnerReceiver(); IntentFilter filter = new IntentFilter(); filter.addAction("com.itheima.mobilesafe.notifydogdelete"); filter.addAction("com.itheima.mobilesafe.notifydogadd"); registerReceiver(receiver, filter);

總結(jié):廣播很靈活,這里使用了自定義廣播讓不同應(yīng)用或組件之間發(fā)送消息接收消息實現(xiàn)通信!!! 改善后的看門狗WatchDogService和 ApplockDao的完整邏輯

WatchDogService.java

/*** 監(jiān)視當前用戶操作的應(yīng)用程序,如果這個應(yīng)用程序需要保護,看門狗就蹦出來,讓用戶輸入密碼* //其實該service所做的事件就是創(chuàng)建一個死循環(huán),不斷查看tempStopPacknames集合里的數(shù)據(jù)和新打開的app,從而進行比較操作* @author Administrator* */ public class WatchDogService extends Service {private Intent intent;private String packname;//應(yīng)用管理器private ActivityManager am;private boolean flag;// 程序鎖的數(shù)據(jù)庫daoprivate ApplockDao dao;//內(nèi)部廣播類private InnerReceiver receiver;/*** 臨時停止保護的包名集合*/private List<String> tempStopPacknames;private List<String> lockPacknames;@Overridepublic IBinder onBind(Intent intent) {return null;}//內(nèi)部廣播類:該廣播類主要是操作tempStopPacknames集合,即操作從程序鎖界面?zhèn)鱽淼膽?yīng)用的名字和操作鎖屏的狀態(tài)中//集合的清空,把集合提供給service去控制應(yīng)用的加鎖效果//專門獲取程序界面中傳來的廣播信號,通知WatchDogService去解除程序鎖界面private class InnerReceiver extends BroadcastReceiver{@Overridepublic void onReceive(Context context, Intent intent) {//廣播一直接收所有的頻道信息String action = intent.getAction();//獲取WatchDogEnterPwdActivity傳來的StringExtraif("com.itheima.doggoaway".equals(action)){//StringExtra的keyString packname = intent.getStringExtra("packname");//看門狗把主人要求臨時停止保護的包名給記錄下來,即把WatchDogEnterPwdActicity傳來的//StringExtra:packname,把其app加入臨時停止保護的集合中tempStopPacknames.add(packname);}else if(Intent.ACTION_SCREEN_OFF.equals(action)){//當接收的廣播頻道是鎖屏,就把臨時停止保護的應(yīng)用集合清零,即讓下次打開要保護的應(yīng)用再次輸入密碼tempStopPacknames.clear();//停止看門狗flag = false;}else if(Intent.ACTION_SCREEN_ON.equals(action)){//當接收的廣播頻道是打開屏幕,if(!flag){startDogThread();}}else if("com.itheima.phonesafeguard.notifydogadd".endsWith(action)){lockPacknames.add(intent.getStringExtra("packname"));}else if("com.itheima.phonesafeguard.notifydogdelete".equals(action)){lockPacknames.remove(intent.getStringExtra("packname"));}}}@Overridepublic void onCreate() {//初始化臨時取消保護的app的集合tempStopPacknames = new ArrayList<String>();//獲取操作加鎖app的數(shù)據(jù)庫的接口dao = new ApplockDao(this);//一旦服務(wù)開啟集合的內(nèi)容就不會再發(fā)生變化lockPacknames = dao.findAll();intent = new Intent(WatchDogService.this,WatchDogEnterPwdActivity.class);//給該WatchDogEnterPwdActivity.class設(shè)置一個開啟任務(wù)的模式intent.setFlags(intent.FLAG_ACTIVITY_NEW_TASK);receiver = new InnerReceiver();IntentFilter filter = new IntentFilter();filter.addAction("com.itheima.doggoaway");filter.addAction(Intent.ACTION_SCREEN_OFF);filter.addAction(Intent.ACTION_SCREEN_ON);filter.addAction("com.itheima.phonesafeguard.notifydogadd");filter.addAction("com.itheima.phonesafeguard.notifydogdelete");registerReceiver(receiver, filter);am = (ActivityManager) getSystemService(ACTIVITY_SERVICE);startDogThread();super.onCreate();}private void startDogThread(){flag = true;new Thread(){//其實該service所做的事件就是創(chuàng)建一個死循環(huán),不斷查看//tempStopPacknames集合里的數(shù)據(jù)和新打開的app,從而進行比較操作public void run() {//如果為true就進入死循環(huán)while(flag){//獲取用戶的正在運行的應(yīng)用程序任務(wù)棧的列表,最近使用的在集合的最前面List<RunningTaskInfo> taskinfos = am.getRunningTasks(1);packname = taskinfos.get(0).topActivity.getPackageName();System.out.println(packname);//對比獲得的最新加載的app的名字是否存在于加鎖app數(shù)據(jù)庫中;//當在加鎖數(shù)據(jù)庫中找到該packname,那么再比較該packname的app有沒有通過密碼校驗 // if(dao.find(packname)){if(lockPacknames.contains(packname)){// 查詢內(nèi)存//有通過密碼校驗//檢查主人是否發(fā)過臨時停止保護的指令if(tempStopPacknames.contains(packname)){//什么事情都不做,即不在彈出程序鎖界面//沒有通過密碼校驗}else{//用意圖開啟界面,并加上應(yīng)用的消息intent.putExtra("packname", packname);startActivity(intent);}}try {Thread.sleep(30);} catch (InterruptedException e) {e.printStackTrace();}}};}.start();}@Overridepublic void onDestroy() {flag = false;//動態(tài)注銷廣播unregisterReceiver(receiver);receiver = null;super.onDestroy();}}

ApplockDao.java

/*** 程序鎖增刪改查的業(yè)務(wù)類**/ public class ApplockDao {private ApplockDBHelper helper ;private Context context;public ApplockDao(Context context){helper = new ApplockDBHelper(context);this.context = context;}/*** 添加一條鎖定的應(yīng)用程序* @param packname 包名*/public void add(String packname){SQLiteDatabase db = helper.getWritableDatabase();ContentValues values = new ContentValues();values.put("packname", packname);db.insert("lockinfo", null, values);db.close();//通知看門狗更新鎖定的應(yīng)用程序集合Intent intent = new Intent();intent.setAction("com.itheima.phonesafeguard.notifydogadd");intent.putExtra("packname", packname);context.sendBroadcast(intent);}/*** 刪除一條鎖定的應(yīng)用程序* @param packname 包名*/public void delete(String packname){SQLiteDatabase db = helper.getWritableDatabase();db.delete("lockinfo", "packname=?", new String[]{packname});db.close();//通知看門狗更新鎖定的包名集合Intent intent = new Intent();intent.setAction("com.itheima.phonesafeguard.notifydogdelete");intent.putExtra("packname", packname);context.sendBroadcast(intent);}/*** 查詢一條鎖定的應(yīng)用程序* @param packname 包名* @return true 被鎖定false 沒有鎖定*/public boolean find(String packname){boolean result = false;SQLiteDatabase db = helper.getWritableDatabase();Cursor cursor = db.query("lockinfo", null, "packname=?", new String[]{packname}, null, null, null);if(cursor.moveToNext()){result = true;}cursor.close();db.close();return result;}/*** 查詢?nèi)康囊i定的應(yīng)用程序* @return 全部鎖定應(yīng)用程序包名的集合*/public List<String> findAll() {List<String> packnames = new ArrayList<String>();SQLiteDatabase db = helper.getWritableDatabase();Cursor cursor = db.query("lockinfo", null, null , null, null, null, null); while(cursor.moveToNext()){String packname = cursor.getString(cursor.getColumnIndex("packname"));packnames.add(packname);}cursor.close();db.close();//返回一個叫packnames的集合return packnames;} }

課2

Activity的啟動模式重溫

  • standard 標準啟動模式

  • singletop 單一頂部模式

    如果棧頂已經(jīng)存在了activity,就不會重復(fù)的創(chuàng)建,而是復(fù)用棧頂已經(jīng)存在的activity(瀏覽器的書簽)

  • singletask 單一任務(wù)棧模式

    在任務(wù)棧里面只能存在一個實例,這個實例是存在在手機衛(wèi)士默認的任務(wù)棧里面的

  • singleinstance 單例模式

    在任務(wù)棧里面只能存在一個實例,這個實例是在自己單獨新建的任務(wù)棧里面 6

  • 修改成7 其實這些啟動模式都是為了解決用戶的體驗感受的 程序鎖開發(fā)結(jié)束

    流量統(tǒng)計

    開始流量統(tǒng)計模塊 新建TrafficManagerActivity.java并配置清單文件

    TrafficManagerActivity.java

    public class TrafficManagerActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);<!-- 流量統(tǒng)計 --> <activity android:name="com.itheima.mobile47.TrafficManagerActivity"></activity>

    MainActivity加入節(jié)點case MainActivity.java

    case 4://流量管理intent = new Intent(MainActivity.this,TrafficManagerActivity.class);startActivity(intent);break;

    安卓下如何獲取系統(tǒng)的流量信息?

    類比:Windows-->網(wǎng)絡(luò)連接-->本地連接(網(wǎng)卡信息)可以統(tǒng)計出流量信息 安卓:TrafficStats類

    Class that provides network traffic statistics. These statistics include bytes transmitted and received and network packets transmitted and received, over all interfaces, over the mobile interface, and on a per-UID basis. TrafficStats類的重要api

    //獲取手機所有的網(wǎng)絡(luò)端口 , wifi , 2g/3g/4g(手機卡產(chǎn)生的流量) TrafficStats.getTotalRxBytes(); //r -->receive接收,獲取全部的接受的byte (下載) TrafficStats.getTotalTxBytes(); //t -->translate發(fā)送,獲取全部的發(fā)送的byte (上傳)TrafficStats.getMobileRxBytes();// 手機卡下載產(chǎn)生的流量 從開機開始到現(xiàn)在產(chǎn)出的流量 TrafficStats.getMobileTxBytes();// 手機卡上傳產(chǎn)生的流量


    流量管理app由于TrafficStats類api的特點:

    TrafficStats.getMobileRxBytes();// 手機卡下載產(chǎn)生的流量 從開機開始到現(xiàn)在產(chǎn)出的流量?
    一般每5分鐘計算一次流量,一天下來就相加,來計算一個總的流量消耗量。

    其實流量管理的app的程序邏輯無非是計時器和數(shù)據(jù)庫的操作

    該方法就是獲取某個應(yīng)用程序的流量,每個應(yīng)用程序都有一個對應(yīng)的uid用戶id,就因為有了內(nèi)核uid,每個程序就有了不同的權(quán)限,換句話說,每個應(yīng)用程序都有一個uid。

    //在Android系統(tǒng)里面 ,給每一個應(yīng)用程序都分配了一個用戶id //pid:進程id uid:用戶id TrafficStats.getUidRxBytes(10014); TrafficStats.getUidTxBytes(10014);


    獲得3g/wifi產(chǎn)生了多少的流量

    //分別列出來3g/wifi產(chǎn)生了多少的流量 //計時器。 ConnectivityManager cm = (ConnectivityManager) getSystemService(CONNECTIVITY_SERVICE); cm.getActiveNetworkInfo().getType();

    //自動校驗流量 , 偷偷后臺給運營商發(fā)送短信。 //10010 10086 llcx

    //聯(lián)網(wǎng)禁用 //linux平臺的防火墻軟件。必須手機要有root權(quán)限,修改iptable

    實現(xiàn)騰訊的抽屜效果


    TrafficManagerActivity.java的布局 activitytrafficmanager.xml 抽屜控件 要使用必須給該控件定義一個id TrafficManagerActivity.java

    <SlidingDrawerandroid:orientation="horizontal"android:id="@+id/my_drawer"android:layout_width="match_parent"android:layout_height="match_parent"></SlidingDrawer>

    但是其實一個也不夠(設(shè)計界面報錯),還要定義很多個id:給把手handle也定義一個id,還有content內(nèi)容也要定義一個id(還報錯),

    <SlidingDrawerandroid:orientation="horizontal"android:id="@+id/my_drawer"android:layout_width="match_parent"android:layout_height="match_parent"android:content="@+id/my_content"android:handle="@+id/my_handle"> </SlidingDrawer>

    還需定義一個ImageView,然后imageview的id要引用SlidingDrawer的handle的id,

    <SlidingDrawerandroid:orientation="horizontal"android:id="@+id/my_drawer"android:layout_width="match_parent"android:layout_height="match_parent"android:content="@+id/my_content"android:handle="@+id/my_handle" ><ImageViewandroid:id="@id/my_handle"android:layout_width="wrap_content"android:layout_height="wrap_content"android:src="@drawable/ic_launcher" /></SlidingDrawer>

    還有LinearLayout里要引用SlidingDrawer的content的id,

    <SlidingDrawerandroid:orientation="horizontal"android:id="@+id/my_drawer"android:layout_width="match_parent"android:layout_height="match_parent"android:content="@+id/my_content"android:handle="@+id/my_handle" ><ImageViewandroid:id="@id/my_handle"android:layout_width="wrap_content"android:layout_height="wrap_content"android:src="@drawable/ic_launcher" /><LinearLayoutandroid:background="#22000000"android:id="@id/my_content"android:layout_width="match_parent"android:layout_height="match_parent"android:gravity="center"android:orientation="vertical" ><TextView android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="我是抽屜里面的內(nèi)容,哈哈哈"/></LinearLayout> </SlidingDrawer>


    注意要把該布局文件復(fù)制過來! 這是從下往上拉 我們可以改變里orientation的屬性去改變拉的方向

    課3

    實現(xiàn)金山手機衛(wèi)士小功能:常用手機號碼的效果

    金山手機衛(wèi)士小功能:常用手機號碼:


    (listview嵌套這listview) 把金山apk里的數(shù)據(jù)庫文件拷貝過來?

    觀察其數(shù)據(jù)庫 把數(shù)據(jù)庫信息展現(xiàn)到我們想實現(xiàn)的效果里 新建CommonNumberActivity.java和配置清單文件

    CommonNumberActivity.java

    public class CommonNumberActivity extends Activity {

    private static final String path = "/data/data/com.itheima.mobile47/files/commonnum.db"; private ExpandableListView elv; private SQLiteDatabase db; @Override protected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_common_num);

    清單文件

    <!-- 常用號碼查詢 --> <activity android:name="com.itheima.mobile47.CommonNumberActivity"/>

    布局怎么寫呢? activitycommonnum.xml

    <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" ><TextViewstyle="@style/textview_title_style"android:text="常用號碼查詢" /><ExpandableListViewandroid:id="@+id/elv"android:layout_width="match_parent"android:layout_height="match_parent" ></ExpandableListView></LinearLayout>

    使用ExpandableListView控件,這樣的控件可以實現(xiàn)我們想要的效果 在CommonNumberActivity.java加載ExpandableListView控件。 ExpandableListView控件是Listview的子類。 在實現(xiàn)該控件的adapter的實現(xiàn)類時,如圖


    這樣實現(xiàn)了接口中太多的方法,換成繼承BaseExpandableListAdapter 這靠譜 里面有方法我們需要的不多, 寫個界面加入到高級工具里 activity_tools.xml+ToolsActivity.java修改,進入到CommonNumberActivity.java

    的效果如下



    把數(shù)據(jù)庫的內(nèi)容加載到ExpandableListView控件里

    先把數(shù)據(jù)庫放到assets里 我們改造SplashActivcity-->copyDB--->data\data目錄下 SplashActivcity.java

    try {// 拷貝數(shù)據(jù)庫到data/data目錄下面copyDB("address.db");copyDB("commonnum.db");

    新建CommonNumberDao.java:獲取到數(shù)據(jù)庫的路徑,然后就是操作數(shù)據(jù)庫獲取數(shù)據(jù)庫的信息

    CommonNumberDao.java

    /*** 獲取數(shù)據(jù)庫里面一共有多少個分組* @return*/ public static int getGroupCount(SQLiteDatabase db){Cursor cursor = db.rawQuery("select count(*) from classlist", null);cursor.moveToNext();int groupcount = cursor.getInt(0);cursor.close();return groupcount; }

    CommonNumberDao.java:

    1、獲取數(shù)據(jù)庫里面一共有多少個分組 2、獲取數(shù)據(jù)庫里面某個分組有多少個孩子 3、獲取數(shù)據(jù)庫里面某個分組的名字 4、獲取數(shù)據(jù)庫里面某個分組里面某個孩子的名字和電話

    改造完CommonNumberDao.java后,回到CommonNumberActivity.java里的adapter進行改造。

    CommonNumberActivity.java

    //BaseXXX SimpleXXX DefaultXXX BaiscXXXX private class CommonNumAdapter extends BaseExpandableListAdapter{//獲取分組的數(shù)量@Overridepublic int getGroupCount() {return CommonNumberDao.getGroupCount(db);}//獲取孩子的數(shù)量@Overridepublic int getChildrenCount(int groupPosition) {return CommonNumberDao.getChildrenCount(db,groupPosition);}@Overridepublic Object getGroup(int groupPosition) {return null;}@Overridepublic Object getChild(int groupPosition, int childPosition) {return null;}@Overridepublic long getGroupId(int groupPosition) {return 0;}@Overridepublic long getChildId(int groupPosition, int childPosition) {return 0;}@Overridepublic boolean hasStableIds() {return false;}//獲取分組顯示的view@Overridepublic View getGroupView(int groupPosition, boolean isExpanded,View convertView, ViewGroup parent) {TextView tv;if(convertView!=null && convertView instanceof TextView){tv = (TextView) convertView;}else{tv= new TextView(getApplicationContext());}tv.setTextSize(20);tv.setTextColor(Color.RED);tv.setText(" "+CommonNumberDao.getGroupNameByPosition(db,groupPosition));return tv;}//獲取某個位置的孩子的view對象@Overridepublic View getChildView(int groupPosition, int childPosition,boolean isLastChild, View convertView, ViewGroup parent) {TextView tv;if(convertView!=null && convertView instanceof TextView){tv = (TextView) convertView;}else{tv= new TextView(getApplicationContext());}tv.setTextSize(16);tv.setTextColor(Color.BLACK);String info = CommonNumberDao.getChildInfoByPosition(db,groupPosition, childPosition);tv.setText(info.split("#")[0]+"\n"+info.split("#")[1]);return tv;}//指定孩子可以被點擊@Overridepublic boolean isChildSelectable(int groupPosition, int childPosition) {return true;}}

    運行:


    但是不斷滑動時的最后app還是掛了,報出了內(nèi)存溢出 改善程序:?

    view復(fù)用 還有bug:展開所有,然后不斷拖動,也不會掛掉。但是如果拖動個幾千次上萬次,有可能數(shù)據(jù)打不開了,原因也是程序不斷打開關(guān)閉打開關(guān)閉的操作也讓數(shù)據(jù)庫最后垮了,

    改造:數(shù)據(jù)庫改成在onCreate里打開,

    @Override protected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_common_num);db = SQLiteDatabase.openDatabase(path, null, SQLiteDatabase.OPEN_READONLY);

    然后在onDestroy里關(guān)閉數(shù)據(jù)庫,同時要在CommonNumberDao.java里的增刪查改api把數(shù)據(jù)庫對象傳過來

    @Override protected void onDestroy() {super.onDestroy();db.close(); }

    如果想讓孩子條目可以被點擊,首先要設(shè)置如下:

    //指定孩子可以被點擊 @Override public boolean isChildSelectable(int groupPosition, int childPosition) {return true; } 然后在控件對象上設(shè)置:elv = (ExpandableListView) findViewById(R.id.elv); elv.setAdapter(new CommonNumAdapter()); elv.setOnChildClickListener(new OnChildClickListener() {@Overridepublic boolean onChildClick(ExpandableListView parent, View v,int groupPosition, int childPosition, long id) {System.out.println("被點擊了。"+CommonNumberDao.getChildInfoByPosition(db, groupPosition, childPosition));return false;} });


    課4

    緩存清理模塊:獲取緩存信息 (建議看多一次視頻:實用!)

    緩存清理模塊介紹 什么是緩存?

    data/data里的cache文件夾 每一個文件夾有可以創(chuàng)建一個cache的文件夾緩存文件

    一個應(yīng)用程序如何生成緩存?

    新建工程:緩存測試

    public class MainActivity extends Activity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);getFilesDir();//得到應(yīng)用程序在文件系統(tǒng)上的路徑 /data/data/包名/filesgetCacheDir();//應(yīng)用程序有臨時的數(shù)據(jù)需要保存 ,就可以存放在cache目錄,/data/data/包名/cachetry {File file = new File(getCacheDir(),"gaga.txt");FileOutputStream fos = new FileOutputStream(file);fos.write("sfdaf".getBytes());fos.close();} catch (Exception e) {e.printStackTrace();}}}


    新建了工程后就可以使用金山手機位置使用緩存清理功能,可以搜出該工程產(chǎn)生的緩存文件。?

    在手機設(shè)置里的應(yīng)用欄目也會顯示:?

    一般我們從網(wǎng)上下載了一些臨時的圖片,應(yīng)用會自動放在cache里,當在手機設(shè)置里的應(yīng)用欄目點擊清空緩存,可以把cache刪除。

    新建工程:獲取緩存測試

    劃紅線的數(shù)據(jù)谷歌怎么賦值的? 我們看系統(tǒng)上層所有的源碼中找到Setting 在工程里導(dǎo)入Setting,為了查找里面的源碼實現(xiàn)。 查找技巧,先搜界面,然后再搜java代碼,看源碼是怎么給劃紅線的數(shù)據(jù)賦值的 ctr+h--->全局搜索 緩存+*.xml?

    然后根據(jù)線索一步一步的最后定位到了res--->cachesizetext--->InstalledAppDetails.java,獲取緩存的方法很明顯都在該java文件里

    目標InstalledAppDetails.java,在該類里可以找到谷歌如何給劃紅線的數(shù)據(jù)賦值(緩存)

    ctr+k快速搜索 下次嘗試操作尋找下。

    源碼定位的邏輯:

    我們的目標是劃紅線的緩存數(shù)據(jù)谷歌是怎么賦值

    1、從布局出發(fā):首先在Eclipse里使用ctr+h全局搜索:緩存 *.xml

    2、從搜索到的信息


    這是在setting源碼中布局文件xml中出現(xiàn)過的字段,打開文件可以找到該字段的來源

    <string name="cache_header_label" msgid="1877197634162461830">"緩存"</string> <string name="clear_cache_btn_text" msgid="5756314834291116325">"清除緩存"</string> <string name="cache_size_label" msgid="7505481393108282913">"緩存"</string>

    3、根據(jù)這個來源再繼續(xù)全局搜索:cachesizelabel *.xml,這樣的目的是繼續(xù)搜索到設(shè)置中心的布局文件,并找到布局文件中cachesizelabel的id,搜索結(jié)果是installedappdetails.xml出現(xiàn)過cachesizelabel這個id


    4、根據(jù)該布局文件可以分析出顯示緩存的數(shù)字的變量是:

    <TextViewandroid:id="@+id/cache_size_text"android:textAppearance="?android:attr/textAppearanceMedium"android:paddingTop="6dip"android:paddingRight="6dip"android:layout_height="wrap_content"android:layout_width="wrap_content"android:maxLines="1" />

    5、繼續(xù)使用全局搜索:cachesizetext *.java,這樣可以搜到cachesizetext出自于哪個類文件:搜索結(jié)果是InstalledAppDetails.java里的一段代碼

    // Cache sectionmCacheSize = (TextView) findViewById(R.id.cache_size_text);

    6、繼續(xù)順藤摸瓜:搜索: mCacheSize:找到一段關(guān)鍵代碼:

    if (mLastCacheSize != mAppEntry.cacheSize) { mLastCacheSize = mAppEntry.cacheSize; mCacheSize.setText(getSizeStr(mAppEntry.cacheSize));

    7、我們繼續(xù)點進cacheSize觀察其來自于哪里:然后定位到了目標類ApplocationState.java,并發(fā)現(xiàn)了一段關(guān)鍵代碼:

    public static class SizeInfo {long cacheSize;long codeSize;long dataSize; }

    8、然后繼續(xù)搜索,然后定位到了最關(guān)鍵的代碼塊:分析:


    9、最后搜索遠程服務(wù)接口的實現(xiàn)類對象:mStatsObserver,最后定位到以下這段代碼:


    10、然后我們在工程里使用PackageManager類來使用getPackageSizeInfo方法去獲取緩存值,但是遺憾的是,谷歌早就把getPackageSizeInfo方法給隱藏起來了:因此我們可以考慮使用反射的方法來獲取該方法

    /*** Retrieve the size information for a package.* Since this may take a little while, the result will* be posted back to the given observer. The calling context* should have the {@link android.Manifest.permission#GET_PACKAGE_SIZE} permission.** @param packageName The name of the package whose size information is to be retrieved* @param userHandle The user whose size information should be retrieved.* @param observer An observer callback to get notified when the operation* is complete.* {@link android.content.pm.IPackageStatsObserver#onGetStatsCompleted(PackageStats, boolean)}* The observer's callback is invoked with a PackageStats object(containing the* code, data and cache sizes of the package) and a boolean value representing* the status of the operation. observer may be null to indicate that* no callback is desired.** @hide*/ public abstract void getPackageSizeInfo(String packageName, int userHandle,IPackageStatsObserver observer);

    繼續(xù)在工程里獲取緩存

    技巧:如何觀看安卓源碼的某一個類中的某一個對象

    在程序中打一個斷點。 我們使用debug窗口去查看我們要了解的一些比較難理解的類,配合搜索工具來找出其源碼父類的實現(xiàn)類。

    code.google.com www.github.com 我們工作的好幫手

    使用反射機制拿出PackAgeManager類里被隱藏的方法:getPackageSizeInfo

    然后還需調(diào)用遠程服務(wù) 實現(xiàn)遠程服務(wù)的接口 一系列工程完成后就可以獲取緩存。


    還有權(quán)限要加上:

    獲取緩存的邏輯(結(jié)合上面如何觀察源碼的方法和結(jié)論)

    1、布局文件:需求:就是在輸入框里輸入一個應(yīng)用的包名,然后點擊確定按鈕,就可以在控制臺返回緩存的size

    <RelativeLayout 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" tools:context=".MainActivity" ><EditTextandroid:hint="請輸入要獲取的應(yīng)用程序的包名"android:id="@+id/et_packname"android:layout_width="match_parent"android:layout_height="wrap_content" /><Buttonandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_centerHorizontal="true"android:layout_centerVertical="true"android:onClick="click"android:text="獲取緩存信息" /></RelativeLayout>

    2、從之前源碼分析中知道我們可以從PackageManager類里獲取getPackageSizeInfo方法來獲取緩存size,因此我們獲取到PackageManager類對象pm,然后快捷鍵發(fā)現(xiàn)方法獲取不出來,查看PackageManager發(fā)現(xiàn)該方法是個抽象方法,也就是被谷歌隱藏了(hide)

    3、那么我們就應(yīng)該想辦法怎么獲得那個方法,方法就是(重要!)使用debug的方法來觀察其PackageManager的實現(xiàn)類是什么?我們隨便在程序中輸出些什么,然后在該輸出的地方打上一個斷點

    public class MainActivity extends Activity { private EditText et_packname; @Override protected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);et_packname = (EditText) findViewById(R.id.et_packname); }public void click(View view){//獲取某個應(yīng)用程序的緩存String packname = et_packname.getText().toString().trim();//使用該類可以獲取到緩存的方法getPackageSizeInfoPackageManager pm = getPackageManager();//但是該方法getPackageSizeInfo被谷歌隱藏,其在PackageManager類里只是一個抽象的方法,被隱藏了,那么技巧就是我們隨便在程序中輸出些東西,并打上斷點//以此來獲取PackageManager里的內(nèi)容System.out.println("----");

    4、然后我們進入debug視圖觀察得到結(jié)果:其PackageManager的實現(xiàn)類就是:ApplicationPackageManager,找到其源碼發(fā)現(xiàn):的確是繼承PackageManager并發(fā)現(xiàn):

    /*package*/ final class ApplicationPackageManager extends PackageManager {private static final String TAG = "ApplicationPackageManager";private final static boolean DEBUG = false;private final static boolean DEBUG_ICONS = false;并發(fā)現(xiàn)的確有方法 @Override public void getPackageSizeInfo(String packageName, int userHandle,IPackageStatsObserver observer) {try {mPM.getPackageSizeInfo(packageName, userHandle, observer);} catch (RemoteException e) {// Should never happen!} }

    5、那么我們就可以使用反射的機制把其方法反射出來了:

    //pm.getPackageSizeInfo(); //這樣可以拿到PackageManager實現(xiàn)類的字節(jié)碼Method[] methods = PackageManager.class.getMethods();for(Method method : methods){if("getPackageSizeInfo".equals(method.getName())){try {method.invoke(pm, packname,new MyObserver());} catch (Exception e) {e.printStackTrace();}}}

    6、觀察源碼中的方法,其 mPM.getPackageSizeInfo(packageName, userHandle, observer);中的參數(shù)是一個遠程服務(wù)實現(xiàn)類:因為在之前的源碼分析中得到的結(jié)論,那么我們必須找到該遠程服務(wù)的aidl文件復(fù)制過來放在我們的工程目錄下(注意包名要和原文件里的包名一致)

    7、把aidl遠程服務(wù)接口文件設(shè)置好后,就可以把其遠程服務(wù)的類定義出來并實現(xiàn),并傳到反射出來的getPackageSizeInfo()方法里了。

    private class MyObserver extends android.content.pm.IPackageStatsObserver.Stub{@Overridepublic void onGetStatsCompleted(PackageStats pStats, boolean succeeded)throws RemoteException {long size = pStats.cacheSize;System.out.println("緩存大小為:"+Formatter.formatFileSize(getApplicationContext(), size));} }

    8、最后使用該方法還需要獲取權(quán)限:

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

    課5

    手機衛(wèi)士開發(fā):緩存清理 (重看視頻如何引入源碼中的progressBar樣式然后如何自定義)

    開發(fā)緩存清理 新建CleanCacheActivity.java+配置清單

    在MainActivity.java增加點擊事件case 6

    需求UI:

    布局:activitycleancache.xml 在布局里引用了一個progressBar的特殊樣式。在源碼那里復(fù)制過來的。 在CleanCacheActivity.java加載自定義的progressBar,測試其效果。 需求:就是在進度條進行完后就顯示被進度條覆蓋的信息。 需求:模仿騰訊:每掃描一條cache,即顯示一行信息條目,動態(tài)的顯示(難) 繼續(xù)實現(xiàn)布局:activitycleancache.xml activitycleancache.xml

    <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical" ><TextViewstyle="@style/textview_title_style"android:gravity="center"android:text="清理緩存" /><FrameLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content" ><ProgressBarandroid:id="@+id/pb_scan"android:layout_width="fill_parent"android:layout_height="15dip"android:indeterminateOnly="false"android:progressDrawable="@drawable/progress_horizontal" /><TextViewandroid:id="@+id/tv_scan_result"android:layout_width="match_parent"android:layout_height="wrap_content"android:text="adfadsfa" /></FrameLayout><ScrollViewandroid:layout_width="match_parent"android:layout_height="0dip"android:layout_weight="1" ><LinearLayoutandroid:id="@+id/ll_container"android:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical" ></LinearLayout></ScrollView><Buttonandroid:onClick="cleanAll"android:layout_width="fill_parent"android:layout_height="wrap_content"android:text="清理全部" /></LinearLayout>

    (觀察源碼后學(xué)習(xí)的進度條樣式)progress_horizontal.xml

    <?xml version="1.0" encoding="utf-8"?> <!-- Copyright (C) 2008 The Android Open Source ProjectLicensed 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 athttp://www.apache.org/licenses/LICENSE-2.0Unless required by applicable law or agreed to in writing, softwaredistributed 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 andlimitations under the License.--><layer-list xmlns:android="http://schemas.android.com/apk/res/android"><item android:id="@android:id/background" android:drawable="@drawable/security_progress_bg"></item><item android:id="@android:id/secondaryProgress" android:drawable="@drawable/security_progress"></item><item android:id="@android:id/progress" android:drawable="@drawable/security_progress"></item></layer-list>


    • CleanCacheActivity.java編程
    • 初始化控件
    • 掃描所有的應(yīng)用程序,查看他們的緩存信息;
    • 掃描應(yīng)用方法:scan(),耗時,所以放在子線程里。
    • 獲取緩存getCacheInfo(String packname)
    • 在掃描應(yīng)用方法里去獲取應(yīng)用的cache信息。

      public class CleanCacheActivity extends Activity {protected static final int SCANNING = 1;protected static final int FINISHED = 2;public static final int ADD_CACHE_VIEW = 3;private TextView tv_scan_result;private ProgressBar pb_scan;private LinearLayout ll_container;private PackageManager pm;private Handler handler = new Handler() {public void handleMessage(android.os.Message msg) {switch (msg.what) {case SCANNING://這樣可以讓子線程中遍歷的應(yīng)用信息滾動PackageInfo info = (PackageInfo) msg.obj;tv_scan_result.setText("正在掃描:"+ info.applicationInfo.loadLabel(pm));break;case ADD_CACHE_VIEW:break;case FINISHED://遍歷完應(yīng)用接收到該信號就把UI中的TextView隱藏掉tv_scan_result.setText("掃描完畢!");pb_scan.setVisibility(View.INVISIBLE);break;}};}; @Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_clean_cache);//該類用來獲取緩存信息的pm = getPackageManager();tv_scan_result = (TextView) findViewById(R.id.tv_scan_result);pb_scan = (ProgressBar) findViewById(R.id.pb_scan);ll_container = (LinearLayout) findViewById(R.id.ll_container);// 掃描所有的應(yīng)用程序,查看他們的緩存信息。scan();}private void scan() {new Thread() {public void run() {//從包管理者中獲取到所有已經(jīng)安裝了的應(yīng)用的包名集合List<PackageInfo> infos = pm.getInstalledPackages(0);//給進度條設(shè)置最大數(shù)pb_scan.setMax(infos.size());int progress = 0;for (PackageInfo info : infos) {//調(diào)用內(nèi)部方法getCacheInfo(info);progress++;pb_scan.setProgress(progress);try {//模擬進度條運行耗時Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}//遍歷已經(jīng)安裝的應(yīng)用安裝包,并讓UI中的TextView顯示遍歷的效果,所以使用HandlerMessage msg = Message.obtain();msg.what = SCANNING;//把應(yīng)用的對象發(fā)過去msg.obj = info;handler.sendMessage(msg);}//遍歷完就給Handler發(fā)信號把UI中的TextView隱藏Message msg = Message.obtain();msg.what = FINISHED;handler.sendMessage(msg);};}.start();}//該方法里使用反射技術(shù)獲取緩存public void getCacheInfo(PackageInfo info) {try {Method method = PackageManager.class.getMethod("getPackageSizeInfo", String.class,IPackageStatsObserver.class);method.invoke(pm, info.packageName, new MyObserver(info));} catch (Exception e) {e.printStackTrace();}}private class MyObserver extendsandroid.content.pm.IPackageStatsObserver.Stub {PackageInfo packinfo;public MyObserver(PackageInfo packinfo) {this.packinfo = packinfo;}@Overridepublic void onGetStatsCompleted(PackageStats pStats, boolean succeeded)throws RemoteException {// 這個方法不是運行在主線程中,所有不可以直接更新uilong size = pStats.cacheSize;System.out.println(packinfo.applicationInfo.loadLabel(pm)+ ",緩存大小為:"+ Formatter.formatFileSize(getApplicationContext(), size));}}}class CacheInfo {Drawable icon;String packname;String appname;long size; }


    能找到數(shù)據(jù)。那么就是在scan給界面更新。 功能完成后 技巧:動態(tài)在布局文件里,例如LinearLayout里加載類似于listView樣式的條目顯示

    我們需要把掃描到的信息條目怎么列在LinearLayout里?

    ll_container.addView(xxx);


    原因:

    使用Handler解決就沒問題了。 但是使用Message發(fā)送緩存信息發(fā)現(xiàn)比較多元化,所以定義一個緩存信息的bean內(nèi)部類CacheInfo,讓MEssage發(fā)給handler去處理。 測試的結(jié)果樣式比較簡單,

    我們做的出專業(yè)些,定義一個條目布局:item_cacheinfo.xml

    item_cacheinfo.xml

    <?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" ><ImageViewandroid:id="@+id/iv_cache_icon"android:layout_width="40dip"android:layout_height="40dip"android:src="@drawable/ic_launcher" /><TextViewandroid:layout_centerVertical="true"android:layout_width="wrap_content"android:id="@+id/tv_cache_name"android:layout_height="wrap_content"android:layout_toRightOf="@id/iv_cache_icon"android:text="應(yīng)用程序名稱"android:textColor="#000000"android:textSize="20sp" /><TextViewandroid:layout_centerVertical="true"android:layout_width="wrap_content"android:layout_alignParentRight="true"android:id="@+id/tv_cache_size"android:layout_height="wrap_content"android:text="緩存大小"android:textColor="#ff0000"android:textSize="14sp" /> </RelativeLayout>


    然后在CleanCacheActivity.java的Handler里使用打氣筒inflate把條目布局給加載進去,在為條目布局控件賦值,賦值之后讓linearLayout的對象ll_container.addView(view)添加進來。

    考慮到加載的條目比較多,我們在布局文件里再加上一個scrollBar控件,可以讓批量的條目支持滾動

    CleanCacheActivity.java

    public class CleanCacheActivity extends Activity {protected static final int SCANNING = 1;protected static final int FINISHED = 2;public static final int ADD_CACHE_VIEW = 3;private TextView tv_scan_result;private ProgressBar pb_scan;private LinearLayout ll_container;private PackageManager pm;private Handler handler = new Handler() {public void handleMessage(android.os.Message msg) {switch (msg.what) {case SCANNING://這樣可以讓子線程中遍歷的應(yīng)用信息滾動PackageInfo info = (PackageInfo) msg.obj;tv_scan_result.setText("正在掃描:"+ info.applicationInfo.loadLabel(pm));break;case ADD_CACHE_VIEW:final CacheInfo cacheinfo = (CacheInfo) msg.obj;View view = View.inflate(getApplicationContext(),R.layout.item_cacheinfo, null);ImageView icon = (ImageView) view.findViewById(R.id.iv_cache_icon);TextView name = (TextView) view.findViewById(R.id.tv_cache_name);TextView size = (TextView) view.findViewById(R.id.tv_cache_size);icon.setImageDrawable(cacheinfo.icon);name.setText(cacheinfo.appname);size.setText(Formatter.formatFileSize(getApplicationContext(),cacheinfo.size));ll_container.addView(view);view.setOnClickListener(new OnClickListener() {@Overridepublic void onClick(View v) {// pm.deleteApplicationCacheFiles(packageName,// mClearCacheObserver);try {Method method = PackageManager.class.getMethod("deleteApplicationCacheFiles",String.class, IPackageDataObserver.class);method.invoke(pm, cacheinfo.packname,new IPackageDataObserver.Stub() {@Overridepublic void onRemoveCompleted(String packageName, boolean succeeded)throws RemoteException {}});} catch (Exception e) {e.printStackTrace();}}});break;case FINISHED://遍歷完應(yīng)用接收到該信號就把UI中的TextView隱藏掉tv_scan_result.setText("掃描完畢!");pb_scan.setVisibility(View.INVISIBLE);break;}};};@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_clean_cache);//該類用來獲取緩存信息的pm = getPackageManager();tv_scan_result = (TextView) findViewById(R.id.tv_scan_result);pb_scan = (ProgressBar) findViewById(R.id.pb_scan);ll_container = (LinearLayout) findViewById(R.id.ll_container);// 掃描所有的應(yīng)用程序,查看他們的緩存信息。scan();}private void scan() {new Thread() {public void run() {//從包管理者中獲取到所有已經(jīng)安裝了的應(yīng)用的包名集合List<PackageInfo> infos = pm.getInstalledPackages(0);//給進度條設(shè)置最大數(shù)pb_scan.setMax(infos.size());int progress = 0;for (PackageInfo info : infos) {//調(diào)用內(nèi)部方法getCacheInfo(info);progress++;pb_scan.setProgress(progress);try {//模擬進度條運行耗時Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}//遍歷已經(jīng)安裝的應(yīng)用安裝包,并讓UI中的TextView顯示遍歷的效果,所以使用HandlerMessage msg = Message.obtain();msg.what = SCANNING;//把應(yīng)用的對象發(fā)過去msg.obj = info;handler.sendMessage(msg);}//遍歷完就給Handler發(fā)信號把UI中的TextView隱藏Message msg = Message.obtain();msg.what = FINISHED;handler.sendMessage(msg);};}.start();}//該方法里使用反射技術(shù)獲取緩存public void getCacheInfo(PackageInfo info) {try {Method method = PackageManager.class.getMethod("getPackageSizeInfo", String.class,IPackageStatsObserver.class);method.invoke(pm, info.packageName, new MyObserver(info));} catch (Exception e) {e.printStackTrace();}}private class MyObserver extendsandroid.content.pm.IPackageStatsObserver.Stub {PackageInfo packinfo;public MyObserver(PackageInfo packinfo) {this.packinfo = packinfo;}@Overridepublic void onGetStatsCompleted(PackageStats pStats, boolean succeeded)throws RemoteException {// 這個方法不是運行在主線程中,所有不可以直接更新uilong size = pStats.cacheSize;System.out.println(packinfo.applicationInfo.loadLabel(pm)+ ",緩存大小為:"+ Formatter.formatFileSize(getApplicationContext(), size));if (size > 0) {//在該遠程服務(wù)實現(xiàn)類里進行對緩存的數(shù)據(jù)處理,使用把數(shù)據(jù)通過Message發(fā)給Handler然后處理UIMessage msg = Message.obtain();msg.what = ADD_CACHE_VIEW;CacheInfo cacheinfo = new CacheInfo();cacheinfo.icon = packinfo.applicationInfo.loadIcon(pm);cacheinfo.packname = packinfo.packageName;cacheinfo.appname = packinfo.applicationInfo.loadLabel(pm).toString();cacheinfo.size = size;msg.obj = cacheinfo;handler.sendMessage(msg);}}}class CacheInfo {Drawable icon;String packname;String appname;long size;}/*** 清理全部的緩存* @param view*/public void cleanAll(View view){Method[] methods= PackageManager.class.getMethods();for(Method method : methods){if("freeStorageAndNotify".equals(method.getName())){try {method.invoke(pm, Integer.MAX_VALUE,new IPackageDataObserver.Stub() {@Overridepublic void onRemoveCompleted(String packageName, boolean succeeded)throws RemoteException {System.out.println("result:"+succeeded);}});} catch (Exception e) {e.printStackTrace();}return;}}}}

    課6

    實現(xiàn)清理緩存信息(建議下午的觀察源碼的流程視頻看一遍)

    接著清理緩存信息 需求:模仿金山:跳轉(zhuǎn)到設(shè)置中心的app設(shè)置中心 需求2:模仿騰訊:


    找源碼,實現(xiàn)清除緩存的點擊功能

    我們給條目設(shè)計一個點擊事件,把以上的源碼設(shè)計進來。 然后發(fā)現(xiàn)方法有不能直接調(diào)用,被谷歌工程師給隱藏了,所以又需要反射出來:?

    CleanCacheActivity.java

    view.setOnClickListener(new OnClickListener() {@Overridepublic void onClick(View v) {// pm.deleteApplicationCacheFiles(packageName,// mClearCacheObserver);try {Method method = PackageManager.class.getMethod("deleteApplicationCacheFiles",String.class, IPackageDataObserver.class);method.invoke(pm, cacheinfo.packname,new IPackageDataObserver.Stub() {@Overridepublic void onRemoveCompleted(String packageName, boolean succeeded)throws RemoteException {}});} catch (Exception e) {e.printStackTrace();}}});

    然后點擊發(fā)現(xiàn)又報出異常信息:?


    但是在加載權(quán)限是發(fā)現(xiàn)清單文件也報錯了?

    那么我們強行刪除Problems中的信息,看是否可以運行。 發(fā)現(xiàn)不可行:那么按照之前的邏輯去反射谷歌隱藏的方法來實現(xiàn)功能不可行,那么有什么辦法清理應(yīng)用緩存呢?

    但是清理緩存的功能還是能實現(xiàn)的,那就是一鍵清理的安卓漏洞。

    主要是安卓的一個服務(wù)會檢測系統(tǒng)的內(nèi)存,如果發(fā)現(xiàn)內(nèi)存不足,那么會自動把系統(tǒng)里的所有緩存給刪除,我們可以抓住此漏洞,模仿該特點去發(fā)送內(nèi)存不足的信息給系統(tǒng),一次達到一鍵清理緩存的功能實現(xiàn)。 在CleanCacheActivity里加入一鍵清理的功能cleanAll PackageManager-->freeStorageAndNotify

    CleanCacheActivity.java

    /*** 清理全部的緩存* @param view*/ public void cleanAll(View view){Method[] methods= PackageManager.class.getMethods();for(Method method : methods){if("freeStorageAndNotify".equals(method.getName())){try {method.invoke(pm, Integer.MAX_VALUE,new IPackageDataObserver.Stub() {@Overridepublic void onRemoveCompleted(String packageName, boolean succeeded)throws RemoteException {System.out.println("result:"+succeeded);}});} catch (Exception e) {e.printStackTrace();}return;}} }

    源碼解釋:?

    使用反射來調(diào)用該方法。一次給系統(tǒng)發(fā)送信息。 點擊一鍵清理

    雖然報出異常: 但是其實都已經(jīng)刪除了 金山的SD卡清理


    怎么實現(xiàn)?

    SD卡不安全,每個應(yīng)用程序都會在SD卡里寫一些內(nèi)容,這些內(nèi)容我們不可能去手工認知,但是金山已經(jīng)把他們寫成數(shù)據(jù)庫文件了,我們就使用金山的數(shù)據(jù)庫來遍歷SD卡是否有一樣的文件,以此來找到并刪除。 金山的痕跡清理


    沒什么好說的,呵呵

    資料下載

    總結(jié)

    以上是生活随笔為你收集整理的手机卫士-11的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

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