Android中的Context

Context是什么?

中文翻译为:上下文

一个Activity就是一个Context,一个Service也是一个Context,Application也是一个Context。

Context能用来干什么?

启动界面、发送广播、获取资源等。

走进Context的世界

Context的关系图

Context

源码地址:

https://cs.android.com/android/platform/superproject/+/master:frameworks/base/core/java/android/content/Context.java;l=1?q=Context.java

ContextImpl

源码地址:

https://cs.android.com/android/platform/superproject/+/master:frameworks/base/core/java/android/app/ContextImpl.java;l=182?q=ContextIM

ContextWrapper

源码地址:

https://cs.android.com/android/platform/superproject/+/master:frameworks/base/core/java/android/content/ContextWrapper.java;l=62?q=ContextWra&sq=

ContextThemeWrapper

源码地址:

https://cs.android.com/android/platform/superproject/+/master:frameworks/base/core/java/android/view/ContextThemeWrapper.java;l=33?q=ContextThe&sq=

Application

Service

Activity

Context是怎么创建的?又是在哪创建的?

知识前提:

Android系统的启动流程(基于Android5.1.1系统源码)

大图链接:https://www.processon.com/view/link/5fd4b993e401fd06ddbe94b9

创建流程:

将ApplicationThread Binder注册到AMS中。

用于切换线程,处理AMS发送过来的消息。


Application 的 Context

Service 的 Context

划重点

静态注册的广播 和 动态注册的广播 最终在

onReceive(Context context, Intent intent)

方法里面的Context的不一样

Activity 的 Context

Android9.0之前

源码地址:https://cs.android.com/android/platform/superproject/+/android-8.1.0_r9:frameworks/base/core/java/android/app/ActivityThread.java

在mH里面定义了LAUNCH_ACTIVITY,执行handleLaunchActivity(r, null);

Android9.0 及之后

源码地址:

https://cs.android.com/android/platform/superproject/+/master:frameworks/base/core/java/android/app/ActivityThread.java

Activity生命周期相关的方法都直接走 EXECUTE_TRANSACTION 分支处理

ActivityLifecycleItem

ClientTransactionItem子类

ActivityLifeCycleItem子类

LauncherActivityItem实现

ContentProvider 的 Context

Broadcast Receiver 的 Context

广播是一个抽象类,没有继承ContextWrapper,也没有跟Context的成员变量。

如果广播是动态注册的话,onReceived方法里面参数context,就是注册时候的context。

如果是静态注册的话,是以Application Context 为Base的ContextWrapper

总结一下

  1. Context类本身是一个抽象类,其有两个具体的实现类:ContextImpl 和 ContextWrapper。

  2. ContextWrapper类 ,本身不实现具体操作,主要职责是转发和包装。其内部维护一个Context变量mBase,具体实现由该Context完成,该变量由attachBaseContext方法赋值。

  3. ContextThemeWrapper类继承至ContextWrapper,添加了一些与主题相关的操作。

  4. Application、Service 继承于 ContextWrapper。Activity 继承于 ContextThemeWrapper。

  5. ContextImpl类 为 Context类 的具体实现类。

最后来看一下,Android中的四大组件中的各种Context的关系

1.首先创建一个最简单的Android工程,创建一个资源布局文件activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity"
    android:orientation="vertical">

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textAllCaps="false"
        android:onClick="test1"
        android:text="比较 getApplicationContext() 与 getApplication() 区别" />


    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textAllCaps="false"
        android:onClick="test2"
        android:text="查看ContentProvider的Context" />


    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textAllCaps="false"
        android:onClick="test3"
        android:text="查看静态注册广播的Context" />

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textAllCaps="false"
        android:onClick="test4"
        android:text="查看动态注册广播的Context" />

</LinearLayout>

界面如下


2.在MainActivity中创建几个方法


import android.app.Activity;
import android.app.Application;
import android.content.BroadcastReceiver;
import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.util.Log;
import android.view.View;

import com.zct.context.receiver.DynamicRegisterReceiver;

