在android中我们经常遇到这样的情况,在创建一个对象的时候往往需要传递一个this参数,比如:语句 MyView mView = new MyView(this),要求传递一个this参数,这个this究竟指的是什么东西呢? 其实这里的this指的就是当前的Activity.this,是这个语句所在的Activity的this。Activity.this取的是这个Activity的Context,那么这个Context究竟是什么东西呢?它起到什么作用呢?

  Context 按照英文字面意思就是"上下文",它位于位于framework package的android.content.Context中,其实该类为LONG型,类似于句柄。很多方法需要通过 Context才能识别调用者的实例。

Context提供了关于应用环境全局信息的接口。它是一个抽象类,它的执行被Android系统所提供。它允许获取以应用为特征的资源和类型。同时启动应用级的操作,如启动一个Activity,发送广播,接受Intent

Context继承关系如下:

Context有两种类型

androidcontext可以作很多操作,但是最主要的功能是加载和访问资源。在android中有两种context,一种是 application context,一种是activity context。

补充:

getApplicationContext() 返回应用的上下文,生命周期是整个应用,应用摧毁它才摧毁

Activity.this的context 返回当前activity的上下文,属于activity ,activity 摧毁他就摧毁

getBaseContext()  返回由构造函数指定或setBaseContext()设置的上下文,一般不常用。

(1)activity context

通常我们在各种类和方法间传递的是activity context。比如一个activity的onCreate

protected void onCreate(Bundle state) {
super.onCreate(state);
 
TextView label = new TextView(this);

//传递context给view control

label.setText("Leaks are bad");
 
setContentView(label);
}

把activity context传递给view,意味着view拥有一个指向activity的引用,进而引用activity占有的资源:view hierachy, resource等。

内存泄露

context发生内存泄露的话,就会泄露很多内存。这里泄露的意思是gc没有办法回收activity的内存。

注释:为什么GC没有办法回收相应的内存,个人感觉是因为传递Context会增加对象指针的引用计数,所以基于智能指针技术的GC无法释放相应的内存。

当屏幕旋转的时候,系统会销毁当前的activity,保存状态信息,再创建一个新的。比如我们写了一个应用程序,它需要加载一个很大的图片,我们不希望每次旋转屏幕的时候都销毁这个图片,重新加载。实现这个要求的简单想法就是定义一个静态的Drawable,这样Activity 类创建销毁它始终保存在内存中。实现类似:

public class myactivity extends Activity {
private static Drawable sBackground;
protected void onCreate(Bundle state) {
super.onCreate(state); TextView label = new TextView(this);
label.setText("Leaks are bad"); if (sBackground == null) {
sBackground = getDrawable(R.drawable.large_bitmap);
}
label.setBackgroundDrawable(sBackground);//drawable attached to a view setContentView(label);
}
}

  这段程序看起来很简单,但是却问题很大。当屏幕旋转的时候会有leak(即gc没法销毁activity)。我们刚才说过,屏幕旋转的时候系统会销毁当前的activity。但是当drawable和view关联后,drawable保存了view的 reference,即sBackground保存了label的引用,而label保存了activity的引用。既然drawable不能销毁,它所引用和间接引用的都不能销毁,这样系统就没有办法销毁当前的activity,于是造成了内存泄露。gc对这种类型的内存泄露是无能为力的。避免这种内存泄露的方法是避免activity中的任何对象的生命周期长过activity,避免由于对象对 activity的引用导致activity不能正常被销毁。

为了防止内存泄露,我们应该注意以下几点:

    1. 不要让生命周期长的对象引用activity context,即保证引用activity的对象要与activity本身生命周期是一样的
    2. 对于生命周期长的对象,可以使用application context
    3. 避免非静态的内部类,尽量使用静态类,避免生命周期问题,注意内部类对外部对象引用导致的生命周期变化

什么时候创建Context实例

熟悉了Context的继承关系后,我们接下来分析应用程序在什么情况需要创建Context对象的?应用程序创建Context实例的

情况有如下几种情况:

  1、创建Application 对象时, 而且整个App共一个Application对象

  2、创建Service对象时

  3、创建Activity对象时

因此应用程序App共有的Context数目公式为:

                     总Context实例个数 = Service个数 + Activity个数 + 1(Application对应的Context实例)

具体创建Context的时机

1、创建Application对象的时机

  每个应用程序在第一次启动时,都会首先创建Application对象。如果对应用程序启动一个Activity(startActivity)流程比较

