Android后台录音服务

Android后台录音服务,android,service,Android,Service,我需要在android上实现一个录音服务。我花了很多时间阅读有关服务的书籍,在安卓系统的背景下工作,但我还没有弄明白。这就是我到目前为止所做的: 我首先实现了一个服务,该服务在onCreate上创建并维护一个AudioRecord实例 我对应用程序进行了子类化,并从应用程序的onCreate启动了服务 我在onResume上从MainActivity绑定到该服务,并在onPause上解除绑定 该服务的onBind的工作原理类似于androidsdk示例中的LocalService示例,这意味着我

我需要在android上实现一个录音服务。我花了很多时间阅读有关服务的书籍,在安卓系统的背景下工作,但我还没有弄明白。这就是我到目前为止所做的:

  • 我首先实现了一个服务,该服务在onCreate上创建并维护一个AudioRecord实例
  • 我对应用程序进行了子类化,并从应用程序的onCreate启动了服务
  • 我在onResume上从MainActivity绑定到该服务,并在onPause上解除绑定
  • 该服务的onBind的工作原理类似于androidsdk示例中的LocalService示例,这意味着我返回保存该服务实例的binder,并为其提供一个getter
  • 服务API允许其客户端启动和暂停它维护的音频记录
  • 现在我解释的是,在应用程序启动时,服务的onCreate只会被调用一次,但令人惊讶的是,有时当我绑定到该服务时,服务会重新启动(调用onCreate和onStart命令)

    这种行为对我来说非常糟糕,因为当用户从MainActivity启动记录时 (使用服务实例API),然后按home,然后再次返回到我的应用程序,再次调用服务的onCreate,并重新创建录音机


    我缺少什么?

    您根本不需要调用
    startService
    <代码>绑定服务将在必要时创建
    服务
    。这两种方法适用于两种不同的用例

    对评论的答复:

    您的帖子没有提供需要调用
    startService
    的用例。您询问为什么调用了两次
    onCreate
    。它被调用两次,因为它被创建了两次。可能您没有从
    onStartCommand
    返回
    START\u STICKY
    ,或者您正在代码的某个地方停止它。没有密码,所以这里没人可能知道。但是如果调用了两次
    onCreate
    ,则需要创建两次。根据您的描述,这两个调用很可能对应于对
    startService
    bindService
    的调用。根据所提供的信息,我的答案与预期的一样正确,但感谢否决票。

    正确答案是

    关于Dave的回答,我确实需要调用startService,因为当应用程序在后台时,录制应该能够继续,但如果我在某些活动的onCreate上使用bindService,那么我可能必须在其onDestroy上解除连接绑定,这将使服务停止运行(如果此活动是唯一与之绑定的活动


    我总是可以从应用程序onCreate绑定到服务,但我什么时候才能解除绑定到它?即使我只在用户按下主活动时解除绑定,但这可能会导致泄漏。

    如果您想创建类似的录制应用程序

    然后您需要使用IntentService而不是Service,因为IntentService在后台线程中运行

    public class RecorderService extends Service implements SurfaceHolder.Callback {
    
        private WindowManager windowManager;
        private SurfaceView surfaceView;
        private Camera camera = null;
        private MediaRecorder mediaRecorder = null;
        private String fileName = "";
        private StopServiceBroadcast stopServiceBroadcast;
        private String endRecordingDate;
        private String endRecordingTime;
        private SettingsModel settingsModel;
        private Handler enableViewsHandler;
        private Runnable runnable;
    
    
        @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN)
        @Override
        public void onCreate() {
    
            settingsModel = SettingsModel.findById(SettingsModel.class, 1L);
            setupSurfaceViewForRecording();
        }
    
        private void setupSurfaceViewForRecording() {
    
            int LAYOUT_FLAG;
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                LAYOUT_FLAG = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
            } else {
                LAYOUT_FLAG = WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY;
            }
    
            // Create new SurfaceView, set its size to 1x1, move it to the top left corner and set this service as a callback
            windowManager = (WindowManager) this.getSystemService(Context.WINDOW_SERVICE);
            surfaceView = new SurfaceView(this);
            WindowManager.LayoutParams layoutParams = new WindowManager.LayoutParams(
                    1, 1,
    //                WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY,
    //                WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH,
                    LAYOUT_FLAG,
                    WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
                    PixelFormat.TRANSLUCENT
            );
            layoutParams.gravity = Gravity.LEFT | Gravity.TOP;
            windowManager.addView(surfaceView, layoutParams);
            surfaceView.getHolder().addCallback(this);
        }
    
        private void registerBroadcastToStopRec() {
            stopServiceBroadcast = new StopServiceBroadcast();
            IntentFilter filter = new IntentFilter("stop_recording_action");
            registerReceiver(stopServiceBroadcast, filter);
        }
    
        // Method called right after Surface created (initializing and starting MediaRecorder)
        @Override
        public void surfaceCreated(SurfaceHolder surfaceHolder) {
    
            mediaRecorder = new MediaRecorder();
            setupCameraSide();
    
    //        if (settingsModel.getCameraSide().equalsIgnoreCase("front") && !getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA_FRONT)) {
    //            Toast.makeText(this, R.string.front_camera_is_not_available_, Toast.LENGTH_SHORT).show();
    //            if (SharedPreferenceUtility.getInstance().getBooleanPreference(PreferenceKeys.IS_APP_RUNNING))
    //                fabStartStopRec.setEnabled(true);
    //            this.stopSelf();
    //            return;
    //        } else if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA)) {
    //            Toast.makeText(this, R.string.back_camera_is_not_available_, Toast.LENGTH_SHORT).show();
    //            if (SharedPreferenceUtility.getInstance().getBooleanPreference(PreferenceKeys.IS_APP_RUNNING))
    //                fabStartStopRec.setEnabled(true);
    //            this.stopSelf();
    //            return;
    //        }
    
            //______ check if camera is not available ___________
            if (camera == null) {
                if (settingsModel.getCameraSide().equals("front"))
                    Toast.makeText(this, R.string.front_camera_is_not_available_, Toast.LENGTH_SHORT).show();
                else
                    Toast.makeText(this, R.string.back_camera_is_not_available_, Toast.LENGTH_SHORT).show();
    
                if (SharedPreferenceUtility.getPreference(this, PreferenceKeys.IS_APP_RUNNING, true))
                    fabStartStopRec.setEnabled(true);
                this.stopSelf();
                return;
            }
    
            notificationToShowRecordingStarted();
    
            setupCameraParametersForRecording();
            setupCameraOrientation();
            setupAutoStopRecordingFactors();
            setupRingingAndVibrationOnStartRec();
    
    
            try {
                //mCamera.setDisplayOrientation(90);
                camera.setPreviewDisplay(surfaceHolder);
            } catch (IOException e) {
                e.printStackTrace();
            }
    
            camera.startPreview();
            camera.unlock();
    
            mediaRecorder.setPreviewDisplay(surfaceHolder.getSurface());
            mediaRecorder.setCamera(camera);
    
            mediaRecorder.setAudioSource(MediaRecorder.AudioSource.DEFAULT);
            mediaRecorder.setVideoSource(MediaRecorder.VideoSource.DEFAULT);
    
            setupVideoQuality();
            setupVideoStorage();
            startRecording();
        }
    
        private void setupRingingAndVibrationOnStartRec() {
            if (settingsModel.isRingsOnRecStarts()) {
                Uri ringTuneUri = Uri.parse("android.resource://" + getPackageName() + "/" + R.raw.rec_start_sound);
                Ringtone r = RingtoneManager.getRingtone(getApplicationContext(), ringTuneUri);
                r.play();
            } else {
                Camera.CameraInfo info = new Camera.CameraInfo();
                if (info.canDisableShutterSound) {
                    camera.enableShutterSound(false);
                }
    
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
                    // to mute shutter sound of camera
                    final AudioManager mgr = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
                    mgr.setStreamMute(AudioManager.STREAM_SYSTEM, true);
                    mgr.setStreamSolo(AudioManager.STREAM_SYSTEM, true);
                    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                        if (!mgr.isStreamMute(AudioManager.STREAM_SYSTEM))
                            mgr.setStreamMute(AudioManager.STREAM_MUSIC, true);
                    }
    
                } else {
    
                }
            }
    
            if (settingsModel.isVibrateOnRecStarts()) {
                Vibrator v = (Vibrator) getSystemService(Context.VIBRATOR_SERVICE);
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                    v.vibrate(VibrationEffect.createOneShot(500, VibrationEffect.DEFAULT_AMPLITUDE));
                } else
                    v.vibrate(500);
            }
        }
    
        private void setupVideoQuality() {
    
            if (settingsModel.getCameraSide().equals("front")) {
                if (android.os.Build.VERSION.SDK_INT <= Build.VERSION_CODES.KITKAT)
                    mediaRecorder.setProfile(CamcorderProfile.get(CamcorderProfile.QUALITY_480P));
                else
                    mediaRecorder.setProfile(CamcorderProfile.get(CamcorderProfile.QUALITY_480P/*settingsModel.getVideoQuality()*/));/*hot fix 1*/
            } else
                mediaRecorder.setProfile(CamcorderProfile.get(settingsModel.getVideoQuality()));
    
    //        if (settingsModel.getCameraSide().equals("front") &&
    //                (settingsModel.getVideoQuality() == CamcorderProfile.QUALITY_HIGH || settingsModel.getVideoQuality() == CamcorderProfile.QUALITY_1080P)) {
    //            mediaRecorder.setProfile(CamcorderProfile.get(CamcorderProfile.QUALITY_720P));
    //        } else
    //            mediaRecorder.setProfile(CamcorderProfile.get(settingsModel.getVideoQuality()));
        }
    
        private int getVideoQualityWRTCameraCapacity(int videoQuality) {
            switch (videoQuality) {
                case CamcorderProfile.QUALITY_480P:
                    return CamcorderProfile.QUALITY_480P;
    
                case CamcorderProfile.QUALITY_CIF:
                    return CamcorderProfile.QUALITY_CIF;
    
                case CamcorderProfile.QUALITY_QCIF:
                    return CamcorderProfile.QUALITY_QCIF;
    
                case CamcorderProfile.QUALITY_QVGA:
                    return CamcorderProfile.QUALITY_QVGA;
    
                case CamcorderProfile.QUALITY_LOW:
                    return CamcorderProfile.QUALITY_LOW;
    
                case CamcorderProfile.QUALITY_TIME_LAPSE_480P:
                    return CamcorderProfile.QUALITY_TIME_LAPSE_480P;
    
                case CamcorderProfile.QUALITY_TIME_LAPSE_QCIF:
                    return CamcorderProfile.QUALITY_TIME_LAPSE_QCIF;
    
                case CamcorderProfile.QUALITY_TIME_LAPSE_QVGA:
                    return CamcorderProfile.QUALITY_TIME_LAPSE_QVGA;
    
                case CamcorderProfile.QUALITY_TIME_LAPSE_CIF:
                    return CamcorderProfile.QUALITY_TIME_LAPSE_CIF;
    
                case CamcorderProfile.QUALITY_TIME_LAPSE_LOW:
                    return CamcorderProfile.QUALITY_TIME_LAPSE_LOW;
    
                case CamcorderProfile.QUALITY_HIGH_SPEED_480P:
                    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
                        return CamcorderProfile.QUALITY_HIGH_SPEED_480P;
                    }
    
                case CamcorderProfile.QUALITY_HIGH_SPEED_LOW:
                    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
                        return CamcorderProfile.QUALITY_HIGH_SPEED_LOW;
                    }
            }
    
            return CamcorderProfile.QUALITY_HIGH;
        }
    
        private void setupAutoStopRecordingFactors() {
    
            if (settingsModel.isRecordingScheduled() && isValidTime(settingsModel.getWakeUpTimeForTimer())) {
                mediaRecorder.setMaxDuration(settingsModel.getScheduleRecordingTimeDuration() * 60 * 1000);
    
                if (settingsModel.isMaxFileSize())
                    mediaRecorder.setMaxFileSize(settingsModel.getMaxFileSizeLimit() * 1024 * 1024);
            } else {
                if (settingsModel.isLimitRecordingTime())
                    mediaRecorder.setMaxDuration(settingsModel.getRecordingTimeLimit() * 60 * 1000);
    
                if (settingsModel.isMaxFileSize())
                    mediaRecorder.setMaxFileSize(settingsModel.getMaxFileSizeLimit() * 1024 * 1024);
            }
    
            mediaRecorder.setOnInfoListener(new MediaRecorder.OnInfoListener() {
                @Override
                public void onInfo(MediaRecorder mediaRecorder, int i, int i1) {
                    if (i == MediaRecorder.MEDIA_RECORDER_INFO_MAX_DURATION_REACHED) {
                        stopSelf();
                    } else if (i == MediaRecorder.MEDIA_RECORDER_INFO_MAX_FILESIZE_REACHED) {
                        stopSelf();
                    }
                }
            });
        }
    
        public boolean isValidTime(long wakeUpTimeInMillis) {
            Calendar smsTime = Calendar.getInstance();
            smsTime.setTimeInMillis(wakeUpTimeInMillis);
    
            Calendar now = Calendar.getInstance();
    
            if (now.get(Calendar.DATE) == smsTime.get(Calendar.DATE)) {
                if (now.get(Calendar.HOUR_OF_DAY) == smsTime.get(Calendar.HOUR_OF_DAY)) {
                    if (now.get(Calendar.MINUTE) == smsTime.get(Calendar.MINUTE))
                        return true;
                }
            }
    
            return false;
        }
    
        private void setupCameraOrientation() {
    
            if (settingsModel.getCameraSide().equals("front")) {
                if (settingsModel.getCameraOrientation().equals("auto") || settingsModel.getCameraOrientation().equals("portrait")) {
                    mediaRecorder.setOrientationHint(270);
                } else
                    mediaRecorder.setOrientationHint(0);
            } else {
                if (settingsModel.getCameraOrientation().equals("auto") || settingsModel.getCameraOrientation().equals("portrait")) {
                    mediaRecorder.setOrientationHint(90);
                } else
                    mediaRecorder.setOrientationHint(180);
            }
        }
    
        private void setupCameraParametersForRecording() {
            Camera.Parameters parameters = camera.getParameters();
            List<Camera.Size> ss = parameters.getSupportedVideoSizes();
            Log.d("------ 0", ss.get(0).width + "   " + ss.get(0).height);
            Log.d("------ last", ss.get(ss.size() - 1).width + "   " + ss.get(ss.size() - 1).height);
            List<Camera.Size> sizes = parameters.getSupportedVideoSizes();
            for (Camera.Size s : sizes)
                Log.d("----", String.valueOf(s.width) + " * " + String.valueOf(s.height));
    
            if (settingsModel.isFlashlightWhileRec() && !settingsModel.getCameraSide().equalsIgnoreCase("front"))
                parameters.setFlashMode(Camera.Parameters.FLASH_MODE_TORCH);
    
            //_______ setup zoom factor ___________
            if (settingsModel.isZoomEnabled()) {
                if (parameters.isZoomSupported()) {
                    if (settingsModel.getZoomInValue() >= 0 && settingsModel.getZoomInValue() <= parameters.getMaxZoom()) {
                        parameters.setZoom(settingsModel.getZoomInValue());
                    }
                }
            }
    
            camera.setParameters(parameters);
        }
    
        private void setupCameraSide() {
            if (settingsModel.getCameraSide().equals("front"))
                camera = openFrontFacingCamera();
            else
                camera = Camera.open();
        }
    
        private void startRecording() {
            try {
                mediaRecorder.prepare();
                mediaRecorder.start();
                saveRecStartingTime();
                if (SharedPreferenceUtility.getPreference(this, PreferenceKeys.IS_APP_RUNNING, false))
                    updateViewsOnStartRec();
    
            } catch (Exception e) {
                Toast.makeText(this, e.getMessage(), Toast.LENGTH_SHORT).show();
            }
        }
    
        private void updateViewsOnStartRec() {
            sflStartStopRecProgress.stopShimmerAnimation();
    
            chronometer.setBase(SystemClock.elapsedRealtime());
            chronometer.setText("00:00:00");
            chronometer.start();
    
            fabStartStopRec.setIcon(getResources().getDrawable(R.drawable.stop_icon), getResources().getDrawable(R.drawable.start_icon));
            fabStartStopRec.showProgress(true);
            fabStartStopRec.setIndeterminate(true);
    
            runnable = new Runnable() {
                @Override
                public void run() {
                    fabStartStopRec.setEnabled(true);
                }
            };
            enableViewsHandler = new Handler();
            enableViewsHandler.postDelayed(runnable, 1000);
        }
    
        private void saveRecStartingTime() {
            SharedPreferenceUtility.setPreference(this, PreferenceKeys.IS_RECORDING, true);
            SharedPreferenceUtility.setPreference(this, PreferenceKeys.REC_STARTING_TIME, System.currentTimeMillis());
        }
    
        private void setupVideoStorage() {
    
            //___________ creating new directory __________
            File f = new File(settingsModel.getStoragePath(), settingsModel.getStorageFolderName());
            if (!f.exists()) {
                f.mkdirs();
            }
    
            if (isExternalStorageWritable()) {
    
                try {
                    fileName = settingsModel.getStoragePath() + "/" + settingsModel.getStorageFolderName() + "/" +
                            DateFormat.format(settingsModel.getFileNameFormat(), new Date().getTime()) + ".mp4";
                    mediaRecorder.setOutputFile(fileName);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            } else {
                Toast.makeText(this, R.string.you_dont_have_enough_space_, Toast.LENGTH_SHORT).show();
            }
        }
    
        public boolean isExternalStorageWritable() {
            String state = Environment.getExternalStorageState();
            return Environment.MEDIA_MOUNTED.equals(state);
        }
    
        // Stop recording and remove SurfaceView
        @Override
        public void onDestroy() {
    
            updateViewsOnStopRec();
            updateSqlite();
    
            //_________ if camera was not available __________
            if (camera != null && mediaRecorder != null) {
                final File file = new File(fileName);
                saveAndScanFile(file.getAbsolutePath());
    
                mediaRecorder.stop();
                mediaRecorder.reset();
                mediaRecorder.release();
    
                camera.lock();
                camera.release();
    
                Camera.CameraInfo info = new Camera.CameraInfo();
                if (info.canDisableShutterSound) {
                    camera.enableShutterSound(true);
                }
    
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
                    // to mute shutter sound of camera
                    final AudioManager mgr = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
                    mgr.setStreamMute(AudioManager.STREAM_SYSTEM, false);
                    mgr.setStreamSolo(AudioManager.STREAM_SYSTEM, false);
                    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                        if (mgr.isStreamMute(AudioManager.STREAM_MUSIC))
                            mgr.setStreamMute(AudioManager.STREAM_MUSIC, false);
                    }
    
                } else {
    
                }
    
    
                /*if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) {
                    // to unmute shutter sound of camera
                    AudioManager mgr = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
                    mgr.setStreamMute(AudioManager.STREAM_SYSTEM, false);
                } else {
                    Camera.CameraInfo info = new Camera.CameraInfo();
                    if (info.canDisableShutterSound) {
                        camera.enableShutterSound(true);
                    }
                }*/
    
                windowManager.removeView(surfaceView);
    
                if (settingsModel.isShowNotificationOnRecStarts())
                    unregisterReceiver(stopServiceBroadcast);
    
                if (settingsModel.isShowNotificationOnRecStops()) {
                    new Handler().postDelayed(new Runnable() {
                        @Override
                        public void run() {
                            sendRecordedVideoNotification(file.getAbsolutePath());
                        }
                    }, 1000);
                }
    
                setupRiningAndVIberationOnStopRec();
                if (SharedPreferenceUtility.getPreference(this, PreferenceKeys.IS_APP_RUNNING, false) && runnable != null)
                    enableViewsHandler.removeCallbacks(runnable);
            }
        }
    
        private void setupRiningAndVIberationOnStopRec() {
            if (settingsModel.isRingsOnRecStops()) {
                Uri ringTuneUri = Uri.parse("android.resource://" + getPackageName() + "/" + R.raw.rec_end_sound);
                Ringtone r = RingtoneManager.getRingtone(getApplicationContext(), ringTuneUri);
                r.play();
            }
            if (settingsModel.isVibrateOnRecStops()) {
                Vibrator v = (Vibrator) getSystemService(Context.VIBRATOR_SERVICE);
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                    v.vibrate(VibrationEffect.createOneShot(500, VibrationEffect.DEFAULT_AMPLITUDE));
                } else
                    v.vibrate(500);
            }
        }
    
        private void updateViewsOnStopRec() {
            if (SharedPreferenceUtility.getPreference(this, PreferenceKeys.IS_APP_RUNNING, false)) {
                if (chronometer != null)
                    chronometer.stop();
    
                fabStartStopRec.setIcon(getResources().getDrawable(R.drawable.start_icon), getResources().getDrawable(R.drawable.stop_icon));
                fabStartStopRec.showProgress(false);
                fabStartStopRec.setIndeterminate(false);
    
                if (sflStartStopRecProgress.isAnimationStarted())
                    sflStartStopRecProgress.stopShimmerAnimation();
            }
        }
    
        @Override
        public void surfaceChanged(SurfaceHolder surfaceHolder, int format, int width, int height) {
        }
    
        @Override
        public void surfaceDestroyed(SurfaceHolder surfaceHolder) {
    
        }
    
        @Override
        public IBinder onBind(Intent intent) {
            return null;
        }
    
        private void showCustomNotification(String fileName, String videoPath) {
            RemoteViews contentView = new RemoteViews(getPackageName(), R.layout.single_expanded_notification_layout);
            contentView.setTextViewText(R.id.tv_app_name, "Private Video Recorder");
            contentView.setTextViewText(R.id.tv_video_name, fileName);
            contentView.setImageViewBitmap(R.id.iv_video_image, getVideoThumbnail(videoPath));
    
            Intent playVideoIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(videoPath));
            playVideoIntent.setDataAndType(Uri.parse(videoPath), "video/mp4");
            PendingIntent playVideoPendingIntent = PendingIntent.getActivity(this, 0, playVideoIntent, 0);
            contentView.setOnClickPendingIntent(R.id.btn_play_video, playVideoPendingIntent);
            contentView.setOnClickPendingIntent(R.id.btn_cancel, PendingIntent.getActivity(getApplicationContext(), 0, new Intent(), 0, null));
    
            NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(this)
                    .setSmallIcon(R.mipmap.ic_launcher_round)
                    .setCustomBigContentView(contentView);
    
            Notification notification = mBuilder.build();
            notification.flags |= Notification.FLAG_AUTO_CANCEL;
            notification.defaults |= Notification.DEFAULT_SOUND;
            notification.defaults |= Notification.DEFAULT_VIBRATE;
            NotificationManager notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
            notificationManager.notify(1, notification);
        }
    
        private void notificationToShowRecordingStarted() {
            registerBroadcastToStopRec();
    
            Intent notificationIntent = new Intent("stop_recording_action");
            PendingIntent pendingIntent = PendingIntent.getBroadcast(getApplicationContext(), 0, notificationIntent, 0);
    
            Notification notification = null;
            if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
                /*if (settingsModel.isShowNotificationOnRecStarts() || settingsModel.isRecordingScheduled() || !SharedPreferenceUtility.getPreference(this, PreferenceKeys.IS_APP_RUNNING, false)) {*/
                NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(this, createNotificationChannel());
                notification = notificationBuilder.setOngoing(true)
                        .setSmallIcon(R.mipmap.recording_running_icon)
                        .setContentTitle(getString(R.string.private_video_recorder))
                        .setContentText(getString(R.string.click_to_stop_recording))
                        .setPriority(NotificationManager.IMPORTANCE_MIN)
                        .setCategory(Notification.CATEGORY_SERVICE)
                        .setContentIntent(pendingIntent)
                        .build();
    
                int REC_STARTS_NOTIFICATION_ID = 123;
                startForeground(REC_STARTS_NOTIFICATION_ID, notification);
                /*}*/
            } else {
                if (settingsModel.isShowNotificationOnRecStarts()) {
                    notification = new Notification.Builder(this)
                            .setSound(null)
                            .setVibrate(null)
                            .setContentTitle(getString(R.string.private_video_recorder))
                            .setContentText(getString(R.string.click_to_stop_recording))
                            .setSmallIcon(R.mipmap.recording_running_icon)
                            .setContentIntent(pendingIntent)
                            .build();
    
                    int REC_STARTS_NOTIFICATION_ID = 123;
                    startForeground(REC_STARTS_NOTIFICATION_ID, notification);
                }
            }
    
    //        notificationIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
    //        notification.flags |= Notification.FLAG_AUTO_CANCEL;
    
            /*int REC_STARTS_NOTIFICATION_ID = 123;
            startForeground(REC_STARTS_NOTIFICATION_ID, notification);*/
        }
    
    
    
    
        private Camera openFrontFacingCamera() {
            int cameraCount = 0;
            Camera.CameraInfo cameraInfo = new Camera.CameraInfo();
            cameraCount = Camera.getNumberOfCameras();
            for (int camIdx = 0; camIdx < cameraCount; camIdx++) {
                Camera.getCameraInfo(camIdx, cameraInfo);
                if (cameraInfo.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
                    try {
                        camera = Camera.open(camIdx);
                    } catch (RuntimeException e) {
                        Toast.makeText(this, getString(R.string.camera_failed_to_open_) + e.toString(), Toast.LENGTH_SHORT).show();
                    }
                }
            }
    
            return camera;
        }
    
        private void updateSqlite() {
            SettingsModel settingsModel = SettingsModel.findById(SettingsModel.class, 1L);
            settingsModel.setRecordingScheduled(false);
            settingsModel.save();
    
            SharedPreferenceUtility.setPreference(this, PreferenceKeys.IS_RECORDING, false);
        }
    
    }
    
    公共类RecorderService扩展服务实现SurfaceHolder.Callback{
    专用WindowManager WindowManager;
    私人SurfaceView SurfaceView;
    私人摄像机=空;
    专用MediaRecorder MediaRecorder=null;
    私有字符串fileName=“”;
    私人停止服务广播停止服务广播;
    私有字符串endRecordingDate;
    私有字符串endRecordingTime;
    私人设置模型设置模型;
    私有处理程序启用ViewShandler;
    私人可运行;
    @RequiresApi(api=Build.VERSION\u CODES.JELLY\u BEAN)
    @凌驾
    public void onCreate(){
    settingsModel=settingsModel.findById(settingsModel.class,1L);
    setupSurfaceViewForRecording();
    }
    私有void setupSurfaceViewForRecording(){
    int布局_标志;
    if(Build.VERSION.SDK\u INT>=Build.VERSION\u code.O){
    LAYOUT_FLAG=WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
    }否则{
    LAYOUT_FLAG=WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY;
    }
    //创建新的SurfaceView,将其大小设置为1x1,将其移动到左上角,并将此服务设置为回调
    windowManager=(windowManager)this.getSystemService(Context.WINDOW\u服务);
    surfaceView=新的surfaceView(本);
    WindowManager.LayoutParams LayoutParams=新建WindowManager.LayoutParams(
    1, 1,
    //WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY,
    //WindowManager.LayoutParams.FLAG\u WATCH\u Out\u TOUCH,
    布图(u)旗,,
    WindowManager.LayoutParams.FLAG_不可触摸| WindowManager.LayoutParams.FLAG_屏幕中的布局| WindowManager.LayoutParams.FLAG_不可聚焦,
    像素格式。半透明
    );
    layoutParams.gravity=gravity.LEFT | gravity.TOP;
    windowManager.addView(surfaceView、layoutParams);
    surfaceView.getHolder().addCallback(此);
    }
    私有无效注册表broadcasttostoprec(){
    stopServiceBroadcast=新的stopServiceBroadcast();
    IntentFilter filter=新的IntentFilter(“停止录制动作”);
    registerReceiver(stopServiceBroadcast,过滤器);
    }
    //创建曲面后立即调用的方法(初始化并启动MediaRecorder)
    @凌驾
    已创建的公共空隙表面(表面层表面层){
    mediaRecorder=新的mediaRecorder();
    setupCameraSide();
    //if(设置model.getCameraSide().equalsIgnoreCase(“前端”)和&!getPackageManager().hasSystemFeature(PackageManager.FEATURE\u CAMERA\u前端)){
    //Toast.makeText(这个,R.string.front\u摄像头\u不可用\u,Toast.LENGTH\u SHORT.show();
    //if(SharedPreferenceUtility.getInstance().getBooleanPreference(PreferenceKeys.IS_APP_RUNNING))
    //fabStartStopRec.setEnabled(真);
    //这个。stopSelf();
    //返回;
    //