日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 运维知识 > Android >内容正文

Android

Android 常见内存泄漏及解决方法

發(fā)布時(shí)間:2023/12/10 Android 33 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Android 常见内存泄漏及解决方法 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

Android中的內(nèi)存泄漏:

先說一下為什么會出現(xiàn)內(nèi)存泄漏:

Android程序開發(fā)中,如果一個(gè)對象已經(jīng)不需要被使用了,本該被回收時(shí),而這時(shí)另一個(gè)對象還在持有對該對象的引用,這樣就會導(dǎo)致無法被GC回收,就會出現(xiàn)內(nèi)存泄漏的情況。

內(nèi)存泄漏時(shí)Android程序中出現(xiàn)OOM問題的主要原因之一。所以我們在編寫代碼時(shí),一定要細(xì)心處理好這一類的問題。

下面說一下Android開發(fā)中最常見的5個(gè)內(nèi)存泄漏問題:

一:單例設(shè)計(jì)模式造成的內(nèi)存泄漏:

單例設(shè)計(jì)模式我就不多說了,這個(gè)是最基本的設(shè)計(jì)模式,相信大家都會使用,但是時(shí)候我們在使用單例設(shè)計(jì)模式時(shí)沒有注意到其中的細(xì)節(jié),就會造成內(nèi)存泄漏。

單例設(shè)計(jì)模式的靜態(tài)特性會使他的生命周期和應(yīng)用程序的生命周期一樣長,這就說明了如果一個(gè)對象不在使用了,而這時(shí)單例對象還在持有該對象的引用,這時(shí)GC就會無法回收該對象,造成了內(nèi)存泄露的情況。

?下面是錯(cuò)誤的單例設(shè)計(jì)模式的代碼:

public class AppManager {
? ? private static AppManager instance;
? ? private Context context;
? ? private AppManager(Context context) {
? ? ? ? this.context = context;
? ? }
? ? public static AppManager getInstance(Context context) {
? ? ? ? if (instance != null) {
? ? ? ? ? ? instance = new AppManager(context);
? ? ? ? }
? ? ? ? return instance;
? ? }
}
上面的代碼是一個(gè)最普通的單例模式,但是需要注意兩個(gè)問題:
1、如果我們傳入的Context是Application的Context的話,就沒有任何問題,因?yàn)锳pplication的Context生命周期和應(yīng)用程序生命周期一樣長。

2、如果我們傳入的Context是Activity的Context的話,這時(shí)如果我們因?yàn)樾枨箐N毀了該Activity的話,Context也會隨著Activity被銷毀,但是單例還在持有對該類對象的引用,這時(shí)就會造成內(nèi)存泄漏。

所以,正確的單例模式寫法應(yīng)該是這樣的:

public class AppManager {
? ? private static AppManager instance;
? ? private Context context;
? ? private AppManager(Context context) {
? ? ? ? this.context = context.getApplicationContext();
? ? }
? ? public static AppManager getInstance(Context context) {
? ? ? ? if (instance != null) {
? ? ? ? ? ? instance = new AppManager(context);
? ? ? ? }
? ? ? ? return instance;
? ? }
}
這樣的話不管我們傳入什么樣的Context,最終使用的都是Application的Context,單例的生命周期和應(yīng)用一樣長,這樣就不會造成內(nèi)存泄漏了。

二、非靜態(tài)內(nèi)部類創(chuàng)建的靜態(tài)實(shí)例造成的內(nèi)存泄漏

有時(shí)候因?yàn)樾枨笪覀儠ヮl繁的啟動(dòng)一個(gè)Activity,這時(shí)為了避免頻繁的創(chuàng)建相同的數(shù)據(jù)源,我們通常會做如下處理:


