怎么在Android中動(dòng)態(tài)替換Application

這期內(nèi)容當(dāng)中小編將會(huì)給大家?guī)?lái)有關(guān)怎么在Android中動(dòng)態(tài)替換Application,文章內(nèi)容豐富且以專(zhuān)業(yè)的角度為大家分析和敘述,閱讀完這篇文章希望大家可以有所收獲。

在皋蘭等地區(qū),都構(gòu)建了全面的區(qū)域性戰(zhàn)略布局,加強(qiáng)發(fā)展的系統(tǒng)性、市場(chǎng)前瞻性、產(chǎn)品創(chuàng)新能力,以專(zhuān)注、極致的服務(wù)理念,為客戶(hù)提供成都做網(wǎng)站、成都網(wǎng)站設(shè)計(jì) 網(wǎng)站設(shè)計(jì)制作定制網(wǎng)站設(shè)計(jì),公司網(wǎng)站建設(shè),企業(yè)網(wǎng)站建設(shè),成都品牌網(wǎng)站建設(shè),成都營(yíng)銷(xiāo)網(wǎng)站建設(shè),成都外貿(mào)網(wǎng)站建設(shè)公司,皋蘭網(wǎng)站建設(shè)費(fèi)用合理。

在替換Application的過(guò)程中,應(yīng)該注意以下幾點(diǎn):

  1. 創(chuàng)建RealApplication,維護(hù)正常的生命周期,并進(jìn)行回調(diào)。

  2. 對(duì)應(yīng)用中屏蔽掉ProxyApplication,對(duì)于下層無(wú)感知。在Activity等調(diào)用getApplicationContext之后,應(yīng)該返回RealApplication。

  3. ContentProvider創(chuàng)建時(shí)機(jī)比較特殊,在滿(mǎn)足正常的初始化順序之后,也要屏蔽ProxyApplication的存在。

方案實(shí)現(xiàn)

在AndroidManifest.xml文件中替換Application為ProxyApplication,可以使用自動(dòng)化方式,或者打包方式,關(guān)于實(shí)現(xiàn)的具體細(xì)節(jié)此處不討論。這里主要敘述創(chuàng)建RealApplication的過(guò)程。替換了ProxyApplication之后,對(duì)于系統(tǒng)而言ProxyApplication就是應(yīng)用初始化的入口,所有的回調(diào)均是在ProxyApplication中發(fā)生。我們主要關(guān)注attachBaseContext和onCreate的回調(diào)。

創(chuàng)建RealApplication

創(chuàng)建RealApplication,我們可以使用反射的方式newInstance創(chuàng)建對(duì)象,然后執(zhí)行回調(diào)attachBaseContext。但是對(duì)于不同的系統(tǒng)版本,內(nèi)部執(zhí)行的細(xì)節(jié)可能不同,或者有其它相關(guān)邏輯的處理,所以我們采用另一種方式進(jìn)行處理。首先看系統(tǒng)源碼的如何實(shí)現(xiàn),這里選擇8.0.0的系統(tǒng)源碼進(jìn)行分析,其它版本去http://androidxref.com/查看。

我們知道,Android初始化是從android.app.ActivityThread開(kāi)始的,所以從ActivityThread開(kāi)始查看,ActivityThread中存在靜態(tài)方法currentActivityThread返回實(shí)例。可以參考系統(tǒng)的ActivityThread類(lèi):

public static ActivityThread currentActivityThread() {
  return sCurrentActivityThread;
 }

ActivityThread內(nèi)部存在成員變量AppBindData mBoundApplication。AppBindData是一個(gè)靜態(tài)內(nèi)部類(lèi),其中包含成員變量LoadedApk info。查看android.app.LoadedApk源代碼,發(fā)現(xiàn)創(chuàng)建Application的makeApplication方法。

 public Application makeApplication(boolean forceDefaultAppClass,
   Instrumentation instrumentation) {
  if (mApplication != null) {
   return mApplication;
  }

  Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "makeApplication");

  Application app = null;

  String appClass = mApplicationInfo.className;
  if (forceDefaultAppClass || (appClass == null)) {
   appClass = "android.app.Application";
  }

  try {
   java.lang.ClassLoader cl = getClassLoader();
   if (!mPackageName.equals("android")) {
    Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,
      "initializeJavaContextClassLoader");
    initializeJavaContextClassLoader();
    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
   }
   ContextImpl appContext = ContextImpl.createAppContext(mActivityThread, this);
   app = mActivityThread.mInstrumentation.newApplication(
     cl, appClass, appContext);
   appContext.setOuterContext(app);
  } catch (Exception e) {
   if (!mActivityThread.mInstrumentation.onException(app, e)) {
    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
    throw new RuntimeException(
     "Unable to instantiate application " + appClass
     + ": " + e.toString(), e);
   }
  }
  mActivityThread.mAllApplications.add(app);
  mApplication = app;

  if (instrumentation != null) {
   try {
    instrumentation.callApplicationOnCreate(app);
   } catch (Exception e) {
    if (!instrumentation.onException(app, e)) {
     Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
     throw new RuntimeException(
      "Unable to create application " + app.getClass().getName()
      + ": " + e.toString(), e);
    }
   }
  }

  // Rewrite the R 'constants' for all library apks.
  SparseArray<String> packageIdentifiers = getAssets(mActivityThread)
    .getAssignedPackageIdentifiers();
  final int N = packageIdentifiers.size();
  for (int i = 0; i < N; i++) {
   final int id = packageIdentifiers.keyAt(i);
   if (id == 0x01 || id == 0x7f) {
    continue;
   }

   rewriteRValues(getClassLoader(), packageIdentifiers.valueAt(i), id);
  }

  Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);

  return app;
 }

