开发者社区> 问答> 正文

在某些情况下,前台服务冻结

我有前台服务,该服务在延迟一段时间后无需预览即可拍照。它在以下情况下捕获图像(工作正常):1.应用程序当前在我的UI(前景)上。2.当我使用其他应用程序时(但仅当手机插入充电器时才捕捉图像),这很奇怪。我想使我的应用程序即使未插入充电器也能捕获图像(其接线太松,可以在其他应用程序上使用,但只能在充电时)。似乎是电池消耗问题。但是我无法弄清楚。应用程序并没有被操作系统杀死,因为当服务开启时,我会看到通知,并且当我将充电线插入设备时,即使在使用其他应用程序时,它也会开始拍照。它似乎服务冻结本身。

通常,我的意图是在用户意识不到的情况下捕获图像,但仅当设备未锁定时才捕获。经过长期的研究,我无法在互联网上找到解决方案。我非常希望有人能够帮助我。

我需要一个解决方案,使我的应用程序(捕获图像)在另一个应用程序上不断运行,但无需付费... 我使用的是Android 9。

会有一些工作代码,但是不是我想要的。.这是我的简化代码:

主要活动

public class MainActivity extends AppCompatActivity {

    private static final int REQUEST_WRITE_STORAGE_CAMERA_REQUEST_CODE = 1;
    private static final int ACTION_MANAGE_OVERLAY_PERMISSION_REQUEST_CODE = 5469;
    Intent serviceIntent;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Button startBtn = findViewById(R.id.buttonStart);
        Button stopBtn = findViewById(R.id.buttonStop);

        FileManager fileManager = FileManager.getInstance();
        fileManager.initFileManager(this.getResources());

        setUpPrivileges();
        startBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                serviceIntent = new Intent(MainActivity.this, CameraService.class);
                serviceIntent.setPackage("com.example.mytestinz");
                serviceIntent.setAction(CameraService.ACTION_START);
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                    startForegroundService(serviceIntent);
                } else {
                    startService(serviceIntent);
                }
            }
        });

        stopBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                stopService(new Intent(MainActivity.this, CameraService.class));
            }
        });
    }
    public void setUpPrivileges() {
        ActivityCompat.requestPermissions(MainActivity.this, new String[] {
                        Manifest.permission.READ_EXTERNAL_STORAGE,
                        Manifest.permission.WRITE_EXTERNAL_STORAGE,
                        Manifest.permission.CAMERA,
                        Manifest.permission.SYSTEM_ALERT_WINDOW
                },
                REQUEST_WRITE_STORAGE_CAMERA_REQUEST_CODE);

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            if (!Settings.canDrawOverlays(MainActivity.this)) {
                Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION,
                        Uri.parse("package:" + MainActivity.this.getPackageName()));
                MainActivity.this.startActivityForResult(intent, ACTION_MANAGE_OVERLAY_PERMISSION_REQUEST_CODE);
            }
        }
    }
}

相机服务



public class CameraService extends Service {
    Context context = this;

    private static final String TAG = "CameraService";

    static final String ACTION_START = "com.example.mytestinz.START";
    static final String ACTION_START_WITH_PREVIEW = "om.example.mytestinz.START_WITH_PREVIEW";

    private static final Integer ONGOING_NOTIFICATION_ID = 6660;
    private static final String CHANNEL_ID = "cam_service_channel_id";
    private static final String CHANNEL_NAME = "cam_service_channel_name";

    private CameraManager cameraManager;
    private String cameraId;
    private Handler backgroundHandler;
    private HandlerThread backgroundThread;
    private int cameraFacing;
    private CameraDevice cameraDevice;

    private ImageReader imageReader;

    int currentPictureID = 0;
    int pictureTimer = 0;
    ScheduledExecutorService executor =
            Executors.newSingleThreadScheduledExecutor();
    Handler handler;
    int pictureDelay = 5;

    private boolean cameraClosed;

    PowerManager.WakeLock wakeLock;

    private static final SparseIntArray ORIENTATIONS = new SparseIntArray();
    static {
        ORIENTATIONS.append(Surface.ROTATION_0, 90);
        ORIENTATIONS.append(Surface.ROTATION_90, 0);
        ORIENTATIONS.append(Surface.ROTATION_180, 270);
        ORIENTATIONS.append(Surface.ROTATION_270, 180);
    }

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        String action = intent.getAction();
        Log.i(TAG, "onStartCommand(): action = " + action);

