您的位置:首页 > 移动开发 > Android开发

Android开发性能优化(记录、自用)

2017-05-31 14:12 363 查看
虽然做Android开发已经有一段时间了,但是开发过程中经常是忙着实现功能,却忽略了性能上的优化。以下都是来自于各位前辈的总结。特此总结记录一下。

 一、Android性能优化之布局优化技巧

http://blog.csdn.net/qq_17766199

http://blog.csdn.net/u012124438/article/details/54564659

以下为阅读总结,摘取自原博客,详细内容可参看原博客(第一个链接),或第二个链接同样是布局优化

布局优化就是用最少的view实现一样的效果layout。最少的view也就是会减少层级嵌套,从而使渲染的速度加快。

原博客在布局优化方面总结了8个点:

1)重用:利用<include/>标签,可以在一个布局中引入另外一个布局。如果有一个布局是多个页面重复出现的,可以利用<include/>标签这样就可以随用随调。便于统一修改使用。

2)合并:减少嵌套,不影响层级深度的情况下,使用LinearLayout而不是RelativeLayout。如果非要嵌套,那么尽量避免RelativeLayout嵌套RelativeLayout。

在减少嵌套方面还会用到<merge/>标签。该标签会帮助你排除把一个布局插入另一个布局中产生的多余的ViewGroup.比如说,你的复用布局是一个垂直的线性布局,包含两个子视图,当它被插入到另一个垂直的线性布局时,就是一个垂直的LinearLayout里面包含一个垂直的LinearLayout。这个嵌套的布局没有任何意义,而且会让UI性能变差,所以为了避免插入冗余的VeiwGroup的情况,我们可以把复用布局的根节点换成<merge/>。

3)利用TextView同时显示图片和文字。避免嵌套。

4)使用TextView的lineSpacingExta属性。

5)使用Spannable或Html.fromHtml

6)按需载入ViewStub,ViewStub标签同<include/>标签一样可以用来引入一个外来的布局,不同的是ViewStub引入的布局默认不会扩张,即既不会占用显示也不会占用位置,从而在解析layout时节省cpu和内存。ViewStub常常用来引入那些默认不会显示,只在特殊情况下显示的布局,比如进度布局。

7)使用LinearLayout自带的分割线

8)Space控件,它是一个轻量级的控件,我们可以利用一个View然后margin但是违背了少用view的初衷。这种情况我们可以使用Space控件。

二、Android性能优化之apk瘦身

http://blog.csdn.net/u012124438/article/details/54958757


记得今年年初找工作的时候,答了一套题其中一个就是如何给apk瘦身.....当时答的比较片面,结果是并没有面试成功,我想一定是他们没有眼光,一定是这样。

以下为阅读总结,摘取自原博客,详细内容可参看原博客

1)概述:为什么要APK瘦身,因为apk越大,用户在下载过程中,耗费的流量会多,安装等待的时间也会长,对于产品本身下载转化率会越低。所以apk的瘦身优化是很重要的。

2)包体分析:在Android Studio中build--->Analyze APK--->选择要分析的apk包。

3)使用一套资源。只取一套设计图,很多大公司也是如此但却能显著减少资源占用的大小。

4)开启minifyEnabled混淆代码,可以大大减少apk大小。

android {
   buildTypes {
       release {
           minifyEnabled true
       }
   }
}

在proguard中,是否保留符号表对app的大小有显著影响,可酌情不保留,但是建议尽量保留用于调试。

5)开启shrinkResources去除无用的资源

android {
   buildTypes {
       release {
           shrinkResources true
       }
   }
}

6)清理无用的资源

在5)中设置好之后。打包发现只是把部分无用的资源或者是更小的东西代替掉。因为shrinkResources true只是能去除没有任何父函数调用的情况的资源,但是我们想要实现的效果是所有废弃的代码无用的图片都要清理。这时候真正起效果的是AS自带的"Remove Unused Resources"插件来实现。Refactor--->Remove Unused Resources

