context 详解

借鉴:Android应用Context详解及源码解析 

  http://www.androidchina.net/3853.html
  http://www.jianshu.com/p/f24707874b04

1,Context介绍

源码:
/**
 * Interface to global information about an application environment.  This is
 * an abstract class whose implementation is provided by
 * the Android system.  It
 * allows access to application-specific resources and classes, as well as
 * up-calls for application-level operations such as launching activities,
 * broadcasting and receiving intents, etc.
 */
public abstract class Context {
        ......................
}
通过注释得知:

1、它描述的是一个应用程序环境的信息,即上下文。
        2、该类是一个抽象(abstract class)类,Android提供了该抽象类的具体实现类(ContextIml类)。
        3、通过它我们可以获取应用程序的资源和类,也包括一些应用级别操作,例如:启动一个Activity,发送广播,接受Intent信息 等。

2,Context 之间的关系

先介绍一下context 类的继承关系,之后的讲解可以结合这张图来分析:

摘自:http://blog.csdn.net/yanbober/article/details/45967639

ContextImpl:该类实现了Context类的所有功能。

/**
 * Common implementation of Context API, which provides the base
 * context object for Activity and other application components.
 */
class ContextImpl extends Context {
    private Context mOuterContext;
    ......
}

ContextWrapper

/**
 * Proxying implementation of Context that simply delegates all of its calls to
 * another Context.  Can be subclassed to modify behavior without changing
 * the original Context.
 */
public class ContextWrapper extends Context {
    Context mBase;

    public ContextWrapper(Context base) {
        mBase = base;
    }

    /**
     * Set the base context for this ContextWrapper.  All calls will then be
     * delegated to the base context.  Throws
     * IllegalStateException if a base context has already been set.
     * 
     * @param base The new base context for this wrapper.
     */
    protected void attachBaseContext(Context base) {
        if (mBase != null) {
            throw new IllegalStateException("Base context already set");
        }
        mBase = base;
    }
    ......
}

ContextThemeWrapper:

/**
 * A ContextWrapper that allows you to modify the theme from what is in the 
 * wrapped context. 
 */
public class ContextThemeWrapper extends ContextWrapper {
    ......
}


Activity :

public class Activity extends ContextThemeWrapper
        implements LayoutInflater.Factory2,
        Window.Callback, KeyEvent.Callback,
        OnCreateContextMenuListener, ComponentCallbacks2,
        Window.OnWindowDismissedCallback {
    ......
}

Service

public abstract class Service extends ContextWrapper implements ComponentCallbacks2 {
    ......
}
Application

public class Application extends ContextWrapper implements ComponentCallbacks2 {
    ......
}

来说,这些之后都会结合源码详细讲解(只是为了自己方便才先写出来)

  1. context 是一个抽象类,提供类很多抽象的方法
  2. contextImpl 是context的实现类 继承context ,主要对抽象方法具体化,所有的context的具体操作都在这里。
  3. contextWrapper 是对context的包装类 继承context,
  4. contextThemeWrapper 是有主题的context,因为activity有主题(就是我们在Manifest里面配的),所以activity extendscontextThemeWrapper
  5. contextWrapper 有一个特别重要的变量 Context mBase,一般在启动activity、service、application 的时候通过先attachBaseContext ,把new 出来contextImpl赋值给mBase 之后,在通过回调 执行 各自的生命周期。
    /**
     * Set the base context for this ContextWrapper.  All calls will then be
     * delegated to the base context.  Throws
     * IllegalStateException if a base context has already been set.
     * 
     * @param base The new base context for this wrapper.
     */
    protected void attachBaseContext(Context base) {
        if (mBase != null) {
            throw new IllegalStateException("Base context already set");
        }
        mBase = base;
    }
  1. 源码中是将 contextImpl--->base,也就是在contextWrapper中,通过base 来操作 contextImpl
  2. 最终的操作都是在 contextImpl 实现的
activity、service、application 都属于context;      APP Context总数 = Application数(1) + Activity数(Customer) + Service数(Customer);
app中startActivity、startService、sendBroadcast 其实都是context的方法,这样看,四大组件中三个和context相关,由此可见context是多么的重要

3,Context在ActivityThread中实例化过程源码分析

Context的实现是ContextImpl,Activity与Application和Service的创建都是在ActivityThread中完成的,重点分析各context的创建过程。

3.1Activity中ContextImpl实例化源码分析

通过startActivity启动一个新的Activity时系统会回调ActivityThread的handleLaunchActivity()方法,内部在调用performLaunchActivity创建activity,然后回调onCreate等方法,所以activity的contextImpl在performLaunchActivity中

private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
        ......
            //已经创建好新的activity实例
            if (activity != null) {
                //创建一个Context对象
                Context appContext = createBaseContextForActivity(r, activity);
                ......
                //将上面创建的appContext传入到activity的attach方法
                activity.attach(appContext, this, getInstrumentation(), r.token,
                        r.ident, app, r.intent, r.activityInfo, title, r.parent,
                        r.embeddedID, r.lastNonConfigurationInstances, config,
                        r.referrer, r.voiceInteractor);
                ......
            }
        ......
        return activity;
    }

