一、实现获取全局上下文
什么情况下需要使用全局上下文,不能通过参数传过来,但是做其他工作又用到这个参数,这种情况下就需要使用全局上下文,一般情况下都是可以通过参数传进去的。
比如说封装一个工具类,工具类中需要用到上下文,可以通过参数传进去,也可以在工具类中使用全局上下文。以获取getApplicationContext();这个为例。为了便于管理,将其定义在Application中。总结下使用步骤:
1)自定义Application,如:MyApplication
2)实现获取全局上下文的方法
3)AndroidManifest.xml中引用自定义的MyApplication
4)使用获取全局上下文的方法
下面详细说明:
1)自定义一个Application类
import android.app.Application;
import android.content.Context;
/**
* Created by wangwentao on 2017/2/21.
*/
public class MyApplication extends Application {
private static Context context;//全局上下文
@Override
public void onCreate(){
context = getApplicationContext();
}
//获取全局的上下文
public static Context getContext(){
return context;
}
}
2)实现获取全局上下文的方法
MyApplication这个类中定义了一个getContext()方法,用于获取全局的上下文
3)AndroidManifest.xml中引用自定义的MyApplication
<application
android:name=".main.base.MyApplication"
4)使用获取全局上下文的方法
通过写一个小demo,在主界面中添加三个按钮,分别演示传Activity所在的上下文,不传上下文,使用全局上下文,退出app。通过一个工具类来模拟调用Toast显示。
主界面布局activity_get_globle_context.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/activity_get_context"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<Button
android:id="@+id/id_btn_context"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="传入上下文"/>
<Button
android:id="@+id/id_btn_context2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="使用全局上下文"/>
<Button
android:id="@+id/id_btn_context3"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="退出"/>
</LinearLayout>
主界面代码
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.Button;
import com.mobile.cdtx.blog.R;
import com.mobile.cdtx.blog.main.tools.DialogUtil;
public class GetContextActivity extends AppCompatActivity implements View.OnClickListener{
Button btnContext,btnContext2,btnContext3;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_get_globle_context);
initView();//控件初始化
}
//控件的初始化
private void initView(){
btnContext = (Button)findViewById(R.id.id_btn_context);
btnContext.setOnClickListener(this);
btnContext2 = (Button)findViewById(R.id.id_btn_context2);
btnContext2.setOnClickListener(this);
btnContext3 = (Button)findViewById(R.id.id_btn_context3);
btnContext3.setOnClickListener(this);
}
@Override
public void onClick(View v) {
switch (v.getId()){
case R.id.id_btn_context:
btnClickOne();
break;
case R.id.id_btn_context2:
btnClickTwo();
break;
case R.id.id_btn_context3:
btnExit();
break;
}
}
//传入参数
private void btnClickOne(){
DialogUtil.showToastWithContext(GetContextActivity.this,"传入了上下文");
}
//不传入参数
private void btnClickTwo(){
DialogUtil.showToastWithoutContext("使用全局上下文");
}
//退出app
private void btnExit(){
finish();
}
}
测试工具类:
import android.content.Context;
import android.text.TextUtils;
import android.widget.Toast;
import com.mobile.cdtx.blog.main.base.MyApplication;
/**
* Created by wangwentao on 2017/2/21.
* 主要用于测试根据是否传入上下文调用弹出框
*/
public class DialogUtil {
//传入上下文
public static void showToastWithContext(Context context,String content){
if(context != null && !TextUtils.isEmpty(content)){
Toast.makeText(MyApplication.getContext(), content, Toast.LENGTH_SHORT).show();
}
}
//不需要传上下文
public static void showToastWithoutContext(String content){
if(!TextUtils.isEmpty(content)){
Toast.makeText(MyApplication.getContext(), content, Toast.LENGTH_SHORT).show();
}
}
}
主界面效果图:
二、上下文的使用讲解
1)上下文的继承关系
从继承关系可以看出Context是一个抽象类,内部定义了多个抽象的方法;
ContextIml是Context的实现类;
ContextWrapper类是对Context的一种封装,该类的构造函数包含了一个真正的Context引用,即ContextIml对象。一般在创建Application、Service、Activity时赋值
ContextThemeWrapper类,该类内部包含了主题(Theme)相关的接口,即android:theme属性指定的。只有Activity需要主题,Service不需要主题,所以Service直接继承于ContextWrapper类。
Activity类 、Service类 、Application类本质上都是Context子类。
2)Context的创建,主要有以下几种情况:
1、创建Application 对象时, 而且整个App共一个Application对象
2、创建Service对象时
3、创建Activity对象时
通过以上几种方式,可以得出一个结论,整个app中所有的Context的个数应该为
总Context实例个数 = Service个数 + Activity个数 + 1;
其中整个app中只有一个Application的上下文
3)上下文的使用场景
这里引用一张网上的图,可以说明情况:
关于里面的数字的函数说明:
数字1:启动Activity在这些类中是可以的,但是需要创建一个新的task。一般情况不推荐。
数字2:在这些类中去layout inflate是合法的,但是会使用系统默认的主题样式,如果你自定义了某些样式可能不会被使用。
数字3:在receiver为null时允许,在4.2或以上的版本中,用于获取黏性广播的当前值。(可以无视)
注:ContentProvider、BroadcastReceiver之所以在上述表格中,是因为在其内部方法中都有一个context用于使用。
大家注意到没有,测试demo中使用了一个工具类,工具类中使用了Toast测试使用全局上下文的情况,没有问题,如果在工具类中写一个调用对话框,使用全局上下文的情况,会怎么样呢?
直接报错,有兴趣的可以直接试下。
结论就是Context不能随便乱用,当不知道怎么用时,可以参考下这张表。
4)如何获取上下文
1、this 即当前的application或activity或service。
2、getApplication() 系统提供的单例对象,每个应用只有一个。只能在activity和service(以及application类)中调用,获得应用的application单例对象。
3、getApplicationContext() getApplicationContext()返回应用的上下文,生命周期是整个应用。相对比getApplication获取的对象是一样的,都是应用的application单例对象,但是应用范围更加广,可以例如broadcast receiver中可以使用。
4、getBaseContext() 返回由构造函数指定或setBaseContext()设置的上下文,调用该方法时获取一个ContextWrapper初始化为具体的Application、Service、ContextThemeWrapper的context。不建议使用。
5、getActivity() 在fragment中获取fragment从属的activity的context,相当于该Activity的activity.this。存在于该activity的生命周期中。
6.getContext() 返回当前这个View对象的context