無侵入式獲取全局Context

當我們在使用第三方庫耙考,或者自己封裝庫,如果需要需要用到Context時错览,一般做法就是將初始化方法暴露給調(diào)用方,讓調(diào)用方在初始化類庫時煌往,傳入Context倾哺。

publi class App extends Application {
    /**
     * 是否是Debug環(huán)境
     */
    public static final boolean IS_DEBUG = true;

    private static App mContext;

    @Override
    public void onCreate() {
        super.onCreate();
        //ARouter初始化
        if (IS_DEBUG) {
            ARouter.openLog();
            ARouter.openDebug();
        }
        ARouter.init(this);
    }
    
    public static Context getInstance() {
        return mContext;
    }
}

解決方案

其實無侵入式獲取Context的實現(xiàn)很簡單,就是使用一個ContentProvider刽脖,ContentProvider的onCreate()方法調(diào)用時羞海,調(diào)用getContext()即可獲取到Context,再靜態(tài)變量保存曲管,后續(xù)直接獲取即可却邓。

Picasso的無侵入式獲取Context

上述的原理,其實是從Picasso中借鑒的院水,一起來看一下吧腊徙。

  • Picasso實例獲取。重點在Double-Check單例中的PicassoProvider.context檬某,調(diào)用PicassoProvider的context屬性獲取Context撬腾。
static volatile Picasso singleton = null;

public static Picasso get() {
  if (singleton == null) {
    synchronized (Picasso.class) {
      if (singleton == null) {
        //重點在這里,PicassoProvider.context獲取Context
        if (PicassoProvider.context == null) {
          throw new IllegalStateException("context == null");
        }
        singleton = new Builder(PicassoProvider.context).build();
      }
    }
  }
  return singleton;
}
  • PicassoProvider類

PicassoProvider其實是一個ContentProvider的子類恢恼,只要將PicassoProvider在AndroidManifest清單文件中注冊民傻,啟動App時,Android框架會初始化這個PicassoProvider场斑,onCreate()方法被調(diào)用時漓踢,getContext()保存Context即可。

public final class PicassoProvider extends ContentProvider {
  @SuppressLint("StaticFieldLeak") static Context context;

  @Override public boolean onCreate() {
    context = getContext();
    return true;
  }   
}

代碼實現(xiàn)

  • ApplicationContextProvider漏隐,和Picasso一樣喧半,是ContentProvider的子類。在onCreate()中獲取到Context青责,再保存到ContextProvider實例中挺据。
public class ApplicationContextProvider extends ContentProvider {
    @SuppressLint("StaticFieldLeak")
    static Context mContext;

    @Override
    public boolean onCreate() {
        mContext = getContext();
        return false;
    }

    //...省略其他必須復(fù)寫的方法(空實現(xiàn)即可)
}
  • 在AndroidManifest.xml中注冊ApplicationContextProvider
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
        package="com.app.contextprovider">

    <application>
        <!-- 全局Context提供者 -->
        <provider
                android:name=".ApplicationContextProvider"
                android:authorities="${applicationId}.contextprovider"
                android:exported="false" />
    </application>
</manifest>
  • ContextProvider取具,提供全局Context的單例類,提供get()方法獲取單例實例吴菠,第一次構(gòu)造時才從ApplicationContextProvider中獲取Context來初始化自身者填。
public class ContextProvider {
    @SuppressLint("StaticFieldLeak")
    private static volatile ContextProvider instance;
    private Context mContext;

    private ContextProvider(Context context) {
        mContext = context;
    }

    /**
     * 獲取實例
     */
    public static ContextProvider get() {
        if (instance == null) {
            synchronized (ContextProvider.class) {
                if (instance == null) {
                    Context context = ApplicationContextProvider.mContext;
                    if (context == null) {
                        throw new IllegalStateException("context == null");
                    }
                    instance = new ContextProvider(context);
                }
            }
        }
        return instance;
    }

    /**
     * 獲取上下文
     */
    public Context getContext() {
        return mContext;
    }

    public Application getApplication() {
        return (Application) mContext.getApplicationContext();
    }
}
  • 使用,例如使用全局Context發(fā)送廣播
Intent intent = new Intent(action_update_user_info)
ContextProvider.get().getContext().sendBroadcast(intent);

leakcanary中的無侵入式初始化

leakcanary是Android非常出名的內(nèi)存泄露檢測庫做葵,由square公司出品占哟。以前版本的需要我們手動調(diào)用install()初始化,最新版已經(jīng)不需要了酿矢,原因就是也采用ContentProvider自動被Android框架調(diào)用的方式榨乎,獲取到Context后進行install()。

AppWatcherInstaller瘫筐,也是ContentProvider的子類蜜暑。在onCreate()時,調(diào)用了InternalAppWatcher的install()方法進行安裝策肝。

