Android之教你如何搬運(yùn)系統(tǒng)源碼

項(xiàng)目Demo地址:
https://github.com/liaozhoubei/EndCallAndClearCacheDemo
Demo項(xiàng)目以獲取全部緩存和清理全部緩存為主

代碼界中有句話:不要重復(fù)造輪子!
意思就是如果你發(fā)現(xiàn)你想要實(shí)現(xiàn)的功能已經(jīng)有人做出來了喻旷,而且是開源的抖拦,那就不要浪費(fèi)時間了,直接拿過來用吧闷供!
當(dāng)然這句話對于想要提高技術(shù)的人來說不適應(yīng)仍稀。但是對于趕進(jìn)度的碼農(nóng)來說確實(shí)是適用的拳芙。

可惜對于很多Android新手來說想要把輪子拿過來改一改都不會察藐,現(xiàn)在我們就來學(xué)習(xí)如何在Android源碼這個輪子之中找到自己想要的功能并且搬過來吧。

所需文件:Android上層源碼(直接用搜索引擎搜舟扎,能找到一堆)

源碼下載:http://pan.baidu.com/s/1eRKpOOy

Android上層源碼分飞,即packages的源碼并不大,也就30多m睹限。

工具準(zhǔn)備好之后凄敢,我們就開始本次的任務(wù)吧怨规。

實(shí)現(xiàn)清理手機(jī)緩存的功能

現(xiàn)在我們想要獲取手機(jī)緩存丽旅,然后清理手機(jī)緩存豪椿。

但是我們不知道怎樣獲取手機(jī)緩存呀,也不知道怎么清理緩存叨恨,甚至沒怎么接觸過柳刮,這可如何是好!

找到功能在哪里實(shí)現(xiàn)過

且慢,我們先想想曾經(jīng)在哪接觸過它秉颗。沒錯痢毒,對于手機(jī)達(dá)人的我們來說是經(jīng)常會接觸到這個選項(xiàng)的,當(dāng)我們打開手機(jī)設(shè)置-apps蚕甥,隨意選擇一個app進(jìn)入app詳細(xì)頁面的時候我們就會發(fā)現(xiàn)這么一個Cache的文字以及Clear cache的按鈕哪替,Cache的右邊顯示的是當(dāng)前這個app的緩存大小,而我們點(diǎn)擊Clear cache的時候緩存就會清零菇怀。這不正是我們要實(shí)現(xiàn)的功能么凭舶?

app詳情頁.png

導(dǎo)入實(shí)現(xiàn)功能的項(xiàng)目

之后這個功能在哪里實(shí)現(xiàn)過之后,應(yīng)該怎樣找到實(shí)現(xiàn)這個功能的地方呢敏释?

別著急库快,我們打開剛才下載的《Android上層源碼》摸袁,在里面搜索钥顽,發(fā)現(xiàn)里面有個Settings的項(xiàng)目文件夾!那么Settings是不是代表著手機(jī)中的setting這個選項(xiàng)靠汁?我們的清理緩存的功能是不是就在里面蜂大?

話不多說,直接在開發(fā)工具中導(dǎo)入Settings項(xiàng)目蝶怔。

找到具體實(shí)現(xiàn)的代碼(重點(diǎn))

現(xiàn)在問題又來了奶浦,工程已經(jīng)導(dǎo)入了,源代碼也可以查看了踢星,可是一個package動不動就七八個類文件澳叉,直接一個一個文件查看太浪費(fèi)時間和精力了,那么我們怎么找到我們需要的代碼呢沐悦?

這時我們就要充分利用好開發(fā)工具了成洗,非常感謝制作出開發(fā)工具的大神們,讓我們開發(fā)省了多少時間藏否。

我們再次查看剛才的App Info頁面瓶殃,仔細(xì)查看清理緩存這一部分「鼻看到Cache文字和Clear cache按鍵遥椿,似乎并沒什么特別的。

且慢淆储,再仔細(xì)想想冠场。Android之中如果想調(diào)用文字,不是要用android:text="@string/xxxx"這樣的形式么本砰。那么我們就能夠先找到Cache單詞的引用碴裙,然后順藤摸瓜的直接找到使用文字的代碼,既然引用了文字,一定與清理緩存有關(guān)系的青团。

