Androidの一般的なメモリリークと最適化

簡単な紹介

無用のオブジェクト(オブジェクトが使用する必要はありません)、まだターゲット・システムを引き起こし、他のオブジェクトへの参照を保持しているとは、廃棄物に起因するメモリ空間を占有オブジェクトはヒープメモリユニットにリリースすることができないように、回復することができない場合状況はメモリリークです。Androidの開発では、いくつかの悪いプログラミング習慣は、メモリリークがあります我々のアプリの開発につながります。ここではAndroidの開発シーンと最適化におけるメモリリークのいくつかの一般的な概要です。

シングルトンメモリリーク

シングルトンは、頻繁にAndroidの開発に使用されますが、不適切に使用されている場合は、メモリリークにつながることができます。オブジェクトは役に立たないされている場合、単一の例の静的特性は、長い同じアプリケーションのライフサイクルなどとして、そのライフサイクルになりますが、それはまた、シングルトンの参照を保持しているため、その後、アプリケーション全体のライフサイクルは、それが正常であることができません回復、メモリリークにつながります。

public class AppSettings {
    private static AppSettings sInstance;
    private Context mContext;

    private AppSettings(Context context) {
        this.mContext = context;
    }

    public static AppSettings getInstance(Context context) {
        if (sInstance == null) {
            sInstance = new AppSettings(context);
        }
        return sInstance;
    }
}

 パラメータは、コンテキストアクティビティ、サービス、および他の文脈で渡されたときに、あなたがたgetInstance(コンテキストコンテキスト)メソッドを呼び出している場合は、この単一の場合、上記のコードのように、メモリリークにつながります。
      我々は、活動を開始し、基準の活性を保持するsInstanceシングルトン、コンテキストとしてActivity.thisを渡し、単一のケースのAppSettingsを得るために、このようなのAppSettingsクラスのgetInstance(コンテキスト・コンテキスト)メソッドを呼び出し、例えば、活性のために、私たちは、活動、活動なしの使用を終了しますが、静的なシングルトンとしてsIntance(アプリケーションのライフサイクル全体を通じて存在が)活動オブジェクトの解放につながる、この活動への参照を保持していきますので、時に回復することはできません、これはメモリリークが発生します。
メモリリークにこの単一のケースリードを避けるために、我々はグローバルコンテキストのコンテキストパラメータを変更することができます。

    private AppSettings(Context context) {
        this.mContext = context.getApplicationContext();
    }

グローバルコンテキストアプリケーション・コンテキストしたがって、メモリリークを回避し、長いようなアプリケーションのコンテキスト、および単一のケースのライフサイクルです。アプリケーションのライフサイクルに対応したSingletonパターンは、我々はActivityコンテキストを使用しないようにしようとしたが、シングル構成の応用例との関連で使用する場合。

メモリリークが生じ静的変数

静的変数は、そのライフサイクルの負荷クラスは、プロセス全体を始めから終わりまで、メソッド領域に格納されています。静的変数が初期化されると、それはプロセスの終了がリリースされるまでの間のみの参照を保持しています。例えば、このような状況の下で、
       情報の作成で回避重複活動するためには、SINFOは静的変数としては以下となります。  

  public class MainActivity extends AppCompatActivity {
        private static Info sInfo;

        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            if (sInfo != null) {
                sInfo = new Info(this);
            }
        }
    }

    class Info {
        public Info(Activity activity) {
        }
    }

      情報の静的メンバーとして活動し、確かに長い活動のライフサイクルよりも、アクティビティの参照を保持しているが、静的変数としてSINFO。活動が終了したときに、SINFOがまだ活動を引用して、その活性は、リードメモリリークに、リサイクルすることができません。
     Androidの開発では、まだ我々が作成するときに新しい静的変数は、個々のメンバー間の関係を考慮に入れる必要性への参照を保持している、、、そのライフサイクルと原因メモリリークの一貫性のない使用がそこに多くの時間を保持することができる
     し、可能な限り以下の静的変数の使用は、メモリリークを回避するために、開催されました。もちろん、我々はまた、静的な量がnullにリセットされ、それはもはや適切な時期に回避することが可能なメモリリークへの参照を保持して話すことはできません。


