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

Android內存優(yōu)化操作方法梳理總結

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

內存泄露

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

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

該實例的生命周期和應用一樣長,非靜態(tài)內部類會自動持有外部類的引用,這就導致該靜態(tài)實例一直持有外部類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)內部類改為靜態(tà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)內部類
     class Test {
    }
}

注冊對象未注銷或資源對象未關閉

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

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

類的靜態(tài)變量生命周期等于應用程序的生命周期,若其引用耗資過多的實例,如Context,當引用實例需結束生命周期時,會因靜態(tài)變量的持有而無法被回收,從而出現(xià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!!
    }
}

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

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

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

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ā)的內存泄露

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

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

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

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ā)的內存泄露

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

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

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

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

檢測工具

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

LeakCanary

一行代碼引入

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

當你測試包安裝時,手機上就會有個伴生APP,用來記錄內存泄露信息的。

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

Android Studio Profiler

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

然后,普通的內存問題選擇Capture heap dump就行了

點擊Record,就會抓取一段時間的內存分配信息

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

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

內存溢出

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

Bitmap優(yōu)化

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

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

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

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

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

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

內存抖動

頻繁地創(chuàng)建對象,會導致內存抖動,最終可能會導致卡頓或OOM,因為大量臨時對象頻繁創(chuàng)建會導致內存碎片,當需要分配內存時,雖然總體上還有剩余內存,但由于這些內存不連續(xù),無法整塊分配,系統(tǒng)會視為內存不夠,故導致OOM。

常見場景為大循環(huán)中創(chuàng)建對象,自定義View的onDraw方法中創(chuàng)建對象,因為屏幕繪制會頻繁調用onDraw方法。我們可以將這些操作放在循環(huán)外或onDraw方法外,避免頻繁創(chuàng)建對象。

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

相關文章

  • Android客戶端與服務端交互

    Android客戶端與服務端交互

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

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

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

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

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

    詳解Android開發(fā)之MP4文件轉GIF文件

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

    Android編程動態(tài)按鈕實現(xiàn)方法

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

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

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

    android 上傳aar到私有maven服務器的示例

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

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

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

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

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

    Android仿UC瀏覽器左右上下滾動功能

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

最新評論