Androidで不必要なオブジェクトの作成を避けるにはどうすればいいですか?


前言
APPでより多くの対象を割り当てるにつれて、定期的な強制ごみ収集を実施しなければなりません。同時ゴミ処理器はAndroid 2.3に導入されるが、不必要な作業は常に回避されるべきであり、必要でない時には作成対象例を避けるべきである。
プログラミング開発において、メモリの占有は常に直面する現実であり、通常のメモリの調節の方向はメモリの占有をできるだけ減らすことである。
AndroidデバイスはPCのように十分なメモリを持っていません。しかも単一のAppが使っているメモリは実際には比較的小さいです。不必要なオブジェクトの作成を避けることは、Android開発にとって特に重要です。
本論文では、いくつかの一般的な作成対象を避けるためのシーンと方法を紹介します。その中には、微最適化に属するものもあり、符号化技術に属しています。もちろん、効果的な方法もあります。
使用例
単一の例は私たちがよく使うデザインモードです。このモードを使って、全体的な呼び出しのためにオブジェクトだけを提供できます。したがって、単例は不必要なオブジェクトを作成しないようにする方法です。
一例のモードは使いやすいですが、多くの問題に注意しなければなりません。一番重要なのはマルチスレッドを同時にした場合、一例の一意性を保証することです。もちろん方式が多いです。例えば餓漢式、怠け者式double-checkなどです。
ここでは、非常にお客様の書き方を紹介します。

public class SingleInstance {
 private SingleInstance() {
 }

 public static SingleInstance getInstance() {
   return SingleInstanceHolder.sInstance;
 }

 private static class SingleInstanceHolder {
   private static SingleInstance sInstance = new SingleInstance();
 }
}
Javaでは、クラスの静的初期化はクラスがロードされる時にトリガされます。この原理を利用して、この特性を利用して、内部クラスを結合して、上のコードを実現して、怠け者式の作成例を行います。
隠し箱詰めは避ける
自動箱詰めはJava 5が導入した特性の一つで、元のタイプのデータを自動的に対応する参照タイプに変換します。例えばintIntegerに変換するなどです。
このような特性は、符号化時のこまごました作業を大幅に低減していますが、注意しないと不要なオブジェクトを作成することができます。たとえば下のコード

Integer sum = 0;
for(int i=1000; i<5000; i++){
  sum+=i;
}
上のコードsum+=isum = sum + iと見なすことができますが、+このオペレータはIntegerオブジェクトには適用されません。まずsumが自動的に箱を外す操作を行い、数値の加算操作を行い、最後に自動箱詰め操作が発生してIntegerオブジェクトに変換します。
その内部変化は以下の通りです。

int result = sum.intValue() + i;
Integer sum = new Integer(result);
ここで声明しているsumはIntegerタイプですので、上のサイクルでは4000個近くの不要なIntegerオブジェクトを作成します。このような膨大なサイクルの中で、プログラムの性能を低下させ、ゴミ回収の作業量を増やします。したがって、私たちがプログラミングするときは、この点に注意して、変数型を正確に宣言し、自動箱詰めによる性能問題を避ける必要があります。
また、元のデータタイプの値をセットに入れると、自動箱詰めが発生しますので、この過程でもオブジェクトが作成されます。このような場合は、SparseArray SparseBooleanArray SparseLongArray などのコンテナを選択することができます。
慎重に容器を選ぶ
JavaとAndroidは多くの編集者のコンテナセットを提供して、対象を組織します。例えばArrayList ContentValues HashMap などです。
しかし、このような容器は使いやすいですが、彼らは自動的に拡張されます。この中には新しいオブジェクトを作るのではなく、より大きな容器のオブジェクトを作成します。これはより大きなメモリ空間を占めることを意味する。
HashMapを例にとって、私達はput keyvalueの時、拡張が必要かどうかを検出します。必要ならば、倍の拡張容量が必要です。

@Override public V put(K key, V value) {
    if (key == null) {
      return putValueForNullKey(value);
    }
    //some code here

    // No entry for (non-null) key is present; create one
    modCount++;
    if (size++ > threshold) {
      tab = doubleCapacity();
      index = hash & (tab.length - 1);
    }
    addNewEntry(key, value, hash, index);
    return null;
  }
