attachBaseContext里调用getApplicationContext 返回 null的底层原因和解决方法

前言

昨天徒弟终于能把rn工程跑起来了,高兴还没一天,今天在哀嚎着找到我……

徒弟说,“我就在application里的attachBaseContext初始化sdk,把application作为对象传进去,里面拿着这个对象去获取context”

application.getApplicationContext()

“为什么这个值是null呢?我其他地方拿都是正常可以拿到的啊!!!!”

    @Override
    protected void attachBaseContext(Context base) {
        super.attachBaseContext(base);
        initSDK(this);
    }

哎,今天又是一个徒弟哀嚎的日子。

问题

以一个简单的demo来说明问题

public class MyApplication extends Application {
    private static final String TAG = "MyApplication";

    @Override
    protected void attachBaseContext(Context base) {
        super.attachBaseContext(base);
        Log.i(TAG, "attachBaseContext: " + getApplicationContext());
    }

    @Override
    public void onCreate() {
        Log.i(TAG, "onCreate 0: " + getApplicationContext());
        super.onCreate();
        Log.i(TAG, "onCreate 1: " + getApplicationContext());
    }
}

demo输出的日志结果为

2022-01-11 14:57:38.310 19159-19159/com.tomes.testdemo I/MyApplication: attachBaseContext: null
2022-01-11 14:57:38.314 19159-19159/com.tomes.testdemo I/MyApplication: onCreate 0: com.tomes.testdemo.MyApplication@5f12967
2022-01-11 14:57:38.314 19159-19159/com.tomes.testdemo I/MyApplication: onCreate 1: com.tomes.testdemo.MyApplication@5f12967

从结果上来看,attachBaseContext里application去调用getApplicationContext就为null,只要执行完attachBaseContext,如在onCreate里则能通过getApplicationContext获取到context

分析

那我们看看getApplicationContext的具体实现

这里以Android 10的源码为例

getApplicationContext()的真正实现是在frameworks/base/core/java/android/app/ContextImpl.java

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

    final @NonNull LoadedApk mPackageInfo;

    private ContextImpl(@Nullable ContextImpl container, @NonNull ActivityThread mainThread,
            ……
            mPackageInfo = packageInfo;
            ……
    }

接下来我们看看mPackageInfo.getApplication()为什么为null,他的实现在

frameworks/base/core/java/android/app/LoadedApk.java

    Application getApplication() {
        return mApplication;
    }

查看mApplication赋值的位置

    @UnsupportedAppUsage
    public Application makeApplication(boolean forceDefaultAppClass, Instrumentation instrumentation) {
            ……
            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;
            ……
    }

我们看看app的赋值处mActivityThread.mInstrumentation.newApplication(),他的实现是在

frameworks/base/core/java/android/app/Instrumentation.java

    public Application newApplication(ClassLoader cl, String className, Context context)
            throws InstantiationException, IllegalAccessException, 
            ClassNotFoundException {
        Application app = getFactory(context.getPackageName())
                .instantiateApplication(cl, className);
        app.attach(context);//继续跟该方法
        return app;
    }

frameworks/base/core/java/android/app/Application.java

    /**
     * @hide
     */
    @UnsupportedAppUsage
    /* package */ final void attach(Context context) {
        attachBaseContext(context);//这里调到我们demo里继承了Application MyApplication的attachBaseContext方法
        mLoadedApk = ContextImpl.getImpl(context).mPackageInfo;
    }

这里还没有将app的赋值操作mActivityThread.mInstrumentation.newApplication()执行完毕,我们就在里面的内部实现去拿这个还没赋值的mApplication,这怎么可能拿得到呢?

为了方便大家理解,整理以下流程

mApplication赋值流程

getApplicationContext调用流程

 

解决方法

方法一:

注意开发规范,不要在attachBaseContext生命周期里使用getApplicationContext。

如果一定要在attachBaseContext生命周期里拿到context,这时可以用户自定的Application对象【因为application也是context的子类】,或使用重写attachBaseContext方法时Application传过来的context参数

方法二:

在用户自定义的Application中重写getApplicationContext方法,返回自定义的Application对象

    @Override
    public Context getApplicationContext() {
        return this;
    }

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
要在 Android Studio 中固定使用固定的 jar 包方法,可以按照以下步骤操作: 1. 将 jar 包添加到项目中:将 jar 包复制到项目的 `libs` 文件夹中,并在项目的 `build.gradle` 文件中添加以下代码: ``` dependencies { implementation files('libs/your-library.jar') } ``` 2. 创建一个自定义的 `Application` 类:在项目中创建一个自定义的 `Application` 类,并在类中重写 `attachBaseContext()` 方法。在重写方法中,使用 `DexClassLoader` 加载 jar 包中的类,并将类加载器设置为应用程序的上下文类加载器。以下是示例代码: ``` public class MyApplication extends Application { @Override protected void attachBaseContext(Context base) { super.attachBaseContext(base); try { // 加载 jar 包中的类 File dexPath = new File(getApplicationInfo().nativeLibraryDir + "/your-library.jar"); DexClassLoader classLoader = new DexClassLoader(dexPath.getAbsolutePath(), getApplicationInfo().dataDir + "/lib", null, getClassLoader()); // 将类加载器设置为应用程序的上下文类加载器 Thread.currentThread().setContextClassLoader(classLoader); } catch (Exception e) { e.printStackTrace(); } } } ``` 3. 将应用程序的 `Application` 类设置为自定义的 `Application` 类:在项目的 `AndroidManifest.xml` 文件中,将应用程序的 `Application` 类设置为自定义的 `Application` 类。以下是示例代码: ``` <application android:name=".MyApplication" ... ``` 通过上述步骤,就可以固定使用固定的 jar 包方法,而不是系统中的同名方法。但是需要注意的是,这种方法可能会导致应用程序的性能下降,因此应该尽量避免在应用程序中使用这种方法

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值