public class MainActivity extends Activity {

    private BroadcastReceiver mDynamicReceiver = new DynamicRegisterReceiver();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        //动态注册广播
        registerReceiver();
    }


    /**
     * 验证getApplication() 与 getApplicationContext() 区别
     *
     * @param view
     */
    public void test1(View view) {
        Application application = getApplication();
        Context applicationContext = getApplicationContext();
        Log.i(Constants.TAG, "test1 \n application = " + application + " , \n applicationContext = " + applicationContext);
    }

    /**
     * 查看ContextProvider 的 Context 对象
     *
     * @param view
     */
    public void test2(View view) {
        Uri boyUri = Uri.parse("content://com.zct.context.MyContentProvider/test1");
        ContentValues contentValues = new ContentValues();
        Cursor boyCursor = getContentResolver().query(boyUri, new String[]{"_id", "name"}, null, null, null);
    }

    /**
     * 查看静态注册的广播接受者的 Context
     *
     * @param view
     */
    public void test3(View view) {
        Log.i(Constants.TAG, "\n MainActivity test3 = " + this + "");
        Intent intent = new Intent();
        intent.setAction("static_register_broadcast_receiver");
        intent.setPackage(getPackageName());//
        sendBroadcast(intent);
    }

    /**
     * 查看动态注册的广播接受者的 Context
     *
     * @param view
     */
    public void test4(View view) {
        Log.i(Constants.TAG, "\n MainActivity test4 = " + this + "");
        Intent intent = new Intent();
        intent.setAction(Constants.DYNAMIC_ACTION);
        sendBroadcast(intent);
    }


    private void registerReceiver() {
        IntentFilter dynamicFilter = new IntentFilter();
        dynamicFilter.addAction(Constants.DYNAMIC_ACTION);
        registerReceiver(mDynamicReceiver,dynamicFilter);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        unRegisterReceiver();
    }

    private void unRegisterReceiver() {
        unregisterReceiver(mDynamicReceiver);
    }


}

3.DynamicRegisterReceiver 代码如下

public class DynamicRegisterReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        Log.i(Constants.TAG, "DynamicRegisterReceiver onReceive context = " + context);
    }
}

4.StaticRegisterReceiver 代码如下


public class StaticRegisterReceiver extends BroadcastReceiver {

    @Override
    public void onReceive(Context context, Intent intent) {
        //3980
        Log.i(Constants.TAG, "StaticRegisterReceiver onReceive context = " + context);
    }
}

两个同样都是广播,只是StaticRegisterReceiver在AndroidManifest.xml中静态注册,DynamicRegisterReceiver在MainActivity中动态注册。

5. MyContentProvider 为内容提供者,这里没有实现具体的方法,仅为了验证其中Context的关系。


public class MyContentProvider extends ContentProvider {
    @Override
    public boolean onCreate() {
        return false;
    }

    @Nullable
    @Override
    public Cursor query(@NonNull Uri uri, @Nullable String[] strings, @Nullable String s, @Nullable String[] strings1, @Nullable String s1) {
        Context context = getContext();
        Context applicationContext = getContext().getApplicationContext();
        Log.i(Constants.TAG,"ContentProvider \n context = "+context+" , \n applicationContext = "+applicationContext);
        return null;
    }

    @Nullable
    @Override
    public String getType(@NonNull Uri uri) {
        return null;
    }

    @Nullable
    @Override
    public Uri insert(@NonNull Uri uri, @Nullable ContentValues contentValues) {
        return null;
    }

    @Override
    public int delete(@NonNull Uri uri, @Nullable String s, @Nullable String[] strings) {
        return 0;
    }

    @Override
    public int update(@NonNull Uri uri, @Nullable ContentValues contentValues, @Nullable String s, @Nullable String[] strings) {
        return 0;
    }
}

6.最后看一下AndroidManifest.xml的代码

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.zct.context">

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

        <provider
            android:name="com.zct.context.MyContentProvider"
            android:authorities="com.zct.context.MyContentProvider"
            android:exported="true" />

        <receiver android:name=".receiver.StaticRegisterReceiver">
            <intent-filter>
                <action android:name="static_register_broadcast_receiver" />
            </intent-filter>
        </receiver>
    </application>

