一个 Android 程序不可能无限制的使用内存和 CPU 资源,过多的使用内存会导致程序内存溢出,也就是 OOM。过多的使用 CPU 资源,做一些大量的耗时任务会导致手机卡顿或者无法响应,也就是 ANR。性能优化的主要内容包括布局优化、绘制优化、内存泄漏优化、响应速度优化、ListView 优化、Bitmap 优化、线程优化。
布局优化
布局优化主要思想当然是减少布局文件的层级,尽可能少用 ViewGroup,尽可能选用性能高的 ViewGroup,比如能使用 LinearLayout 代替 RelativeLayout 就使用 LinearLayout,因为 RelativeLayout 相对来说要复杂一些。当然能使用一个 ViewGroup 就使用一个,尽量少的去嵌套。
ViewStub 的使用
ViewStub 继承于 View,宽高都为 0,,本身不会参与任何的布局绘制过程,最佳用途就是实现 View 的延迟加载,避免资源浪费,在需要的时候才加载 View,需要注意的是, 加载 view 之后, viewstub 本身就会被新加载进来的 view 替换掉。比如说一个加载失败的界面,是没有必要在初始化的时候就把它加载进来,这时通过 ViewStub 就可以做到在使用的时候再进行加载,提高了程序的性能。看看下面的例子:
加载进来的布局
- final ViewStub viewStub = (ViewStub) findViewById(R.id.viewStub);
- handler.postDelayed(new Runnable() {@Override public void run() {
- TextView view = (TextView) viewStub.inflate();
- view.setText("加载进来的TextView");
- }
- },
- 2000);
这里延迟两秒后调用 ViewStub 的 inflate 方法把 ViewStub 替换成要加载的布局,这时 ViewStub 就不存在了,因为被替换掉了,这个方法返回的正是加载进来的 View,所以不需要采用 findViewById 的方法来找到它,我们加载进来的 View 是个 TextView 所以直接强转为 TextView 就 OK,除了 inflate 方法也可以采用 setVisibility 方法也可以。
绘制优化
绘制优化是指在 onDraw 方法中应该要避免执行大量的操作,主要体现在两个方面:
首先,onDraw 中不要创建新的局部变量,因为 onDraw 方法是会频繁的被调用的,这样每次调用后都会产生临时对象,这样不仅会占用过多的内存还会引起频繁的 GC。
另一方面的话,在 onDraw 中不应该做耗时的任务,也不能执行千万次的循环操作,否则会造成绘制过程的不流畅,降低了用户体验。
内存泄漏优化
一个程序中,已经不再使用某个对象,但是因为仍然有引用指向它,垃圾回收器就无法回收它,当然该对象占用的内存就无法被使用,这就造成了内存泄露。下面看看几种常见场景的内存泄漏
静态变量引起的内存泄漏
- public class MainActivity extends AppCompatActivity {
- private static final String TAG = "lzy";
- private static Drawable sDrawable;@Override protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- TextView textView = new TextView(this);
- sDrawable = getDrawable(R.mipmap.ic_launcher);
- textView.setBackground(sDrawable);
- setContentView(textView);
- }
- }
上述代码中 sDrawable 是一个静态变量,它持有一个 textView 的引用,而 textView 又持有这个 Activity 的引用,也就是说 sDrawable 持有了 Activity 的引用,如果销毁这个 Activity,由于 sDrawable 是一个静态变量它不会被销毁,也就说无法释放 Activity 的引用,所以这个 Activity 也无法释放,于是导致了内存泄漏。
单例模式引起的内存泄漏
因为单例的静态特性使得单例的生命周期和应用的生命周期一样长,这就说明了如果一个对象已经不需要使用了,而单例对象还持有该对象的引用,那么这个对象将不能被正常回收,这就导致了内存泄漏。
- public class AppManager {
- private static AppManager instance;
- private Context context;
- private AppManager(Context context) {
- this.context = context;
- }
- public static AppManager getInstance(Context context) {
- if (instance != null) {
- instance = new AppManager(context);
- }
- return instance;
- }
- }
如果 Activity 中使用这个单例,传入的 Context 为 Activity,那么这个单例就持有了 Activity 的引用,将不会释放,所以这个周期应该和 Application 的周期一样,所以这里 AppManager 的 Context 应该这样传,context.getApplicationContext(),这样就可以得到全局的 Context。
非静态内部类创建静态实例造成的内存泄漏
有的时候我们可能会在启动频繁的 Activity 中,为了避免重复创建相同的数据资源,会出现这种写法:
- public class MainActivity extends AppCompatActivity {
- private static TestResource mResource = null;@Override protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_main);
- if (mManager == null) {
- mManager = new TestResource();
- } //... } class TestResource { //... }}
这样就在 Activity 内部创建了一个非静态内部类的单例,每次启动 Activity 时都会使用该单例的数据,这样虽然避免了资源的重复创建,不过这种写法却会造成内存泄漏,因为非静态内部类默认会持有外部类的引用,而又使用了该非静态内部类创建了一个静态的实例,该实例的生命周期和应用的一样长,这就导致了该静态实例一直会持有该 Activity 的引用,导致 Activity 的内存资源不能正常回收。正确的做法为:
将该内部类设为静态内部类或将该内部类抽取出来封装成一个单例,如果需要使用 Context,请使用 ApplicationContext 。
Handler 造成的内存泄漏
Handler 的使用造成的内存泄漏问题应该说最为常见了,平时在处理网络任务或者封装一些请求回调等 api 都应该会借助 Handler 来处理,对于 Handler 的使用代码编写一不规范即有可能造成内存泄漏,如下示例:
- public class MainActivity extends AppCompatActivity {
- private Handler mHandler = new Handler() {@Override public void handleMessage(Message msg) { //... } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); loadData(); } private void loadData(){ //...request Message message = Message.obtain(); mHandler.sendMessage(message); }}
这种创建 Handler 的方式会造成内存泄漏,由于 mHandler 是 Handler 的非静态匿名内部类的实例,所以它持有外部类 Activity 的引用,我们知道消息队列是在一个 Looper 线程中不断轮询处理消息,那么当这个 Activity 退出时消息队列中还有未处理的消息或者正在处理消息,而消息队列中的 Message 持有 mHandler 实例的引用,mHandler 又持有 Activity 的引用,所以导致该 Activity 的内存资源无法及时回收,引发内存泄漏,所以另外一种做法为:
- public class MainActivity extends AppCompatActivity {
- private MyHandler mHandler = new MyHandler(this);
- private TextView mTextView;
- private static class MyHandler extends Handler {
- private WeakReference reference;
- public MyHandler(Context context) {
- reference = new WeakReference < >(context);
- }@Override public void handleMessage(Message msg) {
- MainActivity activity = (MainActivity) reference.get();
- if (activity != null) {
- activity.mTextView.setText("");
- }
- }
- }@Override protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_main);
- mTextView = (TextView) findViewById(R.id.textview);
- loadData();
- }
- private void loadData() { //...request Message message = Message.obtain(); mHandler.sendMessage(message); }}
创建一个静态 Handler 内部类,然后对 Handler 持有的对象使用弱引用,这样在回收时也可以回收 Handler 持有的对象,这样虽然避免了 Activity 泄漏,不过 Looper 线程的消息队列中还是可能会有待处理的消息,所以我们在 Activity 的 Destroy 时或者 Stop 时应该移除消息队列中的消息,使用 mHandler.removeCallbacksAndMessages(null); 是移除消息队列中所有消息和所有的 Runnable。当然也可以使用 mHandler.removeCallbacks(); 或 mHandler.removeMessages();来移除指定的 Runnable 和 Message。
线程优化
线程优化的思想是采用线程池,避免程序中存在大量的 Thread。线程池可以重用内部的线程,避免线程的创建的销毁带来的开销,同时还能有效的控制线程池的最大并发数量。
一些优化建议
1. 避免创建过多的对象。
2. 不要过多使用枚举,它占用的空间比整型大
3. 常亮使用 static final 修饰
避免资源未关闭造成的内存泄漏
当使用了 BraodcastReceiver、Cursor、Bitmap 等资源时,当不需要使用时,需要及时释放掉,若没有释放,则会引起内存泄漏。
就爱阅读 www.92to.com 网友整理上传, 为您提供最全的知识大全, 期待您的分享,转载请注明出处。
来源: