Android內(nèi)存優(yōu)化操作方法梳理總結(jié)_第1頁
Android內(nèi)存優(yōu)化操作方法梳理總結(jié)_第2頁
Android內(nèi)存優(yōu)化操作方法梳理總結(jié)_第3頁
Android內(nèi)存優(yōu)化操作方法梳理總結(jié)_第4頁
Android內(nèi)存優(yōu)化操作方法梳理總結(jié)_第5頁
已閱讀5頁,還剩5頁未讀, 繼續(xù)免費閱讀

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進行舉報或認領(lǐng)

文檔簡介

第Android內(nèi)存優(yōu)化操作方法梳理總結(jié)目錄內(nèi)存泄露非靜態(tài)內(nèi)部類創(chuàng)建靜態(tài)實例注冊對象未注銷或資源對象未關(guān)閉類的靜態(tài)變量引用耗費資源過多的實例Handler引發(fā)的內(nèi)存泄露集合引發(fā)的內(nèi)存泄露檢測工具LeakCanaryAndroidStudioProfiler內(nèi)存溢出Bitmap優(yōu)化內(nèi)存抖動

內(nèi)存泄露

內(nèi)存泄漏就是在當(dāng)前應(yīng)用周期內(nèi)不再使用的對象被GCRoots引用,導(dǎo)致不能回收,使實際可使用內(nèi)存變小,通俗點講,就是無法回收無用對象。這里總結(jié)了實際開發(fā)中常見的一些內(nèi)存泄露的場景示例和解決方案。

非靜態(tài)內(nèi)部類創(chuàng)建靜態(tài)實例

該實例的生命周期和應(yīng)用一樣長,非靜態(tài)內(nèi)部類會自動持有外部類的引用,這就導(dǎo)致該靜態(tài)實例一直持有外部類Activity的引用。