拡張については、通常次のような方法があります。
     1.大きな容量値をあらかじめ見積もって、複数回の拡大を避ける。
     2.代替のデータ構造を探して、時間と空間のバランスを確保する。
LaunchModeをうまく使う
LaunchModeといえば必ずActivityと関係があります。通常の状況では、我々はmanifestでActivityを宣言します。LaunchModeをセットしないとデフォルトのstandardモードを使います。
Standardに設定すると、Intent要求があるたびに、新しいActivityインスタンスが作成されます。例えば、10個のメール作成Intentがあれば、10個のCompseMail Activityのインスタンスを作成してこれらのIntentを処理します。その結果,このモードはあるActivityの複数の例を作成することが明らかになった。
一つの検索機能のActivityに対して、実際にActivityの一例を維持すればいいです。standardモードを使用するとActivityのインスタンスが多すぎて作成され、よくないです。
常識に合った場合、LaunchModeを合理的に使用し、Activityの作成を減少させることを確保する。
Activity処理onConfigrationChanged
これはまたActivityオブジェクトの作成に関するものです。Activity作成のコストは他のオブジェクトに比べてかなり高いです。
デフォルトでは、画面回転を行うと元Activityが破壊され、新しいActivityが作成されます。このようにするのはレイアウトを適応するためです。もちろんこれはシステムのデフォルトのやり方です。制御できる状況を開発すると、Activityの再作成を避けることができます。
画面切り替えを例に、Activity宣言時に、

<activity
  android:name=".MainActivity"
  android:label="@string/app_name"
  android:theme="@style/AppTheme.NoActionBar"
  android:configChanges="orientation"
>
そしてActivityのonConfigurationChangedメソッドを書き換える。

public void onConfigurationChanged(Configuration newConfig) {
  super.onConfigurationChanged(newConfig);
  if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT) {
    setContentView(R.layout.portrait_layout);
  } else if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) {
    setContentView(R.layout.landscape_layout);
  }
}
文字列スティッチングに注意
文字列というのは一番目立たないかもしれません。ここでは主に文字列のつなぎ合わせを話します。

Log.i(LOGTAG, "onCreate bundle=" + savedInstanceState);
これは私たちの最も一般的なロゴの打ち方であるはずですが、文字列の継ぎ目内部は実際にStringBuilderオブジェクトを生成し、最後にToStringメソッドを呼び出すまで、それぞれappedを行います。
次はコードループのコードです。これは明らかによくないです。これはStringBuilderオブジェクトの多くを作成したからです。

public void implicitUseStringBuilder(String[] values) {
 String result = "";
 for (int i = 0 ; i < values.length; i ++) {
   result += values[i];
 }
 System.out.println(result);
}
文字列のつなぎ合わせを減らす方法はありますか?
     1.Stering.formatで置換する
     2.循環スプラインなら、明示的に循環外部でSteringBuiderを作成して使用することを提案します。
レイアウトの階層を減らす
レイアウトレベルが多すぎて、inflateプロセスが消耗するだけでなく、余分な補助レイアウトを作成しました。補助レイアウトを減らす必要があります。他のレイアウトやカスタムビューを試して問題を解決できます。
事前に点検し、不必要な異常を減らす。
異常はプログラムにとっては普通ですが、実際には異常なコードが高いです。現場のデータを集めるためにstacktraceが必要です。しかし、やはり異常を避けるための措置があります。
例えば、ファイルの各行の文字列を印刷したいですが、チェックしていないコードは以下の通りです。FileNotFoundExceptionの存在が可能です。

private void printFileByLine(String filePath) {
  try {
    FileInputStream inputStream = new FileInputStream("textfile.txt");
    BufferedReader br = new BufferedReader(new InputStreamReader(inputStream));
    String strLine;
    //Read File Line By Line
    while ((strLine = br.readLine()) != null)  {
      // Print the content on the console
      System.out.println (strLine);
    }
    br.close();
  } catch(FileNotFoundException e) {
    e.printStackTrace();
  } catch (IOException e) {
    e.printStackTrace();
  }
}
ファイルが存在するかどうかのチェックをすれば、FileNotFoundExceptionを投げ出す確率が多くなります。

