欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Android內(nèi)存優(yōu)化操作方法梳理總結(jié)

 更新時(shí)間:2022年11月01日 09:08:00   作者:幸大叔  
這篇文章主要介紹了Android 內(nèi)存優(yōu)化知識(shí)點(diǎn)梳理總結(jié),Android 操作系統(tǒng)給每個(gè)進(jìn)程都會(huì)分配指定額度的內(nèi)存空間,App 使用內(nèi)存來(lái)進(jìn)行快速的文件訪問(wèn)交互,長(zhǎng)時(shí)間如此便需要優(yōu)化策略,文章分享優(yōu)化知識(shí)點(diǎn)總結(jié),需要的朋友可以參考一下

內(nèi)存泄露

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

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

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

class MemoryActivity : AppCompatActivity() {
    companion object {
        var test: Test? = null
    }
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_memory)
        test = Test()
    }
    inner class Test {
    }
}

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

class MemoryActivity : AppCompatActivity() {
    companion object {
        var test: Test? = null
    }
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_memory)
        test = Test()
    }
     //kotlin的靜態(tài)內(nèi)部類
     class Test {
    }
}

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

注冊(cè)了像BraodcastReceiver,EventBus這種,沒(méi)有在頁(yè)面銷毀時(shí)注銷的話,會(huì)引發(fā)泄露問(wèn)題,所以應(yīng)該在Activity銷毀時(shí)及時(shí)注銷。

類的靜態(tài)變量引用耗費(fèi)資源過(guò)多的實(shí)例

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

class SingleTon private constructor(val context: Context) {
    companion object {
        private var instance: SingleTon? = null
        fun getInstance(context: Context) =
            if (instance == null) SingleTon(context) else instance!!
    }
}

當(dāng)我們?cè)贏ctivity中使用時(shí),當(dāng)Activity銷毀,就會(huì)出現(xiàn)內(nèi)存泄露

class MemoryActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_memory)
        SingleTon.getInstance(this)
    }
}

這種情況可以使用applicationContext,因?yàn)锳pplication的生命周期就等于整個(gè)應(yīng)用的生命周期

class SingleTon private constructor(context: Context) {
    private var context: Context
    init {
        this.context = context.applicationContext
    }
    companion object {
        private var instance: SingleTon? = null
        fun getInstance(context: Context) =
            if (instance == null) SingleTon(context) else instance!!
    }
}

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

class MemoryActivity : AppCompatActivity() {
    private val tag = javaClass.simpleName
    private val handler = object : Handler(Looper.getMainLooper()) {
        override fun handleMessage(msg: Message) {
            Log.i(tag, "handleMessage:$msg")
        }
    }
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_memory)
        thread(start = true) {
            handler.sendEmptyMessageDelayed(1, 10000)
        }
    }
}

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

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

class MemoryActivity : AppCompatActivity() {
    companion object {
        private const val tag = "uncle"
    }
    private lateinit var handler: Handler
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_memory)
        handler = MyHandler(this)
        thread(start = true) {
            handler.sendEmptyMessageDelayed(1, 10000)
        }
    }
    class MyHandler(activity: Activity) : Handler(Looper.getMainLooper()) {
        private val reference = WeakReference(activity)
        override fun handleMessage(msg: Message) {
            super.handleMessage(msg)
            if (reference.get() != null) {
                Log.i(tag, "handleMessage:$msg")
            }
        }
    }
}

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

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

class GlobalData {
    companion object {
        val activityStack = Stack<Activity>()
    }
}

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

class MemoryActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_memory)
        GlobalData.activityStack.push(this)
    }
}

檢測(cè)工具

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

LeakCanary

一行代碼引入

    debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.9.1'

當(dāng)你測(cè)試包安裝時(shí),手機(jī)上就會(huì)有個(gè)伴生APP,用來(lái)記錄內(nèi)存泄露信息的。

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

Android Studio Profiler

