Android - Camera API需要太多时间来捕获和保存图像

时间:2017-02-10 14:41:00

标签: java android multithreading camera

我正在尝试将Camera API用于我的应用。当我点击拍照按钮时,UI会挂起约10秒钟,然后再做出响应。

我发现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

2 个答案:

答案 0 :(得分:0)

我为一个类似的问题做了一个快速而又肮脏的解决方法,在那里我用相机预览填充屏幕,然后在需要时捕获屏幕抓取(几乎立即响应) - 这实际上允许我基本上拍摄序列帧数合理的帧数。

希望你现在可以继续下去了。如果没有,请告诉我。

答案 1 :(得分:0)

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

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