通過(guò)上面的代碼可以發(fā)現(xiàn),如果緩存mApplication不為空,則直接返回。mApplication為空時(shí),則創(chuàng)建RealApplication,并且執(zhí)行相關(guān)的回調(diào),創(chuàng)建RealApplication時(shí),類(lèi)名是從mApplicationInfo.className中獲取。

添加新創(chuàng)建RealApplication到mActivityThread.mAllApplications。賦值給緩存mApplication。所以我們?cè)谡{(diào)用makeApplication之前,需要將mApplication置為null,否則會(huì)直接返回ProxyApplication的實(shí)例。

首先,通過(guò)android.app.ActivityThread中靜態(tài)方法獲取ActivityThread實(shí)例,然后,通過(guò)ActivityThread實(shí)例,獲得LoadedApk實(shí)例。為了使makeApplication順利執(zhí)行,先設(shè)置mApplication為null。移除mAllApplications中ProxyApplication的實(shí)例。LoadedApk中mApplicationInfo和AppBindData中appInfo都是ApplicationInfo類(lèi)型,需要分別替換className字段的值為RealApplication的實(shí)際類(lèi)全名。

怎么在Android中動(dòng)態(tài)替換Application 

之后,反射調(diào)用系統(tǒng)的makeApplication.

怎么在Android中動(dòng)態(tài)替換Application

這樣,在ProxyApplication.attachBaseContext中,調(diào)用makeApplication創(chuàng)建RealApplication,并且內(nèi)部已經(jīng)完成對(duì)于RealApplication的attchBaseContext的回調(diào)。在ProxyApplication.onCreate中只需要回調(diào)RealApplication實(shí)例的onCreate,即可完成對(duì)于RealApplication的創(chuàng)建,已經(jīng)內(nèi)部替換以及正常的生命周期的回調(diào)。而且在Activity中調(diào)用getApplicationContext返回的值,實(shí)際上也是LoadedApk中mApplication的值,同時(shí)也保證對(duì)于Activity等地方屏蔽ProxyApplication的目的。

ContentProvider中g(shù)etContext

Application和ContentProvider的初始化順序是:Application.attachBaseContext -> ContentProvider.onCreate -> Application.onCreate。ContentProvider中也存在getContext方法,看ContentProvider的源代碼實(shí)現(xiàn):

怎么在Android中動(dòng)態(tài)替換Application

其中mContext被賦值的有兩個(gè)地方,一個(gè)在構(gòu)造方法,一個(gè)是attchInfo的時(shí)候。繼續(xù)追蹤源代碼中使用構(gòu)造方法初始化,或者調(diào)用attachInfo的地方,結(jié)果在android.app.ActivityThread中找到installProvider方法中存在著調(diào)用關(guān)系。

怎么在Android中動(dòng)態(tài)替換Application 

可以看出,使用反射調(diào)用ContentProvider無(wú)參構(gòu)造方法創(chuàng)建實(shí)例,然后調(diào)用了attachInfo,傳遞的Context為installProvider方法中的參數(shù),而installProvider的參數(shù)是在installContentProviders內(nèi)部在初始化中傳遞的。

怎么在Android中動(dòng)態(tài)替換Application

可以明確,installContentProviders中調(diào)用installProvider時(shí)傳遞的Context,也是由方法調(diào)用時(shí)傳遞的參數(shù)。繼續(xù)向上追蹤發(fā)現(xiàn)ActivityThread.handleBindApplication在初始化ContentProvider時(shí)調(diào)用了installContentProviders,最終通過(guò)attachInfo設(shè)置給ContentProvider中的Context的實(shí)際類(lèi)型是Application。

在App初始化時(shí),系統(tǒng)調(diào)用makeApplication創(chuàng)建了ProxyApplication實(shí)例,同時(shí)回調(diào)了attachBaseContext(Context context)。所以這個(gè)方法返回的就是App初始化時(shí)ProxyApplication,調(diào)用發(fā)生ProxyApplication.attachBaseContext之后,ProxyApplication.onCreate之前。所以我們沒(méi)有辦法在這兩個(gè)方法生命周期內(nèi)進(jìn)行替換為RealApplication。

Android是什么

Android是一種基于Linux內(nèi)核的自由及開(kāi)放源代碼的操作系統(tǒng),主要使用于移動(dòng)設(shè)備,如智能手機(jī)和平板電腦,由美國(guó)Google公司和開(kāi)放手機(jī)聯(lián)盟領(lǐng)導(dǎo)及開(kāi)發(fā)。

上述就是小編為大家分享的怎么在Android中動(dòng)態(tài)替換Application了,如果剛好有類(lèi)似的疑惑,不妨參照上述分析進(jìn)行理解。如果想知道更多相關(guān)知識(shí),歡迎關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道。

網(wǎng)頁(yè)題目:怎么在Android中動(dòng)態(tài)替換Application
文章轉(zhuǎn)載:http://bm7419.com/article22/pcspjc.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供企業(yè)網(wǎng)站制作、外貿(mào)建站、App設(shè)計(jì)、網(wǎng)站建設(shè)、網(wǎng)站設(shè)計(jì)公司、

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶(hù)投稿、用戶(hù)轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話(huà):028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來(lái)源: 創(chuàng)新互聯(lián)

手機(jī)網(wǎng)站建設(shè)