2

opencsvを使用してcsvファイル、より具体的にはPOIファイルを解析し、情報をArrayListに読み込んでいます。情報をメモリにキャッシュする必要があるため、ユーザーがボタンを押したときにすべての POI をチェックし、それがマップビューの現在の境界内にあるかどうかを確認します。一部の POI ファイルには 10K ~ 60K 行が含まれる場合があります。アプリの強制終了までに約 50K 行を読み取ることができるため、30K の制限を設定して、他のことのためにメモリを残します。私の問題は、別のファイルをロードするときに、Arraylists を clear() し、trimToSize() し、ArrayLists を新しい ArrayLists として宣言しようとしましたが、GC が古いデータをメモリから解放しないことです。それらを clear() して新しいファイルを読み込むことはできますが、何かが原因で GC がメモリを解放できません。プログラミング、IT、または CS のトレーニングを受けていません。これは、Java / Android で作業または作成した最初のアプリです。私は働いていた、なぜこのメモリリークが発生したのかを理解しようと、約6日間読んで勉強しました。私は完全な初心者なので、コードを最適化する方法についての提案もいただければ幸いです。また、以下のコードは、ファイルをメモリに読み込む方法のみを示しています。opencsv をグーグルで検索して、それがどのように機能するかに関するドキュメントを確認できます。他に何かが必要な場合は、お知らせください。投稿します。