7)删除无用的语言资源,比如设置只支持中文。

8)使用tinypng压缩png图片。

9)使用jpg,在启动页或者活动页采用jpg是明智的,会比png小到一半不止。

10)图片也可以使用webp格式,一种新的图片格式,压缩比比jpg高效果不输jpg.

11)缩小大图,如果工程里还有大图,可以适当的缩小,对视觉影响是极小的。

12)覆盖第三方库里的大图。13)精简so 14)使用微信资源压缩打包工具  15)使用provided编译  16)矢量图

17)使用shape背景,可以省去大量的背景图片。18)使用着色方案可以减少selector这样的文件  

19)在线化素材库   20)避免重复的库    21)清理第三方的库和冗余的代码    22)支持插件化
  

23)Facebook的redex优化字节码   

具体可以查看原博客

三、Android性能优化之App启动优化

http://blog.csdn.net/u012124438/article/details/56340949

以下摘取自原博客

应用的启动方式分为冷启动和热启动,启动优化指的是优化冷启动。

首先大概了解一下App的启动过程:



应用的启动时间指的就是从点击图标开始创建出一个新的进程直到我们看到了界面的第一帧,这段时间就是应用的启动时间。

为了减少应用启动时的耗时可以采取以下策略

1、在Application的构造器方法、attachBaseContext()、onCreate()方法中不要进行耗时操作的初始化,一些数据预取放在异步线程中,可以采取Callable实现。 
2、对于sp的初始化,因为sp的特性在初始化时候会对数据全部读出来存在内存中,所以这个初始化放在主线程中不合适,反而会延迟应用的启动速度,对于这个还是需要放在异步线程中处理。 
3、对于MainActivity,由于在获取到第一帧前,需要对contentView进行测量布局绘制操作,尽量减少布局的层次,考虑StubView的延迟加载策略,当然在onCreate、onStart、onResume方法中避免做耗时操作。

优化启动时的体验:

我们可以加入一些配置来提高app启动时候的用户体验。

方案一、为页面单独写一个主题,可以设置一张待显示的图片也可以是一种颜色,然后在清单文件中给MainActivity设置。

<style name="AppTheme.Launcher">
<item name="android:windowBackground">@drawable/bule</item>
</style>
//...
<activity
android:name=".MainActivity"
android:label="@string/app_name"
android:theme="@style/AppTheme.Launcher">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
然后在MainActivity中加载布局之前把AppTheme重新设置给MainActivity.

@Override
protected void onCreate(Bundle savedInstanceState) {

setTheme(R.style.AppTheme);
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}

方案二、设置AppTheme的style,同样可以设置一张背景图片或设置设置一个背景颜色。

1)设置背景图片,当程序启动的时候,先显示图片,避免了黑屏或者白屏的问题

<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
<item name="android:screenOrientation">portrait</item>
<item name="android:windowBackground">>@mipmap/splash</item>
<item name="android:windowIsTranslucent">true</item>
<item name="android:windowNoTitle">true</item>
</style>


2)设置背景颜色,把颜色设置成了透明的,这样就避免了白屏或者黑屏的问题了。

<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
<item name="android:windowNoTitle">true</item>
<item name="android:windowBackground">@android:color/transparent</item>
<item name="android:windowIsTranslucent">true</item>
<item name="android:screenOrientation">portrait</item>
</style>


详细内容可以查看http://blog.csdn.net/u012124438/article/details/56340949

四、Android性能优化之渲染优化

具体查看原文:http://blog.csdn.net/u012124438/article/details/61016280

五、Android性能优化之Bitmap优化

http://blog.csdn.net/u012124438/article/details/66087785

以下摘取自原博文

Android开发中,Bitmap操作不慎就会造成OOM(内存溢出),所以开发中需要注意Bitmap的优化。

为什么Bitmap操作不慎会导致OOM?

