Android中的Context详解

Android中的Context详解 (2012-07-23 14:21)
标签:   Android   context  分类:  android_context

Android基础概念Context的作用

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:

点击(此处)折叠或打开

  1. protected void onCreate(Bundle state) {
  2.     super.onCreate(state);
  3.     TextView label = newTextView(this); 
  4.    //传递context给view control 
  5.      label.setText("Leaks are bad");
  6.      setContentView(label);
  7. }
把activity context传递给view,意味着view拥有一个指向activity的引用,进而引用activity占有的资源:view hierachy, resource等。 这样如果context发生内存泄露的话,就会泄露很多内存。 这里泄露的意思是gc没有办法回收activity的内存。 Leaking an entire activity是很容易的一件事。 当屏幕旋转的时候,系统会销毁当前的activity,保存状态信息,再创建一个新的。 比如我们写了一个应用程序,它需要加载一个很大的图片,我们不希望每次旋转屏 幕的时候都销毁这个图片,重新加载。实现这个要求的简单想法就是定义一个静态的Drawable,这样Activity 类创建销毁它始终保存在内存中。 实现类似:

点击(此处)折叠或打开

  1. public class myactivity extends Activity {
  2.      private static Drawable sBackground; 
  3.      protected voidonCreate(Bundle state) { 
  4.         super.onCreate(state); 
  5.         TextView label = new TextView(this); 
  6.         label.setText("Leaks are bad"); 
  7.         if (sBackground == null) { 
  8.             sBackground = getDrawable(R.drawable.large_bitmap); 
  9.          } 
  10.         label.setBackgroundDrawable(sBackground);
  11.         //drawable attached to a view 
  12.         setContentView(label); 
  13.       } 
  14. }


这段程序看起来很简单,但是却问题很大。当屏幕旋转的时候会有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. 避免非静态的内部类,尽量使用静态类,避免生命周期问题,注意内部类对外部对象引用导致的生命周期变化

根据packageName构造Context  

通常情况下获取当前应用的context的方法是getApplicationContext, 但是通过根据其他的packageName如何构造 Context呢? Android平台的应用实例其实还可以通过其他方式构造。 
比如代码:

点击(此处)折叠或打开

  1. try {
  2.     Context ctx=createPackageContext("com.android123.Cwj", 0);
  3.     //ctx已经是com.android123.cwj的实例 
  4.     } catch(NameNotFoundException e) {
  5.         //可能由于 pacakgeName不存在所以必须处理该异常
  6.     }
需要注意的是,createPackageContext方法的第二个参数可选为CONTEXT_INCLUDE_CODE 和 CONTEXT_IGNORE_SECURITY , 定义分别为4和2,上面为0。一般忽略安全错误问题可以通过CONTEXT_IGNORE_SECURITY 标记, 同时可能还需要处理 SecurityException 异常.

Android Context应该怎么写?  


如果想要弹出一个AlertDialog,要写如下的代码 

点击(此处)折叠或打开

  1. AlertDialog.Builder alert =newAlertDialog.Builder(this); 
  2. alert.setTitle("Warning"); 
  3. alert.setMessage("Wrong time!"); 
  4. alert.show();
这里构造方法的原型是AlertDialog.Builder(Context arg) 需要一个Context类的对象作为参数,一般我们都在Activity里写,所以用this,表示在当前的会话中弹出AlertDialog。 在我的一个程序里,我自定义了一个接口Public  interfaceCustomPickerListener,在实现这个接口的方法时我需要弹出来一个AlertDialog,这里,参数表里填写this的话会提示错误:The constructor AlertDialog.Builder( new CustomPickerListener(){})  is undefined. 其实这个地方写this很明显是错误的,但是要写什么才能达成目的呢? 官方文档上对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. 
于是我就试着写上了我程序的 包.目标类. this ,如下 
 AlertDialog.Builder   alert = newAlertDialog.Builder(com.android.alcoholtest.AlcoholTest. this); 
然后就成功了

Android中Activity共享变量的另一方法:Application context


Android中在不同Activity中传递变量,通常使用Intent中Bundle添加变量的操作方法。   