            Log.d(TAG, "onStartCommand: *** action = " + action);
            switch (action) {
                case ACTION_START:
                    start();
                    break;
                case ACTION_START_WITH_PREVIEW:
                    break;
            }
        return START_STICKY;
    }

    @Override
    public void onCreate() {
        super.onCreate();
        cameraFacing = CameraCharacteristics.LENS_FACING_FRONT;
        cameraManager = (CameraManager) getSystemService(Context.CAMERA_SERVICE);
        handler = new Handler();
        openBackgroundThread();

        startWithForeground();
    }


    @Override
    public void onDestroy() {
        super.onDestroy();
        stopService();
    }

    public void stopService(){
        stopSelf();
        closeBackgroundThread();

        if(executor != null && handler != null) {
            handler.removeCallbacksAndMessages(null);
            executor.shutdownNow();
        }

        // (START)Edited to work without charging
        if (wakeLock.isHeld()) {
            Log.d(TAG, "stopMyService: Release LOCK");
            wakeLock.release();
        }
        // (END)Edited to work without charging 

        Log.d(TAG, "stopService: Stopping service");
    }

    public void start(){
        Log.d(TAG, "start: Run Service with NO PREVIEW.");

        // (START)Edited to work without charging
        PowerManager powerManager = (PowerManager) getSystemService(POWER_SERVICE);
        wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
                "MyApp::MyWakelockTag");
        wakeLock.acquire();
        // (END)Edited to work without charging 

        setUpCamera();
        runApp();
    }

    private void startWithForeground(){
        Intent notificationIntent = new Intent(CameraService.this, context.getClass());
        PendingIntent pendingIntent = PendingIntent.getActivity(context,0, notificationIntent, 0);

        //ONLY FOR API 26 and HIGHER!
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            NotificationChannel channel = new NotificationChannel(CHANNEL_ID, CHANNEL_NAME, NotificationManager.IMPORTANCE_NONE);
            channel.setLightColor(Color.BLUE);
            channel.setLockscreenVisibility(Notification.VISIBILITY_PRIVATE);

            ((NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE)).createNotificationChannel(channel);
        }

        Notification notification = new NotificationCompat.Builder(this, CHANNEL_ID)
                .setContentTitle(getText(R.string.app_name))
                .setContentText(getText(R.string.app_name))
                .setSmallIcon(R.drawable.ic_launcher_foreground)
                .setContentIntent(pendingIntent)
                .setTicker(getText(R.string.app_name))
                .setPriority(NotificationCompat.PRIORITY_HIGH)
                .build();

        startForeground(ONGOING_NOTIFICATION_ID, notification);
    }

    private void setUpCamera() {
        try {
            for (String cameraId : cameraManager.getCameraIdList()) {
                CameraCharacteristics cameraCharacteristics = cameraManager.getCameraCharacteristics(cameraId);
                if (cameraCharacteristics.get(CameraCharacteristics.LENS_FACING) == cameraFacing) {
                    this.cameraId = cameraId;
                }
            }
        } catch (CameraAccessException e) {
            e.printStackTrace();
        }
    }

    @SuppressLint("MissingPermission")
    private void openCamera() {
        try {
            if (ActivityCompat.checkSelfPermission(getApplicationContext(), android.Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED) {
                cameraManager.openCamera(cameraId, stateCallback, null);
            }
        } catch (CameraAccessException e) {
            Log.i(TAG,":MissingPermission - CAMERA");
            e.printStackTrace();
        }
    }

    private 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);
            Log.i(TAG, "done taking picture from camera " + cameraDevice.getId());
            closeCamera();
        }
    };


    public void takePhoto() throws CameraAccessException {
        if (null == cameraDevice) {
            Log.e(TAG, "cameraDevice is null");
            return;
        }
        final CameraCharacteristics characteristics = cameraManager.getCameraCharacteristics(cameraDevice.getId());
        Size[] jpegSizes = null;
        StreamConfigurationMap streamConfigurationMap = characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
        if (streamConfigurationMap != null) {
            jpegSizes = streamConfigurationMap.getOutputSizes(ImageFormat.JPEG);
        }
        final boolean jpegSizesNotEmpty = jpegSizes != null && 0 < jpegSizes.length;
        int width = jpegSizesNotEmpty ? jpegSizes[0].getWidth() : 640;
        int height = jpegSizesNotEmpty ? jpegSizes[0].getHeight() : 480;
        final ImageReader reader = ImageReader.newInstance(640, 480, ImageFormat.JPEG, 2);
        final CaptureRequest.Builder captureBuilder = cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE);
        captureBuilder.addTarget(reader.getSurface());
        captureBuilder.set(CaptureRequest.CONTROL_MODE, CameraMetadata.CONTROL_MODE_AUTO);
        captureBuilder.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);
        captureBuilder.set(CaptureRequest.CONTROL_AE_MODE, CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH);

        captureBuilder.set(CaptureRequest.JPEG_ORIENTATION, ORIENTATIONS.get(2)/*getOrientation()*/);
        reader.setOnImageAvailableListener(onImageAvailableListener, null);
        cameraDevice.createCaptureSession(Arrays.asList(reader.getSurface()), new CameraCaptureSession.StateCallback() {
            @Override
            public void onConfigured(@NonNull CameraCaptureSession session) {
                try {
                    session.capture(captureBuilder.build(), captureListener, null);
                } catch (final CameraAccessException e) {
                    Log.e(TAG, " exception occurred while accessing " + cameraDevice.getId(), e);
                }
            }

            @Override
            public void onConfigureFailed(@NonNull CameraCaptureSession session) {
                Log.d(TAG, "onConfigureFailed: Faild to create cameraCaptureSession");
            }
        },null);
    }

    ImageReader.OnImageAvailableListener onImageAvailableListener = new ImageReader.OnImageAvailableListener() {
        @Override
        public void onImageAvailable(ImageReader reader) {
            Log.d(TAG, "onImageAvailableListener: Image Available to save.");

            final Image image = reader.acquireNextImage();
            new ImageSaver(image);
        }
    };

    private final CameraDevice.StateCallback stateCallback = new CameraDevice.StateCallback() {
        @Override
        public void onOpened(@NonNull CameraDevice camera) {
            cameraClosed = false;
            cameraDevice = camera;
            Log.d(TAG, "onOpened: Camera " + camera.getId() + " is opened.");

            new Handler().post(new Runnable() {
                @Override
                public void run() {
                    try {
                        takePhoto();
                    } catch (CameraAccessException e) {
                        e.printStackTrace();
                    }
                }
            });
        }
        @Override
        public void onDisconnected(@NonNull CameraDevice camera) {
            Log.d(TAG, "onDisconnected: Camera " + camera.getId() + " is Disconnected");
            if (cameraDevice != null && !cameraClosed) {
                cameraClosed = true;
                cameraDevice.close();
            }
        }
        @Override
        public void onError(CameraDevice camera, int error) {
            Log.e(TAG, "onError: Camera "+ camera.getId() +" has error, code: " + error);
            if (cameraDevice != null && !cameraClosed)
                cameraDevice.close();
            CameraService.this.cameraDevice = null;
        }

        @Override
        public void onClosed(@NonNull CameraDevice camera) {
            cameraClosed = true;
        }
    };

    private void openBackgroundThread() {
        backgroundThread = new HandlerThread("camera_background_thread");
        backgroundThread.start();
        backgroundHandler = new Handler(backgroundThread.getLooper());
    }

    private void closeBackgroundThread() {
        if (backgroundHandler != null) {
            backgroundThread.quitSafely();
            backgroundThread = null;
            backgroundHandler = null;
        }
    }

    public void runApp(){
            currentPictureID = 0;
            executor = Executors.newSingleThreadScheduledExecutor();
            executor.scheduleAtFixedRate(periodicTask, 0, pictureDelay + 3, TimeUnit.SECONDS);
    }

    public void decrementTimer(){
        boolean takePicture = (pictureTimer == 1);
        pictureTimer--;
        if (takePicture) {
            savePictureNow();
        } else if (pictureTimer > 0) {

            System.out.println("Count Down: "+pictureTimer);
            handler.postDelayed(makeDecrementTimerFunction(), 1000);
        }
    }

    Runnable periodicTask = new Runnable() {
        public void run() {
            savePicture();
        }
    };

    public void savePicture(){
        if (this.pictureDelay == 0) {
            savePictureNow();
        } else {
            savePictureAfterDelay(this.pictureDelay);
        }
    }

    void savePictureAfterDelay(int delay) {

        pictureTimer = delay;
        handler.postDelayed(makeDecrementTimerFunction(), 1000);
    }

    Runnable makeDecrementTimerFunction() {
        return new Runnable() {
            public void run() {
                decrementTimer();
            }
        };
    }

    public void savePictureNow(){
        openCamera();
    }

    private void closeCamera() {
        Log.d(TAG, "closing camera " + cameraDevice.getId());
        if (null != cameraDevice && !cameraClosed) {
            Log.d(TAG, "closing camera inside " + cameraDevice.getId());
            cameraDevice.close();
            cameraDevice = null;
        }
        if (null != imageReader) {
            imageReader.close();
            imageReader = null;
        }
    }
}

