2017-02-10 40 views
0

我想为我的应用程序使用Camera API。当我点击拍照按钮时,UI会挂起约10秒钟,然后再进一步回应。Android - 相机API需要太多时间来捕获和保存图像

我在SO here上找到了一篇文章,但是解决方案并没有太多具体的内容。我认为这是一个多线程的问题。我是一名Android开发人员,不熟悉多线程。

UploadPrescription.java:

package com.example.ayusch.medomo; 


import android.Manifest; 
import android.content.Context; 
import android.content.Intent; 
import android.content.pm.PackageManager; 
import android.graphics.Bitmap; 
import android.graphics.BitmapFactory; 
import android.graphics.ImageFormat; 
import android.graphics.Matrix; 
import android.hardware.Camera; 
import android.media.ExifInterface; 
import android.os.Build; 
import android.os.Bundle; 
import android.os.Environment; 
import android.support.annotation.NonNull; 
import android.support.v4.app.Fragment; 
import android.support.v4.app.ShareCompat; 
import android.support.v4.content.ContextCompat; 
import android.util.Log; 
import android.util.Size; 
import android.view.LayoutInflater; 
import android.view.View; 
import android.view.ViewGroup; 
import android.widget.Button; 
import android.widget.FrameLayout; 
import android.widget.Toast; 

import java.io.ByteArrayOutputStream; 
import java.io.FileNotFoundException; 
import java.io.FileOutputStream; 
import java.io.File; 
import java.io.IOException; 
import java.text.SimpleDateFormat; 
import java.util.Date; 
import java.util.List; 


/** 
* A simple {@link Fragment} subclass. 
*/ 
public class UploadPrescription extends Fragment { 
    private static final int REQUEST_EXTERNAL_STORAGE_PERMISSION = 2; 
    private Camera mCamera = null; 
    private CameraPreview mCameraPreview; 
    private final int REQUEST_CAMERA_PERMISSION = 1; 
    private FrameLayout preview; 
    private File pictureFile; 
    private FileOutputStream fos; 
    private byte[] byteArray; 

    public UploadPrescription() { 
     // Required empty public constructor 
    } 


    @Override 
    public View onCreateView(LayoutInflater inflater, ViewGroup container, 
          Bundle savedInstanceState) { 

     // Inflate the layout for this fragment 
     View root = inflater.inflate(R.layout.fragment_upload_prescription, container, false); 
     preview = (FrameLayout) root.findViewById(R.id.camera_preview); 

     setPreview(); 

     Button captureButton = (Button) root.findViewById(R.id.button_capture); 
     captureButton.setOnClickListener(new View.OnClickListener() { 
      @Override 
      public void onClick(View v) { 
       Log.i("ayusch", "inside on click"); 
       mCamera.takePicture(null, null, mPicture); 
      } 

     }); 
     return root; 
    } 

    private void setPreview() { 

     if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { 
      if (ContextCompat.checkSelfPermission(getContext(), Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED) { 
       setup(); 
      } else { 

       if (shouldShowRequestPermissionRationale(Manifest.permission.CAMERA)) { 

        Toast.makeText(getContext(), "Permission needed to use camera", Toast.LENGTH_LONG).show(); 
       } 

       requestPermissions(new String[]{Manifest.permission.CAMERA}, REQUEST_CAMERA_PERMISSION); 
      } 
     } else { 
      setup(); 
     } 

    } 


    @Override 
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { 

     if (requestCode == REQUEST_CAMERA_PERMISSION) { 
      if (grantResults[0] == PackageManager.PERMISSION_GRANTED) { 
       setup(); 
      } else { 
       Toast.makeText(getContext(), "Camera Permission not granted", Toast.LENGTH_LONG).show(); 
       return; 
      } 
     } else if (requestCode == REQUEST_EXTERNAL_STORAGE_PERMISSION) { 
      if (grantResults[0] == PackageManager.PERMISSION_GRANTED) { 
       proceedtowrite(); 
      } else { 
       Toast.makeText(getContext(), "Storage permission not granted", Toast.LENGTH_LONG).show(); 
       return; 
      } 
     } 
    } 

    public void setup() { 

     mCamera = getCameraInstance(); 
     Camera.Parameters parameters = mCamera.getParameters(); 
     parameters.set("jpeg-quality", 70); 
     parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE); 
     parameters.setPictureFormat(ImageFormat.JPEG); 
     List<Camera.Size> sizes = parameters.getSupportedPictureSizes(); 
     Camera.Size size = sizes.get(Integer.valueOf((sizes.size() - 1)/2)); //choose a medium resolution 
     parameters.setPictureSize(size.width, size.height); 
     mCamera.setParameters(parameters); 
     mCameraPreview = new CameraPreview(getContext(), mCamera); 
     preview.addView(mCameraPreview); 
    } 

