我们最常用的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做判断
}
}
这几个对于新手来说,非常的迷惑。到底有说明区别?
先说说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是全局单例。所以自己实现单例是多余的,通过上面的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的方法
}
}