1、 如何对 Android 应用进行性能分析
1)app产品做好之后必须从每个控件在国内不同的手机品牌和不同系统版本进行兼容性测试,业内也叫遍历测试,所谓的遍历测试是可以移动识别应用的控件从而进行多层次的运行测试,当中包含了安装测试,启动测试,控件遍历测试,最后是卸载测试! 2)兼容性测试,也就是适配测试完成之后需要开始对网络性能进行测试,这里大概有几个方面需要进行的:网络性能测试,元素加载性能测试,网络可用性测试等等! 国内现有的测试周期和测试手段都是通过人工化测试,真正实现自动化又节省时间与人力的只有借助第三方应用性能管理提供商才可以实现!2、 什么情况下会导致内存泄露
1)Context使用不当造成内存泄露;不要对一个Activity Context保持长生命周期的引用(譬如上面概念部分给出的示例)。尽量在一切可以使用应用ApplicationContext代替Context的地方进行替换 2)非静态内部类的静态实例容易造成内存泄漏;即一个类中如果你不能够控制它其中内部类的生命周期(譬如Activity中的一些特殊Handler等),则尽量使用静态类和弱引用来处理(譬如ViewRoot的实现)。 3)警惕线程未终止造成的内存泄露;譬如在Activity中关联了一个生命周期超过Activity的Thread,在退出Activity时切记结束线程。一个典型的例子就是HandlerThread的run方法是一个死循环,它不会自己结束,线程的生命周期超过了Activity生命周期,我们必须手动在Activity的销毁方法中中调运thread.getLooper().quit();才不会泄露。 4)对象的注册与反注册没有成对出现造成的内存泄露;譬如注册广播接收器、注册观察者(典型的譬如数据库的监听)等。 5)创建与关闭没有成对出现造成的泄露;譬如Cursor资源必须手动关闭,WebView必须手动销毁,流等对象必须手动关闭等。 6)不要在执行频率很高的方法或者循环中创建对象,可以使用HashTable等创建一组对象容器从容器中取那些对象,而不用每次new与释放。 7)避免代码设计模式的错误造成内存泄露。 3、 如何避免 OOM 异常 1)图片过大导致OOM Android 中用bitmap 时很容易内存溢出, 比如报如下错误: Java.lang.OutOfMemoryError : bitmap size exceeds VM budget。 解决方法: 方法1: 等比例缩小图片BitmapFactory.Options options = new BitmapFactory.Options();options.inSampleSize = 2;//Options 只保存图片尺寸大小,不保存图片到内存BitmapFactory.Options opts = new BitmapFactory.Options();opts.inSampleSize = 2;Bitmap bmp = null;bmp = BitmapFactory.decodeResource(getResources(), mImageIds[position],opts);//回收bmp.recycle();//
以上代码可以优化内存溢出,但它只是改变图片大小,并不能彻底解决内存溢出。
方法2:对图片采用软引用,及时地进行recyle()操作SoftReferencebitmap = new SoftReference (pBitmap);if(bitmap != null){ if(bitmap.get() != null && !bitmap.get().isRecycled()){ bitmap.get().recycle(); bitmap = null; }}
方法3:使用加载图片框架处理图片,如专业处理加载图片的ImageLoader 图片加载框架。
还有我们学的XUtils 的BitMapUtils 来做处理。
2)界面(如:横竖屏)切换导致OOM 一般情况下,开发中都会禁止横屏的。因为如果是来回切换话,activity 的生命周期会重新销毁然后创建。 有时候我们会发现这样的问题,横竖屏切换N 次后OOM 了。 这种问题没有固定的解决方法,但是我们可以从以下几个方面下手分析。 a、看看页面布局当中有没有大的图片,比如背景图之类的。 去除xml 中相关设置,改在程序中设置背景图(放在onCreate()方法中):Drawable drawable = getResources().getDrawable(R.drawable.id);ImageView imageView = new ImageView(this);imageView.setBackgroundDrawable(drawable);
在Activity destory 时注意,drawable.setCallback(null); 防止Activity 得不到及时的释放。
b、跟上面方法相似,直接把xml 配置文件加载成view 再放到一个容器里,然后直接调用this.setContentView(View view);方法,避免xml 的重复加载。
c、在页面切换时尽可能少地重复使用一些代码 比如:重复调用数据库,反复使用某些对象等等…… 3)、查询数据库没有关闭游标 程序中经常会进行查询数据库的操作,但是经常会有使用完毕Cursor后没有关闭的情况。如果我们的查询结果集比较小,对内存的消耗不容易被发现,只有在常时间大量操作的情况下才会出现内存问题,这样就会给以后的测试和问题排查带来困难和风险。 4)、构造Adapter 时,没有使用缓存的convertView 在使用ListView 的时候通常会使用Adapter , 那么我们应该尽可能的使用ConvertView。 为什么要使用convertView? 当convertView为空时,用setTag()方法为每个View绑定一个存放控件的ViewHolder对象。当convertView不为空,重复利用已经创建的view的时候,使用getTag()方法获取绑定的ViewHolder对象,这样就避免了findViewById 对控件的层层查询,而是快速定位到控件。 5)、Bitmap 对象不再使用时调用recycle()释放内存 有时我们会手工的操作Bitmap 对象,如果一个Bitmap 对象比较占内存,当它不再被使用的时候,可以调用Bitmap.recycle()方法回收此对象的像素所占用的内存,但这不是必须的,视情况而定。 6)、其他 Android 应用程序中最典型的需要注意释放资源的情况是在Activity 的生命周期中,在onPause()、onStop()、onDestroy()方法中需要适当的释放资源的情况。使用广播没有注销也会产生OOM。
4、 Android 中如何捕获未捕获的异常
1)首先是定义一个类,我们取名为:CrashHandler,然后实现一个接口 java.lang.Thread.UncaughtExceptionHandler,要实现该接口里面的uncaughtException(Thread t, Throwable e)方法 ,在这个函数里面,我们可以做一些处理,例如将异常信息保存到sdcard上的某个位置,或者提示用户异常出现等等一些操作等。 代码:/** * crash异常log捕获 * 捕获到的log会保存到sdcard文件里 * @author qizhenghao */public class CrashHandler implements UncaughtExceptionHandler { private static CrashHandler instance; private Context context; /** 系统默认的UncaughtException处理类 */ private Thread.UncaughtExceptionHandler defaultHandler; // 单例 public static CrashHandler getInstance() { if (instance == null) { instance = new CrashHandler(); } return instance; } // 初始化 public void init(Context context) { this.context = context; defaultHandler = Thread.getDefaultUncaughtExceptionHandler(); Thread.setDefaultUncaughtExceptionHandler(this); } @Override public void uncaughtException(Thread arg0, Throwable arg1) { DownloadManager.getInstance().clearNotification(); if (!handleException(arg1) && defaultHandler != null) { // 如果用户没有处理则让系统默认的异常处理器来处理 defaultHandler.uncaughtException(arg0, arg1); } else { // 退出进程 System.exit(16); } } // 处理异常 private boolean handleException(Throwable ex) { if (ex == null) { return true; } ex.printStackTrace(); //保存log信息到本地文件 Methods.logCrashOnFile(context, ex); // 等待1s try { Thread.sleep(1000); } catch (Exception e) { e.printStackTrace(); } // 关闭进程 int nPid = android.os.Process.myPid(); android.os.Process.killProcess(nPid); return true; }
其中Thread.setDefaultUncaughtExceptionHandler(this);是最关键的一行代码了。
其次,在Application的onCreate()方法中进行注册:public class ExcApplication extends Application { @Override public void onCreate() { CrashHandler.getInstance().init(getApplicationContext()); }}
2)第三方服务 例:友盟