引起内存泄漏的情况
- 对于使用了BraodcastReceiver,ContentObserver,File,游标 Cursor,Stream,Bitmap等资源的使用,应该在Activity销毁时及时关闭或者注销。
- 静态内部类持有外部成员变量(或context):可以使用弱引用或使用ApplicationContext。
- 内部类持有外部类引用,异步任务中,持有外部成员变量。
- 集合中没用的对象没有及时remove。
- 不用的对象及时释放,如使用完Bitmap后掉用recycle(),再赋null。
- handler引起的内存泄漏,MessageQueue里的消息如果在activity销毁时没有处理完,就会引起内存的泄漏,可以使用弱引用解决。
- 设置过的监听不用时,及时移除。如在Destroy时及时remove。尤其以addListener开头的,在Destroy中都需要remove。
- activity泄漏可以使用LeakCanary。
- 在内存引用上做些处理,常用的有软引用、弱引用
- 在内存中加载图片时直接在内存中作处理,如:边界压缩
- 动态回收内存
- 优化Dalvik虚拟机的堆内存分配
- 自定义堆内存大小
- 单例造成的内存泄漏
- 非静态内部类创建静态实例造成的内存泄漏
- Handler造成的内存泄漏
- 线程造成的内存泄漏
- 资源未关闭造成的内存泄漏
一些建议
- 对于生命周期比Activity长的对象如果需要应该使用ApplicationContext
- 对于需要在静态内部类中使用非静态外部成员变量(如:Context、View ),可以在静态内部类中使用弱引用来引用外部类的变量来避免内存泄漏
- 对于不再需要使用的对象,显示的将其赋值为null,比如使用完Bitmap后先调用recycle(),再赋为null
- 保持对对象生命周期的敏感,特别注意单例、静态对象、全局性集合等的生命周期
- 对于生命周期比Activity长的内部类对象,并且内部类中使用了外部类的成员变量,可以这样做避免内存泄漏:
- 将内部类改为静态内部类
- 静态内部类中使用弱引用来引用外部类的成员变量
- 在涉及到Context时先考虑ApplicationContext,当然它并不是万能的,对于有些地方则必须使用Activity的Context,对于Application,Service,Activity三者的Context的应用场景如下:
其中:NO1表示Application和Service可以启动一个Activity,不过需要创建一个新的task任务队列。而对于Dialog而言,只有在Activity中才能创建
- 强引用、软引用、弱引用、虚引用总结
其它优化方式
-
尽量将Activity中的小图片和背景合并,一张小图片既浪费布局的时间,又平白地增加了内存占用;
-
不要在Activity的主题中为Activity设置默认的背景图片,这样会导致Activity占用的内存翻倍:
<!--千万不要在主题中为Activity设置默认背景
<style name="Activity_Style" parent="@android:Theme.Holo.Light.NoActionBar">
<item name="android:background">@drawable/*</item>
</style> -
对于在需要时才显示的图片或者布局,可以使用ViewStub标签,通过sdk/tools目录下的hierarchyviewer.bat查看布局文件会发现,使用viewstub标签的组件几乎不消耗布局的时间,在代码中当需要显示时再去实例化有助于提高Activity的布局效率和节省Activity消耗的内存。
去掉window的背景可以在onCreate()中setContentView()之后调用
getWindow().setBackgroundDrawable(null);
或者在theme中添加
android:windowbackground="null";
- Error
- OOM,内存溢出
- StackOverFlowError
- Runtime,比如说空指针异常
解决的办法
- 注意内存的使用和管理
- 使用Thread.UncaughtExceptionHandler接口
ANR产生的原因?
ANR产生的根本原因是APP阻塞了UI线程。在android系统中每个App只有一个UI线程,是在App创建时默认生成的,UI线程默认初始化了一个消息循环来处理UI消息,ANR往往就是处理UI消息超时了。那么UI消息来源有哪些呢?主要有两种来源:
Activity: onCreate(), onResume(), onDestroy(),
AsyncTask: onPreExecute(), onProgressUpdate(), onPostExecute(), onCancel()等,超时5s
2:让耗时的工作(比如数据库操作,I/O,连接网络或者别的有可能阻碍UI线程的操作)把它放入单独的线程处理。
3:尽量用Handler来处理UIthread和别的thread之间的交互。
内存优化
核心思想:减少内存使用,能不new的不new,能少分配的少分配。因为分配更多的内存就意味着发生更多的GC,每次触发GC都会占用CPU时间,影响性能。
- 集合优化:Android提供了一系列优化过后的数据集合工具类,如SparseArray、SparseBooleanArray、LongSparseArray,使用这些API可以让我们的程序更加高效。HashMap工具类会相对比较低效,因为它需要为每一个键值对都提供一个对象入口,而SparseArray就避免掉了基本数据类型转换成对象数据类型的时间。
- Bitmap优化:读取一个Bitmap图片的时候,千万不要去加载不需要的分辨率。可以压缩图片等操作。
- 尽量避免使用依赖注入框架。
- 避免创作不必要的对象:字符串拼接使用StringBuffer,StringBuilder。
- onDraw方法里面不要执行对象的创建.
- 重写onTrimMemory,根据传入的参数,进行内存释放。
- 使用static final 优化成员变量。
移动端获取网络数据优化的几个点
连接复用:节省连接建立时间,如开启 keep-alive。
对于Android来说默认情况下HttpURLConnection和HttpClient都开启了keep-alive。只是2.2之前HttpURLConnection存在影响连接池的Bug,具体可见:Android HttpURLConnection及HttpClient选择
请求合并:即将多个请求合并为一个进行请求,比较常见的就是网页中的CSS Image Sprites。如果某个页面内请求过多,也可以考虑做一定的请求合并。
减少请求数据的大小:对于post请求,body可以做gzip压缩的,header也可以做数据压缩(不过只支持http 2.0)。
返回数据的body也可以做gzip压缩,body数据体积可以缩小到原来的30%左右。(也可以考虑压缩返回的json数据的key数据的体积,尤其是针对返回数据格式变化不大的情况,支付宝聊天返回的数据用到了)
根据用户的当前的网络质量来判断下载什么质量的图片(电商用的比较多)
Android系统启动过程,App启动过程
]从桌面点击到activity启动的过程
1.Launcher线程捕获onclick的点击事件,调用Launcher.startActivitySafely,进一步调用Launcher.startActivity,最后调用父类Activity的startActivity。
3.调用ActivityManagerService的startActivity方法,这里做了进程切换(具体过程请查看源码)。
4.开启Activity,调用onCreate方法
- 比如问一下内存泄露,那么试着多列举一些情况:
- SurfaceView和View最本质的区别在于,surfaceView是在一个新起的单独线程中可以重新绘制画面而View必须在UI的主线程中更新画面。那么在UI的主线程中更新画面 可能会引发问题,比如你更新画面的时间过长,那么你的主UI线程会被你正在画的函数阻塞。
Listview 优化问题
1.ListView 如何提高其效率?
当 convertView 为空时,用 setTag()方法为每个 View 绑定一个存放控件的 ViewHolder 对象。当 convertView 不为空,重复利用已经创建的 view 的时候, 使用 getTag()方法获取绑定的 ViewHolder 对象,这样就避免了 findViewById
对控件的层层查询,而是快速定位到控件。
- 复用 ConvertView,使用历史的 view,提升效率 200%
- 自定义静态类 ViewHolder,减少 findViewById 的次数。提 升效率 50%
- 异步加载数据,分页加载数据。
-
使用 WeakRefrence 引用 ImageView 对象
ListView 中加载优化图片
ListView 图片的优化策略比较多。
-
处理图片的方式:
如果 ListView 中自定义的 Item 中有涉及到大量图片的,一定要对图片进行
细心的处理,因为图片占的内存是 ListView 项中最头疼的,处理图片的方法大 致有以下几种:- 不要直接拿路径就去循环 BitmapFactory.decodeFile;使用 Options 保 存图片大小、不要加载图片到内存去。
- 对图片一定要经过边界压缩尤其是比较大的图片,如果你的图片是后台 服务器处理好的那就不需要了
- 在 ListView 中取图片时也不要直接拿个路径去取图片,而是以 WeakReference(使用 WeakReference 代替强引用。比如可以使用 WeakReference mContextRef)、SoftReference、WeakHashMap 等的来存 储图片信息。
- 在 getView 中做图片转换时,产生的中间变量一定及时释放
1). 先从内存缓存中获取图片显示(内存缓冲)
2). 获取不到的话从 SD 卡里获取(SD 卡缓冲)
3). 都获取不到的话从网络下载图片并保存到 SD 卡同时加入内存并显示(视情况看是否要显示)
Listview 的替代方案
RecyclerView
RecyclerView是一个比ListView更灵活的一个控件,以后可以直接抛弃ListView了,也可以替代Gridview。
RecyclerView改善了ListView的编程接口,他其实是一个ViewGroup ,能配合任何基于adapter的view实现多种布局
更好的实现局部刷新 ,调用notifyItemChanged(position)即可
Listview 原理
首先要理解Listview 三个重要的成员,用MVC思路来理解:
* ListVeiw: 用来展示列表的View (V)
* Adapter : 用来把数据映射到ListView上,(C)
* 数据:item显示的数据(M)
ListView 针对每个item,要求 adapter “返回一个视图” (getView),也就是说ListView在开始绘制的时候,系统首先调用getCount()函数,根据他的返回值得到ListView的长度,然后根据这个长度,调用getView()一行一行的绘制ListView的每一项。如果你的getCount()返回值是0的话,列表一行都不会显示,如果返回1,就只显示一行.如果我们有几千几万甚至更多的item要显示怎么办?为每个Item创建一个新的View?不可能!!!实际上Android早已经缓存了这些视图.
有哪些开源的ListView控件?
这里主要列出几个比较常见的:
-
android-pulltorefresh
一个强大的拉动刷新开源项目,支持各种控件下拉刷新,ListView、ViewPager、WebView、ExpandableListView、GridView、ScrollView、Horizontal ScrollView、Fragment 上下左右拉动刷新,比下面 johannilsson 那个只支持 ListView 的强大的多。并且它实现的下拉刷新 ListView 在 item 不足一屏情况下也不会显示刷新提示,体验更好。
Github:https://github.com/chrisbanes/Android-PullToRefresh -
Android-PullToRefreshRecyclerView
支持下拉刷新的RecyclerView,同时支持滑动到底部自动加载数据、给RecyclerView添加Header。并且不更改原有RecyclerView的逻辑。
Github:https://github.com/HomHomLin/Android-PullToRefreshRecyclerView -
android-pulltorefresh-listview
下拉刷新 ListView,这个被很多人使用的项目实际有不少 bug
Github:https://github.com/johannilsson/android-pulltorefresh -
SwipeListView
支持定义 ListView 左右滑动事件,支持左右滑动位移,支持定义动画时间
Github:https://github.com/47deg/android-swipelistview -
pinned-section-listview
GroupName 滑动到顶端时会固定不动直到另外一个 GroupName 到达顶端的 ExpandListView
Github:https://github.com/beworker/pinned-section-listview