【Android 源码拆解解析】Context 及其子类

作者:ca88编程

回去最开头以来,App 运营的时候,肯定期存款在起码三个 Application 实例。假设大家可以在系统成立那些实例的时候,获取那么些实例的选用,是还是不是就能够全局获取 Context 实例了(因为那几个实例是运作时一向留存的,所以也就不要操心静态 Context 实例带给的难题)。那么难点来了,Application 实例是如哪天候创立的吗?首先先来拜望大家日常用来获得 Base Context 实例的Application#attachBaseContext方法,它是后续自ContextWrapper#attachBaseContext的。

1、类注释翻译

Context的代理类,具体的调用由其余叁个Context来得以达成,除了不可能修正真正的Context,别的地点子类是足以改过的。

  • 注解说的很明显ContextWrapper是个代理类,在此之前大家早就疏解过了,那是大名鼎鼎的静态代理,起到确实作用的是Context mBase。
  • 我们开采ContextWrapper的兼具办法均是调用的mBase对应的议程,所以尤其证实了ContextWrapper是代理,mBase才是确实的奉行者。
  • 所以说它的三个子类ContextThemeWrapper,Service,Application也是无法改造具体由mBase之处
  • 此类的布局函数满含了一个真正的Context援引(ContextImpl对象),然后就改为了ContextImpl的装裱着方式。
private class H extends Handler {
        public static final int LAUNCH_ACTIVITY         = 100;
        public static final int PAUSE_ACTIVITY          = 101;
        public static final int PAUSE_ACTIVITY_FINISHING= 102;
  public void handleMessage(Message msg) {
            if (DEBUG_MESSAGES) Slog.v(TAG, ">>> handling: "   codeToString(msg.what));
            switch (msg.what) {
                case LAUNCH_ACTIVITY: {
                    Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityStart");
                    final ActivityClientRecord r = (ActivityClientRecord) msg.obj;

                    r.packageInfo = getPackageInfoNoCheck(
                            r.activityInfo.applicationInfo, r.compatInfo);
                    handleLaunchActivity(r, null);
                    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
                } 
    break;
  }
}
单例援引 Activity Context

缓和方法:

  1. 使用 Applicaion Context 代替 Activity Context ;
  2. 在调用的地点使用弱援引

ca88编程 1

1、问题

平常会遇见Android程序中如此使用handler:

public class CustomerActivity {
    // 省略部分代码
void createHandler() {
    new Handler() {
        @Override public void handleMessage(Message message) {
            super.handleMessage(message);
        }
    }.postDelayed(new Runnable() {
        @Override public void run() {
            while(true);
        }
    }, 1000);
}

      View mBtn = findViewById(R.id.h_button);
      mBtn.setOnClickListener(new View.OnClickListener() {
               @Override 
               public void onClick(View v) {
                          createHandler();
                         nextActivity();
                }
         });
      // 省略部分代码
}

当Appl运维之后,framework会首先扶助我们落成UI线程的新闻循环,相当于在UI线程中,Loop、MessageQueue、Message等等这一个实例已经由framework帮大家落实了。全部的App主要事件,比方Activity的生命周期方法、Button的点击事件都包蕴在此个Message里面,那一个Message都会步向到MessageQueue中去,所以,UI线程的音信循环贯穿于整个Application生命周期。因而当你在UI线程中生成Handler的实例,就能够具有Loop甚至MessageQueue的援用。

packageInfo是七个LoadApk对象,假设为空,则抛出非常,由于程序已经起步,因而她不会为空,这时会回到叁个ContextImpl对象。

五、各样 Context 在 ActivityThread 中实例化进度源码深入分析

Context 的兑现是 ContextImpl,Activity、Application 和 瑟维斯的创导都以在 ActivityThread 中成功的。

又是何人调用了Application#attach方法呢?一路下来能够直接固定到Instrumentation#newApplication(Class<?>, ContextState of Qatar方法里(那个主意名很好懂啊,一看就清楚是干啥的)。

二、Context的概念

5.createAppContext(mActivityThread, this)
创立三个ContextImpl实例

二、Context 的多寡及功用域

Context 数量 = Activity 数量 Service 数量 1(Application 数量)

Context 作用域

鉴于 Context 的实际实例是由 ContextImpl 类去得以达成的,因而在大相当多现象下,Activity、Service 和 Application 这两种等级次序的 Context 都以能够通用的。
但是有两种现象相比相当:

  1. Dialog 则必需在一个 Activity 上边弹出(除非是 System Alert类型的 Dialog),由此在这里种光景下,我们不能不使用 Activity 类型的 Context,不然将会出错。
  2. 非 Activity 类型的 Context 并不曾所谓的天职栈,所以待运维的 Activity 就找不到栈了。消除那么些主题材料的诀窍正是为待运维的 Activity 指定FLAG_ACTIVITY_NEW_TASK 标志位,那样起步的时候就为它成立八个新的义务栈,而这个时候 Activity 是以 singleTask 形式运营的。全数这种用 Application 运营 Activity 的办法不引入应用,Service 同 Application。
  3. 在 Application 和 Service 中去 layout inflate 也是法定的,然而会利用系统暗中认可的核心样式,要是你自定义了少数样式大概不会被应用。所以这种艺术也不引入使用。

ca88编程 2

一、前言:

Context在android 系统中的地位显然,并且Context对于我们Android开垦同学来讲,也并不目生。在大家一向职业中我们平日会用Context来获得应用软件的财富,开启Activity,获取系统Service服务,发送广播等,平时常有人会问:

Context到底是何许?
Context怎样落成以上功效的?
何以分裂的Context访谈财富都赢得的是一律套财富?
为啥分歧景观下要利用分裂的Context?
三个APP中有微微个Context?
getApplication和getApplicationContext是同一个对象呢?

那正是说大家就带着上述的内容来伊始我们明天的剧情。

activity.performCreate(icicle)会完成Activity的onCreate回调:

三、获取 Context 对象