注:Android是不建議使用硬編碼即android:text="我是硬編碼"直接使用文字的譬巫。

順藤摸瓜找代碼

在eclipse中直接使用Ctrl+H快捷鍵,打開搜索框選擇File Search督笆,然后在其中的Containing text中填寫要搜索的文字芦昔,然后在File name patterns中填入要搜索哪些文件。
我們首先要找到這個文字的資源文件id娃肿,然后通過資源文件id找到在哪里引用咕缎。

搜索.png
  • 首先在Containing text填入在布局頁面中展現(xiàn)的文字Cache,然后這個文字是出現(xiàn)在xml布局頁面中料扰,所以File name patterns中填入*.xml凭豪,意思是我們要搜索所有的xml頁面中包含Cache文字的地方

然后在結(jié)果頁面中打開res-value-strings,發(fā)現(xiàn)將所有包含Cache文字的行都列了出來晒杈。我們發(fā)現(xiàn)有兩個地方直接使用了Cache

<!-- Manage applications, Header name used for cache information -->
<string name="cache_header_label">Cache</string>
<!-- Manage applications, text label for button -->
<string name="clear_cache_btn_text">Clear cache</string>
<!-- Manage applications, label that appears next to the cache size -->
<string name="cache_size_label">Cache</string>

分別是cache_header_label和cache_size_label嫂伞,翻譯成中文不就是緩存標(biāo)題欄和緩存大小欄么。

  • 重復(fù)上面的方法拯钻,直接搜索cache_size_label帖努,這次在搜索結(jié)果中顯示installed_app_details.xml這個布局頁面使用了。
layout.png

打開布局頁面粪般,直接查看布局預(yù)覽拼余,這次我們終于找到了App Info的布局頁面了!如下圖

layout1.png
  • 找到布局之后亩歹,查看布局中的id名匙监,發(fā)現(xiàn)TextView中android:id="@+id/cache_size_text"這一行就是我們所需要的放置緩存大小的文字,我們自己通過這個id尋找在哪里引用

  • 再次打開搜索框小作,Containing text填入:cache_size_text亭姥,F(xiàn)ile name patterns中填入*.java,顯示以下結(jié)果

cache-size.png

在打開InstalledAppDetails.java之后躲惰,發(fā)現(xiàn)其中的322行

mCacheSize = (TextView) findViewById(R.id.cache_size_text);

這毫無疑問就是設(shè)置緩存大小的地方了致份。
那么mCacheSize的文字又是從哪里來的呢?

繼續(xù)在InstalledAppDetails.java尋找础拨,發(fā)現(xiàn):

mCacheSize.setText(getSizeStr(mAppEntry.cacheSize));

設(shè)置了緩存的大小氮块,那么它是怎么獲得緩存大小的呢?
分析getSizeStr(mAppEntry.cacheSize))诡宗,發(fā)現(xiàn)getSizeStr()這個方法只是純粹用來格式化緩存的滔蝉,也就是將b轉(zhuǎn)換為kb,kb轉(zhuǎn)換為mb而已塔沃,在InstalledAppDetails可以找到它的源碼:

private String getSizeStr(long size) {
 if (size == SIZE_INVALID) {
     return mInvalidSizeStr.toString();
 }
 return Formatter.formatFileSize(this, size);
 }

這樣一來就更清晰明了了蝠引,mAppEntry.cacheSize就是緩存的大小,我們繼續(xù)探索它的數(shù)值是如何獲取出來的。

Ctrl + 左鍵點(diǎn)擊cacheSize螃概,我們就進(jìn)入了ApplicationsState的源碼矫夯,然后又驚奇的發(fā)現(xiàn)mAppEntry是AppEntry的實(shí)例,而AppEntry則是ApplicationsState的靜態(tài)內(nèi)部類〉跬荩現(xiàn)在看來一切的答案都能在ApplicationsState這個類中尋找训貌。

在ApplicationsState中仔細(xì)搜索cacheSize,我們發(fā)現(xiàn)在以下方法中entry.cacheSize = stats.cacheSize冒窍,被賦值了:

