在这个二维码满天飞的社会里,似乎每个app都会拥有自己的扫码功能模块,大多数公司都没法像微信一样研究自己的一套扫码库,从而采用第三方开源库来使用,在安卓开源库里,使用最多的就是
Zbar
Zxing
本文通过分析Zxing 官网给出的android 样例工程,来讲解如何使用Zxing这个开源库。
Zxing demo工程导入
第一步,进入github https://github.com/zxing/zxing 下载demo代码,github上代码有两种方式,一种是通过 git clone ,一种是直接下载到桌面,这里采用直接下载到桌面方式。
android 需要导入的只是
这个目录是一个 android完整的工程(Eclipse),用android studio 直接导入即可
file->New->import project 各种next
工程导入后,就会出现一堆错误,放心,都是gradle 配置的问题,通过配置gradle 都能解决。
根目录下的build.gradle 添加如下:
buildscript {
repositories {
jcenter()
google()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.1.2'
}
}
allprojects {
repositories {
jcenter()
google()
}
}
app下的build.gradle添加如下代码(我当时也不知道要怎么弄,但是代码里报缺这些类的错误,就加上这些依赖,结果还真成功了,而且这个版本号很重要,版本号低了,有一些方法就缺失了)
dependencies {
implementation 'com.google.zxing:core:3.3.2'
implementation 'com.google.zxing:android-core:3.3.0'
}
这样整个工程就能跑起来了,代码结构如下
我们跑一下程序,看看这个程序长什么样。
Google 爸爸特别实在,demo里提供了各种设置,点开右上角设置里面,各种历史记录,各种形式的码,Wi-Fi设置等,我们这里主要通过首页分析,扫码API的调用,一些偏门设置可以自行研究。
我们主要需要关注这几个类就好了
CaptureActivity ,ViewfinderView,CaptureActivityHandler,DecodeHandler,DecodeThread
整个流程大概就是这样子滴,CaptureActivity中嵌套ViewfinderView,然后通过CamerManager拍摄到的二维码图片通过CaptureAcitivityHandler 传给DecodeThread处理,DecodeThread处理完结果又通过DecodeHandler回传给CaptureActivityHandler处理,最终在CaptureActivity进行展示。
关键代码分析,看注释
CaptureActivity.class
大多数处理都在 onResume 和onPause中处理,onResume中对camera 进行初始化,处理google 自己定义的一系列标准的Intent。
private void initCamera(SurfaceHolder surfaceHolder) {
if (surfaceHolder == null) {
throw new IllegalStateException("No SurfaceHolder provided");
}
if (cameraManager.isOpen()) {
Log.w(TAG, "initCamera() while already open -- late SurfaceView callback?");
return;
}
try {
//打开相机
cameraManager.openDriver(surfaceHolder);
// Creating the handler starts the preview, which can also throw a RuntimeException.
if (handler == null) {
//将相机等参数在此传给CaptureActivityHandler captureActivityHandler 处理后会 回调CaptureActivity的
// handleDecode()方法 handleDecode方法得到 解析结果 然后做自己的处理
//decodeFomats:支持扫码的类型
handler = new CaptureActivityHandler(this, decodeFormats, decodeHints, characterSet, cameraManager);
}
decodeOrStoreSavedBitmap(null, null);
} catch (IOException ioe) {
Log.w(TAG, ioe);
displayFrameworkBugMessageAndExit();
} catch (RuntimeException e) {
// Barcode Scanner has seen crashes in the wild of this variety:
// java.?lang.?RuntimeException: Fail to connect to camera service
Log.w(TAG, "Unexpected error initializing camera", e);
displayFrameworkBugMessageAndExit();
}
}
CaptureActivityHandler.class 该类将耗时的解析过程交给 DecodeThread处理
CaptureActivityHandler(CaptureActivity activity,
Collection decodeFormats,
Map baseHints,
String characterSet,
CameraManager cameraManager) {
this.activity = activity;
decodeThread = new DecodeThread(activity, decodeFormats, baseHints, characterSet,
new ViewfinderResultPointCallback(activity.getViewfinderView())); //此处传入的viewFinderview 会拿到 扫描的像素信息
decodeThread.start();//处理camera 抓取到的图像像素信息
state = State.SUCCESS;
// Start ourselves capturing previews and decoding.
this.cameraManager = cameraManager;
cameraManager.startPreview();
restartPreviewAndDecode();
}
@Override
public void handleMessage(Message message) {
switch (message.what) {
case R.id.restart_preview:
restartPreviewAndDecode();
break;
case R.id.decode_succeeded:
state = State.SUCCESS;
Bundle bundle = message.getData();
Bitmap barcode = null;
float scaleFactor = 1.0f;
if (bundle != null) {
byte[] compressedBitmap = bundle.getByteArray(DecodeThread.BARCODE_BITMAP);
if (compressedBitmap != null) {
barcode = BitmapFactory.decodeByteArray(compressedBitmap, 0, compressedBitmap.length, null);
// Mutable copy:
barcode = barcode.copy(Bitmap.Config.ARGB_8888, true);
}
scaleFactor = bundle.getFloat(DecodeThread.BARCODE_SCALED_FACTOR);
}
//处理成功后 回调CaptureActivity的handleDecode方法
activity.handleDecode((Result) message.obj, barcode, scaleFactor);
break;
case R.id.decode_failed:
// We're decoding as fast as possible, so when one decode fails, start another.
state = State.PREVIEW;
//给CameraManager 传DecodeHandler CameraManager通过
//DecoderHandler 将扫描的像素信息传给DecoderHandler处理
cameraManager.requestPreviewFrame(decodeThread.getHandler(), R.id.decode);
break;
DecodeThread.class
@Override
public void run() {
Looper.prepare();
//让DecodeHander处理 camera扫描的信息
handler = new DecodeHandler(activity, hints);
handlerInitLatch.countDown();
Looper.loop();
}
DecodeHandler.classs 该类拿到 Camera扫描的数据 进行分析,将结果返回给CaptureActivityHandler
DecodeHandler(CaptureActivity activity, Map hints) {
multiFormatReader = new MultiFormatReader(); //zxing 提供的解析数据的类
multiFormatReader.setHints(hints); //需要解析的二维码类型
this.activity = activity;
}
@Override
public void handleMessage(Message message) {
if (message == null || !running) {
return;
}
switch (message.what) {
case R.id.decode: //camera 传来的message 进行解析
decode((byte[]) message.obj, message.arg1, message.arg2);
break;
case R.id.quit:
running = false;
Looper.myLooper().quit();
break;
}
}
/**
* Decode the data within the viewfinder rectangle, and time how long it took. For efficiency,
* reuse the same reader objects from one decode to the next.
*
* @param data The YUV preview frame.
* @param width The width of the preview frame.
* @param height The height of the preview frame.
*/
private void decode(byte[] data, int width, int height) {
long start = System.currentTimeMillis();
Result rawResult = null;
PlanarYUVLuminanceSource source = activity.getCameraManager().buildLuminanceSource(data, width, height);
if (source != null) {
BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source));
try {
rawResult = multiFormatReader.decodeWithState(bitmap);
} catch (ReaderException re) {
// continue
} finally {
multiFormatReader.reset();
}
}
//处理结果 返回给CaptureActivityHandler处理
Handler handler = activity.getHandler();
if (rawResult != null) {
// Don't log the barcode contents for security.
long end = System.currentTimeMillis();
Log.d(TAG, "Found barcode in " + (end - start) + " ms");
if (handler != null) {
Message message = Message.obtain(handler, R.id.decode_succeeded, rawResult);
Bundle bundle = new Bundle();
bundleThumbnail(source, bundle);
message.setData(bundle);
message.sendToTarget();
}
} else {
if (handler != null) {
Message message = Message.obtain(handler, R.id.decode_failed);
message.sendToTarget();
}
}
}
CameraManager.class 相机相关的处理 接收DecodeHandler 通过decodeHandler发消息返回 camera扫描得到的数据在DecodeHandler里处理
public synchronized void requestPreviewFrame(Handler handler, int message) {
OpenCamera theCamera = camera;
if (theCamera != null && previewing) {
previewCallback.setHandler(handler, message);
theCamera.getCamera().setOneShotPreviewCallback(previewCallback);
}
}
void setHandler(Handler previewHandler, int previewMessage) {
this.previewHandler = previewHandler;
this.previewMessage = previewMessage;
}
@Override
public void onPreviewFrame(byte[] data, Camera camera) {
Point cameraResolution = configManager.getCameraResolution();
Handler thePreviewHandler = previewHandler;
if (cameraResolution != null && thePreviewHandler != null) {
//传给DecodeHandler处理的数据
Message message = thePreviewHandler.obtainMessage(previewMessage, cameraResolution.x,
cameraResolution.y, data);
message.sendToTarget();
previewHandler = null;
} else {
Log.d(TAG, "Got preview callback, but no handler or resolution available");
}
}
到此整个扫描过程在到解析再到返回的流程就跟大家分析完了,最后画个图给大家总结一下
分析先到这里,下一步编写自己的zxing扫描程序通过谷歌官方例子手把手教你改造自己的Zxing扫描android程序(二)。