Android性能优化的一些总结

1.布局优化

1.1.尽量要减少布局文件的层级数量

单个ViewGroup的时候使用LinearLayout,因为RelativeLayout功能复杂,要花费更多的CPU时间,而LinearLayoutFrameLayout是简单高效的ViewGroup。而当出现嵌套的布局时候,尽量使用RelativeLayout

布局优化还可以采用<include><merge>ViewStub来控制

<include> —– 可以将一个指定的布局文件加载到当前布局中,例如:

代码:

<include layout="@layout/title" />

<merge> —– 用于层级包含的时候去除多余的层级,比如上述的内层布局title中,如果也是LinearLayout,则可以直接用<merge>代替,默认布局是FrameLayout,例如:

代码:

<merge xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="我是button3" />

    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="我是button2" />

</merge>

ViewStub —– 继承View,不参与绘制过程,在按需加载的时候才显示出来,比如网络异常出错的时候的页面,不需要在初始化的时候就加载,用ViewStub就可以在使用的时候加载出来,提高初始化页面的效率。例如。

代码:

<ViewStub
    android:id="@+id/stub"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:inflatedId="@+id/root"
    android:layout="@layout/net_error" />

在需要加载的时候使用:

ViewStub stub = (ViewStub) findViewById(R.id.stub);
stub.setVisibility(View.VISIBLE);

或者使用inflate :

View view = stub.inflate();

2.绘制优化

在自定义View中的onDraw()方法里,避免创建新的对象,因为onDraw()会频繁被调用,降低执行效率。而且也不要有太多的循环操作。

3.内存泄漏优化

  1. 避免写出内存泄漏的代码,这需要靠丰富的编程经验和对语言的熟练掌握。
  2. 使用内存泄漏工具MAT来分析

几个内存泄漏的场景:

3.1. 静态变量导致的内存泄漏

代码:

public class AndroidTestActivity extends Activity {

    private static View view;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_common);

        view = new View(this);
    }
}

静态变量生命周期是整个Application的生命周期,而其内部持有当前Activity,所以Activity无法正常释放,造成内存泄漏。

3.2. 单利模式造成的内存泄漏

单例的静态特性使得单例的生命周期和应用的生命周期一样长,若一个对象已经不需要使用了,而单例对象还持有该对象的引用,那么这个对象将不能被正常回收,就导致了内存泄漏。例如:

代码:

public class AppManager {

    private static AppManager appManager;
    private Context context;

    private AppManager(Context context) {
        this.context = context;
    }

    public static AppManager getInstance(Context context) {
        if (appManager == null) {
            appManager = new AppManager(context);
        }
        return appManager;
    }
}

然后在Avtivity中使用该单例

代码:

public class AndroidTestActivity extends Activity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_common);

        AppManager appManager = AppManager.getInstance(this);//传入的是activity
    }
}

Activity退出的时候,单例还持有该Activity的引用,就造成了内存泄漏。

可将

this.context = context;

修改为

this.context = context.getApplicationContext();

这样外加不论传入什么context,生命周期都一样长,就不会存在内存泄漏。

3.3. 非静态内部类导致内存泄漏

非静态内部类(包括匿名内部类)会持有外部类的引用(静态的内部类不会持有外部类的引用),当非静态内部类对象的生命周期比外部类对象的生命周期长时,就会导致内存泄露。

平时经常写的Handler的代码

代码:

public class AndroidTestActivity extends Activity {

    private Handler handler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            //逻辑处理
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_common);

        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(10 * 1000);
                    Message message = Message.obtain();
                    message.what = 1;
                    handler.sendMessage(message);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }).start();
    }
}

如果Activity关闭了,但线程仍在运行,之后发送消息,这时候该线程持有Handler的引用,Handler又持有Activity的引用,就导致了Activity无法回收。可以使用静态内部类加弱引用的方式,或者直接在Activity结束的时候直接清除Handler

代码:(静态内部类+弱引用(因为handler不再持有外部activity的引用,所以无法直接操作activity的数据)

private MyHandler myHandler;
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_common);
    myHandler = new MyHandler(this);
    new Thread(new Runnable() {
        @Override
        public void run() {
            try {
                Thread.sleep(10 * 1000);
                Message message = Message.obtain();
                message.what = 1;
                myHandler.sendMessage(message);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }).start();
}
private static class MyHandler extends Handler {
    private WeakReference<AndroidTestActivity> activityWeakReference;
    public MyHandler(AndroidTestActivity activity) {
        activityWeakReference = new WeakReference<AndroidTestActivity>(activity);
    }
    @Override
    public void handleMessage(Message msg) {
        super.handleMessage(msg);
        AndroidTestActivity androidTestActivity = activityWeakReference.get();
        if (activityWeakReference != null) {
            if (msg.what == 1) {
                //逻辑操作
            }
        }
    }
}

或者直接在结束的时候销毁handler的回调和消息。

代码:

@Override
protected void onDestroy() {
    super.onDestroy();
    handler.removeCallbacksAndMessages(null);
}

线程造成的内存泄漏还有ThreadAsyncTask

上述中的Runnable就是匿名内部类,也需要改成静态内部类的方式。

3.4. 资源未关闭导致内存泄漏

对于使用了BraodcastReceiverContentObserverFileCursorStreamBitmapIO等资源的代码,应该在Activity销毁时及时关闭或者注销,否则这些资源将不会被回收,造成内存泄漏。

3.5. 集合中的资源未及时清除

使用集合的时候,若某个已经加入的对象不再使用,而集合引用还在使用,则会造成内存泄漏,要及时清除掉不使用的集合对象。remove或者clear

3.6. 属性动画造成的内存泄漏

ACtivity中播放属性动画,如果动画没有播放完整就退出Activity,虽然无法再看到动画,但是动画的控件View引用了Activity,即Activity并没有销毁。可在Activity销毁的时候同时清除动画。

代码:

@Override
protected void onDestroy() {
    super.onDestroy();
    animator.cancel();
}

3.7. WebView造成的内存泄漏

销毁WebView之前需要先将WebView从父容器中移除,然后再销毁WebView

@Override
protected void onDestroy() {
    super.onDestroy();
    // 先从父控件中移除WebView
    webViewContainer.removeView(mWebView);
    //一系列关闭操作
    webView.stopLoading();
    webView.getSettings().setJavaScriptEnabled(false);
    webView.clearHistory();
    webView.removeAllViews();
    //最后销毁
    webView.destroy();
}

4.响应速度优化

Activity如果5秒没有屏幕触摸事件或者键盘输入事件,BroadcastReceiver如果10秒内还未执行完操作,都会出现ANR

5.线程优化

尽量采用线程池,既可以重用内部线程,也避免了创建和销毁线程带来性能影响,还可以控制最大并发数。

6.内存泄漏分析工具

6.1.LeakCanary

6.2.MAT

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值