private void printFileByLine(String filePath) {
    if (!new File(filePath).exists()) {
      return;
    }
    try {
      FileInputStream inputStream = new FileInputStream("textfile.txt");
      BufferedReader br = new BufferedReader(new InputStreamReader(inputStream));
      String strLine;
      //Read File Line By Line
      while ((strLine = br.readLine()) != null)  {
        // Print the content on the console
        System.out.println (strLine);
      }
      br.close();
    } catch(FileNotFoundException e) {
      e.printStackTrace();
    } catch (IOException e) {
      e.printStackTrace();
    }
  }
上記の検査はいい符号化技術です。採用を提案します。
スレッドを作成しすぎないでください。
Androidでは、主スレッドで時間のかかる動作をできるだけ避けるべきであり、他のスレッドを使用する必要がある。

private void testThread() {
  new Thread() {
    @Override
    public void run() {
      super.run();
      //do some io work
    }
  }.start();
}
これらは動作可能ですが、スレッドを作成する場合の価格は通常のオブジェクトよりもはるかに高いので、HandlerThreadまたはThreadPoolを使って代替することをお勧めします。
列挙の代わりに注解を使う
列挙は私達がよく使う一つの価値限定の手段で、列挙は単純な定数より約束します。そして、列挙の実質はオブジェクトを作成します。幸い、Androidはコンパイル時に限定された値を提供し、運行時の圧力を低減しました。関連の注釈はIntDefとSteringDefです。
次のようにIntDefを例にとって、どのように使うかを紹介します。
一つの書類の中では次のように声明します。

public class AppConstants {
  public static final int STATE_OPEN = 0;
  public static final int STATE_CLOSE = 1;
  public static final int STATE_BROKEN = 2;

  @IntDef({STATE_OPEN, STATE_CLOSE, STATE_BROKEN})
  public @interface DoorState {}
}
そして書く方法を設定します。

private void setDoorState(@AppConstants.DoorState int state) {
  //some code
}
方法を呼び出すときは、STATE_OPEN STATE_CLOSE STATE_BROKEN しか使用できません。他の値を使うとリマインダと警告がコンパイルされます。
オブジェクトプールを選択
Androidにはスレッドプール、接続プールなどの多くの池の概念があります。私たちが長く使うのを含めてHandler.Messageはプールの技術を使っています。
例えば、Handlerを使ってメッセージを送りたいです。 Message msg = new Message() を使ってもいいし、Message msg = handler.obtainMessage() を使ってもいいです。プールは毎回新しいオブジェクトを作成するのではなく、プールからオブジェクトを優先的に取ります。
対象のプールを使うには何点に注意が必要ですか?
     1.対象を池に戻し、対象のデータを初期化し、汚データがないように注意する
     2.池の成長を合理的に制御し、大きすぎないようにして、多くの対象が遊休状態にあることを招く。
アプリの初期化を慎重に行う
Androidアプリケーションは、複数のプロセスのオープンをサポートすることができます。通常のやり方はこうです。

<service android:name=".NetworkService"
  android:process=":network"
/>
通常、私たちはAppliationのonCreate方法で多くの初期化操作を行いますが、各プロセスの起動にはこのonCreate方法を実行する必要があります。不必要な初期化を避けるためには、プロセス(現在のプロセス名を判断することによって)対応初期化を推奨します。

public class MyApplication extends Application {
  private static final String LOGTAG = "MyApplication";

  @Override
  public void onCreate() {
    String currentProcessName = getCurrentProcessName();
    Log.i(LOGTAG, "onCreate currentProcessName=" + currentProcessName);
    super.onCreate();
    if (getPackageName().equals(currentProcessName)) {
      //init for default process
    } else if (currentProcessName.endsWith(":network")) {
      //init for netowrk process
    }
  }

  private String getCurrentProcessName() {
    String currentProcessName = "";
    int pid = android.os.Process.myPid();
    ActivityManager manager = (ActivityManager) this.getSystemService(Context.ACTIVITY_SERVICE);
    for (ActivityManager.RunningAppProcessInfo processInfo : manager.getRunningAppProcesses()) {
      if (processInfo.pid == pid) {
        currentProcessName = processInfo.processName;
        break;
      }
    }
    return currentProcessName;
  }
}
締め括りをつける
上記のいくつかの知識は、Androidの中で、余分なオブジェクトを作成することを避けるための方法についてのまとめです。この文章の内容が皆さんの役に立つことを願います。