public class MainActivity extends AppCompatActivity {
?
? ? private static TestResource mResource = null;
?
? ? @Override
?
? ? protected void onCreate(Bundle savedInstanceState) {
?
? ? ? ? super.onCreate(savedInstanceState);
?
? ? ? ? setContentView(R.layout.activity_main);
?
? ? ? ? if(mManager == null){
?
? ? ? ? ? ? mManager = new TestResource();
?
? ? ? ? }
?
? ? ? ? //...
?
? ? }
?
? ? class TestResource {
?
? ? ? ? //...
?
? ? }
?
}
這樣就在Activity中創(chuàng)建了非靜態(tài)內(nèi)部類,非靜態(tài)內(nèi)部類默認(rèn)持有Activity類的引用,但是他的生命周期還是和應(yīng)用程序一樣長,所以當(dāng)Activity銷毀時(shí),靜態(tài)內(nèi)部類的對象引用不會被GC回收,就會造成了內(nèi)存溢出,解決辦法:

1、將內(nèi)部類改為靜態(tài)內(nèi)部類。

2、將這個(gè)內(nèi)部類封裝成一個(gè)單例,Context使用Application的Context

三、Handler造成的內(nèi)存泄漏:

先看一下不規(guī)范的Handler寫法:

public class MainActivity extends AppCompatActivity {
? ? private Handler mHandler = new Handler() {
? ? ? ? @Override
? ? ? ? public void handleMessage(Message msg) {
? ? ? ? ? ? //...
? ? ? ? }
? ? };
? ? @Override
? ? protected void onCreate(Bundle savedInstanceState) {
? ? ? ? super.onCreate(savedInstanceState);
? ? ? ? setContentView(R.layout.activity_main);
? ? ? ? loadData();
? ? }
? ? private void loadData(){
? ? ? ? //...request
? ? ? ? Message message = Message.obtain();
? ? ? ? mHandler.sendMessage(message);
? ? }
}
這里的handler也是一個(gè)非靜態(tài)匿名內(nèi)部類,他跟上面的一樣,也會持有Activity的引用,我們知道handler是運(yùn)行在一個(gè)Looper線程中的,而Looper線程是輪詢來處理消息隊(duì)列中的消息的,假設(shè)我們處理的消息有十條,而當(dāng)他執(zhí)行到第6條的時(shí)候,用戶點(diǎn)擊了back返回鍵,銷毀了當(dāng)前的Activity,這個(gè)時(shí)候消息還沒有處理完,handler還在持有Activity的引用,這個(gè)時(shí)候就會導(dǎo)致無法被GC回收,造成了內(nèi)存泄漏。正確的做法是:

public class MainActivity extends AppCompatActivity {
//new一個(gè)自定義的Handler
? ? private MyHandler mHandler = new MyHandler(this);
? ? private TextView mTextView ;
?
//自定義靜態(tài)內(nèi)部類繼承自Handler
? ? private static class MyHandler extends Handler {
? ? ? ? private WeakReference<Context> reference;
//在構(gòu)造函數(shù)中使用弱引用來引用context對象
? ? ? ? public MyHandler(Context context) {
? ? ? ? ? ? reference = new WeakReference<>(context);
? ? ? ? }
? ? ? ? @Override
? ? ? ? public void handleMessage(Message msg) {
? ? ? ? ? ? MainActivity activity = (MainActivity) reference.get();
? ? ? ? ? ? if(activity != null){
? ? ? ? ? ? ? ? activity.mTextView.setText("");
? ? ? ? ? ? }
? ? ? ? }
? ? }
??
? ? @Override
? ? protected void onCreate(Bundle savedInstanceState) {
? ? ? ? super.onCreate(savedInstanceState);
? ? ? ? setContentView(R.layout.activity_main);
? ? ? ? mTextView = (TextView)findViewById(R.id.textview);
? ? ? ? loadData();
? ? }
??
? ? private void loadData() {
? ? ? ? //...request
? ? ? ? Message message = Message.obtain();
? ? ? ? mHandler.sendMessage(message);
? ? }
?
@Override
? protected void onDestroy() {
? ? ? super.onDestroy();
//移除隊(duì)列中所有的Runable和消息
//這里也可以使用mHandler.removeMessage和mHandler.removeCallBacks來移除指定的Message和Runable
? ? ? mHandler.removeCallbacksAndMessages(null);
? }
}

