Android
2021-04-30 16:33:35 56 举报
AI智能生成
Android 知识图谱
作者其他创作
大纲/内容
UI卡顿
描述
View的绘制帧数保持60fps是最佳,这要求每帧的绘制时间不超过16ms(1000/60),<br>如果安卓不能在16ms内完成界面的渲染,那么就会出现卡顿现象
UI卡顿的原因分析
在UI线程中做轻微的耗时操作,导致UI线程卡顿<br> 布局Layout过于复杂,无法在16ms内完成渲染<br> 同一时间动画执行的次数过多,导致CPU和GPU负载过重<br> overDraw,导致像素在同一帧的时间内被绘制多次,使CPU和GPU负载过重<br> View频繁的触发measure、layout,导致measure、layout累计耗时过多和整个View频繁的重新渲染<br> 频繁的触发GC操作导致线程暂停,会使得安卓系统在16ms内无法完成绘制<br> 冗余资源及逻辑等导致加载和执行缓慢<br> ANR
UI卡顿的优化
布局优化<br> 使用include、ViewStub、merge<br> 不要出现过于嵌套和冗余的布局<br> 使用自定义View取代复杂的View<br> ListView优化<br> 复用convertView<br> 滑动不加载<br>背景和图片优化<br> 缩略图<br> 图片压缩<br> 避免ANR<br> 不要在UI线程中做耗时操作
屏幕适配
简介
适配方法
静态分辨率适配
屏幕分辨率限定符
即每种屏幕分辨率的设备需要定义一套 dimens.xml 文件<br>特点:每种分辨率都需要一套文件,而且还需要考虑虚拟键,<br> 文件体积大,主要使用的px为单位,需要考虑方向性
smallestWidth(最小宽度)<br> 限定符适配原理
只需要少量dimens.xml文件<br>特点:使用dp sp单位
googe开源百分比布局
</android.support.percent.PercentRelativeLayout>
动态适配各个屏幕分辨率
只需要少量dimens.xml文件<br>特点:使用dp sp单位
其他
善于使用 Nine-Patch 图片
使用相对布局 / 约束布局,禁用绝对布局
善于使用 wrap_content、match_parent、weight
内存管理
Android内存管理机制
分配机制<br>
采用“弹性分配方式”,开始给每个进程分配一定大小,随着运行额外分配
回收机制
JVM的内存模型
方法区
是<font color="#f1753f">静态分配的</font>,编译器将变量绑定在某个存储位置上,<br>而且这些绑定不会再运行时改变。<font color="#f15a23">常数池、static变量和String常量保存在方法区</font>
Java栈
是一个逻辑概念,特点是<font color="#f1753f">后进先出</font>。最典型的的栈应用是方法的调用,java虚拟机没调用一次方法就创建一个方法帧,<br>退出该方法则对应的帧被弹出。<font color="#f1753f">栈帧中存放的局部变量有8种基本数据类型,以及引用类型(对象的内存地址)</font><br>
Java堆
随意顺序,在运行时进行储存空间分配和回收内存管理,java对象的内存总在heap中分配,<br>垃圾回收器在这里工作回收无用对象。<font color="#f1753f">new 出来的 对象都放在这</font>
属于线程共享区域
Java堆可以切割成为三个部分:
oung(年轻代):存放新生对象
Tenured(老年代):对象多次回收没有被清除,则移到该区块。
Perm:存放加载的类别还有方法对象
内存管理机制的特点
更少的占用内存<br> 在合适的时候,合理的释放系统资源<br> 在系统内存紧张的时候,能释放掉大部分不重要的资源<br> 能合理的在特殊生命周期中,保存或还原重要数据
内存优化方法
Service完成任务后应停止它,或用IntentService(因为可以自动停止服务)代替Service<br> 在UI不可见的时候,释放其UI资源<br> 在系统内存紧张的时候,尽可能多的释放非重要资源<br> 避免滥用Bitmap导致内存浪费<br> 避免使用依赖注入框架<br> 使用针对内存优化过的数据容器<br> 使用ZIP对齐的APK<br> 使用多进程
GC回收
主要针对JVM<font color="#c41230">堆中的数据</font>回收
检查识别需要回收内存
引用计数法<br>
可达性分析算法
回收算法
标记回收算法
复制算法
Android内存泄漏检测和定位
冷启动和热启动
什么是冷启动和热启动
冷启动:在启动应用前,系统中没有该应用的任何进程信息<br><br> 热启动:在启动应用时,在已有的进程上启动应用(用户使用返回键退出应用,<br>然后马上又重新启动应用)
冷启动和热启动的区别
冷启动:创建Application后再创建和初始化MainActivity<br> 热启动:创建和初始化MainActivity即可
冷启动流程
Zygote进程中fork创建出一个新的进程<br> 创建和初始化Application类、创建MainActivity<br> inflate布局、当onCreate/onStart/onResume方法都走完<br> contentView的measure/layout/draw显示在界面上<br><br>总结:Application构造方法->attachBaseContext()->onCreate()->Activity构造方法-><br>onCreate()->配置主题中背景等属性->onStart()->onResume()->测量布局绘制显示在界面上
冷启动优化
减少第一个界面onCreate()方法的工作量]<br> 不要让Application参与业务的操作<br> 不要在Application进行耗时操作<br> 不要以静态变量的方式在Application中保存数据<br> 减少布局的复杂性和深度<br> 不要在mainThread中加载资源<br> 通过懒加载方式初始化第三方SDK
优化
Android不用静态变量存储数据
静态变量等数据由于进程已经被杀死而被初始化<br>使用其他数据传输方式:文件/sp/contentProvider
SharePreference安全问题
不能跨进程同步<br> 文件不宜过大
内存对象序列化
Serializeble:是java的序列化方式,Serializeble在序列化的时候会产生大量的临时对象,从而引起频繁的GC<br>Parcelable:是Android的序列化方式,且性能比Serializeble高,Parcelable不能使用在要将数据存储在硬盘上的情况
避免在UI线程中做繁重的操作
插件化
插件化解决的问题
动态加载APK(反射、类加载器)<br> 资源加载(反射、AssetManager、独立资源、分段资源)<br> 代码加载(反射获取生命周期)
类加载器(Java中字节码添加到虚拟机中)
DexClassLoader:能够加载未安装的jar/apk/dex,主要用于动态加载和代码热更新<br> PathClassLoader:加载/data/app目录下的apk文件,只要用来加载系统中已经安装过的apk
热更新
热更新主要流程
线上检查到Crash<br> 拉出Bugfix分支修复Crash问题<br> jenkins构建和补丁生成<br> app通过推送或主动拉取补丁文件<br> 将Bugfix代码合到master上
热更新主流框架
Dexposed<br> AndFix<br> Nuwa
热更新的原理
在ClassLoader创建一个dexElements数组<br> 将修复好的dex文件存放在dexElements数组的最前面<br> ClassLoader会遍历dexElements数组,找到最前面的dex文件优先加载
进程
进程保活
进程的优先级
空进程<br> 后台进程<br> 服务进程<br> 可见进程<br> 前台进程
Android进程回收策略
Low memory Killer(定时执行):通过一些比较复杂的评分机制,<br>对进程进行打分,然后将分数高的进程判定为bad进程,杀死并释放内存<br> OOM_ODJ:判别进程的优先级
Android保活方案
利用系统广播拉活<br> 利用系统Service机制拉活<br> 利用Native进程拉活<br> 利用JobScheduler机制拉活<br> 利用账号同步机制拉活
进程间通信
一、使用 Intent<br>二、使用文件共享<br>三、使用 Messenger<br>四、使用 AIDL<br>五、使用 ContentProvider<br>六、使用 Socket
socket
通信原理
基于 server client 端互相通信,指定端口和ip
Tcp socket
面向字节流的
保证数据正确性
保证数据顺序
UDP socket
是基于数据报的
可能丢包
UDP 不保证顺序
面向无连接的
WebSocket
一次握手
Socket是传输控制层协议,WebSocket是应用层协议
WebSocket在建立握手时,数据是通过HTTP传输的。<br>但是建立之后,在真正传输时候是不需要HTTP协议的
虚拟机
JVM与Dalvik不同
类加载系统区别比较大<br><font color="#c41230"> JVM是基于栈</font>,<font color="#c41230">Dalvik是基于寄存器</font><br> JVM执行的是java字节码文件,<font color="#c41230">Dalvik执行的是dex字节码文件</font><br> Dalvik可以同时存在多个,即使一个退出了也不会影响其他程序
Dalvik与ART不同
Dalvik使用JIT(Just In Time <font color="#c41230">运行时编译</font>)来将字节码转换成机器码,效率低<br>Android 5.0后 ART采用AOT(Ahead Of Time <font color="#c41230">运行前编译</font>)预编译技术,执行速度更快<br> ART会占用更多的应用安装时间和存储空间
Activity
简介
四大组件之一,提供一个界面让用户点击和各种滑动操作
Activity四种状态
runing paused stopped killed
Activity生命周期
onCreate()。onStart()。onResume()。onPause()。onStop()。onDestroy()。onRestart()
Activity任务栈
先进后出
Activity启动模式
standard<br> singletop<br> singletask<br> singleinstance
scheme跳转协议
简介:android中的scheme是一种页面内跳转协议
服务器可以定制化告诉app跳转哪个页面
App可以通过跳转到另一个App页面
可以通过H5页面跳转页面
Context、Activity、Application 区别
Context理解就是上下文的意思,Activity和Application都是Context的子类
Activity
维护一个Acitivity的生命周期,其对应的Context也只能访问该activity内的各种资源
Application
维护一个Application的生命周期
Activity启动过程
安装应用----系统会启动<font color="#c41230">PackaManagerService</font>管理服务,会对<font color="#c41230">AndroidManifest</font>进行解析,得到应用程序中的相关信息,<br>比如service,activity,Broadcast等,组件信息
用户点击应用图标----调用<font color="#c41230">startActivitySately()</font>,这个方法内部则是调用startActivty(),startActivity()最终还是会调用<font color="#c41230">startActivityForResult()</font>
startActivityForResult()----通过Instrumentation(监控程序和系统之间的交互)类中的execStartActivity()来启动activity,<br>在这个execStartActivity()中会获取<font color="#c41230">ActivityManagerService</font>的代理对象,通过这个代理对象进行启动activity<br>
ActivityManagerService----此代理对象中,<font color="#c41230">通过Binder通信</font>,调用到<font color="#c41230">ApplicationThread.scheduleLaunchActivity()</font>进行启动activity<br>这个方法中创建一个ActivityClientRecord对象,用来记录启动Activity组件的信息,然后通过handler将ActivityClientRecord发送出去<br>
handler---handler收到消息后,调用<font color="#c41230">ActivityThread.handleLaunchActivity()</font>启动Activity
Activity,View,Window三者关系
<font color="#c41230">Activity本质是上下文</font>,View本质是视图,Window本质是窗口
Activity构造的时候会初始化一个Window,其具体实现是PhoneWindow
PhoneWindow中有一个<font color="#c41230">ViewRoot</font>(<font color="#c41230">View或ViewGroup</font>),是最初始的根视图
ViewRoot通过addView()将View添加到根视图上,实际上是将View交给了PhoneWindow处理
View的事件监听是由<font color="#c41230">WindowManagerService</font>来接受消息,并且回调Activity函数
自定义View
方法
重写
onMeasure、 onLayout、onDraw、onTouchEvent<br>OnMesure 计算出控件的大小。 <br>onLayout 计算出控件的位置。 <br>onDraw 画出样式 <br>
View必须重写两个构造方法
过程
调用ViewGroup中的onMeasure方法。<br> 在方法中调用了measureChild方法,执行了所有子控件的onMesure方法测绘出所有的子控件的大小。<br>调用setMeasureDimension方法 设置测绘后的大小。
调用ViewGroup中的onLayout方法。<br>在方法调用getChildCount方法 获取到子条目数量。<br>用for循环遍历出每一个子条目的对象。 通过对象.layout方法 给子控件设置摆放位置。
首先调用ViewGroup的disPatchDraw方法绘制ViewGroup。然后调用View中的onDraw方 进行绘制。
实现方式
组合控件
自绘控件
继承控件
数据刷新方式
不使用多线程和双缓冲
显式地调用View中的invalidate()方法系统会自动调用View的onDraw()
使用多线程但不使用双缓冲
结合Handler来处理,新建一个Handler,并发送一个Message
使用多线程和双缓冲
通过SurfaceView来实现的
总结:Android程序中可以使用的界面刷新方法有两种,分别是利用Handler和利用postInvalidate()来实现在线程中刷新界面
Fragment
简介
Fragment比Activity更节省内存,其切换模式也更加舒适,使用频率不低于四大组件,且有自己的生命周期,而且必须依附于Activity
Activity创建Fragment的方式
静态创建
动态创建
FragmentPageAdapter和FragmentPageStateAdapter的区别
FragmentPageAdapter在每次切换页面的的时候,是将Fragment进行分离,<br>适合页面较少的Fragment使用以保存一些内存,对系统内存不会多大影响
FragmentPageStateAdapter在每次切换页面的时候,是将Fragment进行回收,<br>适合页面较多的Fragment使用,这样就不会消耗更多的内存
Fragment生命周期
onAttach(在Fragment 和 Activity 建立关联是调用(Activity 传递到此方法内))、 <br>onCreate()、onCreateView()、onActivityCreated()、onStart()、<br> onResume()、onPause()、onStop()、 onDestroyView()、onDestroy()、 onDetach()、
Fragment的通信
Fragment调用Activity中的方法:getActivity
Activity调用Fragment中的方法:接口回调
Fragment调用Fragment中的方法:FragmentManager.findFragmentById
Fragment的replace、add、remove方法
replace:替代Fragment的栈顶页面
add:添加Fragment到栈顶页面
remove:移除Fragment栈顶页面
Service
简介
Service是四大组件之一,它可以在后台执行长时间运行操作而没有用户界面的应用组件
Service和Thread的区别
Service是安卓中系统的组件,它运行在独立进程的主线程中,不可以执行耗时操作。
Thread是程序执行的最小单元,分配CPU的基本单位,可以开启子线程执行耗时操作
Service在不同Activity中可以获取自身实例,可以方便的对Service进行操作。
Thread在不同的Activity中难以获取自身实例,如果Activity被销毁,Thread实例就很难再获取得到
Service启动方式
startService
一旦启动,服务可以在后台无限期运行,即使启动它的组件已经被销毁。
bindService
则服务是Bound状态。Bound状态的服务提供了一个客户服务器接口来允许组件与服务进行交互,如发送请求<br>,获取结果,甚至通过IPC来进行跨进程通信。
Service生命周期
startService<br> onCreate()<br> onStartCommand()<br> onDestroy()
bindService<br> onCreate()<br> onBind()<br> onUnbind()<br> onDestroy()
Broadcast Receiver
简介
Broadcast是四大组件之一,用在应用程序之间传输信息的机制,通过发送Intent来传送我们的数据
使用场景
同一App具有多个进程的不同组件之间的消息通信<br> 不同App之间的组件之间的消息通信
Broadcast Receiver的种类
普通广播<br> 有序广播<br> 本地广播<br>
Broadcast Receiver的实现
静态注册:注册后一直运行,尽管Activity、进程、App被杀死还是可以接收到广播<br> 动态注册:跟随Activity的生命周期
特点
动态注册的广播 永远要快于 静态注册的广播
动态注册广播不是 常驻型广播 ,也就是说广播跟随activity的生命周期
实现机制
自定义广播类继承BroadcastReceiver,复写onReceiver()<br> 通过Binder机制向AMS进行<font color="#c41230">注册</font>广播<br> 广播发送者通过Binder机制向<font color="#16884a">AMS</font><font color="#c41230">发送</font>广播<br> AMS查找符合相应条件的广播发送到BroadcastReceiver相应的循环队列中<br> 消息队列执行拿到广播,回调BroadcastReceiver的onReceiver()
原理:观察者模式:基于消息的发布 / 订阅事件模型
LocalBroadcastManager特点
本地广播只能在自身App内传播,不必担心泄漏隐私数据<br> 本地广播不允许其他App对你的App发送该广播,不必担心安全漏洞被利用<br> 本地广播比全局广播更高效<br> 以上三点都是源于其内部是用Handler实现的
WebView
WebView安全漏洞
API16之前存在远程代码执行安全漏洞,没有正确限制使用WebView.addJavascriptInterface方法<br>远程攻击者可通过使用Java反射机制利用该漏洞执行任意Java对象的方法<br>
WebView销毁步骤
WebView在其他容器上时(如:LinearLayout),当销毁Activity时,<br>需要在onDestroy()中先移除容器上的WebView,然后再将WebView.destroy(),这样就不会导致内存泄漏
WebView的jsbridge
客户端和服务端之间可以通过Javascript来互相调用各自的方法
WebViewClient的onPageFinished
WebViewClient的onPageFinished在每次完成页面的时候调用,但是遇到未加载完成的页面跳转其他页面时,<br>就会一直调用,使用WebChromeClient.onProgressChanged可以替代
WebView后台耗电
在WebView加载页面的时候,会自动开启线程去加载,如果不很好的关闭这些线程,就会导致电量消耗加大,<br>可以采用暴力的方法,直接在onDestroy方法中System.exit(0)结束当前正在运行中的java虚拟机<br>
WebView硬件加速
Android3.0引入硬件加速,默认会开启,WebView在硬件加速的情况下滑动更加平滑,性能更加好,<br>但是会出现白块或者页面闪烁的副作用,建议WebView暂时关闭硬件加速
WebView内存泄漏<br>
由于WebView是依附于Activity的,Activity的生命周期和WebView启动的线程的生命周期是不一致的,<br>这会导致WebView一直持有对这个Activity的引用而无法释放,<br>
解决方案如下:
独立进程,简单暴力,不过可能涉及到进程间通信(推荐)<br> 动态添加WebView,对传入WebView中使用的Context使用弱引用
Binder
Linux内核的基本知识
<font color="#f68b1f">进程隔离/虚拟地址空间</font>:<font color="#00a650">进程间是不可以共享数据的</font>,相当于被隔离,每个进程被分配到不同的虚拟地址中
系统调用:Linux内核对应用有访问权限,用户只能在应用层通过系统调用,调用内核的某些程序
binder驱动:它负责各个用户的进程,通过binder通信内核来进行交互的模块
IPC
主要包含三部分:Serialiazable,Parcelable以及 Binder
跨进程通信
用户空间---内核空间
Android的进程(app),只能运行在自己进程所拥有的<font color="#55beed">虚拟地址空间</font>。虚拟地址空间又分为<font color="#f1753f">用户空间</font>和<font color="#f1753f">内<br>核空间</font>,对于用户空间是不能共享的。内核空间是可以共享的。Client端向Service端进程通信,<br>利用的就是可共享内核内存空间来完成底层底层通信工作的<br>
Binder特点
性能上,相比传统的Socket更加高效
安全性高,支持协议双方互相校验
Binder通信原理
Service端通过Binder驱动在ServiceManager的查找表中注册Object对象的add方法
Client端通过Binder驱动在ServiceManager的查找表中找到Object对象的add方法,并返回proxy对象的add方法,<br>add方法是个空实现,proxy对象也不是真正的Object对象,是通过Binder驱动封装好的代理类的add方法
当Client端调用add方法时,Client端会调用proxy对象的add方法,通过Binder驱动去请求ServiceManager来找到Service<br>端真正对象,然后调用Service端的add方法
Service Manager是一个守护进程,用来管理Server,并向Client提供查询Server接口的能力
Service Manager<br>守护进程<br>
它是一种特殊的Server
查看源码 service_manager.c
一是打开Binder设备文件
二是告诉Binder驱动程序自己是Binder上下文管理者,即我们前面所说的<font color="#f68b1f">守护进程</font>
binder_loop 三是进入一个无穷循环,充当Server的角色,等待Client的请求
AIDL
简介
使用
客户端通过aidl文件的Stub.asInterface()方法,拿到Proxy代理类
通过调用Proxy代理类的方法,将参数进行封包后,调用底层的transact()方法
transact()方法会回调onTransact()方法,进行参数的解封
在onTransact()方法中调用服务端对应的方法,并将结果返回<br>
原理
AIDL其实就是通过Binder实现的
Handler
简介
Handler通过发送和处理Message和Runnable对象来关联相对应线程的MessageQueue
Handler使用方法
post(runnable)<br> sendMessage(message)
Handler工作原理
Android进阶——Android消息机制之Looper、MessageQueue、Message。
Handler引起的内存泄漏
原因:非静态内部类持有外部类的匿名引用,导致Activity无法释放<br> 解决:<br> Handler内部持有外部Activity的弱引用<br> Handler改为静态内部类<br> Handler.removeCallback()
总结:
1.在使用handler的时候,在handler所创建的线程需要维护一个唯一的Looper对象, 每个线程对应一个Looper,<br> 每个线程的Looper通过ThreadLocal来保证,如需了解ThreadLocal,点击查看详细讲解 ,<br> Looper对象的内部又维护有唯一的一个MessageQueue,所以一个线程可以有多个handler,<br> 但是只能有一个Looper和一个MessageQueue。<br>2.Message在MessageQueue不是通过一个列表来存储的,而是将传入的Message存入到了上一个<br> Message的next中,在取出的时候通过顶部的Message就能按放入的顺序依次取出Message。<br>3.Looper对象通过loop()方法开启了一个死循环,不断地从looper内的MessageQueue中取出Message,<br> 然后通过handler将消息分发传回handler所在的线程。 <br>
AsyncTask
简介
它本质上就是一个封装了线程池和Handler的异步框架
使用
三个参数
Params:表示后台任务执行时的参数类型,该参数会传给AysncTask的doInBackground()方法<br> Progress:表示后台任务的执行进度的参数类型,该参数会作为onProgressUpdate()方法的参数<br> Result:表示后台任务的返回结果的参数类型,该参数会作为onPostExecute()方法的参数
五个方法
onPreExecute():异步任务开启之前回调,在主线程中执行<br> doInBackground():执行异步任务,在线程池中执行<br> onProgressUpdate():当doInBackground中调用publishProgress时回调,在主线程中执行<br> onPostExecute():在异步任务执行之后回调,在主线程中执行<br> onCancelled():在异步任务被取消时回调
AsyncTask工作原理
AsyncTask引起的内存泄漏
原因:非静态内部类持有外部类的匿名引用,导致Activity无法释放<br> 解决:<br> AsyncTask内部持有外部Activity的弱引用<br> AsyncTask改为静态内部类<br> AsyncTask.cancel()
HandlerThread
简介
防止出现新建一系列的新线程处理业务,HandlerThread是在线程中创建一个Looper循环器,<br>让Looper轮询消息队列,当有耗时任务进入队列时,则不需要开启新线程,<br>在原有的线程中执行耗时任务即可,否则线程阻塞
HanlderThread的特点
HandlerThread本质上是一个线程,继承自Thread<br> HandlerThread有自己的Looper对象,可以进行Looper循环,可以创建Handler<br> HandlerThread可以在Handler的handlerMessage中执行异步方法<br> HandlerThread优点是异步不会堵塞,减少对性能的消耗<br> HandlerThread缺点是不能同时继续进行多任务处理,需要等待进行处理,处理效率较低<br> HandlerThread与线程池不同,HandlerThread是一个串行队列,背后只有一个线程
ListView
简介
ListView是能将一个数据集合以动态滚动的方式展示到用户界面上的View
ListView的RecycleBin机制
ListView的父类是AbsListView,AbsListView 里面有个子类 RecycleBin,ListView中的 View就是在此处管理的
ListView的优化
重用convertView<br> 使用ViewHolder<br> 图片三级缓存<br> 监听滑动事件<br> 少用透明View<br> 开启硬件加速
ListView和RecyclerView的区别
RecyclerView可以完成ListView,GridView的效果,还可以完成瀑布流的效果,同时还可以设置列表的滚动方向<br> RecyclerView中View的复用不需要开发者自己写代码,系统已经帮封装完成了<br> RecyclerView提供了API来实现item的动画效果<br> RecyclerView可以进行局部刷新
ANR
简介
程序长时间无反应
Application Not Responding,页面无响应的对话框
发生条件
应用程序的响应性是由ActivityManager和WindowManager系统服务监视的,当ANR发生条件满足时,就会弹出ANR的对话框<br> Activity超过5秒无响应<br> BroadcastReceiver超过10秒无响应<br> Service超过20秒无响应
造成ANR的主要原因
主线程被IO操作阻塞
Activity的所有生命周期回调都是执行在主线程的<br> Service默认执行在主线程中<br> BoardcastReceiver的回调onReceive()执行在主线程中<br> AsyncTask的回调除了doInBackground,其他都是在主线程中<br> 没有使用子线程Looper的Handler的handlerMessage,post(Runnable)都是执行在主线程中
解决:
使用AsyncTask处理耗时IO操作<br> 使用Thread或HandlerThread提高优先级<br> 使用Handler处理工作线程的耗时操作<br> Activity的onCreate和onResume回调尽量避免耗时操作
OOM<br>内存溢出<br>
简介
OOM指Out of memory(内存溢出)
当前占用内存加上我们申请的内存资源超过了Dalvik虚拟机的最大内存限制就会抛出Out of memory异常<br>
OOM相关概念
内存溢出:指程序在申请内存时,没有足够的空间供其使用<br> 内存泄漏:指程序分配出去的内存不再使用,无法进行回收<br> 内存抖动:指程序短时间内大量创建对象,然后回收的现象
解决OOM
Bitmap相关<br><br> 图片压缩<br> 加载缩略图<br> 在滚动时不加载图片<br> 回收Bitmap<br> 使用inBitmap属性<br> 捕获异常
listview重用convertView、使用lru<br> 避免onDraw方法执行对象的创建<br> 谨慎使用多进程
内存泄漏
Java内存泄漏主要原因
长生命周期的对象持有短生命周期对象的引用就很可能发生内存泄漏
Java内存分配策略
静态存储区:又称方法区,主要存储全局变量和静态变量,在整个程序运行期间都存在<br> 栈区:方法体的局部变量会在栈区创建空间,并在方法执行结束后会自动释放变量的空间和内存<br> 堆区:保存动态产生的数据,如:new出来的对象和数组,在不使用的时候由Java回收器自动回收
Android解决内存泄漏的例子
1. 单例造成的内存泄漏:在单例中,使用context.getApplicationContext()作为单例的context<br>2. 匿名内部类造成的内存泄漏:由于非静态内部类持有匿名外部类的引用,必须将内部类设置为static<br>3. Handler造成的内存泄漏:使用static的Handler内部类,同时在实现内部类中持有Context的弱引用<br>4. 避免使用static变量:由于static变量会跟Activity生命周期一致,当Activity退出后台被后台回收时,static变量是不安全,<br>所以也要管理好static变量的生命周期<br>5. 资源未关闭造成的内存泄漏:比如Socket、Broadcast、Cursor、Bitmap、ListView等,使用完后要关闭<br>6. AsyncTask造成的内存泄漏:由于非静态内部类持有匿名内部类的引用而造成内存泄漏,可以通过AsyncTask内部持有外<br>部Activity的弱引用同时改为静态内部类或在onDestroy()中执行AsyncTask.cancel()进行修复
Bitmap
recycle
安卓3.0以前Bitmap是存放在堆中的,回收堆内存即可
安卓3.0以后Bitmap是存放在内存中的,需要回收native层和Java层的内存
recycle方法会判断Bitmap在不可用的情况下,将发送指令到垃圾回收器,让其回收native层和Java层的内存,则Bitmap进入dead状态
recycle方法是不可逆的,如果再次调用getPixels()等方法,则获取不到想要的结果
LruCache原理
LruCache是个泛型类,内部采用LinkedHashMap来实现缓存机制,它提供get方法和put方法来获取缓存和添加缓存,其最重要的<br>方法trimToSize是用来移除最少使用的缓存和使用最久的缓存,并添加最新的缓存到队列中
计算采样率
采样率压缩(缩略图)
质量压缩
尺寸压缩
保存到SD卡
NDK压缩
libjpeg.so
三级缓存
网络缓存<br> 本地缓存<br> 内存缓存
webp压缩
Android Webp 完全解析 快来缩小apk的大小吧-鸿洋的博客
0 条评论
下一页