  1. View 的 getContext 方法,再次回到 View 的 Context,经常是当前正在呈现的 Activity 的对象,平时在自定义 View 时会用到;
  2. Activity.this 再次回到当前 Activity 实例,假如是 UI 控件需求采纳Activity 作为 Context 对象,可是私下认可的 Toast 实际上接纳ApplicationContext 也能够;
  3. Activity 和 Service 的 getApplication 方法,返回 Application 的实例;
  4. getApplicationContext 方法,获取当前应用的 Context 对象,也便是Application 的实例,与上贰个主意不一样的是专项的对象分歧;

ca88编程 3

(五State of Qatar、那多少个Context之间的关联

  • 从以上能够见到,创制Context对象的进度基本均等,区别的可是是本着Application、Activity、Service使用了分歧的数额对象。
  • 八个应用程序的Context的个数应为:Context个数=Service个数 Activity个数 Application个数,要是是平日应用Application个数是二个,假使是插件化中的多进度应用,则Application是多进度个数。
  • 应用程序中包括八个ContextImpl对象,而其间变量mPackageInfo却指向了同二个LoadedApk对象,这种布置结构意味着ContextImpl中大多数展开包操作的份量级函数实际上都转载了mPackageInfo对象的响应措施,也正是实在调用了同贰个LoadedApk对象。

5.newActivity

static 间接或直接修饰 Activity Context

解决措施:

  1. 在任哪天候都不建议用 static 修饰 Activity,因为 static 变量和选拔的水保时间是均等的,那样 Activity 生命周期甘休时,援引照旧被全数;
  2. 在合适的地点比方 onDestroy 方法中置空 Activity Context 的援用;
  3. 采用软援用化解,确认保证在 Activity 销毁时,垃圾回笼机制可以将其回笼。

ActivityThread#bindApplication –> ActivityThread#handleBindApplication –> LoadedApk#makeApplication –> Instrumentation#newApplication –> Application#attach –> ContextWrapper#attachBaseContext

三、Context的族谱

那大家来看下他们的切实可行子类,在Context官网的API如下:

ca88编程 4

官网的API.png

图表太大,加上超多类我们临时无需思谋,收拾了下,如下图:

ca88编程 5

Context类及其子类的后续关系.png

我们以activity为本体,以持续的样式来分析,能够大要分为4辈儿,在那之中activity是本级;ContextThemeWrapper是Activity的父类,Service、Aplication是Actiivty的叔类;ContextWrapper是Activity的祖父类、ContextImpl是Activity的叔祖父类;Context是Activity的曾祖父类。

我们再自上而下的去传授下:

  • 1、Context类是叁个自始自终的抽象类
  • 2.1、ContextImpl是Context的宛在近期贯彻类,达成了Context的具有功用。
  • 2.2、ContextWrapper,通过字面就理解是多少个Context的包裹类,由于ContextWrapper就叁个结构函数,且务须求传播多个Context,所以ContextWrap中兼有有二个mBase变量的施用,通过调用attachBaseContext(Context卡塔尔(قطر‎方法用于给ContextWrapper对象中mBase钦点真正的Context对象。传入的变量正是ContextImpl对象。
  • 3.1、ContextThemeWrapper内部卷入了包涵了与宗旨相关的接口,那样就能够在AndroidManifest.xml文件中加多android:theme为Application或许Activity内定的大旨,所以Actvity世襲ContextThemeWrapper
  • 3.2、由于Application不供给分界面,所以直接接轨自ContextWrapper。
  • 3.3、由于Service相符无需分界面,所以也直接接轨自ContextWrapper。
  • 4、由于Activity要求主旨,所以Activity世袭自ContextThemeWrapper。

PS:由此在绝对多数场景下,Activity、Service和Application这两种档期的顺序的Context都是足以通用的,因为他们都以ContextWrapper的子类,可是出于要是是运营Activity,弹出Dialog,日常涉及到"分界面"的天地,都一定要由Activity来运营。因为出于安全的设想,Android是分歧意Activity或然Dialog凭空现身的,叁个Activity的起步必得树立在另三个Activity的底工之上,也便是以此形成了归来栈。而Dialog则必需在二个Activity上边弹出(要是是系统的除此之外State of Qatar,因此这种情景下大家只可以使用Activity类型的Context。收拾了有个别利用情形的规行矩步,也正是Context的效用域,如下图:

ca88编程 6

Context作用域.png

从上图,大家知道Activity作为Context,成效域最广,是因为Activity世袭自ContextThemeWrapper,而Application和Service世襲自ContextWrapper。很扎眼ContextThemeWrapper在ContextWrapper的底蕴上又做了部分操作使Activity卓殊强硬,关于源码,大家上面会讲课。YES和NO,太明了就不提了,轻松说下不引入的两种状态:

  • 1、假若大家用Application作为Context去运维几个并没有设置过LaunchMode的Activity会报错"android.util.AndroidRuntimeException: Calling startActivity from outside of an Activity context requires the FLAG_ACTIVITY_NEW_TASK flag. Is this really what you want?",那是因为非Activity类型的Context并从未所谓的职分栈,所以待运行的Activity就找不到栈了。消灭这几个标题方法正是为待运营的Activity钦定FLAG_ACTIVITY_NEW_TASK的标识位,那样起步这些Activity,它就能够制造二个新的职分栈,当时Activity是以singleTask形式运转的,全部这种用Application运营Activity形式不引入使用,Service同理。
  • 2、在Application和瑟维斯中央银行使LayoutInflater也是合法的,可是会采纳系统默许的核心样式,那样您自定义的表率大概会遭到震慑,所以这种办法也不推荐应用。

小结一下:
和"分界面"(UI卡塔尔有关的,都应当使用Activity作为Context来拍卖,其余的能够应用Service、Application恐怕Activity作为Context。关于Context内部存款和储蓄器败露的标题,上面再详尽将执教。

Service运行上下文意况Context的创始

Service中ContextImpl实例化源码解析

startService 只怕 bindService 方法创建三个新 Service 时就能回调 ActivityThread 类的 handleCreateService(State of Qatar 方法成功相关数据操作:

 private void handleCreateService(CreateServiceData data) {
        ......
        //类似上面Activity的创建,这里创建service对象实例
        Service service = null;
        try {
            java.lang.ClassLoader cl = packageInfo.getClassLoader();
            service = (Service) cl.loadClass(data.info.name).newInstance();
        } catch (Exception e) {
            ......
        }

        try {
            ......
            //不做过多解释,创建一个Context对象
            ContextImpl context = ContextImpl.createAppContext(this, packageInfo);
            //特别特别留意这里!!!
            //ContextImpl中有一个Context的成员叫mOuterContext,通过这条语句就可将当前新Service对象赋值到创建的ContextImpl的成员mOuterContext(也就是让ContextImpl内部持有Service)。
            context.setOuterContext(service);

            Application app = packageInfo.makeApplication(false, mInstrumentation);
            //将上面创建的context传入到service的attach方法
            service.attach(context, this, data.info.name, data.token, app,
                    ActivityManagerNative.getDefault());
            service.onCreate();
            ......
        } catch (Exception e) {
            ......
        }
    }

与实例化 Activity 的进程日常,创造一个 ContextImpl 对象赋值给 mBase 变量,同一时间 ContextImpl 的 mOuterContext 变量也具备 Service的援引,这里创办 ContextImpl 对象是用的 ContextImpl.createAppContext 方法

public final void attach(
            Context context,
            ActivityThread thread, String className, IBinder token,
            Application application, Object activityManager) {
            //特别特别留意这里!!!
            //与上面handleCreateService方法中setOuterContext语句类似,不同的在于:
            //通过ContextWrapper类的attachBaseContext方法,将handleCreateService中实例化的ContextImpl对象传入到ContextWrapper类的mBase变量,这样ContextWrapper(Context子类)类的成员mBase就被实例化为Context的实现类ContextImpl
        attachBaseContext(context);
        ......
    }

简单来讲是在这里边创办了 App 的入口 Application 类实例的,是或不是想办法获得到这些实例的运用就能够了?不,还别喜悦太早。大家得以把 Application 实例当作 Context 实例使用,是因为它具有了一个 Context 实例,实际上 Application 实例都是经过代理调用这些 base 实例的接口达成相应的 Context 工作的。在上边包车型地铁代码中,能够见到系统成立了 Application 实例 app 后,通过app.attach把 context 实例设置给了 app。直觉告诉大家,应该更为关怀这几个 context 实例是怎么开创的,能够稳固到LoadedApk#makeApplication(boolean, Instrumentation卡塔尔代码段里。

1、问题

单例,意味着这一个指标会一贯存在,假设这几个指标中有着了Activity的援引,也会招致Activity不大概被回笼,而发出内部存款和储蓄器败露

最终调用handleLaunchActivity(r, null卡塔尔国;方法来拍卖

非静态或无名氏内部类实践耗费时间任务

非静态内部类或佚名内部类都会具有所在外界类的援用
焚薮而田措施:
用 static 修饰内部类如故无名氏内部类所在的主意;

ca88编程 7

2、施工方案:

将自定义的Handler和Runnable类生命成静态内部类,来消亡和Activity的运用。

 public final void attach(
            Context context,
            ActivityThread thread, String className, IBinder token,
            Application application, Object activityManager) {
        attachBaseContext(context);
        mThread = thread;           // NOTE:  unused - remove?
        mClassName = className;
        mToken = token;
        mApplication = application;
        mActivityManager = (IActivityManager)activityManager;
        mStartCompatibility = getApplicationInfo().targetSdkVersion
                < Build.VERSION_CODES.ECLAIR;
    }

Application中ContextImpl实例化源码解析

开创 Application 的经过也在 ActivityThread 类的 handleBindApplication()方法成功有关数据操作,而 ContextImpl 的创建是在该方式中调运 LoadedApk 类的 makeApplication 方法中达成:

public Application makeApplication(boolean forceDefaultAppClass,
            Instrumentation instrumentation) {
        //只有新创建的APP才会走if代码块之后的剩余逻辑
        if (mApplication != null) {
            return mApplication;
        }
        //即将创建的Application对象
        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")) {
                initializeJavaContextClassLoader();
            }
            //不做过多解释,创建一个Context对象
            ContextImpl appContext = ContextImpl.createAppContext(mActivityThread, this);
            //将Context传入Instrumentation类的newApplication方法
            app = mActivityThread.mInstrumentation.newApplication(
                    cl, appClass, appContext);
            //特别特别留意这里!!!
            //ContextImpl中有一个Context的成员叫mOuterContext,通过这条语句就可将当前新Application对象赋值到创建的ContextImpl的成员mOuterContext(也就是让ContextImpl内部持有Application)。
            appContext.setOuterContext(app);
        } catch (Exception e) {
            ......
        }
        ......
        return app;
    }

参考:
Android应用Context详整及源码解析

此地是我们运营 Activity 的时候,Activity 实例创造的具体位置,以上代码段还可以看见雅俗共赏的”Unable to start activity” 卓殊,你们猜猜这么些丰盛是哪个人抛出来的?这里就不分流了,回到大家的标题来,以上代码段获取了三个Application 实例,不过并未保证住,看起来这里的 Application 实例就如贰个一时变量。不可能,再看看别的地点呢。接着找到ActivityThread#handleCreateService(CreateServiceData卡塔尔,可是这里也一致,并从未把收获的 Application 实例保存起来,那样我们就不曾主意取得到这几个实例了。

(一卡塔尔国、源码解读

由于Context的源码内容太多,4K多行代码,太多了,大家就大概的看下类的的笺注

/**
 * Interface to global information about an application environment.  This is
 * an abstract class whose implementation is provided by
 * the Android system.  It
 * allows access to application-specific resources and classes, as well as
 * up-calls for application-level operations such as launching activities,
 * broadcasting and receiving intents, etc.
 */
public abstract class Context {
   //省略代码
    final void attach(Context context, ActivityThread aThread,
            Instrumentation instr, IBinder token, int ident,
            Application application, Intent intent, ActivityInfo info,
            CharSequence title, Activity parent, String id,
            NonConfigurationInstances lastNonConfigurationInstances,
            Configuration config, String referrer, IVoiceInteractor voiceInteractor,
            Window window) {
        attachBaseContext(context);

        mFragments.attachHost(null /*parent*/);

        mWindow = new PhoneWindow(this, window);
        mWindow.setWindowControllerCallback(this);
        mWindow.setCallback(this);
        mWindow.setOnWindowDismissedCallback(this);
        mWindow.getLayoutInflater().setPrivateFactory(this);
        if (info.softInputMode != WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED) {
            mWindow.setSoftInputMode(info.softInputMode);
        }
        if (info.uiOptions != 0) {
            mWindow.setUiOptions(info.uiOptions);
        }
        mUiThread = Thread.currentThread();

        mMainThread = aThread;
        mInstrumentation = instr;
        mToken = token;
        mIdent = ident;
        mApplication = application;
        mIntent = intent;
        mReferrer = referrer;
        mComponent = intent.getComponent();
        mActivityInfo = info;
        mTitle = title;
        mParent = parent;
        mEmbeddedID = id;
        mLastNonConfigurationInstances = lastNonConfigurationInstances;
        if (voiceInteractor != null) {
            if (lastNonConfigurationInstances != null) {
                mVoiceInteractor = lastNonConfigurationInstances.voiceInteractor;
            } else {
                mVoiceInteractor = new VoiceInteractor(voiceInteractor, this, this,
                        Looper.myLooper());
            }
        }

        mWindow.setWindowManager(
                (WindowManager)context.getSystemService(Context.WINDOW_SERVICE),
                mToken, mComponent.flattenToString(),
                (info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);
        if (mParent != null) {
            mWindow.setContainer(mParent.getWindow());
        }
        mWindowManager = mWindow.getWindowManager();
        mCurrentConfig = config;
    }
   //省略代码
}

咱俩来归纳的翻译一下批注:

赢得一个应用程序全体消息的接口。它是一个抽象类,由Android系统提供它的切实实现类,它富有采访应用程序内一定的财富和类的权能,以招致用程序级操作,举个例子运转activity、广播和摄取intent等。

地点是法定回复,小编的精晓是:

  • ca88编程,从空中楼阁的角度来精通:
    我们日常在做事生活中经常会采纳到三个戏文——"场景",一个使用景况就表示顾客和我们的app软件人机联作的进度。比方,你在家里打”农药“就是二个情景;你在旅店和胞妹一齐用手提式无线电电话机看"小"电影也是三个场面;你在铺子用微微机调节和测量试验你的app也是一个景色。那么Context 就能够抽象的明白为一个气象,各类现象有不一样的内容。在前后相继中,我们得以知晓为某指标在前后相继中所处的"场景"(情况State of Qatar,八个与系统相互的过程,比如下边你在打”农药“,你的现象正是你的操作分界面,和您的手势相关操作的数量与传输。所以Context在加载财富、运营Activity、获取系统服务、制造View等操作都要参加进来。打电话、发短信、玩"农药"等都以有界面包车型地铁,所以是一类"有分界面包车型客车"场景,还恐怕有点从未分界面包车型大巴情景,举例您在后台播放歌曲的程序,正是一类"未有分界面"的场馆。其实二个app就描写了贰个尤为重要的场景,比如天猫,描绘的是购物的现象;微信是聊天的气象;支付宝,描绘的正是付出的情景,所以说Context其实正是二个"场景",因为Context是二个"场景",所以它能够获得这些场景下的有着音信。
  • 从研究开发的角度来精晓:
    Context是叁个抽象类,我们透过这么些Context能够访谈包内的能源(res和assetsState of Qatar和起步别的构件(activity、service、broadcast卡塔尔(قطر‎以致系统服务(systemServiceState of Qatar等。所以Context提供了三个应用程序运行条件,在Context的情状里,应用才得以访谈资源,技术和其它构件、服务交互作用,Context定义了一套根底用接口,大家可以掌握为一套标准,而Activity和Service是完结那套标准的现实性落实类(其实此中是ContextImpl统一完毕的卡塔尔(قطر‎。所以能够那样说,Context是保持Android程序中逐个零件能够寻常干活的二个大旨效率类。

地方说了Context是三个抽象类,那它的现实个性类都有啥样?大家来协同看一下他的族谱。

 public final void scheduleLaunchActivity(Intent intent, IBinder token, int ident,
                ActivityInfo info, Configuration curConfig, Configuration overrideConfig,
                CompatibilityInfo compatInfo, String referrer, IVoiceInteractor voiceInteractor,
                int procState, Bundle state, PersistableBundle persistentState,
                List<ResultInfo> pendingResults, List<ReferrerIntent> pendingNewIntents,
                boolean notResumed, boolean isForward, ProfilerInfo profilerInfo) {

           ....

            ActivityClientRecord r = new ActivityClientRecord();

          ...
            sendMessage(H.LAUNCH_ACTIVITY, r);
        }
Handler 引起的内部存款和储蓄器泄漏

这种情形和非静态或无名氏内部类实践耗费时间职分的因由相似。
总括一下:

  1. 当 Application 的 Context 能化解的情况下,何况生命周期长的靶子,预先接收 Application 的 Context
    2:不要让生命周期长于 Activity 的靶子具备到 Activity 的援引。援引了要立马置空
    3:尽量不要在 Activity 中采纳非静态内部类,因为非静态内部类会隐式持有外部类实例的引用,若是利用静态内部类,将表面实例引用作为弱引用持有。

那再轻松可是了。

(一)、ContextWrapper源码剖析

是因为ContextWrapper源码超级多,笔者只截取了一有个别,源码如下:

/**
 * Proxying implementation of Context that simply delegates all of its calls to
 * another Context.  Can be subclassed to modify behavior without changing
 * the original Context.
 */
public class ContextWrapper extends Context {
    Context mBase;

    public ContextWrapper(Context base) {
        mBase = base;
    }

    /**
     * Set the base context for this ContextWrapper.  All calls will then be
     * delegated to the base context.  Throws
     * IllegalStateException if a base context has already been set.
     * 
     * @param base The new base context for this wrapper.
     */
    protected void attachBaseContext(Context base) {
        if (mBase != null) {
            throw new IllegalStateException("Base context already set");
        }
        mBase = base;
    }

    /**
     * @return the base context as set by the constructor or setBaseContext
     */
    public Context getBaseContext() {
        return mBase;
    }

    @Override
    public AssetManager getAssets() {
        return mBase.getAssets();
    }

    @Override
    public Resources getResources() {
        return mBase.getResources();
    }

    @Override
    public PackageManager getPackageManager() {
        return mBase.getPackageManager();
    }

    @Override
    public ContentResolver getContentResolver() {
        return mBase.getContentResolver();
    }
  //省略了部分代码 
}

ContextWrapper继承自Context,和大家前边的画的类世襲图相符。

3.performLaunchActivity(r, customIntent)

Activity 中 ContextImpl 实例化源码深入分析

startActivity 运营叁个新的 Activity 时系统会回调 ActivityThread 的 handleLaunchActivity(卡塔尔国 方法,该方法内部会调用 performLaunchActivity()方法去创建三个 Activity 实例,然后回调 Activity 的 onCreate(卡塔尔(قطر‎等方法。所以 Activity 的 ContextImpl 实例化是在 ActivityThread 类的 performLaunchActivity 方法中,如下:

private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
        ......
            //已经创建好新的activity实例
            if (activity != null) {
                //创建一个Context对象
                Context appContext = createBaseContextForActivity(r, activity);
                ......
                //将上面创建的appContext传入到activity的attach方法
                activity.attach(appContext, this, getInstrumentation(), r.token,
                        r.ident, app, r.intent, r.activityInfo, title, r.parent,
                        r.embeddedID, r.lastNonConfigurationInstances, config,
                        r.referrer, r.voiceInteractor);
                ......
            }
        ......
        return activity;
    }

上面是 performLaunchActivity 的主导代码,在 performLaunchActivity 方法里面创设了 Activity 的实例,然后然后调用 createBaseContextForActivity 方法创设三个 Context 对象,这么些指标是 ContextImpl,将近日创造的 Context 对象传入到 Activity 的 attach 方法中去。

private Context createBaseContextForActivity(ActivityClientRecord r,
            final Activity activity) {
        //实质就是new一个ContextImpl对象,调运ContextImpl的有参构造初始化一些参数    
        ContextImpl appContext = ContextImpl.createActivityContext(this, r.packageInfo, r.token);
        //特别特别留意这里!!!
        //ContextImpl中有一个Context的成员叫mOuterContext,通过这条语句就可将当前新Activity对象赋值到创建的ContextImpl的成员mOuterContext(也就是让ContextImpl内部持有Activity)。
        appContext.setOuterContext(activity);
        //创建返回值并且赋值
        Context baseContext = appContext;
        ......
        //返回ContextImpl对象
        return baseContext;
    }

再看 createBaseContextForActivity 方法,实质正是经过 ContextImpl 的有参布局初阶化一些参数成立叁个 ContextImpl 对象,创建叁个 Context 对象将创立的 ContextImpl 赋值给 Context 变量并重返。
ContextImpl 有叁个分子变量 mOuterContext,通过 ContextImpl 的 setOuterContext 方法将传进来的 Activity 对象赋值给 mOuterContext,那样 ContextImpl 就全体了 Activity 的引用。

final void attach(Context context, ActivityThread aThread,
            Instrumentation instr, IBinder token, int ident,
            Application application, Intent intent, ActivityInfo info,
            CharSequence title, Activity parent, String id,
            NonConfigurationInstances lastNonConfigurationInstances,
            Configuration config, String referrer, IVoiceInteractor voiceInteractor) {
        //特别特别留意这里!!!
        //与上面createBaseContextForActivity方法中setOuterContext语句类似,不同的在于:
        //通过ContextThemeWrapper类的attachBaseContext方法,将createBaseContextForActivity中实例化的ContextImpl对象传入到ContextWrapper类的mBase变量,这样ContextWrapper(Context子类)类的成员mBase就被实例化为Context的实现类ContextImpl
        attachBaseContext(context);
        ......
    }

attach 里面重要的代码是调用 ContextThemeWrapper 类的 attachBaseContext 方法将 createBaseContextForActivity 成立的 ContextImpl 实例赋值给 ContextWrapper 的 mBase 变量,那样 ContextWrapper 的成员 mBase 就被实例化为 Context 的完成类 ContextImpl

这段代码是自己许久前看过的代码,本人不是何许决定的事物,可是这段代码段笔者到现在纪念长远。虚构,借使Foo 的接口设计能够绝不注重 Context,那么这里起码能够少五个this不是啊。

(六State of Qatar、检查测量检验内部存储器败露的工具:

LeakCanary
推荐应用LeakCanary来对内部存款和储蓄器走漏举行检查测量检验。LeakCanary是不行好用的第三方库,用来检查实验内部存款和储蓄器走漏,感兴趣的爱侣能够去查阅LeakCannary的施用方法,使用它来检查实验App中内部存款和储蓄器走漏。

感谢:
https://juejin.im/post/5865bfa1128fe10057e57c63
http://blog.csdn.net/mr_liabill/article/details/49872527
http://www.cnblogs.com/xgjblog/p/5462417.html
http://blog.csdn.net/feiduclear_up/article/details/47356289
http://www.cnblogs.com/android100/p/Android-Context.html
https://developer.android.com/reference/android/app/Activity.html
http://www.jianshu.com/p/7e78c535f1f5
http://www.jianshu.com/p/f24707874b04
http://www.jianshu.com/p/94e0f9ab3f1d
http://www.jianshu.com/p/994cd73bde53
http://blog.csdn.net/lmj623565791/article/details/40481055/
http://blog.csdn.net/qinjuning/article/details/7310620
http://blog.csdn.net/guolin_blog/article/details/47028975

和成立ApplicationContext的进程常常,首先会透过createActivityContex先成立三个ContextImpl实例appContext,然后把acitivity赋值给ContextImpl中的mOuterContext变量。

静态内部类具备外界类静态成员变量

纵然静态内部类的生命周期和表面类非亲非故,然则假若在内部类中想要引进外界成员变量的话,这些成员变量必得是静态的了,那也会有可能变成内存败露。
解决方法:
利用弱援用

那样的话, 无论在项目的怎么着地点,无论是在 App 模块仍然 Library 模块,都足以透过Applications#context(卡塔尔获取 Context 实例,何况不须要做别的初步化职业,也不用顾忌静态 Context 实例带给的标题,测量检验代码跑起来没难点,接入项目后也从不意识怎么万分,大家大概要上天了。不对,哪个地方不对。不得法,日常的话不容许那样顺遂的,那势必是错觉。果然项目上线没多长期后立即原地爆炸了,在一部分机型上,通过Applications#context(卡塔尔国获取到的 Context 恒为 null。

2、技术方案

制止在单例格局下持有Context,若是必必要持有也是Application,绝对无法是Activity。

LoadApk:
    Application getApplication() {
        return mApplication;
    }

四、Context 引起的内部存储器泄漏

貌似 Context 产生的内部存款和储蓄器泄漏都以 Activity 被灭绝后还被此外对象强援引也正是长生命周期对象具备短生命周期对象的援引而导致Activity 不可能被 GC 回笼而招致的。
招致内部存款和储蓄器泄漏的原委主要有多个:

  1. 静态的 Activity Context 或别的带有 Activity Context 的靶子(如 View)未有在该 Activity 销毁的时候置空引用;
  2. 非静态内部类或无名内部类具备外界类的援用,在 Activity 销毁后依旧实行职分;

相近的光景如下:

ca88编程 8

(三卡塔尔(قطر‎、Activity及相应的mBase实例创设进程

  • 启动Activity时,AMS(ActivityManagerService)会通过IPC调用到
    ActivtyThread的scheduleLaunchActivity() 方法,该形式满含五个参数(不是唯有五个参数State of Qatar。一种是 ActivityInfo ,那是二个贯彻了 Parcelable 接口的数据类,意味着该对象是AMS创制的,并透过IPC传递到 ActivityThread ;另一种是此外的有个别参数。
        @Override
        public final void scheduleLaunchActivity(Intent intent, IBinder token, int ident,
                ActivityInfo info, Configuration curConfig, Configuration overrideConfig,
                CompatibilityInfo compatInfo, String referrer, IVoiceInteractor voiceInteractor,
                int procState, Bundle state, PersistableBundle persistentState,
                List<ResultInfo> pendingResults, List<ReferrerIntent> pendingNewIntents,
                boolean notResumed, boolean isForward, ProfilerInfo profilerInfo) {
            updateProcessState(procState, false);
            ActivityClientRecord r = new ActivityClientRecord();
            r.token = token;
            r.ident = ident;
            r.intent = intent;
            r.referrer = referrer;
            r.voiceInteractor = voiceInteractor;
            r.activityInfo = info;
            r.compatInfo = compatInfo;
            r.state = state;
            r.persistentState = persistentState;
            r.pendingResults = pendingResults;
            r.pendingIntents = pendingNewIntents;
            r.startsNotResumed = notResumed;
            r.isForward = isForward;
            r.profilerInfo = profilerInfo;
            r.overrideConfig = overrideConfig;
            updatePendingConfiguration(curConfig);
            sendMessage(H.LAUNCH_ACTIVITY, r);
        }

H类其实是三个Handler

  private class H extends Handler {
        //省略部分代码
       public void handleMessage(Message msg) {
            if (DEBUG_MESSAGES) Slog.v(TAG, ">>> handling: "   codeToString(msg.what));
            switch (msg.what) {
                case LAUNCH_ACTIVITY: {
                    Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityStart");
                    final ActivityClientRecord r = (ActivityClientRecord) msg.obj;

                    r.packageInfo = getPackageInfoNoCheck(
                            r.activityInfo.applicationInfo, r.compatInfo);
                    handleLaunchActivity(r, null, "LAUNCH_ACTIVITY");
                    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
                } break;
            //省略部分代码
         }
      //省略部分代码
}

scheduleLaunchActivity() 方法中会根据以上二种参数布局三个 ActivityRecord 数据类, ActivityThread 内部会为每三个 Activity 创制二个 ActivityRecord 对象,并行使这几个数量对象来管理 ActivityscheduleLaunchActivity() 方法施行完后会调用发送二个Message到H,H类重写了叁个 handleMessage() ,然后会调用到 case LAUNCH_ACTIVITY中 ,在内部又调用了 handleLaunchActivity(ActivityClientRecord , Intent, String) ,那大家进来 handleLaunchActivity 方法内部看个毕竟

     //ActiviyThread.java
    private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent, String reason) {
         //省略部分代码
        Activity a = performLaunchActivity(r, customIntent);
        if (a != null) {
            r.createdConfig = new Configuration(mConfiguration);
            reportSizeConfigurations(r);
            Bundle oldState = r.state;
            handleResumeActivity(r.token, false, r.isForward,
                    !r.activity.mFinished && !r.startsNotResumed, r.lastProcessedSeq, reason);

            if (!r.activity.mFinished && r.startsNotResumed) {

                performPauseActivityIfNeeded(r, reason);

                if (r.isPreHoneycomb()) {
                    r.state = oldState;
                }
            }
        } else {
             //省略部分代码
        }
    }

handleLaunchActivity(ActivityClientRecord , Intent, String) 方法调用 performLaunchActivity() 来获取一个Activity,大家猜到其实是 performLaunchActivity(ActivityClientRecord , Intent) 方法内部才是真的创制Activity的地点,那大家就来看下源码,代码如下:

     //ActiviyThread.java
 private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
        //省略部分代码
************************重点******************************
        Activity activity = null;
        try {
            java.lang.ClassLoader cl = r.packageInfo.getClassLoader();
            activity = mInstrumentation.newActivity(
                    cl, component.getClassName(), r.intent);
            StrictMode.incrementExpectedActivityCount(activity.getClass());
            r.intent.setExtrasClassLoader(cl);
            r.intent.prepareToEnterProcess();
            if (r.state != null) {
                r.state.setClassLoader(cl);
            }
        } catch (Exception e) {
            if (!mInstrumentation.onException(activity, e)) {
                throw new RuntimeException(
                    "Unable to instantiate activity "   component
                      ": "   e.toString(), e);
            }
        }
            //省略部分代码
            if (activity != null) {
                Context appContext = createBaseContextForActivity(r, activity);
                //省略部分代码
                activity.attach(appContext, this, getInstrumentation(), r.token,
                        r.ident, app, r.intent, r.activityInfo, title, r.parent,
                        r.embeddedID, r.lastNonConfigurationInstances, config,
                        r.referrer, r.voiceInteractor, window);
************************重点******************************
              //省略部分代码
    }

世家注重大区域,通过翻阅源码,大家能够收获如下新闻:

  • 1、 activity 成立时通过调用 Instrumentationd的newActivity(ClassLoader, String,Intent) 方法来收获的
  • 2、里面调用 ContextImpl的(ActivityClientRecord , Activity) 方法得到三个 ContextImpl 对象
  • 3、 Activity 的attach(Context,ActivityThread,Instrumentation, IBinder, int,Application, Intent, ActivityInfo,CharSequence, Activity, String,NonConfigurationInstances,Configuration, String, IVoiceInteractor,Window) ;来设置 Activity的ContextImpl

那大家来看下 Instrumentation的newActivity(ClassLoader, String,Intent) 方法里面是哪些落到实处的

   public Activity newActivity(ClassLoader cl, String className,
            Intent intent)
            throws InstantiationException, IllegalAccessException,
            ClassNotFoundException {
        return (Activity)cl.loadClass(className).newInstance();
    }

实质上很简短,就是一贯调用放射的newInstance(卡塔尔(قطر‎来获取多个对象。

1.handleCreateService

一、Context 类世襲关系

Context 类世袭关系

上航海用体育地方描述了 Context 类的重视世袭关系,Context 是抽象类,提供了应用遭遇全局音讯的接口;Context 有三个平素子类 ContextWrapper 和 ContextImpl,看名就可见意思,ContextWrapper 是上下文效能的封装类,ContextImpl 是上下文功效的落实类;Applicaiton 和 Service 直接接轨自 ContextWrapper 类,Activity 世袭自 ContextThemeWrapper 类,因为 Activity 提供 UI 展现,供给有主旨。

  • Context 类提供了一组通用的 API;
  • ContextImpl 完成了 Context 全体的效益,为 Activity 等选择组件提供 Context 对象;
  • ContextWrapper 包罗三个确实的 ContextImpl 的援引 mBase,然后就是ContextImpl 的装饰者格局;
  • ContextThemeWrapper 内部含有了 Theme 相关的接口,即 android:theme 内定的个性;

不过如此又有推动了另叁个标题,通常情况下,我们是把利用的入口程序类FooApplication放在 App 模块下的,那样一来,Library 模块里面代码就访问不到FooApplication#getContext(卡塔尔(قطر‎了。当然把FooApplication下移到幼功Curry面也是一种艺术,可是以自身的才智又立马想到了个好规范。

七、Context的内部存款和储蓄器走漏难点

因为Activity是生命周期是由系统调控的,所以一旦长周期的对象具有对Activit的援用,会产生Activity不能够回笼。

到此处,Activity的Context就创建达成了,最终会把ContextImpl对象保存在Activity的父类ContextThemeWrapper变量mBase中,同不常间会把Activity对象保存在ContextImpl的变量mOuterContext中。

这种措施应该是最遍布的获取 Context 实例的不二诀窍了,优点正是严苛依据代码标准来,不用操心宽容性难题;劣势就是API 设计严重信任于 Context 那么些API,假使开始时代接口设计不严格,早先时期代码重构的时候或许相当特别。此外还会有二个相比有趣的主题素材,大家平时采取Activity 或然 Application 类的实例作为 Context 的实例使用,而前面三个自己又完毕了其他接口,比如以下代码。

1、问题
//省略部分代码
void registerListener() {
       SensorManager sensorManager = (SensorManager) getSystemService(SENSOR_SERVICE);
       Sensor sensor = sensorManager.getDefaultSensor(Sensor.TYPE_ALL);
       sensorManager.registerListener(this, sensor, SensorManager.SENSOR_DELAY_FASTEST);
}

View smButton = findViewById(R.id.sm_button);
smButton.setOnClickListener(new View.OnClickListener() {
    @Override public void onClick(View v) {
        registerListener();
        nextActivity();
    }
});
//省略部分代码

透过Context调用的getSystemService获取系统服务,这么些劳动运作在她们协和的经过施行一五颜六色后台专门的工作依旧提供和硬件交互作用的食指,假诺Context对象要求在三个Service内部事件发生时任何时候受到通报,则须要把自身当做二个监听器注册进去,这样服务就能具备贰个Activity。假使开荒者忘记了在Activity被销毁钱收回这么些监听器,就能够促成内部存款和储蓄器走漏。

 static ContextImpl createAppContext(ActivityThread mainThread, LoadedApk packageInfo) {
        if (packageInfo == null) throw new IllegalArgumentException("packageInfo");
        return new ContextImpl(null, mainThread,
                packageInfo, null, null, false, null, null, Display.INVALID_DISPLAY);
 }

源码撸完了,再重临大家一最早的供给来。以后我们要得到 ActivityThread 的静态成员变量 sCurrentActivityThread。阅读源码后我们开掘能够透过ActivityThread#currentActivityThread(卡塔尔(قطر‎那个静态方法来赢得那几个静态对象,然后经过ActivityThread#getApplication(State of Qatar方法就也许一直获得大家要求的 Application 实例了。啊,那用反射搞起来几乎再简单然则了!说搞就搞。

于今实现了 Application 及其 mBase 的实例制程,这我们再来看下 Instrumentationd的newApplication(ClassLoader ,String, Context) 方法的源码
  public Application newApplication(ClassLoader cl, String className, Context context)
            throws InstantiationException, IllegalAccessException, 
            ClassNotFoundException {
        return newApplication(cl.loadClass(className), context);
    }

    static public Application newApplication(Class<?> clazz, Context context)
            throws InstantiationException, IllegalAccessException, 
            ClassNotFoundException {
        Application app = (Application)clazz.newInstance();
        app.attach(context);
        return app;
    }

咱俩开掘 Instrumentationd的newApplication(ClassLoader ,String, Context) 方法内部又调用 Instrumentationd 的静态方法 newApplication(Class<?> clazz, Context context) 来完成的,在 Instrumentationd 的静态方法 newApplication(Class<?> clazz, Context context) 里面是调用反射的newInstance(卡塔尔方法来得到的。原本 Application 是通过反射来实例化的啊。

7.app.attach(context)

ca88编程 9

(四)、Application源码拆解深入分析

public class Application extends ContextWrapper implements ComponentCallbacks2 {
   //省略部分代码
    public Application() {
        super(null);
    }

    final void attach(Context context) {
        attachBaseContext(context);
        mLoadedApk = ContextImpl.getImpl(context).mPackageInfo;
    }
   //省略部分代码
}

Application世袭自ContextWrapper,和大家在此之前的画的类继承图同样。
有关Application大家都很熟悉了,我就不说类的注明,然则本人建议我们要么去看下,对大家更精晓Application是很有支持的。

透过H发送三个LAUNCH_ACTIVITY的新闻,H接收到新闻后最后会在handMessage中末了调用handleLaunchActivity(r, null卡塔尔(قطر‎;方法来管理

  • 类 API 设计要求依赖 Context(那是一种好习于旧贯,小编可没说那不佳);
  • 享有静态的 Context 实例轻易引发的内部存款和储蓄器败露难题;
  • 要求提注册 Context 实例;
  • 污染程序的 Application 类;那么,有未有一种方式,能够让我们在整个项目中能够全局访谈到 Context 实例,不要超前注册,不会传染 Application 类,尤其不会掀起静态 Context 实例带来的内部存款和储蓄器走漏呢?

Android插件化底蕴的机要内容囊括

那边再次来到Application类型对象mApplication,他是二个Context对象,在第四步makeApplication方法中创立的。

因而测量检验开采,在 4.1.1 系统的机型上,会平稳现身获取结果为 null 的景色,看来是系统源码的贯彻上有一点点出入引致,说来讲去先看看源码吧。

2、应用方案

在onDestroy方法里面注销监听器。

回去第四部中的makeApplication方法中,通过setOuterContext方法把Application对象赋值给ContextImpl中的mOut而Context变量,至此Application Context就创设完结了。那么如何获取ApplicationContext对象呢。

ca88编程 10

综述,咱们能够看看在装置其余因素不变的图景下我们通过分化的Context实例获得的Resources是一致套能源。

至于Resources作者继续会特意开个篇章去详细批注。

private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
        ...

    Activity activity = null;
        try {
            java.lang.ClassLoader cl = r.packageInfo.getClassLoader();
            activity = mInstrumentation.newActivity(
                    cl, component.getClassName(), r.intent);
            StrictMode.incrementExpectedActivityCount(activity.getClass());
            r.intent.setExtrasClassLoader(cl);
            r.intent.prepareToEnterProcess();
            if (r.state != null) {
                r.state.setClassLoader(cl);
            }
        } catch (Exception e) {
            if (!mInstrumentation.onException(activity, e)) {
                throw new RuntimeException(
                    "Unable to instantiate activity "   component
                      ": "   e.toString(), e);
            }
        }

        try {
            Application app = r.packageInfo.makeApplication(false, mInstrumentation);

           ...

            if (activity != null) {
                Context appContext = createBaseContextForActivity(r, activity);//如果Activity不为空,创建Activity的Context
               .......
                activity.attach(appContext, this, getInstrumentation(), r.token,//attach方法用来初始化正在启动的Activity
                        r.ident, app, r.intent, r.activityInfo, title, r.parent,
                        r.embeddedID, r.lastNonConfigurationInstances, config,
                        r.referrer, r.voiceInteractor);

                if (customIntent != null) {
                    activity.mIntent = customIntent;
                }
                r.lastNonConfigurationInstances = null;
                activity.mStartedActivity = false;
                int theme = r.activityInfo.getThemeResource();
                if (theme != 0) {
                    activity.setTheme(theme);
                }

    //回调Activity的onCreate和一系列回调

                activity.mCalled = false;
                if (r.isPersistable()) {
                    mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
                } else {
                    mInstrumentation.callActivityOnCreate(activity, r.state);
                }
                if (!activity.mCalled) {
                    throw new SuperNotCalledException(
                        "Activity "   r.intent.getComponent().toShortString()  
                        " did not call through to super.onCreate()");
                }
                r.activity = activity;
                r.stopped = true;
                if (!r.activity.mFinished) {
                    activity.performStart();
                    r.stopped = false;
                }
                if (!r.activity.mFinished) {
                    if (r.isPersistable()) {
                        if (r.state != null || r.persistentState != null) {
                            mInstrumentation.callActivityOnRestoreInstanceState(activity, r.state,
                                    r.persistentState);
                        }
                    } else if (r.state != null) {
                        mInstrumentation.callActivityOnRestoreInstanceState(activity, r.state);
                    }
                }
                if (!r.activity.mFinished) {
                    activity.mCalled = false;
                    if (r.isPersistable()) {
                        mInstrumentation.callActivityOnPostCreate(activity, r.state,
                                r.persistentState);
                    } else {
                        mInstrumentation.callActivityOnPostCreate(activity, r.state);
                    }
                    if (!activity.mCalled) {
                        throw new SuperNotCalledException(
                            "Activity "   r.intent.getComponent().toShortString()  
                            " did not call through to super.onPostCreate()");
                    }
                }
            }
           ...

        } catch (SuperNotCalledException e) {
            throw e;

        } catch (Exception e) {
            ...
        }

        return activity;
 }

原来是如此一个幺蛾子,在 4.1.1 系统上,ActivityThread 是运用三个ThreadLocal 实例来寄存在静态 ActivityThread 实例的。至于 ThreadLocal 是怎么用的此处暂不张开,简单说来,就是系统独有在 UI 线程使用 sThreadLocal 来保存静态 ActivityThread 实例,所以大家只好在 UI 线程通过 sThreadLocal 获取到那一个保存的实例,在 Worker 线程 sThreadLocal 会直接回到空。

(四卡塔尔(قطر‎、Handler使用招致内部存储器走漏

   public void callActivityOnCreate(Activity activity, Bundle icicle) {
        prePerformCreate(activity);
        activity.performCreate(icicle);
        postPerformCreate(activity);
    }

是哪个人调用了那一个主意呢?能够便捷定位到Application#attach。

(七)getApplication()和getApplicationContext()

上面说起了取稳妥前Application对象用getAppcationContext,不知晓您有没有联想到getAppliation(State of Qatar,那多少个主意有啥界别?上面我们就精研下。
以activiy为例,Activity.getAppliation(卡塔尔重回的是mApplication,代码如下:

    public final Application getApplication() {
        return mApplication;
    }

这就是说mApplication是哪一天被开始化的那,其实只要您恰恰稳重读过源码正是会精晓是在activity的attach里面被开头化的

final void attach(Context context, ActivityThread aThread,
            Instrumentation instr, IBinder token, int ident,
            Application application, Intent intent, ActivityInfo info,
            CharSequence title, Activity parent, String id,
            NonConfigurationInstances lastNonConfigurationInstances,
            Configuration config, String referrer, IVoiceInteractor voiceInteractor,
            Window window) {
      //省略部分代码
      mApplication = application;
      //省略部分代码
}

那调用attach方法中的第6个参数application,是怎么来的?
那我们回来ActivityThread的performLaunchActivity(卡塔尔国方法里面一看毕竟。

private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
          //省略部分代码
           Application app = r.packageInfo.makeApplication(false, mInstrumentation);
         //省略部分代码
           activity.attach(appContext, this, getInstrumentation(), r.token,
                        r.ident, app, r.intent, r.activityInfo, title, r.parent,
                        r.embeddedID, r.lastNonConfigurationInstances, config,
                        r.referrer, r.voiceInteractor, window);
        //省略部分代码
}

经过上述代码大家知道app来自LoadedApk的makeApplication(卡塔尔方法里面。
那我们来看下LoadedApk.makeApplication(State of Qatar里面具体的执行

    //LoadedApk.java
    public Application makeApplication(boolean forceDefaultAppClass,
            Instrumentation instrumentation) {
        if (mApplication != null) {
            return mApplication;
        }
        //省略部分代码
       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; 
        //省略部分代码
    }

透过上述代码,大家了解在LoadedApk的makeApplication里面首先会咬定mApplication是还是不是为空,倘若不为空间接再次来到,假设为空则通过反射实例化二个。那大家来看下getAppliationContext(卡塔尔(قطر‎里面包车型客车具体得以达成,首先表达下getApplicationContext(State of Qatar那几个方式是ContextWrapper的章程,代码如下:

   //ContextWrapper.java
    @Override
    public Context getApplicationContext() {
        return mBase.getApplicationContext();
    }

所以我们驾驭,其实调用的是mBase.getApplicationContext(卡塔尔方法。因为大家知道mBase其实正是ContextImpl所以我们看下ContextImpl里面包车型客车具体得以完结

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

啊,mPackageInfo不是LoadedApk对象呢?那调用 mPackageInfo.getApplication(State of Qatar的源码是:

    Application getApplication() {
        return mApplication;
    }

哈哈,我们其实看懂了吗,其实不管getApplication(卡塔尔国依旧getApplicationContext(卡塔尔(قطر‎最终实际上都是调用的LoadedApk的mApplication。而在多个安装包下LoadedApk是同多个指标,所以getApplication(卡塔尔(قطر‎==getApplicationContext(卡塔尔(قطر‎是true。不清楚我们看懂了没。

实在还大概有一种方式,大家在向来打字与印刷getApplication(卡塔尔国和getApplicationContext(卡塔尔(قطر‎,最后大家会开采是指向同三个内部存款和储蓄器地址,生硬提出大家去试试!

那么难题来了,既然那多少个艺术取得结果都以同一的,那么Android为何要提供那五个效果与利益重新的办法那?实际上那四个法子在功用域上有超大的区分。getApplication(卡塔尔国方法在语义性上十三分强,一看就精通来博取Application的,不过她只在Activity和Service上技能调用。而在有个别任何场景,比方布罗兹castReceiver中也想获取Application实例,那时就足以信赖getApplicationContext(State of Qatar方法如下:

publicclassMyReceiverextendsBroadcastReceiver{

     @Override
     public void onReceive(Context context,Intent intent){
                       ApplicationmyApp=(Application)context.getApplicationContext();
     }
}

当今期望大家可以理解的知道getApplicationContext(卡塔尔国与getApplication(卡塔尔国。

4.attach(context, this, data.info.name, data.token, app,ActivityManagerNative.getDefault());

本文由ca88发布,转载请注明来源

关键词: ca88网址 实例 全局 装置 半栈工程师