    private Camera getCameraInstance() { 

     Camera camera = null; 
     try { 
      camera = Camera.open(0); 

     } catch (Exception e) { 
      e.printStackTrace(); 
      // cannot get camera or does not exist 
     } 
     return camera; 
    } 

    Camera.PictureCallback mPicture = new Camera.PictureCallback() { 
     @Override 
     public void onPictureTaken(byte[] data, Camera camera) { 
      Log.i("ayusch", "inside onPictureTaken"); 

      pictureFile = getOutputMediaFile(); 

      if (pictureFile == null) { 
       Log.i("ayusch", "Null Picture file, returning..."); 
       return; 
      } 
      try { 
       fos = new FileOutputStream(pictureFile); 

       //////////////////////////////////// 
       Bitmap realImage = BitmapFactory.decodeByteArray(data, 0, data.length); 

       ExifInterface exif = new ExifInterface(pictureFile.toString()); 

       Log.d("ayusch", exif.getAttribute(ExifInterface.TAG_ORIENTATION)); 
       if (exif.getAttribute(ExifInterface.TAG_ORIENTATION).equalsIgnoreCase("6")) { 
        realImage = rotate(realImage, 90); 
       } else if (exif.getAttribute(ExifInterface.TAG_ORIENTATION).equalsIgnoreCase("8")) { 
        realImage = rotate(realImage, 270); 
       } else if (exif.getAttribute(ExifInterface.TAG_ORIENTATION).equalsIgnoreCase("3")) { 
        realImage = rotate(realImage, 180); 
       } else if (exif.getAttribute(ExifInterface.TAG_ORIENTATION).equalsIgnoreCase("0")) { 
        realImage = rotate(realImage, 90); 
       } 
       ByteArrayOutputStream stream = new ByteArrayOutputStream(); 
       realImage.compress(Bitmap.CompressFormat.PNG, 100, stream); 
       byteArray = stream.toByteArray(); 

       //////////////////////////////////// 
       if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { 
        if (ContextCompat.checkSelfPermission(getContext(), Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) { 
         proceedtowrite(); 
        } else { 

         if (shouldShowRequestPermissionRationale(Manifest.permission.WRITE_EXTERNAL_STORAGE)) { 

          Toast.makeText(getContext(), "Permission needed to store and upload Prescription", Toast.LENGTH_LONG).show(); 
         } 

         requestPermissions(new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, REQUEST_EXTERNAL_STORAGE_PERMISSION); 
        } 
       } else { 
        proceedtowrite(); 
       } 
      } catch (FileNotFoundException e) { 

      } catch (IOException e) { 
      } 
     } 
    }; 

    public void proceedtowrite() { 
     try { 
      fos.write(byteArray); 
      fos.close(); 
     } catch (Exception e) { 
      e.printStackTrace(); 
     } 
     Intent i = new Intent(getActivity(), PrescriptionUpload.class); 
     i.putExtra("prescription", pictureFile); 
     startActivity(new Intent(i)); 
    } 

    public static Bitmap rotate(Bitmap bitmap, int degree) { 
     int w = bitmap.getWidth(); 
     int h = bitmap.getHeight(); 

     Matrix mtx = new Matrix(); 
     //  mtx.postRotate(degree); 
     mtx.setRotate(degree); 

     return Bitmap.createBitmap(bitmap, 0, 0, w, h, mtx, true); 
    } 

    private static File getOutputMediaFile() { 
     Log.i("ayusch", "Getting output media file"); 
     File mediaStorageDir = new File(
       Environment 
         .getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES), 
       "MyCameraApp"); 
     if (!mediaStorageDir.exists()) { 
      if (!mediaStorageDir.mkdirs()) { 
       Log.d("MyCameraApp", "failed to create directory"); 
       return null; 
      } 
     } 
     // Create a media file name 
     String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss") 
       .format(new Date()); 
     File mediaFile; 
     mediaFile = new File(mediaStorageDir.getPath() + File.separator 
       + "IMG_" + timeStamp + ".jpg"); 
     Log.i("ayusch", "Filename = " + mediaFile); 
     return mediaFile; 
    } 