文件管理器

jpublic class FileManager {
    private static final String TAG = "FileManager";
    private static FileManager fileManager = null;
    private File galleryFolder;
    private File gallerySourceFolder;
    private Resources resources;
    private Boolean init;

    private File currentTakenPhotoFile;

    private FileManager() {
        init = false;
    }

    public static FileManager getInstance(){
        if(fileManager == null) {
            synchronized (FileManager.class) {
                fileManager = new FileManager();
            }
        }
        return fileManager;
    }

    public void initFileManager(Resources resources){
        if (!init){
            this.resources = resources;
            init = true;
            createImageGallery();
        }
    }

    public File createImageFile() throws IOException {
        String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.getDefault()).format(new Date());
        String imageFileName = "image_" + timeStamp;
        String sufix = ".jpg";
        Log.i(TAG, "createdImageFile:" + galleryFolder + "/" + imageFileName + sufix);
        return File.createTempFile(imageFileName, sufix, galleryFolder);
    }

    public void createImageGallery() {
        File storageDirectory = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES);
        if(resources == null){
            Log.i(TAG,"Resources is null.");
            return;
        }

        galleryFolder = new File(storageDirectory, resources.getString(R.string.app_name)+"/targetFolder");
        if (!galleryFolder.exists()) {
            boolean wasCreated = galleryFolder.mkdirs();
            if (!wasCreated) {
                Log.i(TAG, "Failed to create target gallery directory");
            }
        }

        gallerySourceFolder = new File(storageDirectory, resources.getString(R.string.app_name)+"/sourceFolder");
        if (!gallerySourceFolder.exists()) {
            boolean wasCreated = gallerySourceFolder.mkdirs();
            if (!wasCreated) {
                Log.i(TAG, "Failed to create source gallery directory");
            }
        }
    }

    public File getCurrentTakenPhotoFile() {
        return currentTakenPhotoFile;
    }

    public void setCurrentTakenPhotoFile(File currentTakenPhotoFile) {
        this.currentTakenPhotoFile = currentTakenPhotoFile;
    }
}