同樣,我們拿上面集合的泄漏例子來(lái)看,首先,我們點(diǎn)擊MEMORY

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

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

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

再點(diǎn)擊進(jìn)去具體的類,就可以看到泄漏的原因啦

內(nèi)存溢出

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

Bitmap優(yōu)化

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

        if (bitmap != null && !bitmap.isRecycled) {
            bitmap.recycle()
            bitmap = null
        }

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

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

        val options = BitmapFactory.Options()
        //設(shè)置為true可以讓解析方法禁止為bitmap分配內(nèi)存,返回null,同時(shí)能獲取到長(zhǎng)寬值,從而根據(jù)情況進(jìn)行壓縮
        options.inJustDecodeBounds = true
        BitmapFactory.decodeResource(resources, R.drawable.large_picture, options)
        val imgHeight = options.outHeight
        val imgWidth = options.outWidth
        //通過(guò)改變inSampleSize的值來(lái)壓縮圖片
        var inSampleSize = 1
        //imgWidth為圖片的寬,viewWidth為實(shí)際控件的寬
        if (imgHeight > viewHeight || imgWidth > viewWidth) {
            val heightRatio = round(imgHeight / viewHeight.toFloat()).toInt()
            val widthRatio = round(imgWidth / viewWidth.toFloat()).toInt()
            //選擇最小比率作為inSampleSize的值,可保證最終圖片的寬高一定大于等于目標(biāo)的寬高
            inSampleSize = if (heightRatio < widthRatio) heightRatio else widthRatio
        }
        options.inSampleSize = inSampleSize
        //計(jì)算完后inJustDecodeBounds重置為false
        options.inJustDecodeBounds = false
        val bitmap = BitmapFactory.decodeResource(resources, R.drawable.large_picture, options)
        imageView.setImageBitmap(bitmap)

如果程序中的圖片是本地資源或者是自己服務(wù)器上的,那這個(gè)大小我們可以自行調(diào)整,只要注意圖片不要太大,及時(shí)回收Bitmap,就能避免OOM的發(fā)生。如果圖片來(lái)源是外界,這個(gè)時(shí)候就要特別注意了,可以采用壓縮圖片或捕獲異常,避免OOM的產(chǎn)生而導(dǎo)致程序崩潰。

內(nèi)存抖動(dòng)

頻繁地創(chuàng)建對(duì)象,會(huì)導(dǎo)致內(nèi)存抖動(dòng),最終可能會(huì)導(dǎo)致卡頓或OOM,因?yàn)榇罅颗R時(shí)對(duì)象頻繁創(chuàng)建會(huì)導(dǎo)致內(nèi)存碎片,當(dāng)需要分配內(nèi)存時(shí),雖然總體上還有剩余內(nèi)存,但由于這些內(nèi)存不連續(xù),無(wú)法整塊分配,系統(tǒng)會(huì)視為內(nèi)存不夠,故導(dǎo)致OOM。

常見(jiàn)場(chǎng)景為大循環(huán)中創(chuàng)建對(duì)象,自定義View的onDraw方法中創(chuàng)建對(duì)象,因?yàn)槠聊焕L制會(huì)頻繁調(diào)用onDraw方法。我們可以將這些操作放在循環(huán)外或onDraw方法外,避免頻繁創(chuàng)建對(duì)象。