1.每个机型在编译ROM时都设置了一个应用堆内存VM值上限dalvik.vm.heapgrowthlimit,用来限定每个应用可用的最大内存,超出这个最大值将会报OOM。这个阀值,一般根据手机屏幕dpi大小递增,dpi越小的手机,每个应用可用最大内存就越低。所以当加载图片的数量很多时,就很容易超过这个阀值,造成OOM。

2.图片分辨率越高,消耗的内存越大,当加载高分辨率图片的时候,将会非常占用内存,一旦处理不当就会OOM。例如,一张分辨率为:1920x1080的图片。如果Bitmap使用 ARGB_8888 32位来平铺显示的话,占用的内存是1920x1080x4个字节,占用将近8M内存,可想而知,如果不对图片进行处理的话,就会OOM。

3.在使用ListView, GridView等这些大量加载view的组件时,如果没有合理的处理缓存,大量加载Bitmap的时候,也将容易引发OOM.

文中将Bitmap优化的策略总结为以下3种: 
1.对图片质量进行压缩 
2.对图片尺寸进行压缩 
3.使用libjpeg.so库进行压缩

具体查看原博文。当然平时开发中我们使用Glide等框架加载图片,框架已经对图片做了处理
六、Andorid性能优化之内存优化

原博:http://blog.csdn.net/u012124438/article/details/54647287

以下摘自原博客

再java中内存的分配是由程序完成的,而内存的释放是由垃圾收集器(Garbage Collection ,GC)完成的。也就是说释放内存是不需要手动调用的,当然随之就会带来内存泄漏的的问题。Android中常见的内存泄漏可以查看:Android中常见的内存泄漏

程序在运行的时候内存分配有三种策略,分别是静态的,栈式的,堆式的,三种存储策略使用的内存空间主要分别是静态存储区(方法区)、栈区、堆区。

静态存储区(方法区)是指内存在程序编译的时候就已经分配好了,这块内存在程序运行的整个过程都是存在的,主要存储的是静态的数据,全局的static数据,常量。

栈区  是指在执行函数的时候,函数内的局部变量的存储单元可以在栈区找到,当函数执行结束后这些存储单元会自动被释放。栈内存分配运算内置于处理器的指令集中,效率高但是分配的内存容量有限。

堆区  也是一个动态分配内存区,动态内存的生存期可以由我们来决定,程序在运行的时候可以通过malloc或者new
申请任意大小的内存,程序员自己要负责在适当的时候释放掉内存,如果不释放的话,程序在最后会释放掉动态内存。所以一个好的编码习惯是当某动态内存不需要的时候就要及时的清理掉。

栈区和堆区的区别:

二者的区别其实从上面的解释中已经能够看出,简单的来说就是栈区分配的内存自动释放,堆区分配的内存,系统不是放,哪怕程序退出,内存还是在那里。所以堆内存要自己释放,不然就会出现“内存泄漏”的情况。

堆是不连续的内存区域,堆的大小是受限于计算机系统中有效的虚拟内存,所以堆的空间比较大,比较灵活。

栈是一块连续的内存区域,大小是操作系统预定好的。

对于堆,频繁的new/delete会造成大量内存碎片,使程序效率降低。对于栈,它是先进后出的队列,进出一一对应,不产生碎片,运行效率高。



所以我们可以得出结论: 

1.局部变量的基本数据类型和引用存储于栈中,引用的对象实体存储于堆中。因为它们属于方法中的变量,生命周期随方法而结束。

2.成员变量全部存储与堆中(包括基本数据类型,引用和引用的对象实体),因为它们属于类,类对象终究是要被new出来使用的。

3.我们所说的内存泄露,只针对堆内存,他们存放的就是引用指向的对象实体。
内存泄漏产生的原因:
在java中内存的分配是由程序完成的,而内存的释放是由垃圾收集器(Garbage Collection,GC)完成的。程序员不需要调用函数来释放内存,并且不再被其他对象引用的那些对象所占用的空间。

GC为了能够正确的释放对象,必须监控每一个对象的运行状态,包括对象的申请、引用、被引用、赋值,GC都进行监控。监视对象

