最近在项目中遇到一个需求,实现一个后台拍照的功能。一开始在网上寻找解决方案,也尝试了很多种实现方式,都没有满意的方案。不过确定了难点:即拍照要先预览,然后再调用拍照方法。问题也随之而来,既然是要实现后台拍照,就希望能在Service中或者是异步的线程中进行,这和预览这个步骤有点相矛盾。那有什么方式能够既能正常的实现预览、拍照,又不让使用者察觉呢?想必大家也会想到一个取巧的办法:隐藏预览界面。
说明一下,这只是我在摸索中想到的一种解决方案,能很好的解决业务上的需求。对于像很多手机厂商提供的“找回手机”功能时提供的拍照,我不确定他们的实现方式。如果大家有更好的实现方案,不妨交流一下。
关于这个功能是否侵犯了用户的隐私,影响用户的安全等等问题,不在我们的考虑和讨论范围之内。
方案实现步骤大致如下:
先大概介绍下业务需求:从用户登录到注销这段时间内,收到后台拍照的指令后完成拍照、保存、上传。以下会基于这个业务场景来详细介绍各步骤的实现。
package com.yuexunit.zjjk.service;
import com.yuexunit.zjjk.util.Logger;
import android.content.Context;
import android.view.SurfaceView;
import android.view.WindowManager;
import android.view.WindowManager.LayoutParams;
/**
* 隐藏的全局窗口,用于后台拍照
*
* @author WuRS
*/
public class CameraWindow {
private static final String TAG = CameraWindow.class.getSimpleName();
private static WindowManager windowManager;
private static Context applicationContext;
private static SurfaceView dummyCameraView;
/**
* 显示全局窗口
*
* @param context
*/
public static void show(Context context) {
if (applicationContext == null) {
applicationContext = context.getApplicationContext();
windowManager = (WindowManager) applicationContext
.getSystemService(Context.WINDOW_SERVICE);
dummyCameraView = new SurfaceView(applicationContext);
LayoutParams params = new LayoutParams();
params.width = 1;
params.height = 1;
params.alpha = 0;
params.type = LayoutParams.TYPE_SYSTEM_ALERT;
// 屏蔽点击事件
params.flags = LayoutParams.FLAG_NOT_TOUCH_MODAL
| LayoutParams.FLAG_NOT_FOCUSABLE
| LayoutParams.FLAG_NOT_TOUCHABLE;
windowManager.addView(dummyCameraView, params);
Logger.d(TAG, TAG + " showing");
}
}
/**
* @return 获取窗口视图
*/
public static SurfaceView getDummyCameraView() {
return dummyCameraView;
}
/**
* 隐藏窗口
*/
public static void dismiss() {
try {
if (windowManager != null && dummyCameraView != null) {
windowManager.removeView(dummyCameraView);
Logger.d(TAG, TAG + " dismissed");
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
package com.yuexunit.zjjk.service;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import android.app.Service;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.BitmapFactory.Options;
import android.hardware.Camera;
import android.hardware.Camera.CameraInfo;
import android.hardware.Camera.PictureCallback;
import android.os.IBinder;
import android.os.Message;
import android.text.TextUtils;
import android.view.SurfaceView;
import com.yuexunit.sortnetwork.android4task.UiHandler;
import com.yuexunit.sortnetwork.task.TaskStatus;
import com.yuexunit.zjjk.network.RequestHttp;
import com.yuexunit.zjjk.util.FilePathUtil;
import com.yuexunit.zjjk.util.ImageCompressUtil;
import com.yuexunit.zjjk.util.Logger;
import com.yuexunit.zjjk.util.WakeLockManager;
/**
* 后台拍照服务,配合全局窗口使用
*
* @author WuRS
*/
public class CameraService extends Service implements PictureCallback {
private static final String TAG = CameraService.class.getSimpleName();
private Camera mCamera;
private boolean isRunning; // 是否已在监控拍照
private String commandId; // 指令ID
@Override
public void onCreate() {
Logger.d(TAG, "onCreate...");
super.onCreate();
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
WakeLockManager.acquire(this);
Logger.d(TAG, "onStartCommand...");
startTakePic(intent);
return START_NOT_STICKY;
}
private void startTakePic(Intent intent) {
if (!isRunning) {
commandId = intent.getStringExtra("commandId");
SurfaceView preview = CameraWindow.getDummyCameraView();
if (!TextUtils.isEmpty(commandId) && preview != null) {
autoTakePic(preview);
} else {
stopSelf();
}
}
}
private void autoTakePic(SurfaceView preview) {
Logger.d(TAG, "autoTakePic...");
isRunning = true;
mCamera = getFacingFrontCamera();
if (mCamera == null) {
Logger.w(TAG, "getFacingFrontCamera return null");
stopSelf();
return;
}
try {
mCamera.setPreviewDisplay(preview.getHolder());
mCamera.startPreview();// 开始预览
// 防止某些手机拍摄的照片亮度不够
Thread.sleep(200);
takePicture();
} catch (Exception e) {
e.printStackTrace();
releaseCamera();
stopSelf();
}
}
private void takePicture() throws Exception {
Logger.d(TAG, "takePicture...");
try {
mCamera.takePicture(null, null, this);
} catch (Exception e) {
Logger.d(TAG, "takePicture failed!");
e.printStackTrace();
throw e;
}
}
private Camera getFacingFrontCamera() {
CameraInfo cameraInfo = new CameraInfo();
int numberOfCameras = Camera.getNumberOfCameras();
for (int i = 0; i < numberOfCameras; i++) {
Camera.getCameraInfo(i, cameraInfo);
if (cameraInfo.facing == CameraInfo.CAMERA_FACING_FRONT) {
try {
return Camera.open(i);
} catch (Exception e) {
e.printStackTrace();
}
}
}
return null;
}
@Override
public void onPictureTaken(byte[] data, Camera camera) {
Logger.d(TAG, "onPictureTaken...");
releaseCamera();
try {
// 大于500K,压缩预防内存溢出
Options opts = null;
if (data.length > 500 * 1024) {
opts = new Options();
opts.inSampleSize = 2;
}
Bitmap bitmap = BitmapFactory.decodeByteArray(data, 0, data.length,
opts);
// 旋转270度
Bitmap newBitmap = ImageCompressUtil.rotateBitmap(bitmap, 270);
// 保存
String fullFileName = FilePathUtil.getMonitorPicPath()
+ System.currentTimeMillis() + ".jpeg";
File saveFile = ImageCompressUtil.convertBmpToFile(newBitmap,
fullFileName);
ImageCompressUtil.recyleBitmap(newBitmap);
if (saveFile != null) {
// 上传
RequestHttp.uploadMonitorPic(callbackHandler, commandId,
saveFile);
} else {
// 保存失败,关闭
stopSelf();
}
} catch (Exception e) {
e.printStackTrace();
stopSelf();
}
}
private UiHandler callbackHandler = new UiHandler() {
@Override
public void receiverMessage(Message msg) {
switch (msg.arg1) {
case TaskStatus.LISTENNERTIMEOUT:
case TaskStatus.ERROR:
case TaskStatus.FINISHED:
// 请求结束,关闭服务
stopSelf();
break;
}
}
};
// 保存照片
private boolean savePic(byte[] data, File savefile) {
FileOutputStream fos = null;
try {
fos = new FileOutputStream(savefile);
fos.write(data);
fos.flush();
fos.close();
return true;
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (fos != null) {
try {
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return false;
}
private void releaseCamera() {
if (mCamera != null) {
Logger.d(TAG, "releaseCamera...");
mCamera.stopPreview();
mCamera.release();
mCamera = null;
}
}
@Override
public void onDestroy() {
super.onDestroy();
Logger.d(TAG, "onDestroy...");
commandId = null;
isRunning = false;
FilePathUtil.deleteMonitorUploadFiles();
releaseCamera();
WakeLockManager.release();
}
@Override
public IBinder onBind(Intent intent) {
return null;
}
}
代码也不多,不过有几个点需要特别注意下,版权声明:本文为博主原创文章,未经博主允许不得转载。
原文:http://blog.csdn.net/wurensen/article/details/47024961