createBaseContextForActivity()创建context,再通过attach,把appContext传入activity中。

    private Context createBaseContextForActivity(ActivityClientRecord r,
            final Activity activity) {
        //实质就是new一个ContextImpl对象,调运ContextImpl的有参构造初始化一些参数    
        ContextImpl appContext = ContextImpl.createActivityContext(this, r.packageInfo, r.token);
        //特别特别留意这里!!!
        //ContextImpl中有一个Context的成员叫mOuterContext,通过这条语句就可将当前新Activity对象赋值到创建的ContextImpl的成员mOuterContext(也就是让ContextImpl内部持有Activity)。
        appContext.setOuterContext(activity);
        //创建返回值并且赋值
        Context baseContext = appContext;
        ......
        //返回ContextImpl对象
        return baseContext;
    }


activity.attach

    final void attach(Context context, ActivityThread aThread,
            Instrumentation instr, IBinder token, int ident,
            Application application, Intent intent, ActivityInfo info,
            CharSequence title, Activity parent, String id,
            NonConfigurationInstances lastNonConfigurationInstances,
            Configuration config, String referrer, IVoiceInteractor voiceInteractor) {
        //特别特别留意这里!!!
        //与上面createBaseContextForActivity方法中setOuterContext语句类似,不同的在于:
        //通过ContextThemeWrapper类的attachBaseContext方法,将createBaseContextForActivity中实例化的ContextImpl对象传入到ContextWrapper类的mBase变量,这样ContextWrapper(Context子类)类的成员mBase就被实例化为Context的实现类ContextImpl
        attachBaseContext(context);
        ......
    }

Activity通过ContextWrapper的成员mBase来引用了一个ContextImpl对象,这样,Activity组件以后就可以通过这个ContextImpl对象来执行一些具体的操作(启动Service等);

同时ContextImpl类又通过自己的成员mOuterContext引用了与它关联的Activity,这样ContextImpl类也可以操作Activity。

3.2Service中ContextImpl实例化源码分析

写APP时我们通过startService或者bindService方法创建一个新Service时就会回调ActivityThread类的handleCreateService()方法完成相关数据操作
  private void handleCreateService(CreateServiceData data) {
        ......
        //类似上面Activity的创建,这里创建service对象实例
        Service service = null;
        try {
            java.lang.ClassLoader cl = packageInfo.getClassLoader();
            service = (Service) cl.loadClass(data.info.name).newInstance();
        } catch (Exception e) {
            ......
        }

        try {
            ......
            //不做过多解释,创建一个Context对象
            ContextImpl context = ContextImpl.createAppContext(this, packageInfo);
            //特别特别留意这里!!!
            //ContextImpl中有一个Context的成员叫mOuterContext,通过这条语句就可将当前新Service对象赋值到创建的ContextImpl的成员mOuterContext(也就是让ContextImpl内部持有Service)。
            context.setOuterContext(service);

            Application app = packageInfo.makeApplication(false, mInstrumentation);
            //将上面创建的context传入到service的attach方法
            service.attach(context, this, data.info.name, data.token, app,
                    ActivityManagerNative.getDefault());
            service.onCreate();
            ......
        } catch (Exception e) {
            ......
        }
    }

service.attach
    public final void attach(
            Context context,
            ActivityThread thread, String className, IBinder token,
            Application application, Object activityManager) {
            //特别特别留意这里!!!
            //与上面handleCreateService方法中setOuterContext语句类似,不同的在于:
            //通过ContextWrapper类的attachBaseContext方法,将handleCreateService中实例化的ContextImpl对象传入到ContextWrapper类的mBase变量,这样ContextWrapper(Context子类)类的成员mBase就被实例化为Context的实现类ContextImpl
        attachBaseContext(context);
        ......
    }

3.3Application中ContextImpl实例化源码分析

当我们写好一个APP以后每次重新启动时都会首先创建Application对象(每个APP都有一个唯一的全局Application对象,与整个APP的生命周期相同)。

创建Application的过程也在ActivityThread类的handleBindApplication()方法完成相关数据操作(具体关于ActivityThread调运handleBindApplication时机等

细节分析与上面Activity雷同,后边文章会做分析)。而ContextImpl的创建是在该方法中调运LoadedApk类的makeApplication方法中实现,