图像保护程序

public class ImageSaver{
    public static final String TAG = "ImageSaver";
    private Image image;

    public ImageSaver(Image image) {
        this.image = image;
        saveImage();
    }

    private void saveImage() {
        Long tsLong = System.currentTimeMillis()/1000;
        String timeStampStart = tsLong.toString();

        Log.d(TAG, "saveImage: Start at: "+ timeStampStart);
        FileOutputStream outputPhoto = null;
        if (image == null)
            return;
        try {
            ByteBuffer buffer = image.getPlanes()[0].getBuffer();
            byte[] ImageBytes = new byte[buffer.capacity()];
            buffer.get(ImageBytes);

            final Bitmap bmp = BitmapFactory.decodeByteArray(ImageBytes, 0, ImageBytes.length);

            FileManager fileManager = FileManager.getInstance();
            fileManager.setCurrentTakenPhotoFile(fileManager.createImageFile());

            outputPhoto = new FileOutputStream(fileManager.getCurrentTakenPhotoFile());

            bmp.compress(Bitmap.CompressFormat.PNG, 100, outputPhoto);

            tsLong = System.currentTimeMillis()/1000;
            String timeStampEnd = tsLong.toString();
            Log.d(TAG, "saveImage: End at: "+ timeStampEnd);
            Log.d(TAG, "saveImage: Has taken: "+ (Long.parseLong(timeStampEnd )- Long.parseLong(timeStampStart)));

        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (image != null) {
                image.close();
            }
            if (outputPhoto != null) {
                try {
                    outputPhoto.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

表现


 <uses-permission
        android:name="android.permission.CAMERA"
        android:required="true" />
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.WRITE_INTERNAL_STORAGE" />
    <!--<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />-->
    <!-- Permission require to retrieve View, over other apps. -->
    <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
    <!-- Since android Pie (API level 28), need to add the FOREGROUND_SERVICE permission.-->
    <uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
<!-- For Wake Lock -->
    <uses-permission android:name="android.permission.WAKE_LOCK" tools:node="replace" />

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

        <service android:name=".CameraService" >
            <intent-filter>
                <action android:name="com.example.mytestinz.START"/>
            </intent-filter>

 </service>

    </application>

展开
收起
Puppet 2019-12-04 09:44:30 428 0
0 条回答
写回答
取消 提交回答
问答分类:
问答地址:
问答排行榜
最热
最新

相关电子书

更多
低代码开发师(初级)实战教程 立即下载
冬季实战营第三期:MySQL数据库进阶实战 立即下载
阿里巴巴DevOps 最佳实践手册 立即下载