总篇地址:Android内存溢出与优化(零)——开题篇

前言:在Java中,如果将一个对象加上static修饰符,那么JVM虚拟机就会在内存中一直保留这个对象,这个对象不会被垃圾回收器清理,直到应用退出。为了达到目的,随意使用static修饰符是不好的表现。不过有时候又不得不使用static修饰,那么我们只用尽量避免消耗大内存的对象被static修饰或间接产生引用。下面开始Android中的代码讨论。

1.常用的Utils中的static修饰

为了方便调用,在Utils工具类中使用static修饰是我们常用的方式,不过可能存在一些问题,例如:

public class ToastUtil {

    private static Toast toast;

    public static void show(Context context, String message) {
        if (toast == null) {
            toast = Toast.makeText(context, message, Toast.LENGTH_SHORT);
        } else {
            toast.setText(message);
        }      
        toast.show();
    }

}

分析:请看static修饰的toast对象,在show方法中这个对象同时对context持有了引用。toast是static修饰的,意味着toast将不会从内存中消失,那么其持有的引用对象context将一直保持强引用,也不会在内存中消失。如果传个占用大内存的Activity的context进来,那么将会导致大量的内存泄漏。

提供2种解决办法:
1. 将context改为context.getApplicationContext(),由于ApplicationContext是一个全局的context,会在app运行期间一直存在,就不存在内存泄露的问题了
2. 创建Application类,提供获取ApplicationContext的方法,直接在show方法中获取,不需要每次都传context了(推荐)
代码如下:

//Application类
public class MyApplication extends Application {

    private static Context context;

    @Override
    public void onCreate() {
        super.onCreate();

        context = getApplicationContext();
    }

    public static Context getContext(){
        return context;
    }
}
//
//Toast工具类
public class ToastUtil {

    private static Toast toast;

    public static void show(String message) {
        if (toast == null) {
            toast = Toast.makeText(MyApplication.getContext(), message, Toast.LENGTH_SHORT);
        } else {
            toast.setText(message);
        }      
        toast.show();
    }
}

2.单例模式中的static修饰

在单例中我也可能会有传入context这种对象,例如数据库Dao层的设计,如下:

1.AppDemo数据库创建的class

/**
 * AppDemo数据库创建
 * @author ALion
 */
public class AppDemoOpenHelper extends SQLiteOpenHelper {

    public AppDemoOpenHelper(Context context) {
        super(context, "appDemo.db", null, 1);
    }

    //数据库第一次创建调用
    @Override
    public void onCreate(SQLiteDatabase db) {
        //字段:_id, name
        String sql = "CREATE TABLE t_appDemo (" +
                        "_id INTEGER PRIMARY KEY AUTOINCREAMENT" +
                        "name VARCHAR(20)" +
                    )";
        db.execSQL(sql);
    }

    //数据库更新时
    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {

    }
}

2.数据库Dao封装的class

/**
 * 数据库Dao封装
 * @author ALion
 */
public class AppDemoDao {

    private static AppDemoDao sInstance = null;

    public AppDemoOpenHelper mHelper;

    private AppDemoDao(Context context) {
        mHelper = new AppDemoOpenHelper(context);
    }

    public static AppDemoDao getInstance(Context context) {
        if (sInstance == null) {
            synchronized (AppDemoDao.class) {
                if (sInstance == null)
                    sInstance = new AppDemoDao(context);
            }
        }
        return sInstance;
    }

    // TODO 增加

    // TODO 删除

    // TODO 修改

    // TODO 查询
}

分析:可以看到创建数据库的AppDemoOpenHelper类需要传入一个context对象,而在Dao封装层的AppDemoDao类中又使用了单例设计模式。代码中,sInstance对象使用了static修饰,其new AppDemoDao(context)需要传入一个context对象,context被sInstance持有了强引用,从而导致了context对象的内存泄露。

解决方式同上,就不再给出代码了。

Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