Android context(Application/Activity)与内存泄露

android中context可以做很多操作,但是最主要的功能是加载和访问资源。

在android中有两种context,一种是 application context,一种是activity context,通常我们在各种类和方法间传递的是activity context。 

比如一个activity的onCreate: 
01 public void onCreate(Bundle savedInstanceState) {
02     super.onCreate(savedInstanceState);
03  
04     requestWindowFeature(Window.FEATURE_NO_TITLE);
05     getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
06                          WindowManager.LayoutParams.FLAG_FULLSCREEN);
07     setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
08  
09  
10     mGameView = new GameView(this);  
11     setContentView(mGameView);
12 }

把activity context传递给view,意味着view拥有一个指向activity的引用,进而引用activity UI占有的资源:view , resource, SensorManager等。 
但是这样如果context发生内存泄露的话,就会泄露很多内存,这里泄露的意思是gc没有办法回收activity的内存(当前Activity为活动或finish后还没来得及回收)。

Leaking an entire activity是很容易的一件事。 
当屏幕旋转的时候,系统会销毁当前的activity,保存状态信息再创建一个新的。 

比如我们写了一个应用程序,需要加载一个很大的图片,我们不希望每次旋转屏幕的时候都销毁这个图片重新加载。

实现这个要求的简单想法就是定义一个静态的Drawable,这样Activity 类创建销毁它始终保存在内存中,访问速度会很快。 

实现类似: 
01 public class myactivity extends Activity {
02     private static Drawable sBackground;
03     protected void onCreate(Bundle state) {
04         super.onCreate(state);
05  
06         TextView label = new TextView(this);
07         label.setText("Leaks are bad");
08  
09         if (sBackground == null) {
10             sBackground = getDrawable(R.drawable.large_bitmap);
11         }
12         label.setBackgroundDrawable(sBackground);//drawable attached to a view
13  
14         setContentView(label);
15     }
16 }

这段程序看起来很简单,但是却问题很大。当屏幕旋转的时候会有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方法获取。 

使用Application,需要在 AndroidManifest.xml 文件注册,即android:name=".GApplication"

01 <application android:icon="@drawable/icon"
02              android:label="@string/app_name"
03              android:name=".GApplication">
04  
05     <activity android:name=".WordSearch"
06               android:label="@string/app_name"
07               android:theme="@android:style/Theme.NoTitleBar.Fullscreen"
08               android:screenOrientation="portrait"
09               android:configChanges="keyboardHidden|orientation|keyboard|screenLayout">
10         <intent-filter>
11             <action android:name="android.intent.action.MAIN" />
12             <category android:name="android.intent.category.LAUNCHER" />
13         </intent-filter>
14     </activity>
15 </application>

避免context相关的内存泄露,记住以下几点: 
1. 不要让生命周期长的对象引用activity context,即保证引用activity的对象要与activity本身生命周期是一样的 
2. 对于生命周期长的对象,可以使用application context (继承类:public class GApplicationextends Application)
3. 尽量使用静态类(全局),避免非静态的内部类,避免生命周期问题,注意内部类对外部对象引用导致的生命周期变化

文章出处:http://blog.csdn.net/sunboy_2050/article/details/6891665

android中context可以做很多操作,但是最主要的功能是加载和访问资源。

在android中有两种context,一种是 application context,一种是activity context,通常我们在各种类和方法间传递的是activity context。 

比如一个activity的onCreate: 

01 public void onCreate(Bundle savedInstanceState) {
02     super.onCreate(savedInstanceState);
03  
04     requestWindowFeature(Window.FEATURE_NO_TITLE);
05     getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
06                          WindowManager.LayoutParams.FLAG_FULLSCREEN);
07     setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
08  
09  
10     mGameView = new GameView(this);  
11     setContentView(mGameView);
12 }

把activity context传递给view,意味着view拥有一个指向activity的引用,进而引用activity UI占有的资源:view , resource, SensorManager等。 
但是这样如果context发生内存泄露的话,就会泄露很多内存,这里泄露的意思是gc没有办法回收activity的内存(当前Activity为活动或finish后还没来得及回收)。

Leaking an entire activity是很容易的一件事。 
当屏幕旋转的时候,系统会销毁当前的activity,保存状态信息再创建一个新的。 