非静的内部クラスは、メモリリークにつながります


非静的な内部クラスのオブジェクトのライフサイクルが長い外部オブジェクトのライフサイクルよりも、それはメモリリークにつながるとき(匿名内部クラスを含む)非静的内部クラスは、外部のデフォルトのクラスへの参照を保持します。非静的な内部クラスが一つの典型的なシナリオは、Androidの開発にハンドラを使用することですメモリリークにつながる、ハンドラの使用には多くの小さなパートナーは、次のように書かれています: 

public class MainActivity extends AppCompatActivity {
	      @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            start();
        }

	      private void start() {
            Message msg = Message.obtain();
            msg.what = 1;
            mHandler.sendMessage(msg);
        }

private Handler mHandler = new Handler() {
          @Override
            public void handleMessage(Message msg) {
          if (msg.what == 1) {
         //做 相 应 逻 辑
    }
      }
   };
    
}

   いくつかは、言うかもしれないmHandlerアクティビティは、静的変数として引用保持していない、活動のライフサイクルよりも長くないかもしれないが、それは必ずしもそれが明確にそうではありませんメモリリークにつながるべきではありません!馴染みのメッセージハンドラ機構は我々次いで、mHandlerが対照活性を保持すること、mHandler基準mHandlerのMSGを保持するメンバ変数としてで送信メッセージmsgに保存され、mHandler非静的内部クラスアクティビティインスタンスであり、知っています活性はMSGへの参照を保持し、間接的に理解することができます。最初のメッセージ・キューメッセージキューに送られた後、MSG、次いでルーパー処理ポーリングを待つ(メッセージキューと
ルーパーは、スレッドに関連付けられている、とルーパーメッセージキュー参照メンバ変数、及びルーパーはThreadLocalの中に格納されます)。だから、活動が終了すると、MSGはまだメモリリークが活動を発生するように、処理されるか、または未処理の場合、このつながる活動が回復することはできませんされているニュースコラムメッセージキューに存在することができます。一般的に、あなたがAndroidの開発にメモリリークを避けるために内部クラスを使用しますが、持っているしたい場合は、通常、静的な内部クラス+モード弱参照。  

 public class MainActivity extends AppCompatActivity {
        private Handler mHandler;

        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            mHandler = new MyHandler(this);
            start();
        }

        private void start() {
            Message msg = Message.obtain();
            msg.what = 1;
            mHandler.sendMessage(msg);
        }

        private static class MyHandler extends Handler {
            private WeakReference<MainActivity> activityWeakReference;

            public MyHandler(MainActivity activity) {
                activityWeakReference = new WeakReference<>(activity);
            }

            @Override
            public void handleMessage(Message msg) {
                MainActivity activity=activityWeakReference.get();if(activity!=null){if(msg.what==1){
                // 做 相 应 逻 辑}}
            }
        }
    }

GCのガベージコレクション、リサイクルやメモリを解放します出会い活動は、ユニットによって占められるときmHandlerは、弱参照を介しての活動を保持しています。これは、メモリリークが発生しません。上記のアプローチは、MSGはもはや送信され、参照活動を保持する必要がありますが、そこにはまだMSGメッセージキューメッセージキュー、より良い、それが破壊されることがあり、活動を引き起こし回避のメモリリークを実際にされていないときmHandler活動コールバックとメッセージを削除するために送り出されました。

  @Overrideprotected
    void onDestroy() {
        super.onDestroy();
        mHandler.removeCallbacksAndMessages(null);
    }

メモリ原因非静的内部クラスは別のケースに漏れるスレッドまたはAsyncTaskを使用することです。例えば活動では、新しい直接の子スレッドのスレッド:

 public class MainActivity extends AppCompatActivity {
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);new Thread(new Runnable(){@Override public void run(){
         //模 拟 相 应 耗 时 逻 辑 try{Thread.sleep(2000);}catch(InterruptedException e){e.printStackTrace();}}}).start();
        }
    }

新AsyncTask直接または非同期タスク:

public class MainActivity extends AppCompatActivity {
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);new AsyncTask<Void,Void,Void>(){@Override protected Void doInBackground(Void...params){
            //模 拟 相 应 耗 时 逻 辑 try{Thread.sleep(2000);}catch(InterruptedException e){e.printStackTrace();}return null;}}.execute();
        }
    }

多くの初心者はので、新しいスレッドと上記のような非同期タスク、つまり、そのような言葉遣いは非常に非友好を知らない、このように新しい子スレッドのスレッドAsyncTask匿名内部クラスのオブジェクト、外部活動のホルダーへのデフォルトの暗黙的な参照、あります活動は、メモリリークにつながります。回避メモリリークには、依然としてハンドラ静的インナークラス+弱いアプリケーション(コードが表示されていない、とHanlder上記の正しい表現を参照)は、上記と同じ方法を使用する必要があります。

登録が取り消されていないか、メモリリークが発生するコールバック

たとえば、私たちは活動の登録で放送、登録が活動の破壊後にキャンセルされていない場合、これは常にちょうど放送システムは存在しますが、同じアクティビティがメモリリークにつながる、言及した非静的内部クラス上で引用保持しています。だから、破壊後の活性放送を登録した後に登録解除する必要があります。    

public class MainActivity extends AppCompatActivity {
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            this.registerReceiver(mReceiver, new IntentFilter());
        }

private BroadcastReceiver mReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
//接 收 到 广 播 需 要 做 的 逻 辑
}
};

        @Override
        protected void onDestroy() {
            super.onDestroy();
            this.unregisterReceiver(mReceiver);
        }
    }

