Android中的Context详解(2012-07-23 14:21)
Context字面意思上下文,位于framework package的android.content.Context中,其实该类为LONG型,类似Win32中的Handle句柄,很多方法需要通过Context才能识别调用者的实例,比如说Toast的第一个参数就是Context,一般在Activity中我们直接用this代替,代表调用者的实例为Activity,而到了一个button的onClick(View view)等方法时,我们用this时就会报错,所以我们可能使用ActivityName.this来解决,主要原因是因为实现Context的类主要有Android特有的几个模型,Activity、Service以及BroadcastReceiver。 常规需要Context实例的方法主要有各种Service实现的类,比如说SensorManager在实例化时需要getSystemService(String)方法就必须由Context的实例执行,还有一些私有的文件系统I
/
O比如说openFileInput以及常用的Toast的makeText方法。
android context理解
在android中context可以作很多操作,但是最主要的功能是加载和访问资源。在android中有两种context,一种是 application context,一种是activity context,通常我们在各种类和方法间传递的是activity context。 比如一个activity的onCreate:
把activity context传递给view,意味着view拥有一个指向activity的引用,进而引用activity占有的资源:view hierachy, resource等。 这样如果context发生内存泄露的话,就会泄露很多内存。 这里泄露的意思是gc没有办法回收activity的内存。 Leaking an entire activity是很容易的一件事。 当屏幕旋转的时候,系统会销毁当前的activity,保存状态信息,再创建一个新的。 比如我们写了一个应用程序,它需要加载一个很大的图片,我们不希望每次旋转屏 幕的时候都销毁这个图片,重新加载。实现这个要求的简单想法就是定义一个静态的Drawable,这样Activity 类创建销毁它始终保存在内存中。 实现类似:
这段程序看起来很简单,但是却问题很大。当屏幕旋转的时候会有leak(即gc没法销毁activity)。 我们刚才说过,屏幕旋转的时候系统会销毁当前的activity。但是当drawable和view关联后,drawable保存了view的 reference,即sBackground保存了label的引用,而label保存了activity的引用。既然drawable不能销毁,它所 引用和间接引用的都不能销毁,这样系统就没有办法销毁当前的activity,于是造成了内存泄露。gc对这种类型的内存泄露是无能为力的。 避免这种内存泄露的方法是避免activity中的任何对象的生命周期长过activity,避免由于对象对 activity的引用导致activity不能正常被销毁。我们可以使用application context。application context伴随application的一生,与activity的生命周期无关。application context可以通过Context.getApplicationContext或者Activity.getApplication方法获取。 避免context相关的内存泄露,记住以下几点:
1. 不要让生命周期长的对象引用activity context,即保证引用activity的对象要与activity本身生命周期是一样的
2. 对于生命周期长的对象,可以使用application context
3. 避免非静态的内部类,尽量使用静态类,避免生命周期问题,注意内部类对外部对象引用导致的生命周期变化
android context理解
在android中context可以作很多操作,但是最主要的功能是加载和访问资源。在android中有两种context,一种是 application context,一种是activity context,通常我们在各种类和方法间传递的是activity context。 比如一个activity的onCreate:
点击(此处)折叠或打开
- protected void onCreate(Bundle state) {
- super.onCreate(state);
- TextView label = newTextView(this);
- //传递context给view control
- label.setText("Leaks are bad");
- setContentView(label);
- }
点击(此处)折叠或打开
- publicclassmyactivityextendsActivity{
- privatestaticDrawable sBackground;
- protectedvoidonCreate(Bundlestate){
- super.onCreate(state);
- TextViewlabel=newTextView(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);
- }
- }
根据packageName构造Context
通常情况下获取当前应用的context的方法是getApplicationContext, 但是通过根据其他的packageName如何构造 Context呢?Android平台的应用实例其实还可以通过其他方式构造。
比如代码:
需要注意的是,createPackageContext方法的第二个参数可选为CONTEXT_INCLUDE_CODE 和 CONTEXT_IGNORE_SECURITY , 定义分别为4和2,上面为0。一般忽略安全错误问题可以通过CONTEXT_IGNORE_SECURITY 标记, 同时可能还需要处理 SecurityException 异常.
点击(此处)折叠或打开
- try {
- Context ctx=createPackageContext("com.android123.Cwj", 0);
- //ctx已经是com.android123.cwj的实例
- } catch(NameNotFoundException e) {
- //可能由于 pacakgeName不存在所以必须处理该异常
- }
Android Context应该怎么写?
如果想要弹出一个AlertDialog,要写如下的代码
这里构造方法的原型是AlertDialog.Builder(Context arg) 需要一个Context类的对象作为参数,一般我们都在Activity里写,所以用this,表示在当前的会话中弹出AlertDialog。 在我的一个程序里,我自定义了一个接口Public
interfaceCustomPickerListener,在实现这个接口的方法时我需要弹出来一个AlertDialog,这里,参数表里填写this的话会提示错误:The constructor AlertDialog.Builder(
newCustomPickerListener(){})
isundefined.其实这个地方写this很明显是错误的,但是要写什么才能达成目的呢? 官方文档上对context的解释是
点击(此处)折叠或打开
- AlertDialog.Builder alert=newAlertDialog.Builder(this);
- alert.setTitle("Warning");
- alert.setMessage("Wrong time!");
- alert.show();
Interface to
globalinformation about an application environment. This
isan
abstract
classwhose implementation
isprovided by the Android system. It allows access to application-specific resources and classes,
aswell
asup-calls
forapplication-level operations such
aslaunching activities, broadcasting and receiving intents, etc.
于是我就试着写上了我程序的 包.目标类.
this,如下
AlertDialog.Builder alert=
newAlertDialog.Builder(com.android.alcoholtest.AlcoholTest.
this);
然后就成功了
Android中Activity共享变量的另一方法:Application context
Android中在不同Activity中传递变量,通常使用Intent中Bundle添加变量的操作方法。
点击(此处)折叠或打开
- Intent intent=newIntent();
- intent.setClass(A.this,B.class);
- Bundle bundle=newBundle();
- bundle.putString("Info","Information");
- intent.putExtras(bundle);
- startActivity(intent);
- Intent intent=newIntent();
- intent.setClass(A.this,B.class);
- Bundle bundle=newBundle();
- bundle.putString("Info","Information");
- intent.putExtras(bundle);
- startActivity(intent);
-
点击(此处)折叠或打开
- classMyAppextendsApplication{
- privateStringmyState;
- publicStringgetState(){
- returnmyState;
- }
- publicvoidsetState(Strings){
- myState=s;
- }
- }
点击(此处)折叠或打开
- android:name=".MyApp"
- android:icon="@drawable/icon"
- android:label="@string/app_name">
点击(此处)折叠或打开
- classBlahextendsActivity{
- @Override
- publicvoidonCreate(Bundle b){
- ...
- MyApp appState=((MyApp)getApplicationContext());
- Stringstate=appState.getState(); ...
- }
- }
-
http:
//
stackoverflow.com/questions/708012/android-how-to-declare- global-variables
The more general problem you are encountering
ishow to save stateacross several Activities and all parts of your application. A staticvariable (
forinstance, a singleton)
isa common Java way of achievingthis. I have found however, that a more elegant way
inAndroid
istoassociate your state with the Application context.
--如想在整个应用中使用,在java中一般是使用静态变量,而在android中有个更优雅的方式是使用Application context。
As you know, each Activity
isalso a Context, which
isinformationabout its execution environment
inthe broadest sense. Your applicationalso has a context, and Android guarantees that it will exist
asasingle instance across your application.
--每个Activity 都是Context,其包含了其运行时的一些状态,android保证了其是single instance的。
The way to
do
this
isto create your own subclass of android.app.Application,and then specify that
class
inthe application tag
inyour manifest.Now Android will automatically create an instance of that
classandmake it available
foryour entire application. You can access it fromany context
usingthe Context.getApplicationContext() method (Activityalso provides a method getApplication() which has the exact sameeffect): --方法是创建一个属于你自己的android.app.Application的子类,然后在manifest中申明一下这个类,这是 android就为此建立一个全局可用的实例,你可以在其他任何地方使用Context.getApplicationContext()方法获取这个实例,进而获取其中的状态(变量)。
然后在manifest中<application>中加入name=
"
mypackage.MyApplication
"就可以在任意类中使用MyApplication.getInstance()来获取应用程序Context了。
[小技巧]在任意位置获取应用程序Context - [Android学习笔记]
Android程序中访问资源时需要提供Context,一般来说只有在各种component中(Activity, Provider等等)才能方便的使用api来获取Context, 而在某些工具类中要获取就很麻烦了。为此,我们可以自定义一个Application类来实现这种功能。
点击(此处)折叠或打开
- importandroid.app.Application;
- publicclassMyApplicationextendsApplication{
- privatestaticMyApplication instance;
- publicstaticMyApplicationgetInstance(){returninstance;}
- @Override
- publicvoidonCreate(){
- // TODO Auto-generated method stub
- super.onCreate();
- instance=this;
- }
- }
Android获取其他包的Context实例然后干坏事
本包的调用Main的print方法的代码块如下:
ok,这样,我们就调用了chroya.demo包的Main类的print方法,执行结果,打印出了Hello。 怎么样,这只是一个调用其他包的代码的例子,我们获取到Context,还可以做很多事情,当然,题目所说的坏事,还是不要做为好。
Android中有Context的概念,想必大家都知道。Context可以做很多事情,打开activity、发送广播、打开本包下文件夹和数据库、获取classLoader、获取资源等等。如果我们得到了一个包的Context对象,那我们基本上可以做这个包自己能做的大部分事情。 那我们能得到吗?很高兴的告诉你,能! Context有个createPackageContext方法,可以创建另外一个包的上下文,这个实例不同于它本身的Context实例,但是功能是一样的。 这个方法有两个参数: 1。packageName 包名,要得到Context的包名 2。flags 标志位,有CONTEXT_INCLUDE_CODE和CONTEXT_IGNORE_SECURITY两个选项。CONTEXT_INCLUDE_CODE的意思是包括代码,也就是说可以执行这个包里面的代码。CONTEXT_IGNORE_SECURITY的意思是忽略安全警告,如果不加这个标志的话,有些功能是用不了的,会出现安全警告。下面给个小例子,执行另外一个包里面的某个类的方法。 另外一个包的包名是chroya.demo,类名Main,方法名print,代码如下:
点击(此处)折叠或打开
- packagechroya.demo;
- importandroid.app.Activity;
- importandroid.os.Bundle;
- importandroid.util.Log;
- classMainextendsActivity{
- @Override
- publicvoidonCreate(Bundle savedInstanceState){
- super.onCreate(savedInstanceState);
- }
- publicvoidprint(Stringmsg){
- Log.d("Main","msg:"+msg);
- }
- }
点击(此处)折叠或打开
- Contextc=createPackageContext("chroya.demo",
- Context.CONTEXT_INCLUDE_CODE|Context.CONTEXT_IGNORE_SECURITY);
- //载入这个类
- Classclazz=c.getClassLoader().loadClass("chroya.demo.Main");
- //新建一个实例
- Objectowner=clazz.newInstance();
- //获取print方法,传入参数并执行
- Objectobj=clazz.getMethod("print",String.class).invoke(owner,"Hello");
-