到此這篇關(guān)于Android內(nèi)存優(yōu)化操作方法梳理總結(jié)的文章就介紹到這了,更多相關(guān)Android內(nèi)存優(yōu)化內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Android客戶端與服務(wù)端交互

    Android客戶端與服務(wù)端交互

    這篇文章主要為大家詳細(xì)介紹了Android客戶端與服務(wù)端交互之登陸示例,感興趣的小伙伴們可以參考一下
    2016-02-02
  • Android遞歸方式刪除某文件夾下的所有文件(.mp3文件等等)

    Android遞歸方式刪除某文件夾下的所有文件(.mp3文件等等)

    以刪除為例,當(dāng)然,對(duì)于遍歷某文件夾下的所有文件均可用這個(gè)方法。如搜索.mp3文件等,具體實(shí)現(xiàn)如下,感興趣的朋友可以參考下哈
    2013-06-06
  • Android使用Jetpack Compose開(kāi)發(fā)零基礎(chǔ)起步教程

    Android使用Jetpack Compose開(kāi)發(fā)零基礎(chǔ)起步教程

    Jetpack Compose是用于構(gòu)建原生Android UI的現(xiàn)代工具包。Jetpack Compose使用更少的代碼,強(qiáng)大的工具和直觀的Kotlin API,簡(jiǎn)化并加速了Android上的UI開(kāi)發(fā)
    2023-04-04
  • 詳解Android開(kāi)發(fā)之MP4文件轉(zhuǎn)GIF文件

    詳解Android開(kāi)發(fā)之MP4文件轉(zhuǎn)GIF文件

    這篇文章介紹的是將錄下來(lái)的視頻選取一小段轉(zhuǎn)為 GIF 文件,不僅時(shí)間段可以手動(dòng)選取,而且還需要支持截取視頻的局部區(qū)域轉(zhuǎn)為 GIF,網(wǎng)上調(diào)研了一下技術(shù)方案,覺(jué)得還是有必要把實(shí)現(xiàn)過(guò)程拿出來(lái)分享下,有需要的可以直接拿過(guò)去用。下面來(lái)一起看看。
    2016-08-08
  • Android編程動(dòng)態(tài)按鈕實(shí)現(xiàn)方法

    Android編程動(dòng)態(tài)按鈕實(shí)現(xiàn)方法

    這篇文章主要介紹了Android編程動(dòng)態(tài)按鈕實(shí)現(xiàn)方法,分享了onTouch方法及xml調(diào)用兩種實(shí)現(xiàn)技巧,需要的朋友可以參考下
    2016-10-10
  • Android使用CardView作為RecyclerView的Item并實(shí)現(xiàn)拖拽和左滑刪除

    Android使用CardView作為RecyclerView的Item并實(shí)現(xiàn)拖拽和左滑刪除

    這篇文章主要介紹了Android使用CardView作為RecyclerView的Item并實(shí)現(xiàn)拖拽和左滑刪除,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2017-11-11
  • android 上傳aar到私有maven服務(wù)器的示例

    android 上傳aar到私有maven服務(wù)器的示例

    這篇文章主要介紹了android 上傳aar到私有maven服務(wù)器,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2017-11-11
  • Android自定義view實(shí)現(xiàn)雪花特效實(shí)例代碼

    Android自定義view實(shí)現(xiàn)雪花特效實(shí)例代碼

    實(shí)現(xiàn)雪花的效果其實(shí)也可以通過(guò)自定義View的方式來(lái)實(shí)現(xiàn)的,而且操作上也相對(duì)簡(jiǎn)單一些,下面這篇文章主要給大家介紹了關(guān)于Android自定義view實(shí)現(xiàn)雪花特效的相關(guān)資料,文中通過(guò)實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2022-12-12
  • Android 邊播邊緩存的實(shí)現(xiàn)(MP4 未加密m3u8)

    Android 邊播邊緩存的實(shí)現(xiàn)(MP4 未加密m3u8)

    這篇文章主要介紹了Android 邊播邊緩存的實(shí)現(xiàn)(MP4 未加密m3u8),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2020-11-11
  • Android仿UC瀏覽器左右上下滾動(dòng)功能

    Android仿UC瀏覽器左右上下滾動(dòng)功能

    這篇文章主要介紹了Android仿UC瀏覽器左右上下滾動(dòng)功能,左右滑動(dòng)顯示菜單,上下滑動(dòng)滾動(dòng)內(nèi)容,需要的朋友可以參考下
    2015-12-12

最新評(píng)論