//ContentProvider子類
internal class AppWatcherInstaller : ContentProvider() {
    override fun onCreate(): Boolean {
        SharkLog.logger = DefaultCanaryLog()
        val application = context!!.applicationContext as Application
        //無侵入式安裝
        InternalAppWatcher.install(application)
        return true
  }
  
  //省略其他代碼...
}

internal object InternalAppWatcher {
  //省略其他代碼...

  //安裝
  fun install(application: Application) {
    SharkLog.d { "Installing AppWatcher" }
    checkMainThread()
    //安裝過就不安裝了肛捍,避免多進程重復(fù)初始化
    if (this::application.isInitialized) {
      return
    }
    InternalAppWatcher.application = application
    //安裝
    val configProvider = { AppWatcher.config }
    ActivityDestroyWatcher.install(application, objectWatcher, configProvider)
    FragmentDestroyWatcher.install(application, objectWatcher, configProvider)
    onAppWatcherInstalled(application)
  }
  
  //省略其他代碼...
}

AutoSize屏幕適配框架自動初始化

AutoSize是一個今日頭條屏幕適配方案的第三方庫,內(nèi)部也有一個InitProvider之众,繼承于ContentProvider拙毫,在onCreate()中調(diào)用AutoSizeConfig類的init()方法進行初始化,減少了配置棺禾。

public class InitProvider extends ContentProvider {
    @Override
    public boolean onCreate() {
        AutoSizeConfig.getInstance()
                .setLog(true)
                .init((Application) getContext().getApplicationContext())
                .setUseDeviceSize(false);
        return true;
    }
    
    //省略其他代碼...
}

總結(jié)

ContentProvider方法無侵入式初始化方案的優(yōu)缺點:

  • 優(yōu)點:對于固定的初始化配置缀蹄,可以使用ContextProvider方案減少調(diào)用方的配置,減少出錯膘婶。

  • 缺點:如果初始化非常耗時缺前,無疑會拖慢App的啟動,如果是耗時初始化悬襟,應(yīng)該提供給調(diào)用方自行決定衅码,例如將初始化推遲到主界面onCreate()時才調(diào)用初始化。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末古胆,一起剝皮案震驚了整個濱河市肆良,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌逸绎,老刑警劉巖,帶你破解...
    沈念sama閱讀 207,113評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件夭谤,死亡現(xiàn)場離奇詭異棺牧,居然都是意外死亡,警方通過查閱死者的電腦和手機朗儒,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,644評論 2 381
  • 文/潘曉璐 我一進店門颊乘,熙熙樓的掌柜王于貴愁眉苦臉地迎上來参淹,“玉大人,你說我怎么就攤上這事乏悄≌阒担” “怎么了?”我有些...
    開封第一講書人閱讀 153,340評論 0 344
  • 文/不壞的土叔 我叫張陵檩小,是天一觀的道長开呐。 經(jīng)常有香客問我,道長规求,這世上最難降的妖魔是什么筐付? 我笑而不...
    開封第一講書人閱讀 55,449評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮阻肿,結(jié)果婚禮上瓦戚,老公的妹妹穿的比我還像新娘。我一直安慰自己丛塌,他們只是感情好较解,可當我...
    茶點故事閱讀 64,445評論 5 374
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著赴邻,像睡著了一般印衔。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上乍楚,一...
    開封第一講書人閱讀 49,166評論 1 284
  • 那天当编,我揣著相機與錄音,去河邊找鬼徒溪。 笑死忿偷,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的臊泌。 我是一名探鬼主播鲤桥,決...
    沈念sama閱讀 38,442評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼渠概!你這毒婦竟也來了茶凳?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,105評論 0 261
  • 序言:老撾萬榮一對情侶失蹤播揪,失蹤者是張志新(化名)和其女友劉穎贮喧,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體猪狈,經(jīng)...
    沈念sama閱讀 43,601評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡箱沦,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,066評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了雇庙。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片谓形。...
    茶點故事閱讀 38,161評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡灶伊,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出寒跳,到底是詐尸還是另有隱情聘萨,我是刑警寧澤,帶...
    沈念sama閱讀 33,792評論 4 323
  • 正文 年R本政府宣布童太,位于F島的核電站米辐,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏康愤。R本人自食惡果不足惜儡循,卻給世界環(huán)境...
    茶點故事閱讀 39,351評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望征冷。 院中可真熱鬧择膝,春花似錦、人聲如沸检激。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,352評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽叔收。三九已至齿穗,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間饺律,已是汗流浹背窃页。 一陣腳步聲響...
    開封第一講書人閱讀 31,584評論 1 261
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留复濒,地道東北人脖卖。 一個月前我還...
    沈念sama閱讀 45,618評論 2 355
  • 正文 我出身青樓,卻偏偏與公主長得像巧颈,于是被迫代替她去往敵國和親畦木。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 42,916評論 2 344