聊聊Android中的ContextImpl

說起這個(gè)ContextImpl.可能有些同學(xué)不太熟悉手蝎,但說起Context,我想都認(rèn)識(shí)它吧俐芯,上下文棵介,也可以說是代表一種所在的場(chǎng)景,由于Context只是一個(gè)抽象類吧史,而抽象類必定是有一個(gè)具體的實(shí)現(xiàn)類的邮辽,另外還有ContextThemeWrapper和ContextWrapper,不過這些都是Context的子類而已,他們是以裝飾模式而存在的一種關(guān)系,簡(jiǎn)單說下裝飾模式吨述,裝飾模式是常用的設(shè)計(jì)模式之一岩睁,一般情況下如果需要?jiǎng)討B(tài)地給一個(gè)對(duì)象添加一些額外的職責(zé)但又不想增加子類,那么就可以用到裝飾模式了揣云,如果單純?cè)黾庸δ軄碚f笙僚,Decorator模式相比生成子類更為靈活,該模式以對(duì)客 戶端透明的方式擴(kuò)展對(duì)象的功能,下面舉一個(gè)簡(jiǎn)單的例子說明一下,首先一個(gè)人灵再,有吃飯的功能接口

代碼如下:

Component

 public interface Person {

    void eat();
}

ConcreteComponent

public class Man implements Person {

    public void eat() {
        System.out.println("男人在吃飯");
    }
}

Decorator(這個(gè)類是關(guān)鍵肋层,實(shí)現(xiàn)了接口并且有真實(shí)對(duì)象的引用)

public abstract class Decorator implements Person {

    protected Person person;

    public Decorator(Person person){
        this.person=person;
    }

    public void eat() {
        person.eat();
    }
}

下面的就是具體的添加額外功能的了具體子類,比如有些人吃飯前先洗手或者吃完飯后洗碗之類翎迁,當(dāng)然具體什么事情根據(jù)業(yè)務(wù)決定

public class ManDecoratorA extends Decorator {

    public ManDecoratorA(Person person) {
        super(person);
    }
    public void eat() {
        wash();
        super.eat();
    }

    private void wash() {
        System.out.println("飯前先洗洗手");
    }
}
public class ManDecoratorB extends Decorator {

    public ManDecoratorB(Person person) {
        super(person);
    }
    public void eat() {
        super.eat();
        washDishes();
    }

    private void washDishes(){
          System.out.println("吃完飯后洗碗");
    }
}

測(cè)試的結(jié)果為:

 public static void main(String[] args) {
        Person person=new Man();
        ManDecoratorA md1=new ManDecoratorA(person);
        ManDecoratorB md2=new ManDecoratorB(person);
        md1.eat();
        System.out.println("===============");
        md2.eat();
    }

運(yùn)行結(jié)果為:

QQ圖片20171208164154.png

可以看到在吃飯前和吃飯后做了一些事情栋猖,這就達(dá)到了不增加子類而又可以添加一些額外功能的作用

回到ContextImpl和Context,其實(shí)也是一樣的汪榔,這個(gè)ContextImpl相當(dāng)于代碼中的Man,而Context相當(dāng)于Person,只是一個(gè)接口蒲拉,一個(gè)抽象類,但本質(zhì)都是一樣痴腌,而Decorator相當(dāng)于ContextWrapper雌团,那么具體的抽象實(shí)現(xiàn)類為什么呢,在Android中Activity和Service就是類似代碼中的ManDecoratorA和ManDecoratorB了士聪,只不過Activity有界面锦援,自然就有Theme主題,因此對(duì)應(yīng)的是ContextThemeWrapper剥悟,現(xiàn)在已經(jīng)對(duì)裝飾模式理解的更深了吧灵寺。

現(xiàn)在回到ContextImpl本身來,ContextImpl作為Context的抽象類区岗,實(shí)現(xiàn)了所有的方法略板,我們常見的getResources(),getAssets(),getApplication()等等的具體實(shí)現(xiàn)都是在ContextImpl的,下面是具體的一些代碼

 @Override
    public ContentResolver getContentResolver() {
        return mContentResolver;
    }

    @Override
    public Looper getMainLooper() {
        return mMainThread.getLooper();
    }

    @Override
    public Context getApplicationContext() {
        return (mPackageInfo != null) ?
                mPackageInfo.getApplication() : mMainThread.getApplication();
    }

