2016-04-20 9 views
0

Ich gehe durch die Testverfahren von Android und während ich mit StrictMode getestet wurde, bekam ich eine Warnung über ein Ressourcenleck an meiner Kamera, aber ich kann nicht finden, wo. Hier ist die Warnung:Kann nicht geschlossen werden für StrictMode

A resource was acquired at attached stack trace but never released. See java.io.Closeable for information on avoiding resource leaks. 
java.lang.Throwable: Explicit termination method 'release' not called 
at dalvik.system.CloseGuard.open(CloseGuard.java:184) 
at android.view.Surface.<init>(Surface.java:301) 
at android.view.SurfaceView.<init>(SurfaceView.java:102) 
at com.example.sition.diggin.camera.CameraView.<init>(CameraView.java:20) 
at com.example.sition.diggin.CameraActivity.onResume(CameraActivity.java:60) 
at android.app.Instrumentation.callActivityOnResume(Instrumentation.java:1202) 
at android.app.Activity.performResume(Activity.java:5404) 
at android.app.ActivityThread.performResumeActivity(ActivityThread.java:2837) 
at android.app.ActivityThread.handleResumeActivity(ActivityThread.java:2903) 
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2321) 
at android.app.ActivityThread.access$700(ActivityThread.java:158) 
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1296) 
at android.os.Handler.dispatchMessage(Handler.java:99) 
at android.os.Looper.loop(Looper.java:176) 
at android.app.ActivityThread.main(ActivityThread.java:5365) 
at java.lang.reflect.Method.invokeNative(Native Method) 
at java.lang.reflect.Method.invoke(Method.java:511) 
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1102) 
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:869) 
at dalvik.system.NativeStart.main(Native Method) 

Ich weiß, es hat etwas mit der Kamera zu tun hat, aber ich bin nicht sicher, ob es die CameraView oder die CameraActivity, so ist hier die CameraView:

public class CameraView extends SurfaceView implements SurfaceHolder.Callback { 
    private Camera mCamera; 

    // Constructor that obtains context and camera 
    public CameraView(Context context, Camera camera) { 
     super(context); 
     this.mCamera = camera; 
     SurfaceHolder mSurfaceHolder = this.getHolder(); 
     mSurfaceHolder.addCallback(this); 
    } 

    @Override 
    public void surfaceCreated(SurfaceHolder surfaceHolder) { 
     try { 
      SurfaceHolder mSurfaceHolder = this.getHolder(); 
      mSurfaceHolder.addCallback(this); 
      mCamera.setPreviewDisplay(surfaceHolder); 
      mCamera.startPreview(); 
     } catch (IOException e) { 
      e.printStackTrace(); 
     } 
    } 

    @Override 
    public void surfaceDestroyed(SurfaceHolder surfaceHolder) { 
     mCamera.stopPreview(); 
     getHolder().removeCallback(this); 
     mCamera.release(); 
    } 

    @Override 
    public void surfaceChanged(SurfaceHolder surfaceHolder, int format,int width, int height) { 
     // start preview with new settings 
     try { 
      mCamera.setPreviewDisplay(surfaceHolder); 
      mCamera.startPreview(); 
     } catch (Exception e) { 
      e.printStackTrace(); 
     } 
    } 
} 

und hier ist der CameraActivity:

public class CameraActivity extends AppCompatActivity implements BearingToNorthProvider.ChangeEventListener { 

    //region fields 
    private Camera mCamera; 
    private float mDist = 0f; 
    private String flashMode; 
    private ImageButton flashButton; 
    private BearingToNorthProvider mBearingProvider; 
    private boolean pictureTaken = false; 
    private byte[] currentData; 

    private double bearing; 
    private double currentBearing = 0d; 
    private String cardinalDirection = "?"; 
    private double rotation = 0d; 

    private static final int REQUEST_CODE_ASK_PERMISSIONS = 2; 
    //endregion 

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

    @Override protected void onResume() { 
     super.onResume(); 
     mCamera = getCameraInstance(); 
     CameraView mCameraView = new CameraView(this, mCamera); 
     FrameLayout preview = (FrameLayout) findViewById(R.id.camera_preview); 
     preview.addView(mCameraView); 
     ImageButton captureButton = (ImageButton) findViewById(R.id.btnCapture); 
     captureButton.setOnClickListener(new View.OnClickListener() { 
      @Override 
      public void onClick(View v) { 
       Camera.Parameters params = mCamera.getParameters(); 
       params.setFlashMode(flashMode); 
       mCamera.setParameters(params); 
       mCamera.takePicture(null, null, mPicture); 
      } 
     }); 
     SharedPreferences sharedPref = this.getSharedPreferences(getString(R.string.flashMode), Context.MODE_PRIVATE); 
     flashMode = sharedPref.getString(getString(R.string.flashMode), Camera.Parameters.FLASH_MODE_OFF); 
     flashButton = (ImageButton) findViewById(R.id.btnFlash); 
     setFlashButton(); 
     mBearingProvider = new BearingToNorthProvider(this,this); 
     mBearingProvider.setChangeEventListener(this); 
     mBearingProvider.start(); 
    } 