創(chuàng)建一個(gè)靜態(tài)內(nèi)部類繼承自handler,然后再在構(gòu)造參數(shù)中對handler持有的對象做弱引用,這樣在回收時(shí)就會回收了handler持有的對象,這里還做了一處修改,就是當(dāng)我
們的回收了handler持有的對向,即銷毀了該Activity時(shí),這時(shí)如果handler中的還有未處理的消息,我們就需要在OnDestry方法中移除消息隊(duì)列中的消息。


四、線程造成的內(nèi)存泄漏


線程使用不恰當(dāng)造成的內(nèi)存泄漏也是很常見的,下面舉兩個(gè)例子:
//——————test1
? ? ? ? new AsyncTask<Void, Void, Void>() {
? ? ? ? ? ? @Override
? ? ? ? ? ? protected Void doInBackground(Void... params) {
? ? ? ? ? ? ? ? SystemClock.sleep(10000);
? ? ? ? ? ? ? ? return null;
? ? ? ? ? ? }
? ? ? ? }.execute();
//——————test2
? ? ? ? new Thread(new Runnable() {
? ? ? ? ? ? @Override
? ? ? ? ? ? public void run() {
? ? ? ? ? ? ? ? SystemClock.sleep(10000);
? ? ? ? ? ? }
? ? ? ? }).start();
上面是兩個(gè)內(nèi)部類,當(dāng)我們的Activity銷毀時(shí),這兩個(gè)任務(wù)沒有執(zhí)行完畢,就會使Activity的內(nèi)存資源無法被回收,造成了內(nèi)存泄漏。
正確的做法是使用靜態(tài)內(nèi)部類:如下
static class MyAsyncTask extends AsyncTask<Void, Void, Void> {
? ? ? ? private WeakReference<Context> weakReference;
??
? ? ? ? public MyAsyncTask(Context context) {
? ? ? ? ? ? weakReference = new WeakReference<>(context);
? ? ? ? }
??
? ? ? ? @Override
? ? ? ? protected Void doInBackground(Void... params) {
? ? ? ? ? ? SystemClock.sleep(10000);
? ? ? ? ? ? return null;
? ? ? ? }
??
? ? ? ? @Override
? ? ? ? protected void onPostExecute(Void aVoid) {
? ? ? ? ? ? super.onPostExecute(aVoid);
? ? ? ? ? ? MainActivity activity = (MainActivity) weakReference.get();
? ? ? ? ? ? if (activity != null) {
? ? ? ? ? ? ? ? //...
? ? ? ? ? ? }
? ? ? ? }
? ? }
? ? static class MyRunnable implements Runnable{
? ? ? ? @Override
? ? ? ? public void run() {
? ? ? ? ? ? SystemClock.sleep(10000);
? ? ? ? }
? ? }
//——————
? ? new Thread(new MyRunnable()).start();
? ? new MyAsyncTask(this).execute();
這樣就避免了內(nèi)存泄漏,當(dāng)然在Activity銷毀時(shí)也要記得在OnDestry中調(diào)用AsyncTask.cancal()方法來取消相應(yīng)的任務(wù)。避免在后臺運(yùn)行浪費(fèi)資源。

五、資源未關(guān)閉造成的內(nèi)存泄漏


在使用完BraodcastReceiver,ContentObserver,File,Cursor,Stream,Bitmap等資源時(shí),一定要在Activity中的OnDestry中及時(shí)的關(guān)閉、注銷或者釋放內(nèi)存,
否則這些資源不會被GC回收,就會造成內(nèi)存泄漏。
---------------------?

原文:https://blog.csdn.net/qq_35373333/article/details/74909811?
?

總結(jié)

以上是生活随笔為你收集整理的Android 常见内存泄漏及解决方法的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。