清楚的话,创建Application的时机在创建handleBindApplication()方法中,该函数位于 ActivityThread.java类中 ,如下:

 //创建Application时同时创建的ContextIml实例
private final void handleBindApplication(AppBindData data){

///创建Application对象
Application app = data.info.makeApplication(data.restrictedBackupMode, null);

}
public Application makeApplication(boolean forceDefaultAppClass, Instrumentation instrumentation) {

try {
java.lang.ClassLoader cl = getClassLoader();
ContextImpl appContext = new ContextImpl(); //创建一个ContextImpl对象实例
appContext.init(this, null, mActivityThread); //初始化该ContextIml实例的相关属性
///新建一个Application对象
app = mActivityThread.mInstrumentation.newApplication(
cl, appClass, appContext);
appContext.setOuterContext(app); //将该Application实例传递给该ContextImpl实例
}

}

2、创建Activity对象的时机

通过startActivity()或startActivityForResult()请求启动一个Activity时,如果系统检测需要新建一个Activity对象时,就会

回调handleLaunchActivity()方法,该方法继而调用performLaunchActivity()方法,去创建一个Activity实例,并且回调

onCreate(),onStart()方法等, 函数都位于 ActivityThread.java类 ,如下:

//创建一个Activity实例时同时创建ContextIml实例
private final void handleLaunchActivity(ActivityRecord r, Intent customIntent) {

Activity a = performLaunchActivity(r, customIntent); //启动一个Activity
}
private final Activity performLaunchActivity(ActivityRecord r, Intent customIntent) {

Activity activity = null;
try {
//创建一个Activity对象实例
java.lang.ClassLoader cl = r.packageInfo.getClassLoader();
activity = mInstrumentation.newActivity(cl, component.getClassName(), r.intent);
}
if (activity != null) {
ContextImpl appContext = new ContextImpl(); //创建一个Activity实例
appContext.init(r.packageInfo, r.token, this); //初始化该ContextIml实例的相关属性
appContext.setOuterContext(activity); //将该Activity信息传递给该ContextImpl实例

}

}

3、创建Service对象的时机

通过startService或者bindService时,如果系统检测到需要新创建一个Service实例,就会回调handleCreateService()方法,

完成相关数据操作。handleCreateService()函数位于 ActivityThread.java类,如下:

 //创建一个Service实例时同时创建ContextIml实例
private final void handleCreateService(CreateServiceData data){

//创建一个Service实例
Service service = null;
try {
java.lang.ClassLoader cl = packageInfo.getClassLoader();
service = (Service) cl.loadClass(data.info.name).newInstance();
} catch (Exception e) {
}

ContextImpl context = new ContextImpl(); //创建一个ContextImpl对象实例
context.init(packageInfo, null, this); //初始化该ContextIml实例的相关属性
//获得我们之前创建的Application对象信息
Application app = packageInfo.makeApplication(false, mInstrumentation);
//将该Service信息传递给该ContextImpl实例
context.setOuterContext(service);

}

  另外,需要强调一点的是,通过对ContextImp的分析可知,其方法的大多数操作都是直接调用其属性mPackageInfo(该属性类

型为PackageInfo)的相关方法而来。这说明ContextImp是一种轻量级类,而PackageInfo才是真正重量级的类。而一个App里的

所有ContextIml实例,都对应同一个packageInfo对象。

(2)application context

生命周期: application context生命周期比较长,伴随应用程序的存在而存在,与activity的生命周期无关。

获取: application context可以通过Context.getApplicationContext或者Activity.getApplication方法获取。

Java里面通常是用一个static的变量(例如singleton之类的)来同步activity之间(程序里面类之间)的状态。在android里面比较靠谱的做法是用application context来关联这些状态。

每个activity都是context,里面包含了运行时的状态。同样application也有一个contextandroid会保证这个context是唯一的实例。

做一个你自己的application context需要继承android.app.Application,然后在app的manifest里面说明这个类。android会自动帮你创建你这个类的实例,接着你用Context.getApplicationContext()方法就能在各个activity里面获得这个application context了。

class MyApp extends Application {

  private String myState;

  public String getState(){
return myState;
}
public void setState(String s){
myState = s;
}
} class Blah extends Activity { @Override
public void onCreate(Bundle b){
...
MyApp appState = ((MyApp)getApplicationContext());
String state = appState.getState();
...
}
}