点击(此处)折叠或打开

  1. Intent intent = new Intent();
  2. intent.setClass(A.this, B.class);
  3. Bundle bundle =new Bundle();
  4. bundle.putString("Info", "Information");
  5. intent.putExtras(bundle);
  6. startActivity(intent);
  7. Intent   intent = new Intent();
  8. intent.setClass(A.this, B.class);
  9. Bundle bundle = new Bundle();
  10. bundle.putString("Info", "Information");
  11. intent.putExtras(bundle);
  12. startActivity(intent);

  13.    
不过在多个Activity中经常使用同一变量时,使用Bundle则比较麻烦,每次调用Activity都需要设置一次。   如想在整个应用中使用,在java中一般是使用静态变量,而在android中有个更优雅的方式是使用Application context。   新建一个类,继承自Application

点击(此处)折叠或打开

  1. class MyApp extends Application {
  2.     private String myState;
  3.     public String getState() {
  4.         return myState;   
  5.     }
  6.     public void setState(String s) {
  7.         myState = s;   
  8.     }   
  9. }   
  在AndroidManifest.xml的application加个name属性就可以了,如下面所示:

点击(此处)折叠或打开

  1. android:name=".MyApp"
  2. android:icon="@drawable/icon"    
  3. android:label="@string/app_name">
  使用时:

点击(此处)折叠或打开

  1. class Blah extends Activity { 
  2.         @Override   
  3.     public void onCreate(Bundle b){
  4.    ...
  5.     MyApp appState = ((MyApp)getApplicationContext());
  6.     String state = appState.getState();   ...
  7.    }
  8. }

  9.    
   英文引用:
http: // stackoverflow.com/questions/708012/android-how-to-declare- global-variables        The more general problem you are encountering  is how to save stateacross several Activities and all parts of your application. A staticvariable ( for instance, a singleton)  is a common Java way of achievingthis. I have found however, that a more elegant way  in Android  is toassociate your state with the Application context.   
--如想在整个应用中使用,在java中一般是使用静态变量,而在android中有个更优雅的方式是使用Application context。  
 As you know, each Activity  is also a Context, which  is informationabout its execution environment  in the broadest sense. Your applicationalso has a context, and Android guarantees that it will exist  as asingle instance across your application.  
 --每个Activity 都是Context,其包含了其运行时的一些状态,android保证了其是single instance的。 
 The way to  do  this  is to create your own subclass of android.app.Application,and then specify that  class  in the application tag  in your manifest.Now Android will automatically create an instance of that  class andmake it available  for your entire application. You can access it fromany context using the Context.getApplicationContext() method (Activityalso provides a method getApplication() which has the exact sameeffect):   --方法是创建一个属于你自己的android.app.Application的子类,然后在manifest中申明一下这个类,这是 android就为此建立一个全局可用的实例,你可以在其他任何地方使用Context.getApplicationContext()方法获取这个实例,进而获取其中的状态(变量)。

[小技巧]在任意位置获取应用程序Context - [Android学习笔记]

Android程序中访问资源时需要提供Context,一般来说只有在各种component中(Activity, Provider等等)才能方便的使用api来获取Context, 而在某些工具类中要获取就很麻烦了。为此,我们可以自定义一个Application类来实现这种功能。 

点击(此处)折叠或打开

  1. import android.app.Application;
  2. public class MyApplication extends Application { 
  3.     private static MyApplication instance; 
  4.     public static MyApplication getInstance() { return instance; } 
  5.     @Override 
  6.     public void onCreate() {
  7.     // TODO Auto-generated method stub
  8.         super.onCreate();
  9.         instance = this;
  10.      } 
  11. }
然后在manifest中<application>中加入name= " mypackage.MyApplication "就可以在任意类中使用MyApplication.getInstance()来获取应用程序Context了。

Android获取其他包的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,代码如下: 

点击(此处)折叠或打开

  1. package chroya.demo; 
  2. import android.app.Activity; 
  3. import android.os.Bundle; 
  4. import android.util.Log; 
  5. class Main extends Activity { 
  6.         @Override 
  7.     public void onCreate(Bundle savedInstanceState) {
  8.     super.onCreate(savedInstanceState); 
  9.     } 
  10.     public voidprint(String msg) {
  11.     Log.d("Main", "msg:"+ msg); 
  12.     } 
  13. }