final IPackageStatsObserver.Stub mStatsObserver = new IPackageStatsObserver.Stub() {
public void onGetStatsCompleted(PackageStats stats, boolean succeeded) {
                ·······
                if (entry.size != newSize ||
                        entry.cacheSize != stats.cacheSize ||
                        entry.codeSize != stats.codeSize ||
                        entry.dataSize != stats.dataSize) {
                    entry.size = newSize;
                    entry.cacheSize = stats.cacheSize;
                    entry.codeSize = stats.codeSize;
                    entry.dataSize = stats.dataSize;
                    entry.sizeStr = getSizeStr(entry.size);
                    if (DEBUG) Log.i(TAG, "Set size of " + entry.label + " " + entry
                            + ": " + entry.sizeStr);
                    sizeChanged = true;
                }
            }
            ·······
}
};

細(xì)細(xì)研究entry.cacheSize递沪,發(fā)現(xiàn)entry也是AppEntry的變量,那么無疑mAppEntry.cacheSize就是entry.cacheSize综液,毫無疑問我們得出了結(jié)論款慨,這個就是內(nèi)存大小了。

但是在進(jìn)一步研究entry.cacheSize = stats.cacheSize谬莹,我們無法從stats.cacheSize這個方法中直接得到內(nèi)存大小檩奠,因?yàn)樵诟檚tats.cacheSize方法進(jìn)入PackageStats類,繼續(xù)跟蹤的時候届良,發(fā)現(xiàn)它是依靠于底層用C語言實(shí)現(xiàn)的笆凌。

既然我們無法直接獲得PackageStats這個類的方法圣猎,那么我們回過頭來研究

final IPackageStatsObserver.Stub mStatsObserver = new IPackageStatsObserver.Stub()

它創(chuàng)建了一個實(shí)例士葫,并且被賦予final,一般情況下被賦予final的值都會在內(nèi)部被使用送悔,一旦能夠被正常使用慢显,那么就能夠直接獲得PackageStats的默認(rèn)變量,然后使用stats.cacheSize獲得緩存大小了欠啤。

通過搜索mStatsObserver的使用情況荚藻,發(fā)現(xiàn)了這兩行相關(guān)代碼:

mCurComputingSizePkg = entry.info.packageName;
mPm.getPackageSizeInfo(mCurComputingSizePkg, mStatsObserver);

到了這一步一切都差不多了,其中的entry.info.packageName所獲得的是包名洁段,而mPm則是PackageManager的變量应狱。

創(chuàng)建工程獲取緩存

我們直接創(chuàng)建一個工程項(xiàng)目來獲取緩存,在MainActivity中增加以下:

public class MainActivity extends Activity {

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    //獲取緩存
    PackageManager pm = getPackageManager();
pm.getPackageSizeInfo("com.example.writecache", mStatsObserver);
}
IPackageStatsObserver.Stub mStatsObserver = new IPackageStatsObserver.Stub() {
    public void onGetStatsCompleted(PackageStats stats, boolean succeeded) {
        long cachesize = stats.cacheSize;//緩存大小
        long codesize = stats.codeSize;//應(yīng)用程序的大小
        long datasize = stats.dataSize;//數(shù)據(jù)大小
        
        String cache = Formatter.formatFileSize(getApplicationContext(), cachesize);
        String code = Formatter.formatFileSize(getApplicationContext(), codesize);
        String data = Formatter.formatFileSize(getApplicationContext(), datasize);
        
        System.out.println("cachesize:"+cache +" codesize:"+code+" datasize:"+data);
    }
};
}

但是顯示找不到IPackageStatsObserver包祠丝,無法導(dǎo)入疾呻。這是因?yàn)镮PackageStatsObserver是一個aidl類,所以我們從外部直接獲取到了IPackageStatsObserver.aidl,然后再src目錄下創(chuàng)建IPackageStatsObserver的包名写半,將IPackageStatsObserver.aidl粘貼進(jìn)去岸蜗。這時IPackageStatsObserver類便不會報錯了。

但是又有個問題

pm.getPackageSizeInfo("com.example.writecache", mStatsObserver);

這行代碼報錯叠蝇,這是怎么回事呢璃岳?

進(jìn)入PackageManager的源碼,搜索getPackageSizeInfo()方法,卻發(fā)現(xiàn)這是一個隱藏的方法铃慷!代碼如下:

/**
 * @hide
 */
 public abstract void getPackageSizeInfo(String packageName, int userHandle,
   IPackageStatsObserver observer);

