7

我正在使用 android 4.0.4,内核 3.0.8+

每隔一段时间 BitmapFactory.decodeFile 返回一个空位图。

请注意,如果位图工厂无法加载位图,我会立即重试,最多 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 分之一。失败的文件并不总是同一个文件,但某些文件似乎确实比其他文件更频繁地失败。

我最好的预感是 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 回答