本包的调用Main的print方法的代码块如下: 

点击(此处)折叠或打开

  1. Context c createPackageContext("chroya.demo", 
  2.                          Context.CONTEXT_INCLUDE_CODE | Context.CONTEXT_IGNORE_SECURITY); 
  3. //载入这个类 
  4. Class clazz = c.getClassLoader().loadClass("chroya.demo.Main"); 
  5. //新建一个实例 
  6. Object owner =clazz.newInstance(); 
  7. //获取print方法,传入参数并执行 
  8. Object obj = clazz.getMethod("print", String.class).invoke(owner, "Hello");

ok,这样,我们就调用了chroya.demo包的Main类的print方法,执行结果,打印出了Hello。 怎么样,这只是一个调用其他包的代码的例子,我们获取到Context,还可以做很多事情,当然,题目所说的坏事,还是不要做为好。
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
大家好,今天给大家分享一下Android里的Context的一些用法. 这里大致可以分为两种:一是传递Context参数,二是调用全局的Context. 其实我们应用启动的时候会启动Application这个类,这个类是在AndroidManifest.xml文件里其实是默认的 为了让大家更容易理解,了一个简单的Demo.步骤如下: 第1步:新建一个Android工程ApplicationDemo,目录结构如下: 第2步:新建一个工具类ToolsUtil.java,代码如下 package com.tutor.application; import android.content.Context; import android.widget.Toast; /** * @author carlshen. * 应用的一些工具类. */ public class ToolUtils { /** * 参数带Context. * @param context * @param msg */ public static void showToast(Context context,String msg){ Toast.makeText(context, msg, Toast.LENGTH_SHORT).show(); } /** * 调用全局的Context. * @param msg */ public static void showToast(String msg){ Toast.makeText(MainApplication.getContext(), msg, Toast.LENGTH_SHORT).show(); } } 第3步:新建一个View命名为MainView.java就是我们Activity现实的View.代码如下: package com.tutor.application; import android.app.Activity; import android.content.Context; import android.util.AttributeSet; import android.view.LayoutInflater; import android.view.View; import android.widget.Button; import android.widget.FrameLayout; /** * @author carlshen. * 自定义的MainView. */ public class MainView extends FrameLayout implements View.OnClickListener{ private Context mContext; private Activity mActivity; /** * 参数Button. */ private Button mArgButton; /** * 全局Button. */ private Button mGlobleButton; /** * 退出Button. */ private Button mExitButton; public MainView(Context context){ super(context); setupViews(); } public MainView(Context context, AttributeSet attrs) { super(context, attrs); setupViews(); } private void setupViews(){ //获取View的上下文. mContext = getContext(); //这里将Context转换为Activity. mActivity = (Activity)mContext; LayoutInflater inflater = LayoutInflater.from(mContext); View v = inflater.inflate(R.layout.main, null); addView(v); mArgButton = (Button)v.findViewById(R.id.arg_button); mGlobleButton = (Button)v.findViewById(R.id.glo_button); mExitButton = (Button)v.findViewById(R.id.exit_button); mArgButton.setOnClickListener(this); mGlobleButton.setOnClickListener(this); mExitButton.setOnClickListener(this); } public void onClick(View v) { if(v == mArgButton){ ToolUtils.showToast(mContext, "我是通过传递Context参数显示的!"); }else if(v == mGlobleButton){ ToolUtils.showToast("我是通过全局Context显示的!"); }else{ mActivity.finish(); } } } 这里MainView.java使用的布局main.xml代码如下: <?xml version="1.0" encoding="utf-8"?> 第4步:修改ApplicationDemoActivity.java,代码如下: package com.tutor.application; import android.app.Activity; import android.os.Bundle; public class ApplicationDemoActivity extends Activity { private static Context aContext; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); MainView mMainView = new MainView(this); setContentView(mMainView); aContext = getApplicationContext(); } /**获取Context. * @return */ public static Context getContext(){ return aContext; } } 第5步:运行上述工程效果如下:

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值