    @Override 
    public void onPause() { 

     super.onPause(); 
     if (mCamera != null) { 
      mCamera.setPreviewCallback(null); 
      mCameraPreview.getHolder().removeCallback(mCameraPreview); 
      mCamera.stopPreview(); 
      mCamera.release(); 

     } 
    } 

    @Override 
    public void onResume() { 

     super.onResume(); 
     try { 
      mCameraPreview.getHolder().removeCallback(mCameraPreview); 
      setPreview(); 

     } catch (Exception e) { 
      e.printStackTrace(); 
     } 

    } 
} 

CameraPreview.java:

package com.example.ayusch.medomo; 

import android.content.Context; 
import android.hardware.Camera; 
import android.util.Log; 
import android.view.SurfaceHolder; 
import android.view.SurfaceView; 

import java.io.IOException; 

/** 
* Created by Ayusch on 09-Feb-17. 
*/ 
public class CameraPreview extends SurfaceView implements 
     SurfaceHolder.Callback { 
    private SurfaceHolder mSurfaceHolder; 
    private Camera mCamera; 


    // Constructor that obtains context and camera 
    @SuppressWarnings("deprecation") 
    public CameraPreview(Context context, Camera camera) { 
     super(context); 

     this.mCamera = camera; 
     this.mSurfaceHolder = this.getHolder(); 
     this.mSurfaceHolder.addCallback(this); 
     this.mSurfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS); 
    } 


    @Override 
    public void surfaceCreated(SurfaceHolder surfaceHolder) { 

     try { 

      mCamera.setPreviewDisplay(surfaceHolder); 
      mCamera.setDisplayOrientation(90); 
      mCamera.startPreview(); 
     } catch (IOException e) { 
      // left blank for now 
     } 
    } 

    @Override 
    public void surfaceDestroyed(SurfaceHolder surfaceHolder) { 
     Log.i("ayusch", "Inside surfaceDestroyed"); 
     mCamera.stopPreview(); 
     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) { 
      // intentionally left blank for a test 
     } 
    } 
} 

MainActivity.java:

package com.example.ayusch.medomo; 

import android.Manifest; 
import android.content.pm.PackageManager; 
import android.os.Build; 
import android.support.annotation.NonNull; 
import android.support.design.widget.TabLayout; 
import android.support.v4.content.ContextCompat; 
import android.support.v4.view.ViewPager; 
import android.support.v7.app.AppCompatActivity; 
import android.os.Bundle; 
import android.support.v7.widget.Toolbar; 
import android.util.Log; 
import android.view.View; 
import android.widget.Toast; 

public class MainActivity extends AppCompatActivity { 
    private static final int REQUEST_CAMERA_PERMISSION = 1; 
    Toolbar toolbar; 
    TabLayout tabLayout; 
    ViewPager viewPager; 
    ViewPagerAdapter viewPagerAdapter; 