    @Override 
    protected void onPause() { 
     super.onPause(); 
     mBearingProvider.stop(); 
    } 


    /** 
    * Helper method to access the camera returns null if it cannot get the 
    * camera or does not exist 
    * 
    * @return the instance of the camera 
    */ 
    private Camera getCameraInstance() { 
     Camera camera = null; 
     try { 
      camera = Camera.open(); 
     } catch (Exception e) { 
      e.printStackTrace(); 
     } 
     return camera; 
    } 

    private Camera.PictureCallback mPicture = new Camera.PictureCallback() { 
     @Override 
     public void onPictureTaken(byte[] data, Camera camera) { 
      currentData = data; 
      mBearingProvider.updateBearing(); 
      bearing = mBearingProvider.getBearing(); 
      cardinalDirection = bearingToString(bearing); 
      findViewById(R.id.btnFlash).setVisibility(View.INVISIBLE); 
      findViewById(R.id.btnCapture).setVisibility(View.INVISIBLE); 
      findViewById(R.id.ivCompass).setVisibility(View.INVISIBLE); 
      findViewById(R.id.ivFocusPoint).setVisibility(View.INVISIBLE); 
      findViewById(R.id.ivCompassPointer).setVisibility(View.INVISIBLE); 
      findViewById(R.id.pictureOverlay).setVisibility(View.VISIBLE); 
     } 
    }; 

    //region Picture Intents 
    /** 
    * Method that puts the necessary data in the intent and then sends it back to the ProjectOverview 
    * @param v the view that activated this method 
    */ 
    public void confirmPicture(View v) { 
     String path = createFile(currentData); 
     Intent intent = new Intent(); 
     intent.putExtra("path", path); 
     String description = String.valueOf(((EditText) findViewById(R.id.tvPictureDesc)).getText()); 
     intent.putExtra("direction", cardinalDirection); 
     intent.putExtra("description", description); 

     //close this Activity... 
     setResult(Activity.RESULT_OK, intent); 
     finish(); 
    } 

    /** 
    * Method that puts no data in the intent and then sends it back to the ProjectOverview 
    * @param v the view that activated this method 
    */ 
    public void deletePicture(View v) { 
     setResult(Activity.RESULT_CANCELED); 
     finish(); 
    } 

    /** 
    * Method that restarts the camera giving the user a change to retake the picture 
    * @param v the view that activated this method 
    */ 
    public void retryPicture(View v) { 
     findViewById(R.id.btnFlash).setVisibility(View.VISIBLE); 
     findViewById(R.id.btnCapture).setVisibility(View.VISIBLE); 
     findViewById(R.id.ivCompass).setVisibility(View.VISIBLE); 
     findViewById(R.id.ivFocusPoint).setVisibility(View.VISIBLE); 
     findViewById(R.id.ivCompassPointer).setVisibility(View.VISIBLE); 
     findViewById(R.id.pictureOverlay).setVisibility(View.INVISIBLE); 
     pictureTaken = false; 
     mCamera.startPreview(); 
    } 

    //endregion 

    //region File Methods 