観察モードの登録時に、メモリリーク発生する可能性がありますも取り消されていない場合。このような使用のレトロフィット+ RxJavaネットワーク登録要求オブザーバーコールバックとして、同じ匿名内部クラスなどの外部参照を保持し、我々は、使用しないとき、または破壊の登録を解除するために覚えておく必要があります。

 メモリリークのタイマーとTimerTaskをリード

アンドロイドでタイマーとTimerTaskを、通常、このような無制限のカルーセルViewPagerとして、タイミングやサイクルタスクの一部を作るために使用することになります。  

  public class MainActivity extends AppCompatActivity {
        private ViewPager mViewPager;
        private PagerAdapter mAdapter;
        private Timer mTimer;
        private TimerTask mTimerTask;

        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            init();
            mTimer.schedule(mTimerTask, 3000, 3000);
        }

        private void init() {
            mViewPager = (ViewPager) findViewById(R.id.view_pager);
            mAdapter = new ViewPagerAdapter();
            mViewPager.setAdapter(mAdapter);
            mTimer = new Timer();
            mTimerTask = new TimerTask() {
                @Override
                public void run() {
                    MainActivity.this.runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            loopViewpager();
                        }
                    });
                }
            };
        }

        private void loopViewpager() {
            if (mAdapter.getCount() > 0) {
                int curPos = mViewPager.getCurrentItem();
                curPos = (++curPos) % mAdapter.getCount();
                mViewPager.setCurrentItem(curPos);
            }
        }

        private void stopLoopViewPager() {
            if (mTimer != null) {
                mTimer.cancel();
                mTimer.purge();
                mTimer = null;
            }
            if (mTimerTask != null) {
                mTimerTask.cancel();
                mTimerTask = null;
            }
        }

        @Override
        protected void onDestroy() {
            super.onDestroy();
            stopLoopViewPager();
        }
    }

私たちは活動が破壊された場合は、タイマーはTimerTaskをの実装を待ち続け、それが参照の活性を保持している可能性である私たちが活動をキャンセルしたいときに回復し、そうすることはできませんメモリリークを避けるために、すぐにタイマーとTimerTaskをを破壊しました。

コレクション内のオブジェクトはメモリリークのクリーンアップを起こしません