LoadedApk类的makeApplication()方法中源代码如下:


    public Application makeApplication(boolean forceDefaultAppClass,
            Instrumentation instrumentation) {
        //只有新创建的APP才会走if代码块之后的剩余逻辑
        if (mApplication != null) {
            return mApplication;
        }
        //即将创建的Application对象
        Application app = null;

        String appClass = mApplicationInfo.className;
        if (forceDefaultAppClass || (appClass == null)) {
            appClass = "android.app.Application";
        }

        try {
            java.lang.ClassLoader cl = getClassLoader();
            if (!mPackageName.equals("android")) {
                initializeJavaContextClassLoader();
            }
            //不做过多解释,创建一个Context对象
            ContextImpl appContext = ContextImpl.createAppContext(mActivityThread, this);
            //将Context传入Instrumentation类的newApplication方法
            app = mActivityThread.mInstrumentation.newApplication(
                    cl, appClass, appContext);
            //特别特别留意这里!!!
            //ContextImpl中有一个Context的成员叫mOuterContext,通过这条语句就可将当前新Application对象赋值到创建的ContextImpl的成员mOuterContext(也就是让ContextImpl内部持有Application)。
            appContext.setOuterContext(app);
        } catch (Exception e) {
            ......
        }
        ......
        return app;
    }


Instrumentation.newApplication方法

    public Application newApplication(ClassLoader cl, String className, Context context)
            throws InstantiationException, IllegalAccessException, 
            ClassNotFoundException {
        return newApplication(cl.loadClass(className), context);
    }

newApplication

    static public Application newApplication(Class<?> clazz, Context context)
            throws InstantiationException, IllegalAccessException, 
            ClassNotFoundException {
        ......
        //继续传递context
        app.attach(context);
        return app;
    }

attach

final void attach(Context context) {
        //特别特别留意这里!!!
        //与上面makeApplication方法中setOuterContext语句类似,不同的在于:
        //通过ContextWrapper类的attachBaseContext方法,将makeApplication中实例化的ContextImpl对象传入到ContextWrapper类的mBase变量,这样ContextWrapper(Context子类)类的成员mBase就被实例化为Application的实现类ContextImpl
        attachBaseContext(context);
        ......
    }

4,各种Context的使用,注意事项

4.1 getApplication和getApplicationContext的区别:

getApplication()方法的语义性非常强,一看就知道是用来获取Application实例的,但是这个方法只有在Activity和Service中才能调用的到。
那么也许在绝大多数情况下我们都是在Activity或者Service中使用Application的,但是如果在一些其它的场景,比如BroadcastReceiver中也想获得Application的实例,这时就可以借助getApplicationContext()方法了。
1,在activity和service中 getApplication 、getApplicationContext是一样的,
2,getApplicationContext 方法在context类中,作用域更大
3,getApplication只在activity 和 service 中有

4.2 context引起的内存泄漏问题:

单例模式导致内存泄漏
public class CustomManager {
  private static CustomManager sInstance;
  public static CustomManager getInstance(Context context) {
      if (sInstance == null) {
          sInstance = new CustomManager(context);
      }
      return sInstance;
  }
  private Context mContext;
  private CustomManager(Context context) {
      mContext = context;
  }
}




这样单例,有内存泄露的隐患,如果是在Activity中创建这个单例的话,传入的context为Activity的context,如果想要销毁Activity,但是单例的生命周期是整个APP,导致Activity的内存释放不完全。
所以需要修改成如下:
public class CustomManager {
  private static CustomManager sInstance;
  public static CustomManager getInstance(Context context) {
      if (sInstance == null) {
          sInstance = new CustomManager(context.getApplicationContext());
      }
      return sInstance;
  }
  private Context mContext;
  private CustomManager(Context context) {
      mContext = context;
  }
}

将context改为整个Application的Context,这时候单例与Activity就无关了,Activity释放的时候就不会出现内存泄露的问题了。
以后在使用Context对象获取静态资源,创建单例对象或者静态方法的时候,请多考虑Context的生命周期,一定要记得不要使用Activity的Context,切记要使用生命周期长的Application的Context对象。但是并不是所有情况使用Application的Context对象,在创建Dialog,View控件的时候都必须使用Activity的Context对象。

静态资源导致的内存泄漏
有时候旋转屏幕时候,会先销毁原来的Activity,重新建立一个Activity,这时候图片我们并不想重新加载,所以将图片设置为静态对象。
但是静态对象的view.setBackgroundDrawable();方法很容易造成内存泄露。
public class MyCustomResource {
  //静态变量drawable
  private static Drawable drawable;
  private View view;
  public MyCustomResource(Context context) {
      Resources resources = context.getResources();
      drawable = resources.getDrawable(R.drawable.ic_launcher);
      view = new View(context);
      view.setBackgroundDrawable(drawable);
  }
}
public void setBackgroundDrawable(Drawable background) {
       ..........
       /**此处的this就是当前View对象,而View对象又是有Context对象获得
       因此,变量background持有View对象的引用,View持有Context的引用,
       所有background间接持有Context对象的引用了*/
       background.setCallback(this);
       .......
  }

这时候想要销毁原来的Activity时,发现静态对象Drawable间接持有Context对象的引用,导致Activity的内存无法完全释放内存,这时候就造成了内存泄露。
因此,Android系统在在3.0版本之后修改了setBackgroundDrawable内部方法中的 background.setCallback(this);方法,里面的实现使用了 弱引用来持有View对象的引用,从而避免了内存泄漏隐患。所以,以后代码中避免使用静态资源,或者使用弱引用来解决相应的问题也是可以的


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值