</manifest>

激动人心的时刻到了,我们点击不同的按钮,来查看结果。

1. 比较 getApplicationContext() 与 getApplication() 区别

 /**
   * 验证getApplication() 与 getApplicationContext() 区别
   *
   * @param view
   */
 public void test1(View view) {
        Application application = getApplication();
        Context applicationContext = getApplicationContext();
        Log.i(Constants.TAG, "test1 \n application = " + application + " , \n applicationContext = " + applicationContext);
    }

从打印结果可以看出 getApplication 与 getApplicationContext 方法返回的是相同的地址。区别在于getApplication仅在Activity中可以获取的到,返回的Application本身的实例对象。

2.

    /**
     * 查看ContextProvider 的 Context 对象
     *
     * @param view
     */
    public void test2(View view) {
        Uri boyUri = Uri.parse("content://com.zct.context.MyContentProvider/test1");
        ContentValues contentValues = new ContentValues();
        Cursor boyCursor = getContentResolver().query(boyUri, new String[]{"_id", "name"}, null, null, null);
    }

MyContentProvider

 public Cursor query(@NonNull Uri uri, @Nullable String[] strings, @Nullable String s, @Nullable String[] strings1, @Nullable String s1) {
        Context context = getContext();
        Context applicationContext = getContext().getApplicationContext();
        Log.i(Constants.TAG,"ContentProvider \n context = "+context+" , \n applicationContext = "+applicationContext);
        return null;
    }

从执行结果可以看出,ContentProvider中的Context就是Application的Context。

3.查看静态注册广播的Context

 /**
     * 查看静态注册的广播接受者的 Context
     *
     * @param view
     */
    public void test3(View view) {
        Log.i(Constants.TAG, "\n MainActivity test3 = " + this + "");
        Intent intent = new Intent();
        intent.setAction("static_register_broadcast_receiver");
        intent.setPackage(getPackageName());//
        sendBroadcast(intent);
    }

StaticRegisterReceiver.java

public class StaticRegisterReceiver extends BroadcastReceiver {

    @Override
    public void onReceive(Context context, Intent intent) {
        //3980
        Log.i(Constants.TAG, "StaticRegisterReceiver onReceive context = " + context);
    }
}


从打印看到一个很奇怪的东西。ReceiverRestrictedContext是个什么鬼???
要想了解它是个什么东西,我们就翻一下他的源码。



原来它是Context的一个包装类,里面会拦截掉一些原有的方法,以保证某些方法在Receiver中不会被调用。
要想知道mBase具体是什么,这里断点跟一下。



可以得到结论,静态注册的广播,系统会为将Application的Context用ReceiverRestrictedContext进行一层包装,再返回给onReceiver方法。

4.查看动态注册广播的Context

 /**
     * 查看动态注册的广播接受者的 Context
     *
     * @param view
     */
    public void test4(View view) {
        Log.i(Constants.TAG, "\n MainActivity test4 = " + this + "");
        Intent intent = new Intent();
        intent.setAction(Constants.DYNAMIC_ACTION);
        sendBroadcast(intent);
    }

DynamicRegisterReceiver

public class DynamicRegisterReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        Log.i(Constants.TAG, "DynamicRegisterReceiver onReceive context = " + context);
    }
}

动态注册的广播与静态注册的广播就不一样了。其onReceiver方法中的Context就是注册这个广播时候的Context,由于我们在Activity中注册的,所以这里就是我们的MainActivity。

上面是本人学习研究的时候记录的,如果有不对的理解,请麻烦指出。互相学习,谢谢。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,204评论 6 506
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,091评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,548评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,657评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,689评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,554评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,302评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,216评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,661评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,851评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,977评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,697评论 5 347
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,306评论 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,898评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,019评论 1 270
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,138评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,927评论 2 355

推荐阅读更多精彩内容