    /** 
    * Method that creates a file from the given byte array and saves the file in the Pictures Directory 
    * @param data is the array of bytes that represent the picture taken by the camera 
    * @return the path of created file 
    */ 
    private String createFile(byte[] data){ 
     checkFilePermissions(); 
     File picFile = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES) + File.separator + "tempPic.jpg" + File.separator); 
     String path = picFile.getPath(); 
     try { 
      BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(picFile)); 
      bos.write(data); 
      bos.flush(); 
      bos.close(); 
      return path; 
     } catch (IOException e) { 
      e.printStackTrace(); 
      return ""; 
     } 
    } 

    /** 
    * Checks the permission for reading to and writing from the external storage 
    */ 
    private void checkFilePermissions() { 
     if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) { 
      int hasWriteExternalStoragePermission = checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE); 
      if (hasWriteExternalStoragePermission != PackageManager.PERMISSION_GRANTED) { 
       requestPermissions(new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, 
         REQUEST_CODE_ASK_PERMISSIONS); 
      } 
     } 
    } 

    //endregion 

    //region Zoom Methods 

    @Override 
    public boolean onTouchEvent(MotionEvent event) { 
     // Get the pointer ID 
     Camera.Parameters params = mCamera.getParameters(); 
     int action = event.getAction(); 


     if (event.getPointerCount() > 1) { 
      // handle multi-touch events 
      if (action == MotionEvent.ACTION_POINTER_DOWN) { 
       mDist = getFingerSpacing(event); 
      } else if (action == MotionEvent.ACTION_MOVE && params.isZoomSupported()) { 
       mCamera.cancelAutoFocus(); 
       handleZoom(event, params); 
      } 
     } else { 
      // handle single touch events 
      if (action == MotionEvent.ACTION_UP) { 
       handleFocus(event, params); 
      } 
     } 
     return true; 
    } 

    /** 
    * Method that handles the zoom of the camera 
    * @param event the event that activated this method 
    * @param params the parameters of the camera 
    */ 
    private void handleZoom(MotionEvent event, Camera.Parameters params) { 
     int maxZoom = params.getMaxZoom(); 
     int zoom = params.getZoom(); 
     float newDist = getFingerSpacing(event); 
     if (newDist > mDist) { 
      //zoom in 
      if (zoom < maxZoom) 
       zoom++; 
     } else if (newDist < mDist) { 
      //zoom out 
      if (zoom > 0) 
       zoom--; 
     } 
     mDist = newDist; 
     params.setZoom(zoom); 
     mCamera.setParameters(params); 
    } 

    /** 
    * Method that handles the focus of the camera when zooming 
    * @param event the event that activated this method 
    * @param params the parameters of the camera 
    */ 
    private void handleFocus(MotionEvent event, Camera.Parameters params) { 
     int pointerId = event.getPointerId(0); 
     int pointerIndex = event.findPointerIndex(pointerId); 
     // Get the pointer's current position 

     List<String> supportedFocusModes = params.getSupportedFocusModes(); 
     if (supportedFocusModes != null && supportedFocusModes.contains(Camera.Parameters.FOCUS_MODE_AUTO)) { 
      mCamera.autoFocus(new Camera.AutoFocusCallback() { 
       @Override 
       public void onAutoFocus(boolean b, Camera camera) { 
        // currently set to auto-focus on single touch 
       } 
      }); 
     } 
    } 

    /** 
    * Method that determines the space between the first two fingers 
    * @param event the event that activated this method 
    * @return the distance between the two fingers 
    */ 
    private float getFingerSpacing(MotionEvent event) { 
     double x = event.getX(0) - event.getX(1); 
     double y = event.getY(0) - event.getY(1); 
     return (float) Math.sqrt(x * x + y * y); 
    } 

    //endregion 

    //region Flash Methods 

    /** 
    * Method that changes the flash mode the camera currently uses (on/off/auto) 
    * @param v the view that activated this method 
    */ 
    public void changeFlashMode(View v) { 
     switch (flashMode) { 
      case Camera.Parameters.FLASH_MODE_ON : 
       flashMode = Camera.Parameters.FLASH_MODE_AUTO; 
       break; 
      case Camera.Parameters.FLASH_MODE_AUTO : 
       flashMode = Camera.Parameters.FLASH_MODE_OFF; 
       break; 
      case Camera.Parameters.FLASH_MODE_OFF : 
       flashMode = Camera.Parameters.FLASH_MODE_ON; 
       break; 
     } 
     SharedPreferences sharedPref = getSharedPreferences(getString(R.string.flashMode), Context.MODE_PRIVATE); 
     SharedPreferences.Editor editor = sharedPref.edit(); 
     editor.putString(getString(R.string.flashMode), flashMode); 
     editor.apply(); 
     setFlashButton(); 
    } 

    /** 
    * Method that changes the image of the button based on flash mode 
    */ 
    private void setFlashButton() { 
     switch (flashMode) { 
      case Camera.Parameters.FLASH_MODE_ON : 
       flashButton.setImageDrawable(ContextCompat.getDrawable(this,R.drawable.camera_flash_on)); 
       break; 
      case Camera.Parameters.FLASH_MODE_AUTO : 
       flashButton.setImageDrawable(ContextCompat.getDrawable(this, R.drawable.camera_flash_auto)); 
       break; 
      case Camera.Parameters.FLASH_MODE_OFF : 
       flashButton.setImageDrawable(ContextCompat.getDrawable(this, R.drawable.camera_flash_off)); 
       break; 
     } 
    } 

    //endregion 

    //region Bearing Methods 

    /** 
    * Method that gives a cardinal direction based on the current bearing to the true north 
    * @param bearing is the bearing to the true north 
    * @return cardinal direction that belongs to the bearing 
    */ 
    private String bearingToString(Double bearing) { 
     String strHeading = "?"; 
     if (isBetween(bearing,-180.0,-157.5)) { strHeading = "South"; } 
     else if (isBetween(bearing,-157.5,-112.5)) { strHeading = "SouthWest"; } 
     else if (isBetween(bearing,-112.5,-67.5)) { strHeading = "West"; } 
     else if (isBetween(bearing,-67.5,-22.5)) { strHeading = "NorthWest"; } 
     else if (isBetween(bearing,-22.5,22.5)) { strHeading = "North"; } 
     else if (isBetween(bearing,22.5,67.5)) { strHeading = "NorthEast"; } 
     else if (isBetween(bearing,67.5,112.5)) { strHeading = "East"; } 
     else if (isBetween(bearing,112.5,157.5)) { strHeading = "SouthEast"; } 
     else if (isBetween(bearing,157.5,180.0)) { strHeading = "South"; } 
     return strHeading; 
    } 

    /** 
    * Method that checks if a certain number is in a certain range of numbers 
    * @param x is the number to check 
    * @param lower is the number that defines the lower boundary of the number range 
    * @param upper is the number that defines the upper boundary of the number range 
    * @return true if the number is between the other numbers, false otherwise 
    */ 
    private boolean isBetween(double x, double lower, double upper) { 
     return lower <= x && x <= upper; 
    } 

    @Override 
    public void onBearingChanged(double bearing) { 
     this.bearing = bearing; 
     mBearingProvider.setContext(this); 

     ImageView image = (ImageView) findViewById(R.id.ivCompass); 
     if (image.getVisibility() == View.VISIBLE) { 

      // create a rotation animation (reverse turn degree degrees) 
      if (bearing < 0) { 
       bearing += 360; 
      } 
      RotateAnimation ra = new RotateAnimation((float) currentBearing, (float) -bearing, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f); 

      // how long the animation will take place 
      ra.setDuration(210); 

      // set the animation after the end of the reservation status 
      ra.setFillAfter(true); 

      // Start the animation 
      image.startAnimation(ra); 
      rotation += currentBearing + bearing; 
      currentBearing = -bearing; 
     } else if (!pictureTaken){ 

      ImageView image2 = (ImageView) findViewById(R.id.ivCompass2); 
      image2.setRotation((float) -rotation); 
      image.clearAnimation(); 
      pictureTaken = true; 
     } 
     mBearingProvider.setContext(this); 
    } 

    //endregion 
} 