classMemoryActivity:AppCompatActivity(){

companionobject{

vartest:Test=null

overridefunonCreate(savedInstanceState:Bundle){

super.onCreate(savedInstanceState)

setContentView(R.layout.activity_memory)

test=Test()

innerclassTest{

}

解決方案:將非靜態(tài)內(nèi)部類改為靜態(tài)內(nèi)部類

classMemoryActivity:AppCompatActivity(){

companionobject{

vartest:Test=null

overridefunonCreate(savedInstanceState:Bundle){

super.onCreate(savedInstanceState)

setContentView(R.layout.activity_memory)

test=Test()

//kotlin的靜態(tài)內(nèi)部類

classTest{

}

注冊對象未注銷或資源對象未關(guān)閉

注冊了像BraodcastReceiver,EventBus這種,沒有在頁面銷毀時注銷的話,會引發(fā)泄露問題,所以應(yīng)該在Activity銷毀時及時注銷。

類的靜態(tài)變量引用耗費資源過多的實例

類的靜態(tài)變量生命周期等于應(yīng)用程序的生命周期,若其引用耗資過多的實例,如Context,當(dāng)引用實例需結(jié)束生命周期時,會因靜態(tài)變量的持有而無法被回收,從而出現(xiàn)內(nèi)存泄露,這種情況比較常見的有單例持有context。

classSingleTonprivateconstructor(valcontext:Context){

companionobject{

privatevarinstance:SingleTon=null

fungetInstance(context:Context)=

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

}

當(dāng)我們在Activity中使用時,當(dāng)Activity銷毀,就會出現(xiàn)內(nèi)存泄露

classMemoryActivity:AppCompatActivity(){

overridefunonCreate(savedInstanceState:Bundle){

super.onCreate(savedInstanceState)

setContentView(R.layout.activity_memory)

SingleTon.getInstance(this)

}

這種情況可以使用applicationContext,因為Application的生命周期就等于整個應(yīng)用的生命周期

classSingleTonprivateconstructor(context:Context){

privatevarcontext:Context

init{

this.context=context.applicationContext

companionobject{

privatevarinstance:SingleTon=null

fungetInstance(context:Context)=

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

}

Handler引發(fā)的內(nèi)存泄露

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)

}

當(dāng)Activity被finish時,延遲發(fā)送的消息仍會存活在UI線程的消息隊列中,直到10s后才被處理,這個消息持有handler的引用,由于非靜態(tài)內(nèi)部類或匿名類會隱式持有外部類的引用,handler隱式持有外部類也就是Activity的引用,這個引用會一直存在直到這個消息被處理,所以垃圾回收機制就沒法回收而導(dǎo)致內(nèi)存泄露。

解決方案:靜態(tài)內(nèi)部類+弱引用,靜態(tài)內(nèi)部類不會持有外部類的引用,如需handler內(nèi)調(diào)用外部類Activity的方法的話,可以讓handler持有外部類Activity的弱引用,這樣Activity就不會有泄露風(fēng)險了。

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")

}

集合引發(fā)的內(nèi)存泄露

先看個例子,我們定義一個棧,裝著所有的Activity

classGlobalData{

companionobject{

valactivityStack=StackActivity()

然后每啟動一個Activity,就把此Activity加進去,這個時候,如果你沒有在Activity銷毀時清掉集合中對應(yīng)的引用,就會出現(xiàn)泄露問題。當(dāng)然,實際開發(fā)中我們不會寫這么差的代碼,這只是簡單提個醒,需要注意一下集合中的一些引用,如果會導(dǎo)致泄露的,記得及時在銷毀時清掉。

classMemoryActivity:AppCompatActivity(){

overridefunonCreate(savedInstanceState:Bundle){

super.onCreate(savedInstanceState)

setContentView(R.layout.activity_memory)

GlobalData.activityStack.push(this)

}

檢測工具

排查內(nèi)存泄露,需要一些工具的支持,這里主要介紹常用的兩個,LeakCanary和AndroidStudioProfiler。

LeakCanary

一行代碼引入

debugImplementationcom.squareup.leakcanary:leakcanary-android:2.9.1

當(dāng)你測試包安裝時,手機上就會有個伴生APP,用來記錄內(nèi)存泄露信息的。

就拿上面集合引發(fā)的泄露例子來說,LeakCanary就會彈出通知并且LeaksAPP中顯示內(nèi)存泄露信息,我們以此來定位內(nèi)存泄露問題。

AndroidStudioProfiler

同樣,我們拿上面集合的泄漏例子來看,首先,我們點擊MEMORY

然后,普通的內(nèi)存問題選擇Captureheapdump就行了

點擊Record,就會抓取一段時間的內(nèi)存分配信息

Leaks就是記錄內(nèi)存泄漏的,然后我們點擊進去,就可以看到具體類位置了

再點擊進去具體的類,就可以看到泄漏的原因啦

內(nèi)存溢出

Android系統(tǒng)中每個應(yīng)用程序可以向系統(tǒng)申請一定的內(nèi)存,當(dāng)申請的內(nèi)存不夠用的時候,就會產(chǎn)生內(nèi)存溢出,俗稱OOM,全稱OutOfMemory,就是內(nèi)存用完了。在實際開發(fā)中,出現(xiàn)這種現(xiàn)象通常是因為內(nèi)存泄露太多或大圖加載問題,內(nèi)存泄露上面已經(jīng)講了,那么,下面就主要講講圖片的優(yōu)化吧!

Bitmap優(yōu)化

(1)及時回收Bitmap內(nèi)存,這時可能有人就要問了,Android有自己的垃圾回收機制,為什么還要我們?nèi)セ厥漳??因為生成Bitmap最終是通過JNI方法實現(xiàn)的,也就是說,Bitmap的加載包含兩部分的內(nèi)存區(qū)域,一是Java部分,一是C部分。Java部分會自動回收,但是C部分不會,所以需要調(diào)用recycle來釋放C部分的內(nèi)存。那如果不調(diào)用就一定會出現(xiàn)泄露嗎?那也不是的,Android每個應(yīng)用都在獨立的進程,進程被關(guān)掉的話,內(nèi)存也就都被釋放了。

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

bitmap.recycle()

bitmap=null

}

(2)捕獲異常,Bitmap在使用的時候,最好捕獲一下OutOfMemoryError以免crash掉,你還可以設(shè)置一個默認的圖片。

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)壓縮,對于分辨率比較高的圖片,我們應(yīng)該加載一個縮小版,這里采用的是采樣率壓縮法。

valoptions=BitmapFactory.Options()

//設(shè)置為true可以讓解析方法禁止為bitmap分配內(nèi)存,返回null,同時能獲取到長寬值,從而根據(jù)情況進行壓縮

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. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論