【问题标题】:Android: Memory leak due to AsyncTaskAndroid:由于 AsyncTask 导致的内存泄漏
【发布时间】:2011-03-03 06:12:24
【问题描述】:

我遇到了无法修复的内存泄漏问题。我使用 MemoryAnalizer 确定了它发生的位置,但我徒劳地努力摆脱它。代码如下:

public class MyActivity extends Activity implements SurfaceHolder.Callback {
 ...

Camera.PictureCallback mPictureCallbackJpeg = new Camera.PictureCallback() {
    public void onPictureTaken(byte[] data, Camera c) {
        try  {
            // log the action
            Log.e(getClass().getSimpleName(), "PICTURE CALLBACK JPEG: data.length = " + data);

            // Show the ProgressDialog on this thread 
            pd = ProgressDialog.show(MyActivity.this, "", "Préparation", true, false); 

            // Start a new thread that will manage the capture 
            new ManageCaptureTask().execute(data, c); 
        }
        catch(Exception e){
            AlertDialog.Builder dialog = new AlertDialog.Builder(MyActivity.this);
            ...
            dialog.create().show();
        }
    }

    class ManageCaptureTask extends AsyncTask<Object, Void, Boolean> { 
        protected Boolean doInBackground(Object... args) {
            Boolean isSuccess = false;

            // initialize the bitmap before the capture
            ((myApp) getApplication()).setBitmapX(null);
            try{

                // Check if it is a real device or an emulator
                TelephonyManager telmgr = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);
                String deviceID = telmgr.getDeviceId();
                boolean isEmulator = "000000000000000".equalsIgnoreCase(deviceID);

                // get the bitmap
                if (isEmulator) {
                    ((myApp) getApplication()).setBitmapX(BitmapFactory.decodeFile(imageFileName));
                } else {
                    ((myApp) getApplication()).setBitmapX(BitmapFactory.decodeByteArray((byte[]) args[0], 0, ((byte[])args[0]).length));
                }

                ((myApp) getApplication()).setImageForDB(ImageTools.resizeBmp(((myApp) getApplication()).getBmp()));
                // convert the bitmap into a grayscale image and display it in the preview
                ((myApp) getApplication()).setImage(makeGrayScale());
                isSuccess = true;
            }
            catch (Exception connEx){
                errorMessageFromBkgndThread = getString(R.string.errcapture);
            }
            return isSuccess; 
        } 

        protected void onPostExecute(Boolean result) { 
            // Pass the result data back to the main activity 
            if (MyActivity.this.pd != null) { 
                MyActivity.this.pd.dismiss(); 
            } 
            if (result){
                ((ImageView) findViewById(R.id.apercu)).setImageBitmap(((myApp) getApplication()).getBmp());    
                ((myApp) getApplication()).setBitmapX(null);
            }
            else{
                // there was an error
                ErrAlert();
            }
        } 
    }     
};
private void ErrAlert(){
    // notify the user about the error
    AlertDialog.Builder dialog = new AlertDialog.Builder(this);
    ...
    dialog.create().show();
}

}

活动在单击按钮时终止,如下所示:

Button use = (Button) findViewById(R.id.use);
use.setOnClickListener(new OnClickListener() {
    @Override
    public void onClick(View v) {
        Intent intent = new Intent(MyActivity.this, NextActivity.class);
        intent.putExtra("dbID", "-1");
        intent.putExtra("category", category);
        ((myApp) getApplication()).setBitmapX(null);
        MyActivity.this.startActivity(intent);
        MyActivity.this.finish();
        }
    });

MemoryAnalyzer 指出内存泄漏:

((myApp) getApplication()).setBitmapX(BitmapFactory.decodeByteArray((byte[]) args[0], 0, ((byte[])args[0]).length));

感谢您的任何建议,提前感谢您。

【问题讨论】:

  • 半受过教育的猜测:你是否应该在完成 Bitmap 对象后调用 recycle()?此外,在您给出的上下文中,对PickerActivity.this 的调用似乎没有意义。而且你应该缓存经常使用的(myApp) getApplication() 调用。
  • Christopher,感谢您的回复,事实上 PickerActivity 是 MyActivity(我编辑了帖子);位图是在应用程序级别定义的,这就是我不回收它们的原因,因为我在后续活动中需要它们。调试应用程序和 MemoryAnalyzer 告诉我,即使 MyActivity 终止,ManageCaptureTask 仍然存在,这是我不知道该怎么做的:终止该异步任务。我编辑了帖子,添加了终止 MyActivity 并启动下一个活动的 onClick 事件。
  • 我看到你在整个Application 中使用Bitmap,但看到你在某个时候将它设置为null,所以想知道它是否应该在那个时候回收。跨度>

标签: android memory memory-leaks android-asynctask


【解决方案1】:

你的线程垃圾是在 onPostExecute 被调用后收集的还是还在内存中?

Async Task 不会在 Activity 关闭时被取消或销毁。如果您的线程或多或少是轻量级的并且在一小段时间后完成,只需保持其运行并在 onPostExecute() 方法中添加 MyActivity.this.isFinishing() 子句。

您的 Task 存储对 Activity MyActivity.this 的隐式引用,因为它是 Activity 内部的私有类。这意味着在任务退出之前,您的 Activity 不会被垃圾收集。

【讨论】:

  • 谢谢 Janusz,我知道“您的任务存储对您的 Activity MyActivity.this 的隐式引用”,但我不知道如何管理它。我会尝试您的建议“在 onPostExecuteMethod 中添加 MyActivity.this.isFinishing() 子句”并发布反馈。再次感谢您!
  • @Manu 解决了这个问题吗?
  • @Manu 如何添加 isFinishing() 子句?您可以发布几行作为示例吗?
【解决方案2】:

你可以试试下面的代码sn -p

protected void onPostExecute(Boolean result) {
    if(YourActivity.this.isFinished()){
        //to smomething here
    }
}

【讨论】:

    猜你喜欢
    • 2016-02-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-01-31
    • 2016-07-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多