ich auch ein wenig fragen, wie schlecht diese Warnung ist, ich mache mir Sorgen, es ist ziemlich schlecht, weil die Warnung in rot war, während alle andere Warnung, die ich bis jetzt habe grau waren. Wenn es keine wichtige Warnung ist, lassen Sie es mich wissen, damit ich mit dem Testen fortfahren kann.

EDIT:

ich jetzt fast sicher bin, dass das Problem mit dem CameraView liegt testete ich die VideoActivity jetzt (was auch eine CameraView verwendet), und es bekam ich die gleiche Warnung.

EDIT 2:

Diese Warnung scheint nicht jedes Mal auftreten (die meiste Zeit es tut), aber ich kann nicht ein Muster oder einen logischen Grund, warum es eine Zeit arbeitet und doesn arbeite nicht das andere Mal, aber ich werde weiter suchen.

+0

Ich empfehle gegen von Surface vererben. Wenn Sie sowohl den View-Part als auch die Surface für eine Vorschau verwenden möchten, ist das zwar sinnvoll, aber es wäre trotzdem eine gute Idee, den Camera-Content getrennt zu halten. – fadden

+0

@fadden Was soll ich stattdessen verwenden? –

+0

Ich sage nicht, dass Sie nicht SurfaceView verwenden sollten, ich sage, dass Sie * von SurfaceView nicht erben sollten. Machen Sie stattdessen ein Mitglied der Klasse. Sie finden mehrere Beispiele für dieses Muster in Grafika (https://github.com/google/grafika). Welche Version von Android verwenden Sie? – fadden

Antwort

0

Hier ist ein Fehlerbericht des gleichen Problems.
https://code.google.com/p/android/issues/detail?id=54285

Diese Problemumgehung kann möglicherweise Ihr Problem beheben.
How to fix memory leak in SurfaceView

bearbeiten

wie diese,

public class CameraView extends ... { 
    ... 

    public void release(){ 
     getHolder().getSurface().release(); 
    } 

    ... 
} 


public class CameraActivity extends ... { 
    ... 
    CameraView mCameraView; 
    ... 

    @Override protected void onResume() { 
     ... 
     mCameraView = new CameraView(this, mCamera); 
     ... 
    } 

    @Override protected void onPause() { 
     super.onPause(); 
     mCameraView.release(); 
    } 

    ... 
} 
+0

Das behebt mein Problem nicht, leider bekomme ich immer noch dieselbe Warnung –

+0

Ich denke, dass die Freigabemethode in Activity.onPase() aufgerufen werden sollte. Ich füge Code zu meiner Antwort hinzu. – nshmura

+0

Leider funktioniert das auch nicht –

Verwandte Themen