深入浅出安卓Context

169 阅读4分钟

深入浅出安卓Context

一、Context是什么?——安卓世界的"万能钥匙"

想象Context就像是你在一家大公司的工作牌:

  • 身份证明:你是谁(哪个组件)
  • 权限通行证:你能访问哪些资源
  • 工具百宝箱:可以调用哪些系统服务

简单说,Context是安卓应用的运行环境信息,几乎所有操作都需要它:

  • 启动Activity
  • 访问资源(图片、字符串等)
  • 获取系统服务(如定位、WiFi)
  • 创建View对象

二、Context的三大具体类型

1. Application Context(公司总部通行证)

// 获取方式
Context appContext = getApplicationContext();

特点

  • 生命周期 = 整个应用
  • 不能做UI相关操作(如弹Toast、启动Activity需要FLAG)
  • 适合场景:
    • 全局数据存储
    • 初始化第三方库
    • 获取应用级资源

2. Activity Context(部门通行证)

// 在Activity中直接使用this
Context activityContext = MainActivity.this;

特点

  • 生命周期 = Activity生命周期
  • 可以做所有UI操作
  • 注意:
    • 不能长期持有(会导致内存泄漏)
    • 每个Activity实例有自己独立的Context

3. Service Context(后勤部门通行证)

// 在Service中
Context serviceContext = MyService.this;

特点

  • 生命周期 = Service生命周期
  • 没有UI能力(不能直接启动Activity)
  • 适合后台任务

三、Context能干什么?——工作牌的使用权限

1. 访问资源

// 获取字符串
String appName = context.getString(R.string.app_name);

// 获取图片
Drawable logo = context.getDrawable(R.drawable.logo);

// 获取颜色
int color = context.getColor(R.color.primary);

2. 启动组件

// 启动Activity
context.startActivity(new Intent(context, MainActivity.class));

// 启动Service
context.startService(new Intent(context, MyService.class));

// 发送广播
context.sendBroadcast(new Intent("com.example.ACTION"));

3. 获取系统服务

// 获取窗口管理器
WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);

// 获取定位服务
LocationManager lm = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);

四、Context的常见坑点

1. 内存泄漏——长期持有Activity Context

错误示范

public class AppUtils {
    private static Context sContext; // 危险!可能持有Activity
    
    public static void init(Context context) {
        sContext = context;
    }
}

正确做法

public class AppUtils {
    private static Context sAppContext; // 使用Application Context
    
    public static void init(Context context) {
        sAppContext = context.getApplicationContext();
    }
}

2. 错误类型使用——用Application Context做UI操作

问题代码

// 在非Activity环境中
getApplicationContext().startActivity(intent); // 可能崩溃

解决方案

intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); // 添加标记
getApplicationContext().startActivity(intent);

3. 空指针问题——Context未初始化

典型场景

  • 静态工具类中使用Context
  • 自定义View构造函数中

防御方案

public class MyView extends View {
    public MyView(Context context) {
        super(context);
        // 必须检查context是否为空
        if (context == null) {
            throw new IllegalArgumentException("Context不能为空");
        }
    }
}

五、Context的获取方式大全

1. 在Activity中

Context context = this; // Activity本身
Context appContext = getApplicationContext(); // 应用级Context
Context baseContext = getBaseContext(); // 底层Context(慎用)

2. 在Fragment中

// 方式1(可能为空)
Context context = getContext();

// 方式2(更可靠)
Context activityContext = requireActivity();

// 应用级
Context appContext = requireContext().getApplicationContext();

3. 在BroadcastReceiver中

public void onReceive(Context context, Intent intent) {
    // 参数中的context就是可用Context
}

4. 在Service中

Context context = this; // Service本身
Context appContext = getApplicationContext();

5. 在ContentProvider中

@Override
public boolean onCreate() {
    Context context = getContext();
    return true;
}

六、Context的底层原理

1. Context类结构

Context(抽象类)
├── ContextWrapper(装饰器模式)
│   └── ContextThemeWrapper(带主题)
│       └── Activity
└── Application
└── Service

2. 资源访问机制

  • 每个Context维护一个Resources对象
  • Activity会覆盖主题相关的资源
  • Application使用基础资源

3. 系统服务获取

// 实际调用流程
context.getSystemService()
 → ContextImpl.getSystemService()
 → SystemServiceRegistry.getSystemService()

七、正确使用Context的准则

  1. 生命周期匹配原则

    • 长生命周期的对象用Application Context
    • 短生命周期的对象用Activity Context
  2. 最小权限原则

    • 能用Application Context就不用Activity Context
  3. 及时释放原则

    • 不再使用的Context及时置null
  4. 类型明确原则

    • 需要Activity时不要传Application Context

八、Context常见面试题

1. getApplication()和getApplicationContext()的区别?

  • getApplication():返回Application实例(只在Activity/Service中可用)
  • getApplicationContext():返回应用级Context(任何Context都可调用)

2. 为什么有时Toast需要Activity Context?

// 这样可能崩溃(缺少FLAG)
getApplicationContext().showToast("Hello");

// 正确方式
Toast.makeText(activityContext, "Hello", Toast.LENGTH_SHORT).show();

原因:Toast需要UI上下文来显示窗口

3. 为什么Dialog需要Activity Context?

// 这样会崩溃
new Dialog(getApplicationContext());

// 必须用Activity
new Dialog(activityContext);

原因:Dialog需要附加到Activity的窗口

九、总结

Context就像安卓世界的空气:

  • 无处不在:几乎所有操作都需要它
  • 形态多样:Application/Activity/Service各有特点
  • 使用需谨慎:错误使用会导致内存泄漏或崩溃

记住三个关键点:

  1. 生命周期:选择匹配生命周期的Context类型
  2. 作用范围:UI操作必须用Activity Context
  3. 内存管理:避免长期持有Activity Context

掌握Context的正确使用,你的安卓开发之路会更加顺畅!