    @Override 
    protected void onCreate(Bundle savedInstanceState) { 
     super.onCreate(savedInstanceState); 

     setContentView(R.layout.activity_main); 
     toolbar = (Toolbar) findViewById(R.id.toolbar); 
     setSupportActionBar(toolbar); 

     tabLayout = (TabLayout) findViewById(R.id.tabLayout); 
     viewPager = (ViewPager) findViewById(R.id.viewPager); 
     viewPagerAdapter = new ViewPagerAdapter(getSupportFragmentManager()); 
     viewPagerAdapter.addFragments(new UploadPrescription(), "Upload Prescription"); 
     viewPagerAdapter.addFragments(new OrderByName(), "Enter Medicine Name"); 
     viewPagerAdapter.addFragments(new Contact(), "Contact Us"); 
     viewPager.setAdapter(viewPagerAdapter); 
     tabLayout.setupWithViewPager(viewPager); 

    } 

} 

而且,在挂机的时间,如果我再次按下按钮,应用程序崩溃:

02-10 19:57:31.481 966-966/com.example.ayusch.medomo E/AndroidRuntime: FATAL EXCEPTION: main 
                     Process: com.example.ayusch.medomo, PID: 966 
                     java.lang.RuntimeException: Camera is being used after Camera.release() was called 
                      at android.hardware.Camera.native_takePicture(Native Method) 
                      at android.hardware.Camera.takePicture(Camera.java:1523) 
                      at android.hardware.Camera.takePicture(Camera.java:1468) 
                      at com.example.ayusch.medomo.UploadPrescription$1.onClick(UploadPrescription.java:73) 
                      at android.view.View.performClick(View.java:5714) 
                      at android.widget.TextView.performClick(TextView.java:10926) 
                      at android.view.View$PerformClick.run(View.java:22589) 
                      at android.os.Handler.handleCallback(Handler.java:739) 
                      at android.os.Handler.dispatchMessage(Handler.java:95) 
                      at android.os.Looper.loop(Looper.java:148) 
                      at android.app.ActivityThread.main(ActivityThread.java:7325) 
                      at java.lang.reflect.Method.invoke(Native Method) 
                      at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1230) 
                      at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1120) 

这是由于UploadPrescription.java

的onclick方法行mCamera.takePicture(null, null, mPicture);我一直在试图让我的头周围由于2-天没有任何成功。请帮忙 !!

在此先感谢

enter image description here

+0

目前尚不清楚** PrescriptionUpload **活动预计会做什么。无论如何,为了使你的UI响应你*必须*执行耗时的任务(和你的** onPictureTaken()**是一个耗时的任务)。 ** AsyncTask **在这里可能是一个简单的工具。强烈建议在主线程中执行** Camera.open()**:在某些设备上,它速度相当快,但在其他设备上,速度可能非常慢。 –

+0

关于捕获过程中的点击,您应该在点击时禁用CAPTURE按钮,并在处理结束后再次启用。 –

+0

那么你的意思是我需要将我的onClick方法任务移动到AsyncTask?我的意思是说在AsyncTask中调用takePicture方法。对不起,如果我听起来像一个noob(因为我是:) – Ayusch

回答

0

我做了一个快速和肮脏的解决方法类似的问题,在这里我有一个摄像头预览填充的屏幕,然后就拍摄的screengrab在需要时(响应漂亮很快) - 这实际上让我基本上以合理的帧速率拍摄一系列帧。

希望这足以让你现在继续。如果不是,请告诉我。

+0

对不起,我无法理解你的解决方案。你能否提一下关于程序或者某些东西的更多细节。 – Ayusch

+0

明天我会更详细地回复你,我会编辑我的答案,但简单地说,我的解决方案是用相机预览填满整个屏幕,然后当我想拍摄图像时,我立即捕获屏幕上的内容 - 方位记住其他任何东西(任何重叠或任何东西)也将作为图像的一部分被捕获。不理想,但为我工作。这听起来适合你吗? – HomerPlata

+0

好的,确定....我有你的解决方案,但我需要在我的活动中显示操作栏,因此无法用Camera Preview遮盖屏幕。你能提出其他建议吗? – Ayusch

0
 ByteArrayOutputStream stream = new ByteArrayOutputStream(); 
    realImage.compress(Bitmap.CompressFormat.PNG, 100, stream); 
    byteArray = stream.toByteArray(); 

对于PNG格式(没有硬件加速等),位图#compress的效率极低。尝试在保存之前调整位图的大小,将其压缩为JPEG或使用本地io转储原始位图。

相关问题