可以看到我們平常開發(fā)所調(diào)用的方法的實(shí)現(xiàn)都是在這里完成慈缔,作為一名android開發(fā)者叮称,我們應(yīng)該多去研究一些源碼之類的,這樣在出問題的時(shí)候可以根據(jù)源碼找出問題的具體所在藐鹤,ContextImpl在主線程ActivityThread通過傳入主線程對(duì)象創(chuàng)建了一個(gè)系統(tǒng)的ContextImpl,下面是代碼:

 public ContextImpl getSystemContext() {
        synchronized (this) {
            if (mSystemContext == null) {
                mSystemContext = ContextImpl.createSystemContext(this);
            }
            return mSystemContext;
        }
    }

這個(gè)是在ActivityThread里面完成的瓤檐,ActivityThread是Android應(yīng)用程序的主線程環(huán)境,關(guān)于對(duì)ActivityThread的分析教藻,可以看我的另一篇文章 關(guān)于Android主線程(ActivityThread)源代碼分析以及一些特殊問題的非常規(guī)方法
大家有空可以去看看,實(shí)際上Activity和Service中的Context也是通過ContextImpl來的,大家有時(shí)間可以去看看主線程的源碼距帅,好了,我們知道了ContextImpl里面的方法了括堤,下面說一個(gè)具體的不太常見的需求.曾經(jīng)有個(gè)需求碌秸,要求用戶在卸載之后再重新安裝進(jìn)入的時(shí)候能夠讀取一些配置信息绍移,當(dāng)初服務(wù)端要求這個(gè)客戶端自己去實(shí)現(xiàn),有同學(xué)說了這個(gè)本身不難讥电,很簡(jiǎn)單啊蹂窖,用SharePreference就可以搞定,恩恩敌,是很簡(jiǎn)單瞬测,可是需求說了再卸載之后重新進(jìn)入的時(shí)候需要讀取出來原來的配置,卸載之后整個(gè)應(yīng)用程序的目錄都不見了纠炮,所有數(shù)據(jù)都消失了月趟,配置文件哪里來呢?,我們知道恢口,SharePreference是保存在一個(gè)叫做shared_prefs目錄下面的孝宗,這個(gè)目錄隨著程序卸載也會(huì)被刪掉,也就是說卸載之后耕肩,保存在原來默認(rèn)的存儲(chǔ)就會(huì)全部消失因妇,那應(yīng)該怎么辦呢,其實(shí)只需要修改原來的路徑改為自定義的路徑就好猿诸,比如放在外部SD卡或者其他地方婚被,這樣程序卸載的時(shí)候就不會(huì)刪除這些自定義的目錄了,從而可以在安裝再次進(jìn)入的時(shí)候讀取出來梳虽,看起來這個(gè)方法可以的

ContextImpl里面有一個(gè)字段mPreferencesDir址芯,這個(gè)文件目錄就是保存了SharePreference路徑的,我們只需要修改這個(gè)為我們自定義的路徑就好了怖辆,由于ContextImpl是一個(gè)隱藏類是复,我們需要使用反射去實(shí)現(xiàn)删顶,隨我走一波吧竖螃,下面是具體的代碼:

try {
            Class<?> clazz=Class.forName("android.app.ContextImpl");
            Method method=clazz.getDeclaredMethod("getImpl", Context.class);
            method.setAccessible(true);
            Object mContextImpl=method.invoke(null,this);
            //獲取ContextImpl的實(shí)例
            Log.d("[app]","mContextImpl="+mContextImpl);
            Field mPreferencesDir=clazz.getDeclaredField("mPreferencesDir");
            mPreferencesDir.setAccessible(true);
            //我們自定義的目錄假設(shè)在SD卡, 其他目錄也是一樣的
            if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)){
                File file=new File(Environment.getExternalStorageDirectory(),"new_shared_pres");
                if (!file.exists()){
                    file.mkdirs();
                }
                mPreferencesDir.set(mContextImpl,file);
                Log.d("[app]","修改sp路徑成功");
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

    下面是具體的執(zhí)行結(jié)果:
    12-08 17:28:42.811 12404-12404/com.example.hotfixdemo D/[app]: mContextImpl=android.app.ContextImpl@db6fc37
12-08 17:28:42.818 12404-12404/com.example.hotfixdemo D/[app]: 修改sp路徑成功

OK,已經(jīng)成功修改為自定義的路徑了,這樣就達(dá)到目的了逗余。

實(shí)際上特咆,除了SP的路徑,ContextImpl里面還有很多類似這樣的路徑录粱,一樣是可以通過類似的手段修改的腻格,達(dá)到一些特定的目的,比如360的插件框架也是Hook了ContextImpl類的數(shù)據(jù)庫路徑啥繁,達(dá)到加載的目的菜职,大家有空可以去看看,Java層的Hook基本以反射和動(dòng)態(tài)代理為主旗闽,這兩方面的內(nèi)容酬核,有時(shí)間再寫蜜另,今天就寫到這里,感謝大家閱讀嫡意。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末举瑰,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子蔬螟,更是在濱河造成了極大的恐慌此迅,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,639評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件旧巾,死亡現(xiàn)場(chǎng)離奇詭異耸序,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)鲁猩,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,277評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門佑吝,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人绳匀,你說我怎么就攤上這事芋忿。” “怎么了疾棵?”我有些...
    開封第一講書人閱讀 157,221評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵戈钢,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我是尔,道長(zhǎng)殉了,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,474評(píng)論 1 283
  • 正文 為了忘掉前任拟枚,我火速辦了婚禮薪铜,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘恩溅。我一直安慰自己隔箍,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,570評(píng)論 6 386
  • 文/花漫 我一把揭開白布脚乡。 她就那樣靜靜地躺著蜒滩,像睡著了一般。 火紅的嫁衣襯著肌膚如雪奶稠。 梳的紋絲不亂的頭發(fā)上俯艰,一...
    開封第一講書人閱讀 49,816評(píng)論 1 290
  • 那天,我揣著相機(jī)與錄音锌订,去河邊找鬼竹握。 笑死,一個(gè)胖子當(dāng)著我的面吹牛辆飘,可吹牛的內(nèi)容都是我干的啦辐。 我是一名探鬼主播污秆,決...
    沈念sama閱讀 38,957評(píng)論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼昧甘!你這毒婦竟也來了良拼?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,718評(píng)論 0 266
  • 序言:老撾萬榮一對(duì)情侶失蹤充边,失蹤者是張志新(化名)和其女友劉穎庸推,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體浇冰,經(jīng)...
    沈念sama閱讀 44,176評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡贬媒,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,511評(píng)論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了肘习。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片际乘。...
    茶點(diǎn)故事閱讀 38,646評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖漂佩,靈堂內(nèi)的尸體忽然破棺而出脖含,到底是詐尸還是另有隱情,我是刑警寧澤投蝉,帶...
    沈念sama閱讀 34,322評(píng)論 4 330
  • 正文 年R本政府宣布养葵,位于F島的核電站,受9級(jí)特大地震影響瘩缆,放射性物質(zhì)發(fā)生泄漏关拒。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,934評(píng)論 3 313
  • 文/蒙蒙 一庸娱、第九天 我趴在偏房一處隱蔽的房頂上張望着绊。 院中可真熱鬧,春花似錦熟尉、人聲如沸归露。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,755評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽靶擦。三九已至,卻和暖如春雇毫,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背踩蔚。 一陣腳步聲響...
    開封第一講書人閱讀 31,987評(píng)論 1 266
  • 我被黑心中介騙來泰國(guó)打工棚放, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人馅闽。 一個(gè)月前我還...
    沈念sama閱讀 46,358評(píng)論 2 360
  • 正文 我出身青樓飘蚯,卻偏偏與公主長(zhǎng)得像馍迄,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子局骤,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,514評(píng)論 2 348

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