前もって感謝します!

    public class MainActivity extends MapActivity implements LocationListener {
    private static MapView mapView;
    int counter = 0;
    private ArrayList<String> arrLat = new ArrayList<String>();
    private ArrayList<String> arrLong = new ArrayList<String>();
    private ArrayList<String> arrName = new ArrayList<String>();
    private ArrayList<String> arrInfo = new ArrayList<String>();
    private ArrayList<Boolean> arrCheck = new ArrayList<Boolean>();
    private ProgressDialog progressDialog;

@Override
public void onCreate(Bundle savedInstanceState) {

    super.onCreate(savedInstanceState);
    // main.xml contains a MapView
    setContentView(R.layout.main); 

    //Gets file name from ListOfFiles Activity Class
            Bundle extras = getIntent().getExtras();
            if (extras != null) {
                boolean callreadPOIFile = extras.getBoolean("callreadPOIFile");
                if(callreadPOIFile) {
                    String filePath = extras.getString("filePath");
                    readPOIFileInThread(filePath);

                }else{
                    // Show user alert box                      
                }

            }
}

public void readPOIFileInThread(String filePath) {


    progressDialog = ProgressDialog.show(this, "", "LOADING:\n" + filePath + "\nPLEASE WAIT...");
    final String finalFilePath = filePath;

    new Thread(new Runnable(){
        public void run(){
            try{
                readPOIFile(finalFilePath);
            }catch(Exception e){
                runOnUiThread(new Runnable() {
                    public void run() {
                Toast.makeText(getApplicationContext(), "Exception, readPOIFileInThread", Toast.LENGTH_SHORT).show();
                //progressDialog.dismiss();
                    }
                });
            }

            progressDialog.dismiss();

        }
    }).start();

}       


//Parse and load POI CSV File
public void readPOIFile(String filePath){


    arrLat.clear();
    arrLong.clear();
    arrName.clear();
    arrInfo.clear();
    arrCheck.clear();

    arrLat.trimToSize();
    arrLong.trimToSize();
    arrName.trimToSize();
    arrInfo.trimToSize();
    arrCheck.trimToSize();

            //arrLat = null;
            //arrLong = null;
            //arrName = null;
            //arrInfo = null;
            //arrCheck = null;

            //arrLat = new ArrayList<String>();
            //arrLong = new ArrayList<String>();
            //arrName = new ArrayList<String>();
            //arrInfo = new ArrayList<String>();
            //arrCheck = new ArrayList<Boolean>();

    System.out.println(arrLat.isEmpty());

    String lat = null;
    String lng = null;
    Double dLat;
    Double dLng;
    int lati;
    int lngi;
    String name = null;
    String info = null;

    CSVReader reader = null;
    //System.out.println(filePath);
    try {
        reader = new CSVReader(new FileReader(filePath));
    } catch (FileNotFoundException e) {
        // prepare the alert box
        AlertDialog.Builder alertbox = new AlertDialog.Builder(this);

        // set the message to display
        alertbox.setMessage("There was an error reading file: " + filePath
                + "\n Please check the file format and try again.");

        // add a neutral button to the alert box and assign a click listener
        alertbox.setNeutralButton("Ok", new DialogInterface.OnClickListener() {

            // click listener on the alert box
            public void onClick(DialogInterface arg0, int arg1) {
                // the button was clicked
                //Toast.makeText(getApplicationContext(), "OK button clicked", Toast.LENGTH_SHORT).show();
            }
        });

        // show it
        alertbox.show();
        e.printStackTrace();
    }
    String [] nextLine = null;
    int count = 0;
    try {
        while ((nextLine = reader.readNext()) != null) {
            // nextLine[] is an array of values from the line
            //System.out.println(nextLine[0]+ "\n" + nextLine[1]+ "\n"  + nextLine[2]+ "\n"  + nextLine[3] + "\n");

            try {
                lng = nextLine[0];

            } catch (Exception e) {
                lng = Integer.toString(1);
            }

            try {
                lat = nextLine[1];

            } catch (Exception e) {
                lat = Integer.toString(1);
            }
            try {
                name = nextLine[2];

            } catch (Exception e) {
                name = "No Name...";
            }
            try {
                info = nextLine[3];
            } catch (Exception e) {

                info = "No Info...";
            }
            //convert lat and long to double
            try{
                dLat = Double.parseDouble(lat);
                dLng = Double.parseDouble(lng);
            }catch(Exception e){

                System.out.println("error converting lat long to Double at row: " + count);
                break;

            }
            //convert lat lng to int
            lati = (int)(dLat * 1E6);
            lngi = (int)(dLng * 1E6);

            //add line to ArrayLists
            try{
            arrLat.add(Integer.toString(lati));
            arrLong.add(Integer.toString(lngi));
            arrName.add(name);
            arrInfo.add(info);
            arrCheck.add(false);
            }catch (Exception e){

                runOnUiThread(new Runnable() {
                    public void run() {
                        //Toast.makeText(getApplicationContext(), "Error reading. Please check the file. ", Toast.LENGTH_SHORT).show();
                        System.out.println("Error reading file.");

                    }
                });
            }
            count++;
            if(count == 10000 || count == 20000){
                final int showcount = count;
                runOnUiThread(new Runnable() {
                    public void run() {
                        Toast.makeText(getApplicationContext(), showcount + " POI's loaded",
                                Toast.LENGTH_LONG).show();              
                    }
                });
            }

            if(count == 30000)
                break;

            System.out.println(count);
        }
        final String toastFilePath = filePath;
        final int toastcount = count;

        runOnUiThread(new Runnable() {
            public void run() {
                if(toastcount > 0){
                    Toast.makeText(getApplicationContext(), "File: " + toastFilePath + " read... \n"
                            + toastcount + " point(s) were loaded...",
                            Toast.LENGTH_LONG).show();
                }else{
                    Toast.makeText(getApplicationContext(), "INVALIDE FILE!\nFile: " + toastFilePath + " read... \n"
                            + toastcount + " points.",
                            Toast.LENGTH_LONG).show();
                }
            }
        });



    } catch (IOException e) {

        e.printStackTrace();
    }

}

修繕:

私はついに私の問題を見つけました!アクティビティのライフ サイクルを調べたところ、リスト アクティビティに移動してファイルを選択し、それをキャッシュするたびに新しいインスタンスが作成されていることがわかりました。MainActivity の新しいインスタンスを作成していました。マニフェストで MainActivity を singleTop モードに設定し、一部のコードを onNewIntent() メソッドに移動しましたが、すべて問題ありません。アプリがうまく機能するようになりました。

4

4 に答える 4

4

いくつかのアドバイス:

  1. Viewオブジェクト (または drawable 、またはコンテキストへの参照を持つもの)への静的参照はありません。これは非常に悪い実践であり、簡単にメモリ リークを引き起こす可能性があります。理由: アクティビティを終了した後でも、残したアクティビティを参照するビューへの静的参照が存在し、そのすべてのフィールド (大きなコレクション、 例えば)。詳細については、こちらをお読みください。

  2. 本当にファイル全体を読み取って、その内容全体をメモリに保存する必要がありますか? もちろん、それはあなたにとって簡単であり、他の何よりもはるかに高速ですが、特にこの方法で使用すると、大量のメモリを簡単に消費する可能性があります。必要なものだけを読み取り、必要なものだけを保存するようにしてください

  3. メモリリークを見つけて処理する方法を示すGoogleのビデオを見る

  4. 本当にデータを文字列に保存する必要がありますか? チェックする必要がある値 (座標、おそらく?) またはPois のコレクションで、それぞれに独自のフィールド (id、name、coordinates、...) がありますか? Java の文字列は文字の配列であり、それぞれが 2 バイト (Unicode であるため) を必要とするため、メモリ内で多くのスペースが必要になる場合があります。例として、60000 行 x 80 文字 x 2 バイト/文字は 9,600,000 バイトです。これはほぼ10MBです。メモリ使用量をより厳しくする必要があります。これは最優先事項の 1 つでメモリ効率が高いモバイル プラットフォームであることを忘れないでください (タスクの切り替えを改善するため)。

    Pois のコレクションを使用することは、設計の観点から優れているだけではありません (読みやすく、理解しやすく、維持しやすい...)。また、ラッパーの代わりにプリミティブを使用すると、スペースも少なくて済みます (たとえば、Integer ではなく int)。

于 2012-06-14T22:56:41.060 に答える
2

カップルの考え:

  1. Integer.toString(1)"1"String プールを利用するものに置き換えることができます。
  2. すべての値を s として格納する代わりに、Stringプリミティブを使用してみましたか?
  3. lat緯度とlng経度を保存しようとしているようです。おそらく、そのために a を使用したいと思うでしょうDouble
  4. を使用する代わりにArrayList、静的サイズの配列を割り当てて長さを保存することができます。
于 2012-06-14T22:35:32.030 に答える
1

私がチェックしたところ、Mapviewへの静的参照と静的な削除は、ゲッターとセッターを壊した以外は何も変わりませんでした。私はついに私の問題を見つけました!アクティビティのライフ サイクルを調べたところ、リスト アクティビティに移動してファイルを選択し、それをキャッシュするたびに新しいインスタンスが作成されていることがわかりました。MainActivity の新しいインスタンスを作成していました。マニフェストで MainActivity を singleTop モードに設定し、一部のコードを onNewIntent() メソッドに移動しましたが、すべて問題ありません。アプリがうまく機能するようになりました。

于 2012-09-13T17:43:32.573 に答える