Android中Context的总结及其用法的更多相关文章

  1. Android中Context详解 ---- 你所不知道的Context(转)

    Android中Context详解 ---- 你所不知道的Context(转)                                               本文出处 :http://b ...

  2. Android中Context具体解释 ---- 你所不知道的Context

                                                                                                         ...

  3. Android中Context详解

    大家好,  今天给大家介绍下我们在应用开发中最熟悉而陌生的朋友-----Context类 ,说它熟悉,是应为我们在开发中时刻的在与它打交道,例如:Service.BroadcastReceiver.A ...

  4. Android中Context详解 ---- 你所不知道的Context

    转自:http://blog.csdn.net/qinjuning/article/details/7310620Android中Context详解 ---- 你所不知道的Context 大家好,  ...

  5. Android中的Handler的具体用法

    Android UI操作并不是线程安全的并且这些操作必须在UI线程中执行.Android利用Handler来实现UI线程的更新的. Handler是Android中的消息发送器,其在哪个Activit ...

  6. 转:Android中Context详解 ---- 你所不知道的Context

    转:http://blog.csdn.net/qinjuning/article/details/7310620 转:http://blog.csdn.net/lmj623565791/article ...

  7. Android中Parcelable与Serializable接口用法

    转自: Android中Parcelable接口用法 1. Parcelable接口 Interface for classes whose instances can be written to a ...

  8. android 中context的具体作用和意义

    context在android中是非常重要的一个类,此类一般用于activity之中 从字面意思来看,这是环境变量,内部实现了一些方法,但是此类也可以看做是一个句柄,用来唯一标示activity 举个 ...

  9. Android中Context解析

    Context概念 当我们访问当前应用的资源,启动一个新的activity的时候都需要提供Context. Context是一个抽象基类,我们通过它访问当前包的资源(getResources.getA ...

随机推荐

  1. [BZOJ5046]分糖果游戏

    题目大意: 有a,b两个人分糖,每个人都有一个能量值. 每个人每一轮可以选择进行两种操作: 1.取走最左边的糖果,补充相应的能量值并获取相应的美味度. 2.跳过这一轮,能量值-1. 问在每个人都采取最 ...

  2. Node.js+MySQL管理工作的详细信息所遇到的问题

    问题陈述: Error: ER_TRUNCATED_WRONG_VALUE_FOR_FIELD: Incorrect string value: '\xE8\xB4\xAD\xE7\x89\xA9' ...

  3. IOS集成到支付宝的步骤及问题

    一,在支付宝开放平台下载支付宝SDK(https://openhome.alipay.com/platform/document.htm#down) 二,添加sdk文件到xcode 1,新建一个ali ...

  4. Mysql客户端下载地址

    官网:http://dev.mysql.com/downloads/mysql/   上述千万不要下载免安装版本. 千万记住一定要下载MSI安装版本.

  5. Vue实例详解与生命周期

    http://www.jianshu.com/p/b5858f1e6e76 Vue的实例是Vue框架的入口,其实也就是前端的ViewModel,它包含了页面中的业务逻辑处理.数据模型等,当然它也有自己 ...

  6. html之marquee会移动的文字

    该标签不是HTML3.2的一部分,并且只支持MSIE3以后内核,所以如果你使用非IE内核浏览器(如:Netscape)可能无法看到下面一些很有意思的效果 该标签是个容器标签 语法: <marqu ...

  7. [转]SSIS ProtectionLevel 对包中敏感数据的访问控制

    本文转自:http://technet.microsoft.com/zh-cn/library/ms141747.aspx 为了保护 Integration Services 包中的数据,可以设置保护 ...

  8. [转] SQL Server 批量 停用/启用 外键约束

    本文转自:http://hi.baidu.com/wangzhiqing999/item/ca699308de4f1ff9a1103429 今天百度知道上面,看到这样一个要求: 现在有一个库,有很多张 ...

  9. iOS:触摸事件和手势识别的介绍

    触摸事件和手势识别的介绍 1.iOS的输入事件  UIKit可识别三种类型的输入事件: 触摸事件 运动事件 远程控制事件 iOS中许多事件对象都是UIEvent类的实例,UIEvent记录了事件所产生 ...

  10. 流畅的python第十三章正确重载运算符

    运算符重载基础 不能重载内置类型的运算符 不能新建运算符,只能重载现有的 某些运算符不能重载-------is,and,or和not(不过位运算符&,|和~可以) 一元运算符