我有前台服务,该服务在延迟一段时间后无需预览即可拍照。它在以下情况下捕获图像(工作正常):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>
版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。