オブジェクトがArrayListに、HashMapのおよび他のコレクションに配置される場合、これはよりよく理解され、このセットは、対象物を保持します。私たちは、もはやこのオブジェクトを必要とするとき、それはとても長い間、コレクションが使用中であるとして、コレクションから削除しません(ただし、このオブジェクトはすでに無用である)、このオブジェクトはメモリリークが発生します。セットはまだ引用されている場合は、それらは役に立たないのコレクション内のオブジェクトが、メモリリークが発生します。だから、コレクション削除から、またはセットを使用する場合のセットをクリアすると、メモリリークを避けるために、タイムリーなオブジェクトではありません。

リソースが近くないか、メモリリークを解放します

IO、ファイルストリームやsqliteは、カーソルおよびその他のリソースを使用する場合は、速やかに閉じなければなりません。読み取りおよび書き込み動作中にこれらのリソースは、典型的には、メモリリークが発生するように、タイムリーに閉じられていない場合は、これらのバッファオブジェクトが解放されていたであろうし、占有されていない、バッファを使用しています。我々は、彼らが使用された場合、速やかにバッファがメモリリークを避けるために、時間内に解放することができますように、シャットダウンする必要はありません。

メモリリーク原因プロパティのアニメーション

アニメーションはまた、このような活動にプロパティアニメーション(ObjectAnimator)を開始するなど、時間のかかる作業ですが、私たちは映画を見ることができないが、破壊の時に、cancleメソッドへの呼び出しは、ありませんが、映画はまだダウンプレーしていきます、アニメーションアクティビティをもたらし参照アクティビティコントロールは、適切に解放しない対照存在を指します。したがって、アクティビティを相殺すると同時に、メモリリークを回避するために、プロパティのアニメーションを破壊しました。

  @Overrideprotected
    void onDestroy() {
        super.onDestroy();
        mAnimator.cancel();
    }

    メモリリークの原因のWebView

  私たちが破壊されたアクティビティの後にメモリを解放するためにそれを破壊するDestoryは()メソッドを呼び出すようにしたいので、WebViewのリークメモリについて、WebViewのため、長期記憶を取り上げるページをロードした後、解放することはできません。WebViewの以下の参考文献コールバックを解除することができないのWebViewメモリを引き起こし、活性を保持した後、偶数Webview.destory(と呼ばれる)および他の方法は、問題(Android5.1を解決することはできません。このWebViewのメモリリークに関連する情報を表示するにチェックに加えて)。
最終溶液は、次のとおりのWebView必要が最初のWebViewを破壊する前に、親コンテナから除去され、その後、WebViewのを破壊します。詳細な分析は、こちらの記事を参照してください。((http://blog.csdn.net/xygy8860/article/details http://blog.csdn.net/xygy8860/article/details/53334476?utm_source=itdadao&utm_medium =紹介) / 53334476)[WebViewのメモリリーク溶液]。

   @Overrideprotected
    void onDestroy() {
        super.onDestroy();
        //先 从 父 控 件 中 移 除 WebView mWebViewContainer.removeView(mWebView);mWebView.stopLoading();mWebView.getSettings().setJavaScriptEnabled(false);mWebView.clearHistory();mWebView.removeAllViews();mWebView.destroy();
}

概要

Androidのメモリ最適化でメモリリークがより重要な側面である、プログラムは、我々はすべての良い習慣を開発するために符号化する過程で、メモリリークに気づくことができない場合があり場所を何度も取りました。長いこれらの点は、ほとんどの場合、より少ないメモリリークに回避することが可能な限りダウン概要:

  1. 使用しないようにしようの単一の実施形態の活動の設定を参照する場合、
  2. 静的アプリケーションオブジェクト参照がブランキング以下静的参照ことに注意してください。
  3. +クラスの静的インナーソフト参照の代わりに、非静的な内部クラスを使用しました。
  4. 速やかに放送または登録オブザーバーをキャンセルします。
  5. プロパティのアニメーションが活動を破壊したときに時間のかかる作業は、キャンセルすることを忘れないでください。
  6. ファイルストリームは、タイムリーにカーソルや他のリソースが閉じられています。
  7. 活動が破壊されたのWebViewの除去と破壊。
发布了40 篇原创文章 · 获赞 70 · 访问量 9万+

おすすめ

転載: blog.csdn.net/weixin_35959554/article/details/103873886