而添加了hide注釋的方法是無法直接別調(diào)用的单芜,那我們該怎么辦呢?這時我們只能采用反射來調(diào)用這個方法了犁柜!
將以下兩行代碼:

PackageManager pm = getPackageManager();
pm.getPackageSizeInfo("com.android.browser", mStatsObserver);

替換為

//反射獲取緩存
try {
Class<?> loadClass = MainActivity.class.getClassLoader().loadClass("android.content.pm.PackageManager");
Method method = loadClass.getDeclaredMethod("getPackageSizeInfo", String.class,IPackageStatsObserver.class);    
method.invoke(pm, "com.android.browser",mStatsObserver);
} catch (Exception e) {
  // TODO Auto-generated catch block
  e.printStackTrace();
}

其中com.android.browser是系統(tǒng)內(nèi)置瀏覽器的包名缓溅。當(dāng)我們要測試的時候只需打開系統(tǒng)瀏覽器,隨意瀏覽幾個網(wǎng)站就有緩存信息了赁温。

現(xiàn)在可以正常獲得緩存信息了坛怪!

我們通過反射的調(diào)用了IPackageStatsObserver,然后再IPackageStatsObserver的構(gòu)造方法中獲取了系統(tǒng)內(nèi)置瀏覽器的緩存大小/應(yīng)用程序大小/數(shù)據(jù)大小等信息股囊。

當(dāng)然了袜匿,還需要添加獲取緩存的權(quán)限,如下:

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

清理緩存

當(dāng)我們正式獲取到了緩存之后我們就可以進(jìn)行清理緩存的工作了稚疹,那么清理緩存要怎么做呢居灯?Android也沒有直接給出方法,大家可以根據(jù)我上面給出的方法自己查找怎樣清理應(yīng)用緩存内狗。

不過我相信很多人都會找到deleteApplicationCacheFiles()這個清理緩存的方法怪嫌,但是這是一個清理單個應(yīng)用緩存的方法,如果想一次清理所有的緩存呢柳沙?
當(dāng)然是有的岩灭,同樣在PackageManager的源碼中,在deleteApplicationCacheFiles()方法下面一個方法赂鲤,freeStorageAndNotify()便是清理所有緩存的方法了噪径。
freeStorageAndNotify()方法的工作原理是向系統(tǒng)申請最大的內(nèi)存使用空間,而系統(tǒng)一旦發(fā)現(xiàn)需要用到最大的內(nèi)存使用量的時候数初,就會自動清理所有的緩存了找爱!

這里還有一個坑,那便是deleteApplicationCacheFiles()和freeStorageAndNotify()方法同樣都是標(biāo)注為hide的泡孩,需要使用反射來調(diào)用车摄。

