2010-06-13 2 views
4

Je suis coincé avec une fuite de mémoire que je ne peux pas réparer. J'ai identifié où il se produit, en utilisant le MemoryAnalizer mais j'ai vainement du mal à m'en débarrasser. Voici le code:Android: fuite de mémoire due à AsyncTask

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(); 
} 

}

L'activité se termine sur un bouton clic, comme ceci:

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 indique la fuite de mémoire à:

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

Je suis reconnaissant pour toute suggestion, merci d'avance.

+0

estimation semi-éduquée: si vous appellerez 'recycler()' 'sur vos objets Bitmap' une fois que vous avez terminé avec eux? En outre, l'appel à 'PickerActivity.this' ne semble pas avoir de sens dans le contexte que vous avez donné. Et vous devriez mettre en cache l'appel '(myApp) getApplication()' qui est si souvent utilisé. –

+0

Christopher, Merci pour votre réponse, en effet PickerActivity est MyActivity (j'ai édité le post); les bitmaps sont définis au niveau de l'application, c'est pourquoi je ne les recycle pas, car j'en ai besoin dans les activités suivantes. Le débogage de l'application et MemoryAnalyzer m'indiquent que ManageCaptureTask persiste même lorsque MyActivity est terminé et c'est ce que je ne sais pas comment faire: terminer cette tâche asynchrone. J'ai modifié le post, en ajoutant l'événement onClick où MyActivity est terminé et l'activité suivante est lancée. – Manu

+0

J'ai vu que vous utilisiez 'Bitmap' dans' Application', mais vous avez vu que vous l'avez défini sur 'null' à un moment donné, alors je me suis demandé si cela devrait être recyclé à ce moment-là. –

Répondre

10

Votre nettoyage des threads est-il effectué après l'appel de onPostExecute ou est-il toujours en mémoire?

Une tâche asynchrone ne sera pas annulée ou détruite au moment où l'activité est rejetée. Si votre thread est plus ou moins léger et se termine après un petit moment, gardez-le en marche et ajoutez un MyActivity.this. Clause isFinishing() dans la méthode onPostExecute().

Votre tâche stocke une référence implicite à votre activité MyActivity.this car il s'agit d'une classe privée dans l'activité. Cela signifie que votre activité ne sera pas collectée avant la fin de la tâche.

+0

Merci Janusz, je savais que "Votre tâche stocke une référence implicite à votre activité MyActivity.this", mais je ne savais pas comment le gérer. Je vais essayer votre suggestion "ajouter une clause MyActivity.this.isFinishing() dans le onPostExecuteMethod" et poster un feed back. Merci encore! – Manu

+0

@Manu a-t-il résolu le problème? –

+0

@Hisoka Oui c'est fait. – Manu

0

Vous pouvez ci-dessous l'extrait de code

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