Android内存溢出与优化(一)——不要随意new对象

来源:互联网 发布:linux系统查看cpu主频 编辑:程序博客网 时间:2024/06/03 08:00
总篇地址:Android内存溢出与优化(零)——开题篇 

       在开发中,必然会new对象,一般为了方便,很可能我们在每次要用的时候,直接就new对象拿来用,这样既方便又省事。同时,又由于内存垃圾回收器的机制,一般情况下可以让我们不必担心new对象会产生内存溢出的问题(相对于C语言每次要考虑用完后释放,省去了很多麻烦)。但是,对于需要频繁执行的代码块,不必要的执行就会浪费很多性能,对于移动端的开发,这一点还是很值得我们去优化的!下面,我举几个开发中常见例子,进行说明。


*****个人经验狭隘,难免有所疏漏,若有问题,恳请斧正!*****


1.提升至成员变量,在其他位置初始化

        我们时常需要去画一些自定义控件,例如用MyView继承View,然后onMeasure、onLayout、onDraw完成一系列绘制。在使用Paint的时候,我们很可能直接在onDraw中new Paint(),如下:

@Override    protected void onDraw(Canvas canvas) {        Paint paint = new Paint();    }

             由于onDraw在绘图时会被频繁调用,这样你每次都会new Paint(),浪费性能,同时屏幕UI界面可能不是很顺滑(由于垃圾回收器机制的影响)。

        针对于这种需要重复使用的对象,我们应该提高其复用性,而不是每次都new。所以,此例中你可以将Paint提升为成员变量,先在构造函数中初始化,如下:

    private Paint mPaint;    public MyView(Context context) {        super(context);        mPaint = new Paint();    }

              onLayout也是同理。同时,你还需要注意开发中的其他需要多次执行的代码块,“是不是有必要每次都new对象呢?“这块代码是否可以复用呢?”等等。


2.维护一个集合,集合中有就直接拿,没有才new

       开发中有很多资源都会被频繁重复使用(例如同一张处理后的图片),我们每次都重新去处理再设置,是相当不合理的。既然我们之前已经处理过了,那么我们就应该拿一个集合(例如HashMap)将他们维护起来,需要的时候直接从集合中取出就可以了,而不是每次都去重新处理、重新new对象。

       现在App开发中最常见的界面就是通过Fragment实现卡片式布局(底部有一排按钮,点击切换不同页面),而Fragment一般都是有好几个的,一个Fragment中有很多代码需要处理,对于Fragment就应该使用集合进行维护,而不是每次都new一个Fragment对象返回。

       具体如何实现呢?我举出一个FragmentFactory的例子供大家参考,如下:

public class FragmentFactory {//    private static HashMap<Integer, BaseFragment> mFragmentMap = new HashMap<>();    private static SparseArray<BaseFragment> mFragmentArray = new SparseArray<>();    //数量不大,key为int类型时,效果比HashMap更好    public static BaseFragment createFragment(int pos) {        BaseFragment fragment = mFragmentArray.get(pos);//先从集合中取        if (fragment == null) {//如果集合中没有,才创建            switch (pos) {                case 0:     //首页                    fragment = new HomeFragment();                    break;                case 1:     //应用                    fragment = new AppFragment();                    break;                case 2:     //游戏                    fragment = new GameFragment();                    break;                case 3:     //专题                    fragment = new SubjectFragment();                    break;                case 4:     //推荐                    fragment = new RecommendFragment();                    break;            }            mFragmentArray.put(pos, fragment);//将fragment保存在集合中        }        return fragment;    }}

        完成上述FragmentFactory类后,以后使用Fragment时,你只需要调用FragmentFactory.createFragment(position)即可。另外推荐大家查询一下SparseArray,SparseArray是android提供的一个工具类,在android.util下。它在key为int类型、低数量的条件下,比HashMap使用起来效果更佳。)

         这样,如果集合中存在就取,不存在才会去新建一个,而不是每次都new对象了。另外,像对于图片集合的维护,这种方式也很常用。(图片缓存的维护还涉及到软引用、弱引用,LruCache缓存策略,在后面的内容中会讲到)


3.善用单例模式和全局Application类

        开发中,很多时候,有的对象在App全程运行时只需要一个即可,每次使用前就去new一个是相当不合适的。这个时候就可以考虑到单例模式了,如下:

public class Demo {    //单例——懒汉模式    private static Demo mInstance;    private Demo() {}    public static Demo getInstance() {        if (mInstance == null) {            synchronized (Demo.class) {                if (mInstance == null) {                    mInstance = new Demo();                }            }        }        return mInstance;    }}

        单例模式在实际开发中有很多应用,例如HttpManager、ThreadManager、DownloadManager等等。同时你还可以使用全局Application类,它是伴随着App启动到结束的一个存在。

        在Application类,初始化一些恒定的对象和设置,是很不错的方案,例如常见的第三方框架OkHttp的OkHttpClient。OkHttpClient是一个重量级的对象,可以实现很大的并发量,因此我们无需为每次Http请求都去new一个OkHttpClient,而只需要在Application类中申明一个即可。实现方式如下:

public class MyApplication extends Application {    private static OkHttpClient mHttpClient;    @Override    public void onCreate() {        super.onCreate();        mHttpClient = new OkHttpClient();        //TODO 下面可以做一些OkHttp的网络设置            }    public static OkHttpClient getHttpClient() {        return mHttpClient;    }}

        最后,不要忘记在AndroidManifest.xml中设置一下name=“Application类名路径”,作为Application类的入口。例如:

<application        android:name=".MyApplication"        android:allowBackup="true"        android:icon="@mipmap/ic_launcher"        android:label="@string/app_name"        android:supportsRtl="true"        android:theme="@style/AppTheme">        <activity android:name=".MainActivity">            <intent-filter>                <action android:name="android.intent.action.MAIN" />                <category android:name="android.intent.category.LAUNCHER" />            </intent-filter>        </activity></application>

        当然Application的应用不止这些方面,例如我们还可以做一些初始化设置、获取一个全局context、设置一个handler等等,开发中可能还有很多问题需要我们去思考、去处理,我们应该做到“学于此,而不止于此”。(对于这两个例子,值得注意的是static引用问题,很可能也会导致内存泄露,这个在后续讨论中会详细讲到


       至此,“Android内存溢出与优化(一)——不要随意new对象”结束,虽然只举出了几个例子,但是这些都是开发中常见的情况,实际中还有很多具有相似之处的地方,需要我们举一反三,而不是固定的只能解决这几个情况。最后,我想说“我真心诚意的渴求与大家一同分享、探讨、进步!”。


1 0
原创粉丝点击