Context相关的杂货

Context
这里写图片描述
1、ContextImpl和ContextWraper关系
ContextImpl最终会通过setOuterContext方法接收的ContextWraper对象

接收Activity的对象

ActivityThread .java
private Context createBaseContextForActivity(ActivityClientRecord r,final Activity activity) {
    int displayId = Display.DEFAULT_DISPLAY;
    try {
        displayId =ActivityManagerNative.getDefault().getActivityDisplayId(r.token);
    } catch(RemoteException e) {
    }

    ContextImpl appContext = ContextImpl.createActivityContext(
            this, r.packageInfo, displayId, r.overrideConfig);
    appContext.setOuterContext(activity);
    Context baseContext = appContext;
...
…
}

接收Service 对象

ActivityThread.java
private void handleCreateService(CreateServiceData data) {
    unscheduleGcIdler();
    LoadedApk packageInfo = getPackageInfoNoCheck(
            data.info.applicationInfo, data.compatInfo);
    Service service = null;
    try {
        java.lang.ClassLoader cl = packageInfo.getClassLoader();
        service = (Service) cl.loadClass(data.info.name).newInstance();
    } catch (Exception e) {
        if (!mInstrumentation.onException(service, e)) {
            throw new RuntimeException(
                "Unable to instantiate service " + data.info.name
                + ": " + e.toString(), e);
        }
    }

    try {
        if (localLOGV) Slog.v(TAG, "Creating service " + data.info.name);
        ContextImpl context = ContextImpl.createAppContext(this, packageInfo);
        context.setOuterContext(service);

接收Application对象

“`
ActivityThread.java
private void handleBindApplication(AppBindData data) {

data.info = getPackageInfoNoCheck(data.appInfo, data.compatInfo);
/**
* Switch this process to density compatibility mode if needed.
*/

Application app = data.info.makeApplication(data.restrictedBackupMode,null);
mInitialApplication= app;

“`LoadedApk.java
public Application makeApplication(boolean forceDefaultAppClass,
Instrumentation instrumentation) {

String appClass = mApplicationInfo.className;
if (forceDefaultAppClass || (appClass == null)) {
appClass = “android.app.Application”;
}
try {
java.lang.ClassLoader cl = getClassLoader();
if (!mPackageName.equals(“android”)) {
initializeJavaContextClassLoader();
}
ContextImpl appContext = ContextImpl.createAppContext(mActivityThread, this);
app = mActivityThread.mInstrumentation.newApplication(
cl, appClass, appContext);
appContext.setOuterContext(app);

ContextImpl实现了我们平常调用的很多和资源有一定关系的方法,比如getAssets(),getResource(),getSharedPreference()..其实ContextImpl只是packginfo的一个代理,最终的资源文件获取还得交由packgeInfo处理。每一个ContextImpl对象对应一个packginfo。

由于ContextImpl里面持有ActivityThread对象(),所以我们才能在context里面去打开一个activity,service等。

2 、Context中的内存泄漏
也正是context里面持有了如此重量级的一些对象,因此对context操作不当引起的内存泄漏时非常致命的,特别是activity做为context时,如果保存在了静态对象中时,很危险。

public class LeakManager {
    private static LeakManager manager;
    private Context mContext;
    private static Context mContext2;
//这两个 context 谁可能会被泄漏?
    private LeakManager() {
    }

    private static class SingleTone {
        public static final LeakManager instance = new LeakManager();
    }

    public static LeakManager getInstance(Context context) {
        mContext2 = context;
        return SingleTone.instance;
    }

    public void setContext(Context context) {
        mContext = context;
    }

    public Resources getResource() {
        if (null == mContext)
            return null;
        return mContext.getResources();
    }

}

//外部调用的时候 LeakManager.getInstance(context).setContext(activity);
//两个context都会被泄漏,因为LeakManager是静态事例,其持有mContext对象。

3、Context的一些使用场景:

这里写图片描述
Y :表示正常的使用习惯
N1:表示不推荐,但是不会出错,如果打开Activity的时候指定的conext非Activity那么该Activity只能使用系统默认的主题。
BroadCastReceiver其实并不是Context子类,但其onReceive方法里面可以获取context。
以下是onReceive中记录的context对象日志:

11-1702:52:47.006 7778-7778/com.example.ndh.myapplication3 D/ndh--: context=com.example.ndh.myapplication3.FloatService@bdf0d5c//这是代码中注册的,其context就是注册广播的context对象
11-1702:52:47.006 7778-7778/com.example.ndh.myapplication3 D/ndh--:action=android.intent.action.USER_PRESENT
11-1702:52:47.006 7778-7778/com.example.ndh.myapplication3 D/ndh--: user_present
11-1702:52:47.028 7778-7778/com.example.ndh.myapplication3 D/ndh--: context=android.app.ReceiverRestrictedContext@4c8d743//这是xml中注册的广播
11-1702:52:47.028 7778-7778/com.example.ndh.myapplication3 D/ndh--:action=android.intent.action.USER_PRESENT

4、Dialog同toast对比

Dialog的创建只能使用Activity,Dialog只能使用Activity的原因其实并不是context出现问题,而是Window相关的环节出现错误。

/

/创建dialog事例
Dialog(@NonNull Context context, @StyleRes int themeResId, boolean createContextThemeWrapper) {
     ...
    mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
// dialog 就是一个没有令牌的应用窗口
    final Window w = new PhoneWindow(mContext);
    mWindow = w;
    w.setCallback(this);
    w.setOnWindowDismissedCallback(this);
    w.setWindowManager(mWindowManager, null, null);
//这里将appToken 设置为null  而activity的appToken是来源于ActivityThread
    w.setGravity(Gravity.CENTER);
//显示dialog
public void show() {
    ...
    mDecor = mWindow.getDecorView();

    try {
        mWindowManager.addView(mDecor, l);
        mShowing = true;
        sendShowMessage();
    } finally {
    }
…

在非activity中通过getSystemService来获取WindowManger,获得的事例WindowManagerImpl里没有token,并且设置的appToken也为空,因此最终wms里面不会存在token,在添加view的时候会报错(viewRootImpl—setView()方法会抛异常)。但是如果是通过activity.getSystemService这种方式获得WindowManager,由于Activity复写了getSystemService方法,这个时候返回的是 当前activity所在的windowManager对象:

//Activity.java中
@Override
public Object getSystemService(@ServiceName @NonNull String name) {
    if (getBaseContext() == null) {
        throw new IllegalStateException(
                "System services not available to Activities before onCreate()");
    }

    if (WINDOW_SERVICE.equals(name)) {
        return mWindowManager;
    } else if (SEARCH_SERVICE.equals(name)) {
        ensureSearchManager();
        return mSearchManager;
    }
    return super.getSystemService(name);
}

所以挂载dialog的窗体管理器最终使用的是当前activity的令牌token。

Toast同为弹窗为全局Context也能满足
这里涉及到窗体分类
1、应用型窗体 Activity Dialog等。 //应用型窗体 必须持有Token
2、子窗体 menu,popupwindow // 子窗体加载到父窗体上,必须有父窗体
3、系统窗体 alert,toast,输入法,等//没有父窗体,Token等限制

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值