Android Context详解(全解析)

我们最常用的Activity,Service,Application都是Context的子类。所以知道Context的具体实现是非常有必要的。
下面是Context的体系结构图:
在这里插入图片描述
Context本身是一个抽象类。他的实现类是ContextImpl。而ContextWrapper是一个包装类(装饰设计模式)。在我们用IDE查看Context继承关系的时候,我们是不能直接看到ContextImpl这个类的,通过这种手段让程序员不能直接看到ContextImpl这个类,而只能看到ContextWrapper这个类。但如果我们真的要想弄明白Context原理的话,ContextImpl才是最重要的,而ContextWrapper只是个外壳而已。

ContextWrapper的子类我们非常熟悉了,Application,Service,ContextThemeWrapper。前两个就不用多说了,ContextThemeWrapper其实我们也不用太关心,有需要的时候可以看看,就是和主题相关的,他的子类Activity也不用多说。所以其实最核心的类就是ContextImpl类。

Context类是一个抽象类,差不多有5000多行,而且几乎都是没有实现的抽象方法,并且差不多4000多行都是注释。真正的内容可能1000行不到。所以Context类几乎就是一个接口说明文档。而ContextImpl也才2700多行。而且很多都是get方法。ContextImpl类继承了抽象类Context并且实现所有的抽象方法,并且自身还实现了很多get,create,check开头的方法,即使这样,也才2700行代码。要理解Context,核心也就在这2700行代码了(很多是get方法),而且还有Context这个文档说明类。

在这2700行代码里面,最重要的是 ActivityThread mMainThread;这个全局变量。他被用的地方最多。
我们最常看到的getApplicationContext,startActivity,getMainLooper都是在这里实现,实现方法也非常的简单。这都是ActivityThread封装好的,具体内容需要看ActivityThread这个类。这里不展开,会写一篇文章专门讨论。其中绝大部分使用和广播有光,如果专门需要研究广播的时候可以看看。

    @Override
    public Looper getMainLooper() {
    
    
        return mMainThread.getLooper();
    }

    @Override
    public Context getApplicationContext() {
    
    
        return (mPackageInfo != null) ?
                mPackageInfo.getApplication() : mMainThread.getApplication();
    }
    
    @Override
    public void startActivity(Intent intent, Bundle options) {
    
    
        warnIfCallingFromSystemProcess();

        mMainThread.getInstrumentation().execStartActivity(
                getOuterContext(), mMainThread.getApplicationThread(), null,
                (Activity) null, intent, -1, options);
    }
    
    //差不多有10个类似方法
    @Override
    public void sendBroadcast(Intent intent, String receiverPermission, int appOp) {
    
    

            ActivityManager.getService().broadcastIntent(
                    mMainThread.getApplicationThread(), intent, resolvedType, null,
                    Activity.RESULT_OK, null, null, receiverPermissions, appOp, null, false, false,
                    getUserId());

    }

第二重要的是LoadedApk mPackageInfo 这个变量,因为他被引用的次数是第二多的,要么通过mPackageInfo获取一些内容,要么通过他做判断。当然,这些功能也都是LoadedApk这个类封装的,通过他的名字也就知道他是干嘛的,这个类可以管理运行的.apk文件。这个类的详细说明也可以单独写一篇文章。

  @Override
    public Context getApplicationContext() {
    
    
        return (mPackageInfo != null) ?
                mPackageInfo.getApplication() : mMainThread.getApplication();
    }
    @Override
    public String getPackageName() {
    
    
        if (mPackageInfo != null) {
    
    
            return mPackageInfo.getPackageName();
        }
        // No mPackageInfo means this is a Context for the system itself,
        // and this here is its name.
        return "android";
    }
    @Override
    public ApplicationInfo getApplicationInfo() {
    
    
        if (mPackageInfo != null) {
    
    
            return mPackageInfo.getApplicationInfo();
        }
        throw new RuntimeException("Not supported in system context");
    }

    @Override
    public String getPackageResourcePath() {
    
    
        if (mPackageInfo != null) {
    
    
            return mPackageInfo.getResDir();
        }
        throw new RuntimeException("Not supported in system context");
    }
    
//差不多有10个
void sendOrderedBroadcast() {
    
    
           多处用mPackageInfo做判断
        }
  
  
    }

getContext(),getBaseContxet(),getApplication,getApplicationContext的区别

这几个对于新手来说,非常的迷惑。到底有说明区别?

先说说getApplication和getApplicationContext的区别。如果你在Activity里面打印这个两个方法的话,得到对象内存地址是一样的,也就是这两个方法获取的对象是一样的?实际上,这两个获取到的对象就是同一个对象。只是可以使用的范围是不一样的,getApplication这个方法只有Activity和Service才有,要想在别的地方获取Application就只能通过getApplicationContext获取。
这里还有一个问题,为什么getApplication和getApplicationContext获取到的是同一个对象?
篇幅原因。可以看这篇文章,只要知道,这两个方法获取到对象是一样的就行了。
也就是他们几乎是等价的。唯一的区别就是有没有getApplication这个方法给你用而已。
https://www.cnblogs.com/mingfeng002/p/11995177.html

getContext(),getBaseContxet(),getApplicationContext
这三个有什么区别?
首先Activity,Service和Application都有getBaseContxet(),getApplicationContext()这两个方法。但没有getContext方法。在Fragment中,才有getContext方法。而Fragment的getContext方法获取的对象是他的寄主对象,也就是Activity。如果查看getContext源码,在一系列包装后,实际上获取的就是他所在的Activity对象。

public class MainActivity extends AppCompatActivity 

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

        Log.d("MainActivity", "getBaseContext():" + getBaseContext());//ContextImpl对象
        Log.d("MainActivity", "getApplicationContext():" + getApplicationContext());
        Log.d("MainActivity", "getApplication():" + getApplication());
        Log.d("MainActivity", "this:" + this);//Activity对象
    }


public class BlankFragment extends Fragment {
    
    
    @Override
    public void onCreate(Bundle savedInstanceState) {
    
    
        super.onCreate(savedInstanceState);
        Log.d("BlankFragment", "getContext():" + getContext());
    }
}

getBaseContext():androidx.appcompat.view.ContextThemeWrapper@72a2522
getApplicationContext():android.app.Application@e8e5bb3
getApplication():android.app.Application@e8e5bb3
this:com.example.viewpagerdemo.MainActivity@b0be2f
getContext():com.example.viewpagerdemo.MainActivity@b0be2f

在输出中,我们可以看到getContext和MainActivity的this是同一个对象,getApplicationContext和getApplication获取到的也是同一个对象,也是符合我们的证明的。

Application的使用

首先Application是全局单例。所以自己实现单例是多余的,通过上面的getApplication或者getApplicationContext获取就行了。
application里面调用context方法,需要在attachBaseContext之后。因为是在attachBaseContext完成context的赋值的。
比如在构造器里面调用Context的方法就会奔溃,因为还没有赋值。
在onCreate方法调用Context是可以的,但极限操作是在attachBaseContext方法里面。

public class MyApplication extends Application {
    
    
	
	@Override
	protected void attachBaseContext(Context base) {
    
    
		// 在这里调用Context的方法会崩溃
		super.attachBaseContext(base);
		// 在这里可以正常调用Context的方法
	}
	
}

猜你喜欢

转载自blog.csdn.net/ScottePerk/article/details/122613765