前言
在前面的博客中,我们已经将 Zxing开源代码中 android包的代码都进行了分析。综合来看,整个 android包实际上就是一个安卓应用“条码扫描器”,其调用 core包中的核心扫码编码算法,来提供相应功能。不过 Zxing除了可以直接使用其提供的应用外,还可以把它集成在自己的 Android项目中,来实现扫码解码等一系列功能。这所依赖的就是 android-integration包中的代码
IntentIntegrator 代码分析
IntentIntegrator是一个实用类,通过 Intent与 android包对应的应用“条码扫描器”集成,可以简单的调用扫码编码功能并接收结果,无需修改或学习项目的源代码
扫码功能集成方法如下:
1、 在自己的项目中,创建 IntentIntegrator对象,调用 initiateScan() 方法
2、如果用户还未下载应用程序“条码扫描器”,initiateScan() 方法会返回一个 AlertDialog 对象,来提示用户下载
3、在自己项目的 Activity中,需要有方法 onActivityResult(int, int, Intent)
来处理扫描后返回的结果
public void onActivityResult(int requestCode, int resultCode, Intent intent) {
IntentResult scanResult = IntentIntegrator.parseActivityResult(requestCode, resultCode, intent);
if (scanResult != null) {
// handle scan result
}
// else continue with any other code you need in the method
...
}
initiateScan
如果外部程序希望利用 Zxing 实现扫码功能,则首先需要调用这一方法,传入参数为:
参数 | 含义 |
---|---|
desiredBarcodeFormats | 将要扫描的条码格式 |
cameraId | 要调用哪一个摄像头(前置/后置) |
方法代码如下:
public final AlertDialog initiateScan(Collection<String> desiredBarcodeFormats, int cameraId) {
// BS_PACKAGE = "com.google.zxing.client.android"
// 为 Intent 设置action:SCAN
Intent intentScan = new Intent(BS_PACKAGE + ".SCAN");
intentScan.addCategory(Intent.CATEGORY_DEFAULT);
// 判断要扫描的何种类型的条码
if (desiredBarcodeFormats != null) {
// 设置希望的码制类型
StringBuilder joinedByComma = new StringBuilder();
for (String format : desiredBarcodeFormats) {
if (joinedByComma.length() > 0) {
joinedByComma.append(',');
}
joinedByComma.append(format);
}
intentScan.putExtra("SCAN_FORMATS", joinedByComma.toString());
}
// 设置需要的 caremaId
if (cameraId >= 0) {
intentScan.putExtra("SCAN_CAMERA_ID", cameraId);
// 在已安装的程序中寻找,是否有“条码扫描器”
String targetAppPackage = findTargetAppPackage(intentScan);
// 没找到的话就要显示弹窗,提示安装
if (targetAppPackage == null) {
return showDownloadDialog();
}
// 为 intent 设定包名
intentScan.setPackage(targetAppPackage);
intentScan.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
intentScan.addFlags(FLAG_NEW_DOC);
attachMoreExtras(intentScan);
// 跳转到intentScan中的Activity(这里应是CaptureActivity 的实例对象)进行扫码处理
startActivityForResult(intentScan, REQUEST_CODE);
return null;
}
方法中首先设置了 Intent 需要携带的信息(如扫描码制、cameraId)等,而后通过方法 findTargetAppPackage
找到目标app的包名,最终通过 startActivityForResult
跳转到了相应 app的 Activity中进行具体的扫码操作
findTargetAppPackage
findTargetAppPackage 方法代码实现如下:
private String findTargetAppPackage(Intent intent) {
// 获得一个PackageManger(管理所有应用程序包的类)的对象
PackageManager pm = activity.getPackageManager();
// 根据 intent 对所有的安装程序进行匹配查询,找到满足条件的app
List<ResolveInfo> availableApps = pm.queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);
if (availableApps != null) {
// 判断满足条件的app是否在事先定义好的目标app列表中
for (String targetApp : targetApplications) {
if (contains(availableApps, targetApp)) {
return targetApp;
}
}
}
return null;
}
PackageManager的介绍:通过PackageManager,我们可以获取应用程序信息,这些信息来自AndroidManifest.XML。PackageManager除了负责Android系统中Package的安装、升级、卸载外,还有一项很重要的职责,就是对外提供统一的信息查询功能,其中包括查询系统中匹配某Intent的Activities、BroadCastReceivers或Services等
showDownloadDialog()
若发现在已安装的应用程序中没有目标app, 则调用了 showDownloadDialog() 方法,来显示弹窗提示下载app
private AlertDialog showDownloadDialog() {
// 创建AlertDialog实例对象
AlertDialog.Builder downloadDialog = new AlertDialog.Builder(activity);
// 为弹窗设置标题
downloadDialog.setTitle(title);
// 为弹窗设置显示内容
downloadDialog.setMessage(message);
// 为点击“Yes”按钮设置监听
downloadDialog.setPositiveButton(buttonYes, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
String packageName;
if (targetApplications.contains(BS_PACKAGE)) {
// 如果目标app列表中存在BS_PACKAGE(也就是Zxing的安卓端应用),那么设置要安装的包名为BS_PACKAGE
packageName = BS_PACKAGE;
} else {
// 否则选择目标app列表的第一个
packageName = targetApplications.get(0);
}
// 跳转到应用商场
Uri uri = Uri.parse("market://details?id=" + packageName);
Intent intent = new Intent(Intent.ACTION_VIEW, uri);
try {
if (fragment == null) {
activity.startActivity(intent);
} else {
fragment.startActivity(intent);
}
} catch (ActivityNotFoundException anfe) {
// Hmm, market is not installed
Log.w(TAG, "Google Play is not installed; cannot install " + packageName);
}
}
});
downloadDialog.setNegativeButton(buttonNo, null);
downloadDialog.setCancelable(true);
return downloadDialog.show();
}
shareText
除了可以通过 IntentIntegrator 集成扫码操作,也可以利用 Zxing的编码功能,将给定文本编码为条形码来共享,以便其他用户可以扫描
public final AlertDialog shareText(CharSequence text, CharSequence type) {
Intent intent = new Intent();
intent.addCategory(Intent.CATEGORY_DEFAULT);
// Intent的action为ENCODE
intent.setAction(BS_PACKAGE + ".ENCODE");
intent.putExtra("ENCODE_TYPE", type);
intent.putExtra("ENCODE_DATA", text);
// 找到目标app 包名
String targetAppPackage = findTargetAppPackage(intent);
// 如果没有找到则弹窗提示下载
if (targetAppPackage == null) {
return showDownloadDialog();
}
intent.setPackage(targetAppPackage);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
intent.addFlags(FLAG_NEW_DOC);
attachMoreExtras(intent);
// 跳转到对应app的Activity中处理
if (fragment == null) {
activity.startActivity(intent);
} else {
fragment.startActivity(intent);
}
return null;
}
其基本流程与扫码类似,只是 Intent 中 Action 设置不同,这样跳转到“条码扫描器”的 Activity后就可以执行编码功能
总结
到这里,整个 Zxing开源代码中与 Android相关的部分就分析完毕了。从一开始面对近八十个类感觉无从下手,到后面从主线——扫码功能出发,一步步对功能逐个进行分析,最后已经基本完全理清 Zxing项目 Android部分的代码逻辑了。
与此同时,也学习了解了 Android的基本组件、Handler消息传递机制、View体系、Adapter机制等等 Android相关的重要知识。
对比以往自己开发的课设项目,整个 Zxing项目的安卓端应用体量庞大,功能处理逻辑完善,并且考虑了各种各样的情况,包括多种解析格式、长时间无活动自动销毁机制等。这也是我第一次阅读大型项目的源码,其中的开发思想、处理逻辑等都对我今后进行软件工程项目实践有很大启发