Android内存优化操作方法梳理总结_第1页
Android内存优化操作方法梳理总结_第2页
Android内存优化操作方法梳理总结_第3页
Android内存优化操作方法梳理总结_第4页
Android内存优化操作方法梳理总结_第5页
已阅读5页,还剩5页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

第Android内存优化操作方法梳理总结目录内存泄露非静态内部类创建静态实例注册对象未注销或资源对象未关闭类的静态变量引用耗费资源过多的实例Handler引发的内存泄露集合引发的内存泄露检测工具LeakCanaryAndroidStudioProfiler内存溢出Bitmap优化内存抖动

内存泄露

内存泄漏就是在当前应用周期内不再使用的对象被GCRoots引用,导致不能回收,使实际可使用内存变小,通俗点讲,就是无法回收无用对象。这里总结了实际开发中常见的一些内存泄露的场景示例和解决方案。

非静态内部类创建静态实例

该实例的生命周期和应用一样长,非静态内部类会自动持有外部类的引用,这就导致该静态实例一直持有外部类Activity的引用。

classMemoryActivity:AppCompatActivity(){

companionobject{

vartest:Test=null

overridefunonCreate(savedInstanceState:Bundle){

super.onCreate(savedInstanceState)

setContentView(R.layout.activity_memory)

test=Test()

innerclassTest{

}

解决方案:将非静态内部类改为静态内部类

classMemoryActivity:AppCompatActivity(){

companionobject{

vartest:Test=null

overridefunonCreate(savedInstanceState:Bundle){

super.onCreate(savedInstanceState)

setContentView(R.layout.activity_memory)

test=Test()

//kotlin的静态内部类

classTest{

}

注册对象未注销或资源对象未关闭

注册了像BraodcastReceiver,EventBus这种,没有在页面销毁时注销的话,会引发泄露问题,所以应该在Activity销毁时及时注销。

类的静态变量引用耗费资源过多的实例

类的静态变量生命周期等于应用程序的生命周期,若其引用耗资过多的实例,如Context,当引用实例需结束生命周期时,会因静态变量的持有而无法被回收,从而出现内存泄露,这种情况比较常见的有单例持有context。

classSingleTonprivateconstructor(valcontext:Context){

companionobject{

privatevarinstance:SingleTon=null

fungetInstance(context:Context)=

if(instance==null)SingleTon(context)elseinstance!!

}

当我们在Activity中使用时,当Activity销毁,就会出现内存泄露

classMemoryActivity:AppCompatActivity(){

overridefunonCreate(savedInstanceState:Bundle){

super.onCreate(savedInstanceState)

setContentView(R.layout.activity_memory)

SingleTon.getInstance(this)

}

这种情况可以使用applicationContext,因为Application的生命周期就等于整个应用的生命周期

classSingleTonprivateconstructor(context:Context){

privatevarcontext:Context

init{

this.context=context.applicationContext

companionobject{

privatevarinstance:SingleTon=null

fungetInstance(context:Context)=

if(instance==null)SingleTon(context)elseinstance!!

}

Handler引发的内存泄露

classMemoryActivity:AppCompatActivity(){

privatevaltag=javaClass.simpleName

privatevalhandler=object:Handler(Looper.getMainLooper()){

overridefunhandleMessage(msg:Message){

Log.i(tag,"handleMessage:$msg")

overridefunonCreate(savedInstanceState:Bundle){

super.onCreate(savedInstanceState)

setContentView(R.layout.activity_memory)

thread(start=true){

handler.sendEmptyMessageDelayed(1,10000)

}

当Activity被finish时,延迟发送的消息仍会存活在UI线程的消息队列中,直到10s后才被处理,这个消息持有handler的引用,由于非静态内部类或匿名类会隐式持有外部类的引用,handler隐式持有外部类也就是Activity的引用,这个引用会一直存在直到这个消息被处理,所以垃圾回收机制就没法回收而导致内存泄露。

解决方案:静态内部类+弱引用,静态内部类不会持有外部类的引用,如需handler内调用外部类Activity的方法的话,可以让handler持有外部类Activity的弱引用,这样Activity就不会有泄露风险了。

classMemoryActivity:AppCompatActivity(){

companionobject{

privateconstvaltag="uncle"

privatelateinitvarhandler:Handler

overridefunonCreate(savedInstanceState:Bundle){

super.onCreate(savedInstanceState)

setContentView(R.layout.activity_memory)

handler=MyHandler(this)

thread(start=true){

handler.sendEmptyMessageDelayed(1,10000)

classMyHandler(activity:Activity):Handler(Looper.getMainLooper()){

privatevalreference=WeakReference(activity)

overridefunhandleMessage(msg:Message){

super.handleMessage(msg)

if(reference.get()!=null){

Log.i(tag,"handleMessage:$msg")

}

集合引发的内存泄露

先看个例子,我们定义一个栈,装着所有的Activity

classGlobalData{

companionobject{

valactivityStack=StackActivity()

然后每启动一个Activity,就把此Activity加进去,这个时候,如果你没有在Activity销毁时清掉集合中对应的引用,就会出现泄露问题。当然,实际开发中我们不会写这么差的代码,这只是简单提个醒,需要注意一下集合中的一些引用,如果会导致泄露的,记得及时在销毁时清掉。

classMemoryActivity:AppCompatActivity(){

overridefunonCreate(savedInstanceState:Bundle){

super.onCreate(savedInstanceState)

setContentView(R.layout.activity_memory)

GlobalData.activityStack.push(this)

}

检测工具

排查内存泄露,需要一些工具的支持,这里主要介绍常用的两个,LeakCanary和AndroidStudioProfiler。

LeakCanary

一行代码引入

debugImplementationcom.squareup.leakcanary:leakcanary-android:2.9.1

当你测试包安装时,手机上就会有个伴生APP,用来记录内存泄露信息的。

就拿上面集合引发的泄露例子来说,LeakCanary就会弹出通知并且LeaksAPP中显示内存泄露信息,我们以此来定位内存泄露问题。

AndroidStudioProfiler

同样,我们拿上面集合的泄漏例子来看,首先,我们点击MEMORY

然后,普通的内存问题选择Captureheapdump就行了

点击Record,就会抓取一段时间的内存分配信息

Leaks就是记录内存泄漏的,然后我们点击进去,就可以看到具体类位置了

再点击进去具体的类,就可以看到泄漏的原因啦

内存溢出

Android系统中每个应用程序可以向系统申请一定的内存,当申请的内存不够用的时候,就会产生内存溢出,俗称OOM,全称OutOfMemory,就是内存用完了。在实际开发中,出现这种现象通常是因为内存泄露太多或大图加载问题,内存泄露上面已经讲了,那么,下面就主要讲讲图片的优化吧!

Bitmap优化

(1)及时回收Bitmap内存,这时可能有人就要问了,Android有自己的垃圾回收机制,为什么还要我们去回收呢?因为生成Bitmap最终是通过JNI方法实现的,也就是说,Bitmap的加载包含两部分的内存区域,一是Java部分,一是C部分。Java部分会自动回收,但是C部分不会,所以需要调用recycle来释放C部分的内存。那如果不调用就一定会出现泄露吗?那也不是的,Android每个应用都在独立的进程,进程被关掉的话,内存也就都被释放了。

if(bitmap!=null!bitmap.isRecycled){

bitmap.recycle()

bitmap=null

}

(2)捕获异常,Bitmap在使用的时候,最好捕获一下OutOfMemoryError以免crash掉,你还可以设置一个默认的图片。

varbitmap:Bitmap=null

try{

bitmap=BitmapFactory.decodeFile(filePath)

imageView.setImageBitmap(bitmap)

}catch(e:OutOfMemoryError){

//捕获异常

if(bitmap==null){

imageView.setImageDrawable(ContextCompat.getDrawable(this,R.drawable.picture))

}

(3)压缩,对于分辨率比较高的图片,我们应该加载一个缩小版,这里采用的是采样率压缩法。

valoptions=BitmapFactory.Options()

//设置为true可以让解析方法禁止为bitmap分配内存,返回null,同时能获取到长宽值,从而根据情况进行压缩

options.inJustDecodeBounds=true

BitmapFactory.decodeResource(resources,R.drawable.large_picture,options)

valimgHeight=options.outHeight

valimgWidth=options.outWidth

//通过改变inSampleSize的值来压缩图片

varinSampleSize=1

//imgWidth为图片的宽,viewWidth为实际控件的宽

if(imgHeightviewHeight||imgWidthviewWidth){

valheightRatio=round(imgHeight/viewHeight.toFloat()).toInt()

valwidthRatio=round(imgWidth/viewWidth.toFloat()).toInt()

//选择最小比率作为inSampleSize的值,可保证最终图片的宽高一定大于等于目标的宽高

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论