7

Android 4.0.4、カーネル 3.0.8+ を使用しています

BitmapFactory.decodeFile が null ビットマップを返すことがよくあります。

ビットマップ ファクトリがビットマップの読み込みに失敗した場合は、すぐに最大 4 回まで再試行します。これは多くの場合うまくいきます (!)

これについて不満を言う人はたくさんいます。ほとんどの質問には、ビットマップが配置されている場所、またはフラッシュされた入力ストリーム/ http 接続/その他の性質に関する回答があります。私はこれらを除外しました - 実際、私は自分の問題をテストケースと呼ぶことができるほどばかげた単純さの Android アプリケーションに減らしました。

私のアプリには、単一のボタンを含む単一のアクティビティがあり、ボタンを押すと、外部ファイル ディレクトリをループしてその中のすべてをビットマップにロードしようとするスレッドが開始されます。私はビットマップを使用したり、保持したりしません。ロードして忘れるだけです。

public class MainActivity extends Activity {

  private static final String TAG = "bmpbash";

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
  }

  @Override
  public boolean onCreateOptionsMenu(Menu menu) {
    // Inflate the menu; this adds items to the action bar if it is present.
    getMenuInflater().inflate(R.menu.main, menu);
    return true;
  }

  public void onGo(View view) {
    view.setVisibility(View.GONE);
    start();
  }

  /**
   * Start the thread.
   */
  public void start() {
    Runnable r = new Runnable() {
      @Override
      public void run() {
        mainLoop();
      }
    };
    Thread thread = new Thread(r);
    thread.start();
  }

  public void mainLoop() {
    int index = 0;
    File top = getExternalFilesDir(null);
    while (true) {
      File[] files = top.listFiles();
      if (files.length < 1) {
        Log.e(TAG, "no files found");
      } else {
        if (files.length <= index) {
          index = 0;
        }
        File file = files[index];

        //byte[] data = readFile(file);

        try {
          boolean ok = false;
          for (int i = 0; i < 4 && !ok; ++i) {
            //Bitmap bitmap = BitmapFactory.decodeByteArray(data, 0, data.length);
            Bitmap bitmap = BitmapFactory.decodeFile(file.getAbsolutePath());
            if (bitmap == null) {
              Log.e(TAG, file.getName() + "[" + i + "] - NULL bitmap");
            } else {
              ok = true;
              Log.w(TAG, file.getName() + "[" + i + "] - OK");
            }
          }
        } catch (Exception e) {
          Log.e(TAG, file.getName() + " - DIED", e);
        } catch (OutOfMemoryError oom) {
          Log.e(TAG, file.getName() + " - OOM");
        }
        ++index;
      }
    }
  }
}    

次のような出力が表示されます。

10-22 17:27:57.688: W/bmpbash(1131): translucent.png[0] - OK
10-22 17:27:57.698: W/bmpbash(1131): fearthecow.png[0] - OK
10-22 17:27:57.798: W/bmpbash(1131): gui2.png[0] - OK
10-22 17:27:57.888: W/bmpbash(1131): gui.png[0] - OK
10-22 17:27:58.058: W/bmpbash(1131): boot.png[0] - OK
10-22 17:27:58.218: E/bmpbash(1131): trainer2.png[0] - NULL bitmap
10-22 17:27:58.378: W/bmpbash(1131): trainer2.png[1] - OK

上記のコードには、decodeFile を使用する代わりに、ファイルを byte[] にロードしてから、decodeByteArray を使用するコメント アウトされた代替ロード シーケンスがあることがわかります。これは同じ効果があります (decodeByteArray が失敗し、まったく同じバイト配列ですぐに成功します!) が、失敗はあまり一般的ではないことに注意してください。

decodeFile の場合、おそらく 10 回に 1 回の試行で null が返されます。decodeByteArray の場合、おそらく 100 分の 1 です。失敗するのは常に同じファイルではありませんが、一部のファイルは他のファイルより頻繁に失敗するようです。

私の最良の予感は、png デコーダーには障害があり、長時間実行すると発生する可能性が高くなるということですが、その後は少し迷っています。誰かが問題に光を当てたり、pngファイルをロードする別の方法を持っていれば、本当に感謝しています!

4

2 に答える 2

2

さらなる実験では、これは、4.0.4 の特定のフレーバーを実行しているすべてのデバイス (私は非常に多く持っています) で発生しますが、4.1.1 デバイスで一晩実行したままにしても発生しないことが示されています。コードの単純さと、それ以降の Android バージョンではそれが複製されないことを考えると、これは Android のバグであり、ある時点で修正されたものとしてマークする傾向があります。その情報を参考にして、これが SKIA のバグであり、このスレッドで n 度まで議論されていると仮定します。

http://code.google.com/p/android/issues/detail?id=6066

私の全体的な意見は、これはめったに発生しないバグであり、入力ストリームを十分に注意深く処理していない膨大な数の人々によってその存在が隠されているということです。この問題に対する唯一の答えは、ビットマップをループ (OMG) でロードしようとすることです。ただし、これらの症状があるためにこの質問を見つけた何百人もの人々が、そのような不浄なハックに頼る前に、実行可能なフラッシュされた入力ストリームを使用していることを 100% 確認していただければ、私たち全員が感謝すると思います!

ありがとう

PSこれを「答え」と呼ぶことに罪悪感を感じても大丈夫ですか?

于 2013-10-23T13:25:34.653 に答える
0

私の問題は似ていましたが、外部ストレージにアクセス許可を追加するだけで解決しました:

<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />

お役に立てれば。

于 2015-03-12T23:51:51.037 に答える