状态是为了更加准确的,及时的释放对象,而释放对象的根本原则就是该对象不再被引用。
什么是引用:通过A能操作对象B,那么就就说A持有B的引用,或者说A是B的引用。B的引用数+1.

(1)比如 Person p1 = new Person();通过P1能操作Person对象,因此P1是Person的引用; 

(2)比如类O中有一个成员变量是I类对象,因此我们可以使用o.i的方式来访问I类对象的成员,因此o持有一个i对象的引用。

GC过程与对象的引用类型是严重相关的,我们来看看Java对引用的分类Strong reference, SoftReference, WeakReference, PhatomReference



在Android应用的开发中,为了防止内存溢出,在处理一些占用内存大而且声明周期较长的对象时候,可以尽量应用软引用和弱引用技术。

软/弱引用可以和一个引用队列(ReferenceQueue)联合使用,如果软引用所引用的对象被垃圾回收器回收,Java虚拟机就会把这个软引用加入到与之关联的引用队列中。利用这个队列可以得知被回收的软/弱引用的对象列表,从而为缓冲器清除已失效的软/弱引用。

假设我们的应用会用到大量的默认图片,比如应用中有默认的头像,默认游戏图标等等,这些图片很多地方会用到。如果每次都去读取图片,由于读取文件需要硬件操作,速度较慢,会导致性能较低。所以我们考虑将图片缓存起来,需要的时候直接从内存中读取。但是,由于图片占用内存空间比较大,缓存很多图片需要很多的内存,就可能比较容易发生OutOfMemory异常。这时,我们可以考虑使用软/弱引用技术来避免这个问题发生。以下就是高速缓冲器的雏形:

首先定义一个HashMap,保存软引用对象。
1.private Map<String, SoftReference<Bitmap>> imageCache = new HashMap<String, SoftReference<Bitmap>>();
1
1

再来定义一个方法,保存Bitmap的软引用到HashMap
public class CacheSoftRef {

//首先定义一个HashMap,保存引用对象
private Map<String, SoftReference<Bitmap>> imageCache = new HashMap<String, SoftReference<Bitmap>>();

//再来定义一个方法,保存Bitmap的软引用到HashMap
public void addBitmapToCache(String path) {
//强引用的Bitmap对象
Bitmap bitmap = BitmapFactory.decodeFile(path);
//软引用的Bitmap对象
SoftReference<Bitmap> softBitmap = new SoftReference<Bitmap>(bitmap);
//添加该对象到Map中使其缓存
imageCache.put(path, softBitmap);
}

//获取的时候,可以通过SoftReference的get()方法得到Bitmap对象
public Bitmap getBitmapByPath(String path) {
//从缓存中取软引用的Bitmap对象
SoftReference<Bitmap> softBitmap = imageCache.get(path);
//判断是否存在软引用
if (softBitmap == null) {
return null;
}
//通过软引用取出Bitmap对象,如果由于内存不足Bitmap被回收,将取得空,如果未被回收,
//则可重复使用,提高速度。
Bitmap bitmap = softBitmap.get();
return bitmap;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

使用软引用以后,在OutOfMemory异常发生之前,这些缓存的图片资源的内存空间可以被释放掉的,从而避免内存达到上限,避免Crash发生。

如果只是想避免OutOfMemory异常的发生,则可以使用软引用。如果对于应用的性能更在意,想尽快回收一些占用内存比较大的对象,则可以使用弱引用。

另外可以根据对象是否经常使用来判断选择软引用还是弱引用。如果该对象可能会经常使用的,就尽量用软引用。如果该对象不被使用的可能性更大些,就可以用弱引用。

所以我们得出内存泄漏的原因:堆内存中的长生命周期的对象持有短生命周期对象的强/软引用,尽管短生命周期对象已经不再需要,但是因为长生命周期对象持有它的引用而导致不能被回收,这就是Java中内存泄露的根本原因。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: