admin管理员组

文章数量:1302328

I hava an andriod app that uses camera2 api to save images in the gallery it works and saves the images well, but when i take three successive images without closing the app it takes much time to save the third image and i don't want the saving to take more than 5 seconds. public void takePicture() {

    try {
        CameraManager manager = (CameraManager) getActivity().getSystemService(Context.CAMERA_SERVICE);
        CameraCharacteristics characteristics = manager.getCameraCharacteristics(cameraDevice.getId());
        Size[] jpegSizes = Objects.requireNonNull(characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP)).getOutputSizes(ImageFormat.JPEG);
        int width = 640;
        int height = 480;
        if (jpegSizes != null && jpegSizes.length > 0) {
            width = jpegSizes[0].getWidth();
            height = jpegSizes[0].getHeight();
        }

        ImageReader reader = ImageReader.newInstance(width, height, ImageFormat.JPEG, 1);
        List<Surface> outputSurfaces = new ArrayList<>(2);
        outputSurfaces.add(reader.getSurface());
        outputSurfaces.add(new Surface(binding.textUreView.getSurfaceTexture()));

        final CaptureRequest.Builder captureBuilder = cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE);
        captureBuilder.addTarget(reader.getSurface());
        captureBuilder.set(CaptureRequest.CONTROL_MODE, CameraMetadata.CONTROL_MODE_AUTO);

        // Orientation
        int rotation = getActivity().getWindowManager().getDefaultDisplay().getRotation();
        captureBuilder.set(CaptureRequest.JPEG_ORIENTATION, ORIENTATIONS.get(rotation));

        // Set the zoom level to match the preview
        Rect sensorArraySize = characteristics.get(CameraCharacteristics.SENSOR_INFO_ACTIVE_ARRAY_SIZE);
        String zoomLevelString = getString(R.string.ZOOM_LEVEL);
        float zoomLevel = Float.parseFloat(zoomLevelString);

        assert sensorArraySize != null;
        int cropW = (int) (sensorArraySize.width() / zoomLevel);
        int cropH = (int) (sensorArraySize.height() / zoomLevel);
        int cropX = (sensorArraySize.width() - cropW) / 2;
        int cropY = (sensorArraySize.height() - cropH) / 2;
        Rect zoomRect = new Rect(cropX, cropY, cropX + cropW, cropY + cropH);
        captureBuilder.set(CaptureRequest.SCALER_CROP_REGION, zoomRect);

        ImageReader.OnImageAvailableListener readerListener = imageReaderListener -> {
            try (Image image = imageReaderListener.acquireLatestImage()) {
                if (image == null) {
                    showUserMessage(getString(R.string.message_image_capture_failed));
                    return;
                }

                // Extract image bytes
                ByteBuffer buffer = image.getPlanes()[0].getBuffer();
                byte[] bytes = new byte[buffer.capacity()];
                buffer.get(bytes);

                userLocation.getLastLocation(location -> {
                    // Retrieve location data
                    double latitude = location.getLatitude();
                    double longitude = location.getLongitude();

                    try {
                        // Save the image with location metadata (if supported by the method)
                        String savedFileName;

                        // Save image with location metadata using a newer API for Android R and above
                        savedFileName = saveImageWithLocation(bytes, latitude, longitude);


                        // Notify the main activity with the saved image path
                        cameraViewModel.setImageFinalName(savedFileName);

                        // Show a message to the user about the successful save operation
                        showUserMessage(getString(R.string.message_image_save_successful));

                    } catch (IOException e) {
                        // Handle exceptions during file saving
                        showUserMessage(getString(R.string.message_image_save_failed));
                        cameraViewModel.setCameraException(e);
                    }
                });

            } catch (Exception e) {
                // Handle any exceptions that occur during image acquisition or processing
                showUserMessage(getString(R.string.message_image_processing_error));
                cameraViewModel.setCameraException(e);
            }
        };

        reader.setOnImageAvailableListener(readerListener, mBackgroundHandler);

        final CameraCaptureSession.CaptureCallback captureListener = new CameraCaptureSession.CaptureCallback() {
            @Override
            public void onCaptureCompleted(@NonNull CameraCaptureSession session, @NonNull CaptureRequest request, @NonNull TotalCaptureResult result) {
                super.onCaptureCompleted(session, request, result);
                createCameraPreview();
            }
        };

        cameraDevice.createCaptureSession(outputSurfaces, new CameraCaptureSession.StateCallback() {
            @Override
            public void onConfigured(@NonNull CameraCaptureSession session) {
                try {
                    session.capture(captureBuilder.build(), captureListener, mBackgroundHandler);
                }catch (CameraAccessException e) {
                    // Show a dialog for CameraAccessException
                    showErrorDialog(getString(R.string.error_title_camera), getString(R.string.camera_access_exception, e.getMessage()));

                } catch (IllegalStateException e) {
                    // Show a dialog for IllegalStateException
                    showErrorDialog(getString(R.string.error_title_runtime), getString(R.string.camera_runtime_error_illegal_state_exception, e.getMessage()));
                }
            }

            @Override
            public void onConfigureFailed(@NonNull CameraCaptureSession session) {
                session.close();
            }
        }, mBackgroundHandler);
    } catch (CameraAccessException e) {
        throw new RuntimeException(e);
    }
}

/**
 * Show a message to the user using a Toast or Dialog.
 *
 * @param message The message to be displayed to the user.
 */
private void showUserMessage(String message) {
    if (getActivity() != null) {
        Toast.makeText(getActivity(), message, Toast.LENGTH_SHORT).show();
    }
}

/**
 * @returnthe image name
 */
@SuppressLint("SimpleDateFormat")
private String generateCapturedImageName() {

    Date date = new Date();
    DateFormat df = new SimpleDateFormat("yyyy-MM-dd-kk-mm-ss");
    String newPicFile = df.format(date);
    return userFullName +"_"+ newPicFile + ".jpg";
}

private String saveImageWithLocation(byte[] bytes, double latitude, double longitude) throws IOException {
    // Generate a single filename
    String fileName = generateCapturedImageName();

    // Ensure external directory exists
    File directory = ensureDirectoryExists();

    // Create the file
    File fileFinal = new File(directory, fileName);

    // Store the filename and directory in the ViewModel
    cameraViewModel.setImageFileName(fileName); // Store the filename
    cameraViewModel.setFileDirectory(directory); // Store the directory

    // Write the image data to the file
    try (OutputStream output = new FileOutputStream(fileFinal)) {
        output.write(bytes);
    }

    // Set EXIF metadata for GPS location
    ExifInterface exif = new ExifInterface(fileFinal.getAbsolutePath());
    userLocation.setExifGpsData(exif, latitude, longitude);
    exif.saveAttributes();

    // Notify MediaScanner
    MediaScannerConnection.scanFile(
            getActivity().getApplicationContext(),
            new String[]{fileFinal.getAbsolutePath()},
            null,
            (path, uri) -> Log.d(TAG, getString(R.string.Image_scanned_into_gallery) + path)
    );
    Log.d("FileDebug", "File created at: " + fileFinal.getAbsolutePath());

    return fileFinal.getAbsolutePath();
}

I tried to create different threads and change the image reader maxImages from 2 to 5 but it didn't work. I also tried to take to clean the image buffer but didn't find the solution.

I hava an andriod app that uses camera2 api to save images in the gallery it works and saves the images well, but when i take three successive images without closing the app it takes much time to save the third image and i don't want the saving to take more than 5 seconds. public void takePicture() {

    try {
        CameraManager manager = (CameraManager) getActivity().getSystemService(Context.CAMERA_SERVICE);
        CameraCharacteristics characteristics = manager.getCameraCharacteristics(cameraDevice.getId());
        Size[] jpegSizes = Objects.requireNonNull(characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP)).getOutputSizes(ImageFormat.JPEG);
        int width = 640;
        int height = 480;
        if (jpegSizes != null && jpegSizes.length > 0) {
            width = jpegSizes[0].getWidth();
            height = jpegSizes[0].getHeight();
        }

        ImageReader reader = ImageReader.newInstance(width, height, ImageFormat.JPEG, 1);
        List<Surface> outputSurfaces = new ArrayList<>(2);
        outputSurfaces.add(reader.getSurface());
        outputSurfaces.add(new Surface(binding.textUreView.getSurfaceTexture()));

        final CaptureRequest.Builder captureBuilder = cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE);
        captureBuilder.addTarget(reader.getSurface());
        captureBuilder.set(CaptureRequest.CONTROL_MODE, CameraMetadata.CONTROL_MODE_AUTO);

        // Orientation
        int rotation = getActivity().getWindowManager().getDefaultDisplay().getRotation();
        captureBuilder.set(CaptureRequest.JPEG_ORIENTATION, ORIENTATIONS.get(rotation));

        // Set the zoom level to match the preview
        Rect sensorArraySize = characteristics.get(CameraCharacteristics.SENSOR_INFO_ACTIVE_ARRAY_SIZE);
        String zoomLevelString = getString(R.string.ZOOM_LEVEL);
        float zoomLevel = Float.parseFloat(zoomLevelString);

        assert sensorArraySize != null;
        int cropW = (int) (sensorArraySize.width() / zoomLevel);
        int cropH = (int) (sensorArraySize.height() / zoomLevel);
        int cropX = (sensorArraySize.width() - cropW) / 2;
        int cropY = (sensorArraySize.height() - cropH) / 2;
        Rect zoomRect = new Rect(cropX, cropY, cropX + cropW, cropY + cropH);
        captureBuilder.set(CaptureRequest.SCALER_CROP_REGION, zoomRect);

        ImageReader.OnImageAvailableListener readerListener = imageReaderListener -> {
            try (Image image = imageReaderListener.acquireLatestImage()) {
                if (image == null) {
                    showUserMessage(getString(R.string.message_image_capture_failed));
                    return;
                }

                // Extract image bytes
                ByteBuffer buffer = image.getPlanes()[0].getBuffer();
                byte[] bytes = new byte[buffer.capacity()];
                buffer.get(bytes);

                userLocation.getLastLocation(location -> {
                    // Retrieve location data
                    double latitude = location.getLatitude();
                    double longitude = location.getLongitude();

                    try {
                        // Save the image with location metadata (if supported by the method)
                        String savedFileName;

                        // Save image with location metadata using a newer API for Android R and above
                        savedFileName = saveImageWithLocation(bytes, latitude, longitude);


                        // Notify the main activity with the saved image path
                        cameraViewModel.setImageFinalName(savedFileName);

                        // Show a message to the user about the successful save operation
                        showUserMessage(getString(R.string.message_image_save_successful));

                    } catch (IOException e) {
                        // Handle exceptions during file saving
                        showUserMessage(getString(R.string.message_image_save_failed));
                        cameraViewModel.setCameraException(e);
                    }
                });

            } catch (Exception e) {
                // Handle any exceptions that occur during image acquisition or processing
                showUserMessage(getString(R.string.message_image_processing_error));
                cameraViewModel.setCameraException(e);
            }
        };

        reader.setOnImageAvailableListener(readerListener, mBackgroundHandler);

        final CameraCaptureSession.CaptureCallback captureListener = new CameraCaptureSession.CaptureCallback() {
            @Override
            public void onCaptureCompleted(@NonNull CameraCaptureSession session, @NonNull CaptureRequest request, @NonNull TotalCaptureResult result) {
                super.onCaptureCompleted(session, request, result);
                createCameraPreview();
            }
        };

        cameraDevice.createCaptureSession(outputSurfaces, new CameraCaptureSession.StateCallback() {
            @Override
            public void onConfigured(@NonNull CameraCaptureSession session) {
                try {
                    session.capture(captureBuilder.build(), captureListener, mBackgroundHandler);
                }catch (CameraAccessException e) {
                    // Show a dialog for CameraAccessException
                    showErrorDialog(getString(R.string.error_title_camera), getString(R.string.camera_access_exception, e.getMessage()));

                } catch (IllegalStateException e) {
                    // Show a dialog for IllegalStateException
                    showErrorDialog(getString(R.string.error_title_runtime), getString(R.string.camera_runtime_error_illegal_state_exception, e.getMessage()));
                }
            }

            @Override
            public void onConfigureFailed(@NonNull CameraCaptureSession session) {
                session.close();
            }
        }, mBackgroundHandler);
    } catch (CameraAccessException e) {
        throw new RuntimeException(e);
    }
}

/**
 * Show a message to the user using a Toast or Dialog.
 *
 * @param message The message to be displayed to the user.
 */
private void showUserMessage(String message) {
    if (getActivity() != null) {
        Toast.makeText(getActivity(), message, Toast.LENGTH_SHORT).show();
    }
}

/**
 * @returnthe image name
 */
@SuppressLint("SimpleDateFormat")
private String generateCapturedImageName() {

    Date date = new Date();
    DateFormat df = new SimpleDateFormat("yyyy-MM-dd-kk-mm-ss");
    String newPicFile = df.format(date);
    return userFullName +"_"+ newPicFile + ".jpg";
}

private String saveImageWithLocation(byte[] bytes, double latitude, double longitude) throws IOException {
    // Generate a single filename
    String fileName = generateCapturedImageName();

    // Ensure external directory exists
    File directory = ensureDirectoryExists();

    // Create the file
    File fileFinal = new File(directory, fileName);

    // Store the filename and directory in the ViewModel
    cameraViewModel.setImageFileName(fileName); // Store the filename
    cameraViewModel.setFileDirectory(directory); // Store the directory

    // Write the image data to the file
    try (OutputStream output = new FileOutputStream(fileFinal)) {
        output.write(bytes);
    }

    // Set EXIF metadata for GPS location
    ExifInterface exif = new ExifInterface(fileFinal.getAbsolutePath());
    userLocation.setExifGpsData(exif, latitude, longitude);
    exif.saveAttributes();

    // Notify MediaScanner
    MediaScannerConnection.scanFile(
            getActivity().getApplicationContext(),
            new String[]{fileFinal.getAbsolutePath()},
            null,
            (path, uri) -> Log.d(TAG, getString(R.string.Image_scanned_into_gallery) + path)
    );
    Log.d("FileDebug", "File created at: " + fileFinal.getAbsolutePath());

    return fileFinal.getAbsolutePath();
}

I tried to create different threads and change the image reader maxImages from 2 to 5 but it didn't work. I also tried to take to clean the image buffer but didn't find the solution.

Share asked Feb 11 at 6:39 LuaiLuai 11 bronze badge
Add a comment  | 

1 Answer 1

Reset to default 0

The delay in saving the third image is due to location fetching latency. The first and second images used cached location data, while the third might be triggering a fresh location request, which can take longer.

Location API Delay: The fusedLocationClient.getCurrentLocation(...) method can take longer if a fresh GPS fix is required. Slow Network: If your phone is relying on network-based location rather than GPS, fetching new location data may be slow. Blocked UI/Main Thread: If location fetching or saving the image runs on the main thread, it can slow down. Power Saving Mode: Some Android devices limit location updates in low-power states. So Instead of calling getCurrentLocation() every time, I used getLastLocation() first, which is much faster because it returns the last known location without waiting for a new GPS fix.

本文标签: androidSaving images to gallery in andriod studio delays after the third successive imageStack Overflow