Android性能优化
来源:互联网 发布:windows vista精简版 编辑:程序博客网 时间:2024/06/14 20:31
今天看了点性能优化的东西,虽然基础,还是拿出来和大家分享下。总所周知啊,手机作为一种移动设备,不管是内存还是CPU的性能都收到了一定的影响,这也意味着Android程序不可能无限制地使用内存和CPU资源,过多的使用内存会导致内存溢出(OOM)。过多地使用CPU资源,即做大量的耗时任务,会导致手机卡顿甚至出现ANR。因此,作为一个开放人员,我们在写代码之前要有一定的意识去优化代码,提高程序的性能。下面我就来介绍下通常我们在代码编程中要注意的内容:布局优化、绘制优化、内存泄露优化、响应速度优化、listview优化、Bitmap优化、线程优化等等。说到性能优化,我们不得不注意的一个问题就是内存泄露,内存泄露并不会导致程序功能异常,但是会导致Android程序的内存占用过大,这会提高内存溢出的几率。
1、布局优化
布局优化的思想很简单,就是尽量减少布局文件的层级,布局中的层级少了,这就意味着Android绘制时的工作量少了,那么程序的性能自然就提高了。
*首先删除布局中无用的控件和层级,有选择地使用性能较低的ViewGroup
*优先使用LinearLayout或FrameLayout,因为RelativeLayout的功能比较复杂,它的布局过程需要花费更多的CPU时间
*复杂的布局场景需要使用嵌套方式来实现时,优先考虑使用RelativeLayout
*采用标签、标签和ViewStub
主要用于布局重用,ViewStub提供了按需加载的功能,这提高了初始化的效率
1、重用
< include/>标签可以在一个布局中引入另外一个布局,这个的好处显而易见。类似于我们经常用到的工具类,随用随调。便于统一修改使用。
2、合并
减少嵌套:首先我们心中要有一个大原则:尽量保持布局层级的扁平化。在这个大原则下我们要知道:在不影响层级深度的情况下,使用LinearLayout而不是RelativeLayout。因为RelativeLayout会让子View调用2次onMeasure,LinearLayout 在有weight时,才会让子View调用2次onMeasure。Measure的耗时越长那么绘制效率就低。如果非要是嵌套,那么尽量避免RelativeLayout嵌套RelativeLayout。这简直就是恶性循环,丧心病狂。实现方法就不细说了,大家都是明白人。
< merge/>主要用来去除不必要的FrameLayout。它的使用最理想的情况就是你的根布局是FrameLayout,同时没有使用background等属性。这时可以直接替换。因为我们布局外层就是FrameLayout,直接“合并”。
<?xml version="1.0" encoding="utf-8"?><merge xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent"> <include layout="@layout/title_bar"/></merge>
3、用TextView同时显示图片和文字
<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent"> <TextView android:drawableLeft="@drawable/icon_1" android:drawableRight="@drawable/icon_4" android:drawablePadding="10dp" android:paddingLeft="10dp" android:paddingRight="10dp" android:textSize="16sp" android:text="我的卡券" android:background="@color/white" android:gravity="center_vertical" android:layout_width="match_parent" android:layout_height="50dp" /></LinearLayout>当然EditView等也一样的,还有属性drawableBottom和drawableTop供你使用。同时利用代码setCompoundDrawables(Drawable left, Drawable top, Drawable right, Drawable bottom)可以让我们动态去设置图片。
4、使用TextView的行间距
<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_height="100dp" android:background="@color/white" android:layout_width="match_parent"> <ImageView android:padding="25dp" android:src="@drawable/kd_1" android:layout_width="100dp" android:layout_height="match_parent"/> <TextView android:textSize="14dp" android:lineSpacingExtra="8dp" android:gravity="center_vertical" android:text="揽件方式:上门取件\n快递公司:顺丰快递\n预约时间:9月6日 立即取件\n快递费用:等待称重确定价格" android:layout_width="match_parent" android:layout_height="match_parent" /></LinearLayout>
可以看到我们仅仅利用Android:lineSpacingExtra=”8dp”这一行代码就省去了3个TextView,如果行数更多呢?是不是方便多了。其中:lineSpacingExtra属性代表的是行间距,他默认是0,是一个绝对高度值。同时还有lineSpacingMultiplier属性,它代表行间距倍数,默认为1.0f,是一个相对高度值。我们来使用一下:
<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_height="100dp" android:background="@color/white" android:layout_width="match_parent"> <ImageView android:padding="25dp" android:src="@drawable/kd_1" android:layout_width="100dp" android:layout_height="100dp"/> <TextView android:textSize="14dp" android:lineSpacingMultiplier="1.3" android:gravity="center_vertical" android:text="揽件方式:上门取件\n快递公司:顺丰快递\n预约时间:9月6日 立即取件\n快递费用:等待称重确定价格" android:layout_width="match_parent" android:layout_height="match_parent" /></LinearLayout>
当然了这两条属性可以同时使用,查看源码可以知道,他们的高度计算规则为mTextPaint.getFontMetricsInt(null) * 行间距倍数 + 行间距。
5、使用Spannable或Html.fromHtml
如果实现上图红框中的效果,笨办法就是写三个TextView,“¥”,“价格”,“门市价”分别实现,其实用一个TextVIew就可以实现,类似如下代码:
String text = String.format("¥%1$s 门市价:¥%2$s", 18.6, 22); int z = text.lastIndexOf("门"); SpannableStringBuilder style = new SpannableStringBuilder(text); style.setSpan(new AbsoluteSizeSpan(DisplayUtil.dip2px(mContext,14)), 0, 1, Spannable.SPAN_EXCLUSIVE_INCLUSIVE); //字号 style.setSpan(new ForegroundColorSpan(Color.parseColor("#afafaf")), z, text.length(), Spannable.SPAN_EXCLUSIVE_INCLUSIVE); //颜色 style.setSpan(new AbsoluteSizeSpan(DisplayUtil.dip2px(mContext,14)), z, text.length(), Spannable.SPAN_EXCLUSIVE_INCLUSIVE); //字号 tv.setText(style);
同样Html.fromHtml也可以实现。
6、按需载入:ViewStub
在开发中经常会遇到这样的情况,会在程序运行时动态根据条件来决定显示哪个View或某个布局。那么通常做法就是把用到的View都写在布局中,然后在代码中动态的更改它的可见性。但是它的这样仍然会创建View,会影响性能。
这时就可以用到ViewStub了,ViewStub是一个轻量级的View,不占布局位置,占用资源非常小。ViewStub标签同include标签一样可以用来引入一个外部布局,不同的是,ViewStub引入的布局默认不会扩张,即既不会占用显示也不会占用位置,从而在解析layout时节省CPU和内存。
例子:比如我们请求网络加载列表,如果网络异常或者加载失败我们可以显示一个提示View,上面可以点击重新加载。当然一直没有错误时,我们就不显示。
<?xml version="1.0" encoding="utf-8"?><merge xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" > …… <ViewStub android:layout_gravity="center" android:id="@+id/hint_view" android:layout_width="match_parent" android:inflatedId="@+id/hint_view" android:layout_height="wrap_content" android:layout="@layout/hint_view"/></merge>用法:private View hintView;if (网络异常。。。) { if (hintView == null) { ViewStub viewStub = (ViewStub)this.findViewById(R.id.hint_view); hintView = viewStub.inflate(); TextView textView = (TextView) hintView.findViewById(R.id.tv); textView.setText("网络异常!"); } hintView.setVisibility(View.VISIBLE);}else{ if (hintView != null) { hintView.setVisibility(View.GONE); }}注意:1.一旦ViewStub可见或是被inflate了,ViewStub就不存在了,取而代之的是被inflate的Layout。所以它也被称做惰性控件。并且android:inflatedId的值将作为根布局的id。注:2. ViewStub目前有个缺陷就是还不支持 <merge /> 标签。
7、其他小技巧
用LinearLayout自带的分割线:
还记得上文用TextView同时显示图片和文字中的例子吗?我们可以看到每个条目之间都是有一根分隔线的,那么怎么实现呢?别人我不知道,反正我原来是用一个View设置高度实现的。相信一定有人和我一样。
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" android:divider="@drawable/divider" android:showDividers="middle"> <TextView android:drawableLeft="@drawable/icon_1" android:drawableRight="@drawable/icon_4" android:drawablePadding="10dp" android:paddingLeft="10dp" android:paddingRight="10dp" android:textSize="16sp" android:text="我的卡券" android:background="@color/white" android:gravity="center_vertical" android:layout_width="match_parent" android:layout_height="50dp" /> <TextView android:drawableLeft="@drawable/icon_2" android:drawableRight="@drawable/icon_4" android:drawablePadding="10dp" android:paddingLeft="10dp" android:paddingRight="10dp" android:textSize="16sp" android:text="地址管理" android:background="@color/white" android:gravity="center_vertical" android:layout_width="match_parent" android:layout_height="50dp" /> <TextView android:drawableLeft="@drawable/icon_3" android:drawableRight="@drawable/icon_4" android:drawablePadding="10dp" android:paddingLeft="10dp" android:paddingRight="10dp" android:textSize="16sp" android:text="检查更新" android:background="@color/white" android:gravity="center_vertical" android:layout_width="match_parent" android:layout_height="50dp" /></LinearLayout>showDividers 是分隔线的显示位置,beginning、middle、end分别代表显示在开始位置,中间,末尾。还有dividerPadding属性这里没有用到,意思很明确给divider添加padding。感兴趣可以试试。
Space控件
还是接着上面的例子,如果要给条目中间添加间距,怎么实现呢?当然也很简单,比如添加一个高10dp的View,或者使用android:layout_marginTop=”10dp”等方法。但是增加View违背了我们的初衷,并且影响性能。使用过多的margin其实会影响代码的可读性。
这时你就可以使用Space,他是一个轻量级的。
<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" android:divider="@drawable/divider" android:showDividers="middle|beginning|end"> <TextView android:drawableLeft="@drawable/icon_1" android:drawableRight="@drawable/icon_4" android:drawablePadding="10dp" android:paddingLeft="10dp" android:paddingRight="10dp" android:textSize="16sp" android:text="我的卡券" android:background="@color/white" android:gravity="center_vertical" android:layout_width="match_parent" android:layout_height="50dp" /> <TextView android:drawableLeft="@drawable/icon_2" android:drawableRight="@drawable/icon_4" android:drawablePadding="10dp" android:paddingLeft="10dp" android:paddingRight="10dp" android:textSize="16sp" android:text="地址管理" android:background="@color/white" android:gravity="center_vertical" android:layout_width="match_parent" android:layout_height="50dp" /> <Space android:layout_width="match_parent" android:layout_height="15dp"/> <TextView android:drawableLeft="@drawable/icon_3" android:drawableRight="@drawable/icon_4" android:drawablePadding="10dp" android:paddingLeft="10dp" android:paddingRight="10dp" android:textSize="16sp" android:text="检查更新" android:background="@color/white" android:gravity="center_vertical" android:layout_width="match_parent" android:layout_height="50dp" /></LinearLayout>
防止过度绘制
这个完全可以看鸿洋大神这篇 《Android UI性能优化实战 识别绘制中的性能问题》:http://blog.csdn.net/lmj623565791/article/details/45556391
2、绘制优化
View的onDraw方法要避免执行大量的操作。首先onDraw中不要创建新的局部对象,这是因为onDraw方法可能会被频繁调用,这样一瞬间产生大量的临时对象,导致系统频繁的Gc,降低了执行效率。另一方面,onDraw中不要做耗时任务,也不能执行成千上万的循环操作
3、内存泄露优化
变量导致的内存泄漏
模式导致的内存泄露
动画导致的内存泄露:属性动画中使用了无限循环,在onDestroy方法中没有停止动画,虽然无法在界面上看到动画了,但是这个时候Activity的View会被动画持有,导致Activity无法释放。
不要长期引用context-activity(引用一个活动应该有相同的生命周期活动本身)
使用getApplicationContext而不是context或activity试试
避免非静态内部类的一个活动如果你不控制自己的生命周期,使用静态内部类,让一个弱引用来传递,并且记住,垃圾收集器对于内存泄漏并不保险。
第三方库如果需要传入Activity的时候,传递一个弱引用进去, 可以避免内存泄漏,如下:
Reference<HomeActivity> reference = new WeakReference(getActivity());new ShareAction(reference.get()).setDisplayList(displaylist);
在Handler中容易造成内存泄漏,最好使用弱引用方式,要么,在Activity销毁的时候清空所有的handler栈。
在使用WebView的时候也可能出现内存泄漏,解决办法参考:
http://blog.csdn.net/fancy_xty/article/details/51595697在使用RxJava的时候也可能会出现内存泄漏,可以使用RxLifecycler来解决。
注:内存泄露检测方法:
今天分享一个非常方便快捷的库来检测内存泄露:LeakCanary
开始使用:
1、在 build.gradle 中加入引用,不同的编译使用不同的引用:
dependencies { debugCompile 'com.squareup.leakcanary:leakcanary-android:1.3' releaseCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.3' }
2、在 Application 中实现:
public class ExampleApplication extends Application { @Override public void onCreate() { super.onCreate(); if(isApkDebugable()){ if (!LeakCanary.isInAnalyzerProcess(this)) { // This process is dedicated to LeakCanary for heap analysis. // You should not init your app in this process. mRefWatcher = LeakCanary.install(this); } } }}public boolean isApkDebugable() { try { ApplicationInfo pkginfo = getApplicationInfo(); if (pkginfo != null ) { return (pkginfo.flags& ApplicationInfo.FLAG_DEBUGGABLE)!=0; } } catch (Exception e) { } return false; } public RefWatcher mRefWatcher; public static RefWatcher getRefWatcher(Context context) { App application = (App) context.getApplicationContext(); return application.mRefWatcher; }
3、如何使用:
使用 RefWatcher 监控那些本该被回收的对象。
RefWatcher refWatcher = {…};
// 监控
refWatcher.watch(schrodingerCat);
LeakCanary.install() 会返回一个预定义的 RefWatcher,同时也会启用一个 ActivityRefWatcher,用于自动监控调用 Activity.onDestroy() 之后泄露的 activity。
在Activity中如何检测:
public class BaseActivity extends AppCompatActivity { @Override protected void onDestroy() { super.onDestroy(); RefWatcher refWatcher = App.getRefWatcher(this); if(refWatcher!=null){ refWatcher.watch(this); } }}
在Fragment中如何检测:
public class BaseFragment extends Fragment { @Override public void onDestroy() { super.onDestroy(); RefWatcher refWatcher = App.getRefWatcher(getActivity()); if(refWatcher!=null){ refWatcher.watch(this); } } }
工作机制:
1、RefWatcher.watch() 创建一个 KeyedWeakReference 到要被监控的对象。
2、然后在后台线程检查引用是否被清除,如果没有,调用GC。
3、如果引用还是未被清除,把 heap 内存 dump 到 APP 对应的文件系统中的一个 .hprof 文件中。
4、在另外一个进程中的 HeapAnalyzerService 有一个 HeapAnalyzer 使用HAHA 解析这个文件。
5、得益于唯一的 reference key, HeapAnalyzer 找到 KeyedWeakReference,定位内存泄露。
6、HeapAnalyzer 计算 到 GC roots 的最短强引用路径,并确定是否是泄露。如果是的话,建立导致泄露的引用链。
7、引用链传递到 APP 进程中的 DisplayLeakService, 并以通知的形式展示出来。上传 leak trace 到服务器:
你可以改变处理完成的默认行为,将 leak trace 和 heap dump 上传到你的服务器以便统计分析。
创建一个 LeakUploadService, 最简单的就是继承DisplayLeakService :
public class LeakUploadService extends DisplayLeakService { @Override protected void afterDefaultHandling(HeapDump heapDump, AnalysisResult result, String leakInfo) { if (!result.leakFound || result.excludedLeak) { return; } myServer.uploadLeakBlocking(heapDump.heapDumpFile, leakInfo); }}
请确认 release 版本 使用 RefWatcher.DISABLED:
public class ExampleApplication extends Application { public static RefWatcher getRefWatcher(Context context) { ExampleApplication application = (ExampleApplication) context.getApplicationContext(); return application.refWatcher; } private RefWatcher refWatcher; @Override public void onCreate() { super.onCreate(); refWatcher = installLeakCanary(); } protected RefWatcher installLeakCanary() { return RefWatcher.DISABLED; }}
自定义 RefWatcher:
public class DebugExampleApplication extends ExampleApplication { protected RefWatcher installLeakCanary() { return LeakCanary.install(app, LeakUploadService.class); }}
别忘了注册 service:
<?xml version="1.0" encoding="utf-8"?><manifest xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" > <application android:name="com.example.DebugExampleApplication"> <service android:name="com.example.LeakUploadService" /> </application></manifest>
详细内容请参考:LeakCanary 中文使用说明
Leaks查看:
LeakCanary.install(this);将会安装一个Leak app,如下图:
当切换到release版本的时候,leakcanary-debug不会被打包.所以切换到release之后不用对leakcanary做注释或者删除等操作.
现在就可以开始使用了,重新编译你的工程,运行在模拟器或真机上.
在各个页面中测试,如果存在内存泄漏的情况,leaks会弹出通知提醒你查看.
4、响应速度优化
核心思想就是避免在主线程中做耗时操作,耗时操作都放到线程中去。响应速度主要体现在Activity的启动速度上面,如果在主线程中做太多事情导致Activity启动时黑屏,甚至出现ANR。Android规定,Activity如果5秒之内无法响应屏幕触摸或者键盘输入操作就会出现ANR,而BroadcastReceiver如果在10s之内还未执行完操作也会出现ANR。当系统出现ANR之后会在/data/anr目录下创建一个traces.txt文件。分析此文件即可看出原因。
5、ListView和Bitmap优化
此问题放在以后详细讲解
6、线程优化
采用线程池,避免 程序中存在大量的Thread。线程池可以重用内部的线程,从而避免了线程创建和销毁带来的性能开销,同事线程池还能有效地控制线程池的最大并发数,避免大量的线程因互相抢占锡系统资源从而导致阻塞现象的发生。
7、性能优化的一些建议
*避免创建过多的对象
*不要过多使用枚举,枚举占用的内存空间要比整型大
*常量请使用static final来修饰
*使用一些Android特有的数据结构,比如SparseArray和Pair等
*适当使用软引用和弱引用
*采用内存缓存和磁盘缓存
*尽量采用静态内部类,这样可以避免潜在的内部类导致的内存泄露
小女不才,内容略显粗鄙,多多包涵。以后再有类似经验会继续分享哒~
- 【Android】android性能优化
- 【Android】Android性能优化
- 【Android】Android性能优化
- 【Android】【性能优化】 Android 性能优化
- Android性能优化---布局优化
- Android性能优化---布局优化
- android 内存优化 性能优化
- android 内存优化 性能优化 .
- Android 性能优化、内存优化
- Android 性能优化、内存优化
- android性能优化--overdraw优化
- android性能优化---数据库优化
- android性能优化--布局优化
- Android 性能优化、内存优化
- android性能优化--overdraw优化
- Android性能优化-布局优化
- Android性能优化-布局优化
- Android性能优化-数据优化
- java中的自增和自减运算符
- Python Django的使用:Writing your first Django app--实践
- mysql查询参数配置及优化性能
- 数据块读函数fread
- 13款开源搜索引擎的介绍
- Android性能优化
- 蓄水池采样
- java
- hdu 3605网络流+状态压缩
- C++多线程--线程间通信与线程同步
- android View 绘制完成监听
- 显示时间与日期
- jsp超链接传值到后台
- 用纯CSS设置Checkbox复选框控件的样式