比如我们写了一个应用程序,需要加载一个很大的图片,我们不希望每次旋转屏幕的时候都销毁这个图片重新加载。

实现这个要求的简单想法就是定义一个静态的Drawable,这样Activity 类创建销毁它始终保存在内存中,访问速度会很快。 

实现类似: 

01 public class myactivity extends Activity {
02     private static Drawable sBackground;
03     protected void onCreate(Bundle state) {
04         super.onCreate(state);
05  
06         TextView label = new TextView(this);
07         label.setText("Leaks are bad");
08  
09         if (sBackground == null) {
10             sBackground = getDrawable(R.drawable.large_bitmap);
11         }
12         label.setBackgroundDrawable(sBackground);//drawable attached to a view
13  
14         setContentView(label);
15     }
16 }

这段程序看起来很简单,但是却问题很大。当屏幕旋转的时候会有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方法获取。 

使用Application,需要在 AndroidManifest.xml 文件注册,即android:name=".GApplication"

01 <application android:icon="@drawable/icon"
02              android:label="@string/app_name"
03              android:name=".GApplication">
04  
05     <activity android:name=".WordSearch"
06               android:label="@string/app_name"
07               android:theme="@android:style/Theme.NoTitleBar.Fullscreen"
08               android:screenOrientation="portrait"
09               android:configChanges="keyboardHidden|orientation|keyboard|screenLayout">
10         <intent-filter>
11             <action android:name="android.intent.action.MAIN" />
12             <category android:name="android.intent.category.LAUNCHER" />
13         </intent-filter>
14     </activity>
15 </application>

避免context相关的内存泄露,记住以下几点: 
1. 不要让生命周期长的对象引用activity context,即保证引用activity的对象要与activity本身生命周期是一样的 
2. 对于生命周期长的对象,可以使用application context (继承类:public class GApplicationextends Application)
3. 尽量使用静态类(全局),避免非静态的内部类,避免生命周期问题,注意内部类对外部对象引用导致的生命周期变化

文章出处:http://blog.csdn.net/sunboy_2050/article/details/6891665

 

 

 

 

 

使用application需要两个步骤:1.复写Application类,2.在配置文件中配置
示例代码如下:
package app.app;
import android.app.Application;
import android.content.res.Configuration;
public class MyApp extends Application {
@Override
public void onConfigurationChanged(Configuration newConfig) {
  super.onConfigurationChanged(newConfig);
}
@Override
public void onCreate() {
  super.onCreate();
  System.out.println("MyApp is called");
}
@Override
public void onLowMemory() {
  super.onLowMemory();
}
@Override
public void onTerminate() {
  super.onTerminate();
}
}

配置文件:
<application android:icon="@drawable/icon" android:label="@string/app_name" android:name="app.app.MyApp" >
  ......
</application>

 
 

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

感觉用这种方式存储登录信息比较好,比SharedPreferences存储登录信息的状态比较合适。 
相当于存储session的数据(打个比喻)。

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

用SharedPreferences存储的话关闭应用的时候这个值还是最后一次设置的值,如果是非正常退出程序时登录状态就会有问题,所以在使用上感觉 application context是最合适存储当前登录状态的信息,当然可是存储一些相当于session当中存储的值,因为关闭应用后这些值就没有了

 
package org.mdx.core.modules;

import android.app.Application;

public class ApplicationHelper extends Application {

 private boolean session_enter = false; // 默认为false

 /**
  * 设置session状态
  * 
  * @param bol
  *            true为以登录,false未登录
  */
 public void putSession(boolean bol) {
  session_enter = bol;
 }

 /**
  * 是否登录
  * 
  * @return true是,false否
  */
 public boolean isEnter() {
  return session_enter;
 }

}
在AndroidManifest.xml的application加个name属性就可以了,如下面所示:
<application android:name=".core.modules.ApplicationHelper"
android:icon="@drawable/icon" android:label="@string/app_name">

activity使用时:(这个是重点)
@Override
protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  getApplicationContext().putSession(false); // 设置登录状态为退出
}
public ApplicationHelper getApplicationContext() {
  return ((ApplicationHelper) super.getApplicationContext());
}

 

 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值