這里給出調(diào)用freeStorageAndNotify()方法的例子,其中pm是PackageManager的變量

   try {
        Class<?> loadClass = getActivity().getClass().getClassLoader().loadClass("android.content.pm.PackageManager");
        Method method = loadClass.getDeclaredMethod("freeStorageAndNotify", Long.TYPE,IPackageDataObserver.class);
        method.invoke(pm, Long.MAX_VALUE,new MyIPackageDataObserver());
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

  private class MyIPackageDataObserver extends IPackageDataObserver.Stub{
        //當(dāng)緩存清理完成之后調(diào)用
       @Override
       public void onRemoveCompleted(String packageName, boolean succeeded)
          throws RemoteException {
                    
           }
      }

當(dāng)然仑鸥,清理緩存也是需要獲得系統(tǒng)權(quán)限的:

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

總結(jié)

相信如果仔細(xì)看完這篇文章吮播,你一定知道該如何尋找到系統(tǒng)源碼中所使用的方法,然后搬到自己的項(xiàng)目中使用了吧锈候!

這里再次給出概要總結(jié):
1薄料、先在系統(tǒng)中尋找到自己實(shí)現(xiàn)的功能,如果你想要的功能系統(tǒng)沒有那就只好自己造輪子了泵琳。
2摄职、先從xml入手誊役,在eclipse中使用Ctrl + H 快捷鍵打開搜索欄。先在string.xml中到到文字資源名
3谷市、使用資源名字繼續(xù)搜索xml文件蛔垢,找到相應(yīng)布局,獲得空間的資源ID
4迫悠、通過資源ID搜索java文檔鹏漆,找到在那個類中被引用
5、一步一步跟進(jìn)创泄,獲得實(shí)現(xiàn)的代碼

項(xiàng)目Demo地址:
https://github.com/liaozhoubei/EndCallAndClearCacheDemo
Demo項(xiàng)目以獲取全部緩存和清理全部緩存為主

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末艺玲,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子鞠抑,更是在濱河造成了極大的恐慌饭聚,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,000評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件搁拙,死亡現(xiàn)場離奇詭異秒梳,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)箕速,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,745評論 3 399
  • 文/潘曉璐 我一進(jìn)店門酪碘,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人盐茎,你說我怎么就攤上這事兴垦。” “怎么了庭呜?”我有些...
    開封第一講書人閱讀 168,561評論 0 360
  • 文/不壞的土叔 我叫張陵滑进,是天一觀的道長。 經(jīng)常有香客問我募谎,道長,這世上最難降的妖魔是什么阴汇? 我笑而不...
    開封第一講書人閱讀 59,782評論 1 298
  • 正文 為了忘掉前任数冬,我火速辦了婚禮,結(jié)果婚禮上搀庶,老公的妹妹穿的比我還像新娘拐纱。我一直安慰自己,他們只是感情好哥倔,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,798評論 6 397
  • 文/花漫 我一把揭開白布秸架。 她就那樣靜靜地躺著,像睡著了一般咆蒿。 火紅的嫁衣襯著肌膚如雪东抹。 梳的紋絲不亂的頭發(fā)上蚂子,一...
    開封第一講書人閱讀 52,394評論 1 310
  • 那天,我揣著相機(jī)與錄音缭黔,去河邊找鬼食茎。 笑死,一個胖子當(dāng)著我的面吹牛馏谨,可吹牛的內(nèi)容都是我干的别渔。 我是一名探鬼主播,決...
    沈念sama閱讀 40,952評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼惧互,長吁一口氣:“原來是場噩夢啊……” “哼哎媚!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起喊儡,我...
    開封第一講書人閱讀 39,852評論 0 276
  • 序言:老撾萬榮一對情侶失蹤抄伍,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后管宵,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體截珍,經(jīng)...
    沈念sama閱讀 46,409評論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,483評論 3 341
  • 正文 我和宋清朗相戀三年箩朴,在試婚紗的時候發(fā)現(xiàn)自己被綠了岗喉。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,615評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡炸庞,死狀恐怖钱床,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情埠居,我是刑警寧澤查牌,帶...
    沈念sama閱讀 36,303評論 5 350
  • 正文 年R本政府宣布,位于F島的核電站滥壕,受9級特大地震影響纸颜,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜绎橘,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,979評論 3 334
  • 文/蒙蒙 一胁孙、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧称鳞,春花似錦涮较、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,470評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至熙暴,卻和暖如春闺属,著一層夾襖步出監(jiān)牢的瞬間慌盯,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,571評論 1 272
  • 我被黑心中介騙來泰國打工屋剑, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留润匙,地道東北人。 一個月前我還...
    沈念sama閱讀 49,041評論 3 377
  • 正文 我出身青樓唉匾,卻偏偏與公主長得像孕讳,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子巍膘,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,630評論 2 359

推薦閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,302評論 25 707
  • 喧囂的城市峡懈,匆忙的行人璃饱,每條街道上都有趕時間的上班族,朝著城市另一端的某座建筑肪康,邁著急促的腳步荚恶,就如郝云在歌曲中所...
    白企閱讀 374評論 0 2
  • 在中國古代神話傳說中,哪吒是一個地位崇高的神磷支,在道教中谒撼,擁有很高的稱號,諸如中壇元帥雾狈、通天太師廓潜、威靈顯赫大將軍、三...
    蟲子天下閱讀 6,459評論 0 3
  • 春分已經(jīng)過了兩天善榛,這個春天已經(jīng)走了一半辩蛋。依然過半的春天里,似乎和往常的春天不太一樣移盆。 因?yàn)槭枭⒎鞘锥脊δ?..
    鄭子衿閱讀 544評論 3 11
  • 不久前在翻閱空間動態(tài)時悼院,一個動態(tài)讓我的心久久不能平靜。又一個小生命被河水帶走了味滞。 偶然想起自己曾在假期的時候樱蛤,做了...
    木東水閱讀 722評論 0 0