日期: 2021 年 6 月 21 日

Android开源库之使用ZBar开源库实现二维码功能

项目中本来已使用Zxing来实现二维码功能,但是在ZXing的识别效率太低,以至于在某些*端情况下,识别效果实在无法忍受,这不一定是算法问题,应该很大原因在于Zxing使用java实现。没办法只能尝试使用ZBar开源库,这个库是基于c/c++的,相比ZXing识别速度快很多是众所周知的!

下面贴出我完整的编译ZBar过程

 

一、下载源码

到ZBar的Github托管主页上下载ZBar;

在ZBar的Github托管主页上点击Android目录,看下面的说明,告知我们编译ZBar的Android SDK需要libiconv,所以我们先去下载libiconv。

二、编译libiconv

编译libiconv需要在linux环境下,我使用的是Cygwin客户端,但是死活编译的都不行,*后还是使用参考博客提供的编译好的;

三、编译zbar

把刚才编译好的libiconv放入我们项目的jni文件夹。
解压刚才下载好的Zbar,首先把Zbar的头文件所在文件夹zbar/include放入我们项目的jni文件夹下。
把Zbar对java的接口文件zbarjni.c放入我们项目的jni文件夹,zbrjni.c在zbar/java文件夹下。
把Zbar的核心库文件所在的文件夹zbar/zbar放到我们项目的jni文件夹下。
把Zbar编译时需要的Android.mk、Applicaiton.mk、config.h从zbar\android\jni下拷贝到我们项目的jni文件夹下。
此时我们项目的jni文件夹是这样的:

%title插图%num
理论上现在可以开始编译了吧,但是呢因为我们改动了zbar的文件夹结构,所以我们要对Android.mk进行改动,主要改的是文件夹路径和文件路径,修改后的Android.mk的内容如下:

MY_LOCAL_PATH := $(call my-dir)
# libiconv
include $(CLEAR_VARS)
LOCAL_PATH := $(MY_LOCAL_PATH)
LOCAL_MODULE := libiconv
LOCAL_CFLAGS := \
-Wno-multichar \
-D_ANDROID \
-DLIBDIR=”c” \
-DBUILDING_LIBICONV \
-DBUILDING_LIBCHARSET \
-DIN_LIBRARY

LOCAL_SRC_FILES := \
libiconv-1.15/lib/iconv.c \
libiconv-1.15/libcharset/lib/localcharset.c \
libiconv-1.15/lib/relocatable.c

LOCAL_C_INCLUDES := \
$(LOCAL_PATH)/libiconv-1.15/include \
$(LOCAL_PATH)/libiconv-1.15/libcharset \
$(LOCAL_PATH)/libiconv-1.15/libcharset/include

include $(BUILD_SHARED_LIBRARY)

LOCAL_LDLIBS := -llog -lcharset
# —————————————————–
# libzbar
include $(CLEAR_VARS)
LOCAL_PATH := $(MY_LOCAL_PATH)
LOCAL_MODULE := zbar
LOCAL_SRC_FILES := \
zbarjni.c \
zbar/img_scanner.c \
zbar/decoder.c \
zbar/image.c \
zbar/symbol.c \
zbar/convert.c \
zbar/config.c \
zbar/scanner.c \
zbar/error.c \
zbar/refcnt.c \
zbar/video.c \
zbar/video/null.c \
zbar/decoder/code128.c \
zbar/decoder/code39.c \
zbar/decoder/code93.c \
zbar/decoder/codabar.c \
zbar/decoder/databar.c \
zbar/decoder/ean.c \
zbar/decoder/i25.c \
zbar/decoder/qr_finder.c \
zbar/qrcode/bch15_5.c \
zbar/qrcode/binarize.c \
zbar/qrcode/isaac.c \
zbar/qrcode/qrdec.c \
zbar/qrcode/qrdectxt.c \
zbar/qrcode/rs.c \
zbar/qrcode/util.c

LOCAL_C_INCLUDES := \
$(LOCAL_PATH)/include \
$(LOCAL_PATH)/zbar \
$(LOCAL_PATH)/libiconv-1.15/include

LOCAL_SHARED_LIBRARIES := libiconv

include $(BUILD_SHARED_LIBRARY)

然后在Application.mk中填写你要编译的平台,如果想全部编译:

APP_ABI := all
1
1
如果要指定编译某几个平台,把平台名称依次空格隔开写上即可:

APP_ABI := armeabi armeabi-v7a x86 x86_64 mips mips_64 arm64_v8a
1
1
此时我们用命令行进入项目的jni文件夹的父目录,然后此时执行ndk-build进行编译。

四、zbar中文乱码问题

1、在sourceforge下载zbar源码,修改了文件

zbar/qrcode/qrdectxt.c

,62行左右,将编码标准ISO8859-1改为GBK或者GB18030,如下:

/*latin1_cd=iconv_open(“UTF-8″,”ISO8859-1”);*/
latin1_cd=iconv_open(“UTF-8″,”GB18030”);

2、继续修改上面的文件 zbar/qrcode/qrdectxt.c,164行左右,调换解码顺序,将中文解码调到首位如下:
/*enc_list[0]=sjis_cd;
enc_list[1]=latin1_cd;*/
enc_list[0]=latin1_cd;
enc_list[1]=sjis_cd;
经过这两部调整,应该就能解决中文乱码的问题;(当然修改源码后,要记得重新执行ndk-build编译zbar)。

3、通过1、2两部基本已经解决大部分中文乱码问题,但是对于某些中文,依旧是乱码,如二维码内容是“粤8888”,“粤”字就会被识别为乱码;

该问题参考博客,只需在识别出二维码数据后,判断识别结果数据是否是日文编码“Shift_JIS”,是的话,转换为“utf-8”编码,如下:

try {
String encodeResust = new String(qrCodeString.getBytes(“Shift_JIS”), “utf-8”);
LogUtils.d(TAG + “–decodeBarcodeZbar–将‘Shift_JIS’编码格式转成‘utf-8’:” + encodeResust);
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
LogUtils.e(TAG + “–decodeBarcodeZbar–识别结果不是‘Shift_JIS’编码格式”);
}

五、将zbar的java代码导入项目

把zbar/Java下在net.sourceforge.zbar包和里边的java文件拷贝到你的项目的java目录下,大概结构如下:

%title插图%num
注意对照一下so库和Image.java,ImageScanner.java,Symbol,java,SymbolSet.java四个java文件中声明静态库的名称是否需要修改,

因为我的so库名称是libzbar.so,所以静态库的声明代码需要改为:

System.loadLibrary(“zbar”);

六、Zbar调用
这里就不贴摄像头的调用和界面代码了,只贴一下调用Zbar识别二维码的代码:

/**
* 使用Zbar库识别二维码
* @param imageData 图像数据(摄像头返回的)
* @param width 图像的宽
* @param height 图像的高
* @param scanRect 扫描区域
*/
public static String decodeBarcodeZbar(byte[] imageData, int width, int height, Rect scanRect){
long start = System.currentTimeMillis();
Image barcode = new Image(width, height, “Y800”);
barcode.setData(imageData);
// 指定二维码在图片中的区域,也可以不指定,识别全图。
if(null != scanRect) barcode.setCrop(scanRect.left, scanRect.top, scanRect.width(), scanRect.height());

String qrCodeString = null;
ImageScanner mImageScanner = new ImageScanner();

int result = mImageScanner.scanImage(barcode);
if (result != 0) {
SymbolSet symSet = mImageScanner.getResults();
for (Symbol sym : symSet)
qrCodeString = sym.getData();
}

LogUtils.w(TAG + “–decodeBarcodeZbar: 总耗时:” + (System.currentTimeMillis() – start));

if (!TextUtils.isEmpty(qrCodeString)) {
// 成功识别二维码,qrCodeString就是数据。
LogUtils.d(TAG + “–decodeBarcodeZbar–识别成功:” + qrCodeString);
try {
String encodeResust = new String(qrCodeString.getBytes(“Shift_JIS”), “utf-8”);
LogUtils.d(TAG + “–decodeBarcodeZbar–将‘Shift_JIS’编码格式转成‘utf-8’:” + encodeResust);
return encodeResust;
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
LogUtils.e(TAG + “–decodeBarcodeZbar–识别结果不是‘Shift_JIS’编码格式”);
return qrCodeString;
}
}else{
LogUtils.d(TAG + “–decodeBarcodeZbar–识别失败”);
return null;
}

}

————————————————

Android使用ZBar扫描二维码/条形码(实例)+常见问题汇总

写在前面 :因项目需求,需要实现二维码扫码功能,笔者测试过多种开源扫码工具,但因不跨平台、扫描速度慢等问题逐个放弃,*后选用ZBar实现功能,笔者发现ZBar扫码在跨主流手机平台、扫码速度等方面有较明显的优势,现将核心功能整理成示例代码,便于日后复用和有需要的读者参考。

 

使用方式

1.复制com.zbar.lib及其下共4个包文件到项目中。

2.在lib下添加armeabi中的libiconv.so和libzbar.so库文件。

3.添加res下的资源文件,包括drawable、layout、raw、values(包含ids.xml)等。

4.在AndroidManifest.xml清单中添加权限和Activity声明。

5.调用扫码功能,在调用处通过以下代码使用扫码功能:

[java]  view plain  copy

  1. Intent intent = new Intent();
  2. intent.setClass(MainActivity.this, CaptureActivity.class);
  3. startActivityForResult(intent, SCANNIN_GREQUEST_CODE);

6.获得扫码结果,在步骤5中代码块所在的Activity中通过以下代码获取扫码结果:

[java]  view plain  copy

  1. @Override
  2. protected void onActivityResult(int requestCode, int resultCode, Intent data) {
  3.     super.onActivityResult(requestCode, resultCode, data);
  4.     switch (requestCode) {
  5.     case SCANNIN_GREQUEST_CODE:
  6.         if (resultCode == RESULT_OK) {
  7.             String result = data.getStringExtra(“QR_CODE”);
  8.             // TODO 获取结果,做逻辑操作
  9.             tvResult.setText(result);
  10.         } else {
  11.             Toast.makeText(this“无法获取扫码结果”2000).show();
  12.         }
  13.         break;
  14.     }
  15. }

测试效果:

1.二维码(一串字符:1234567890)

%title插图%num

2.扫码前(点击“扫码”开始扫码)

%title插图%num

3.扫码时(对准二维码)

%title插图%num

4.扫码后(呈现出扫码结果)

%title插图%num

常见问题汇总

移植后,若项目本身没报错,但不能扫码,可能存在以下问题:

1.未移植armeabi文件夹下的libiconv.so和libzbar.so库文件。(缺少时一般在运行时报错)

2.未在AndroidManifest.xml清单中配置所需权限。(可以运行,但扫码时黑屏,无法开启摄像头)

[html]  view plain  copy

  1.   <!– 二维码扫码 –>
  2. <uses-permission android:name=“android.permission.VIBRATE” />
  3. <uses-permission android:name=“android.permission.CAMERA” />
  4. <uses-feature android:name=“android.hardware.camera” />
  5. <uses-feature android:name=“android.hardware.camera.autofocus” />

3.未在AndroidManifest.xml清单文件中配置Activity:CaptureActivity。(缺少时一般在运行时报错)

[html]  view plain  copy

  1. <activity
  2. android:name=“com.zbar.lib.CaptureActivity”
  3. android:configChanges=“orientation|keyboardHidden”
  4. android:screenOrientation=“portrait”
  5. android:theme=“@android:style/Theme.Black.NoTitleBar”
  6. android:windowSoftInputMode=“stateAlwaysHidden” >

记录Android端使用zbar扫描的相关问题

在此一并做一下记录。

修改Zbar有效扫描区域的关键代码,在CaptureActivity中initCamera函数中

  1. CameraManager.get().openDriver(surfaceHolder);
  2. Point point = CameraManager.get().getCameraResolution();
  3. int width = point.y;
  4. int height = point.x;
  5. int x = mCropLayout.getLeft() * width / mContainer.getWidth();
  6. int y = mCropLayout.getTop() * height / mContainer.getHeight();
  7. int cropWidth = mCropLayout.getWidth() * width / mContainer.getWidth();
  8. int cropHeight = mCropLayout.getHeight() * height / mContainer.getHeight() – (iHeight – height);// 由于扫描框的大小,比实际扫描的高度矮,故减去(iHeight – height)的高度
  9. setX(x);
  10. setY(y);
  11. setCropWidth(cropWidth);
  12. setCropHeight(cropHeight);

解码并保存图片的相关代码,在DecodeHandler中decode函数中

  1. private void decode(byte[] data, int width, int height)
  2. {
  3. byte[] rotatedData = new byte[data.length];
  4. for (int y = 0; y < height; y++)
  5. {
  6. for (int x = 0; x < width; x++)
  7. rotatedData[x * height + height y 1] = data[x + y * width];
  8. }
  9. int tmp = width;// Here we are swapping, thats the difference to #11
  10. width = height;
  11. height = tmp;
  12. ZbarManager manager = new ZbarManager();
  13. String result = manager.decode(rotatedData, width, height, true, activity.getX(), activity.getY(), activity.getCropWidth(), activity.getCropHeight());
  14. if (result != null)
  15. {
  16. if (activity.isNeedCapture())
  17. {
  18. // 生成bitmap
  19. PlanarYUVLuminanceSource source = new PlanarYUVLuminanceSource(rotatedData, width, height, activity.getX(), activity.getY(), activity.getCropWidth(),
  20. activity.getCropHeight(), false);
  21. int[] pixels = source.renderThumbnail();
  22. int w = source.getThumbnailWidth();
  23. int h = source.getThumbnailHeight();
  24. Bitmap bitmap = Bitmap.createBitmap(pixels, 0, w, w, h, Bitmap.Config.ARGB_8888);
  25. try
  26. {
  27. String rootPath = Environment.getExternalStorageDirectory().getAbsolutePath() + “/Qrcode/”;
  28. File root = new File(rootPath);
  29. if (!root.exists())
  30. {
  31. root.mkdirs();
  32. }
  33. File f = new File(rootPath + “Qrcode.jpg“);
  34. if (f.exists())
  35. {
  36. f.delete();
  37. }
  38. f.createNewFile();
  39. FileOutputStream out = new FileOutputStream(f);
  40. bitmap.compress(Bitmap.CompressFormat.JPEG, 100, out);
  41. out.flush();
  42. out.close();
  43. }
  44. catch (Exception e)
  45. {
  46. e.printStackTrace();
  47. }
  48. }
  49. if (null != activity.getHandler())
  50. {
  51. Message msg = new Message();
  52. msg.obj = result;
  53. msg.what = R.id.decode_succeeded;
  54. activity.getHandler().sendMessage(msg);
  55. }
  56. }
  57. else
  58. {
  59. if (null != activity.getHandler())
  60. {
  61. activity.getHandler().sendEmptyMessage(R.id.decode_failed);
  62. }
  63. }
  64. }

界面自定义扫描框,可在CaptureActivity中自定义即可。也可仿照ZXing的ViewfinderView界面编写自己的布局。

 

Android快速实现二维码扫描–Zxing

Android中二维码扫描的*常用库是zxing和zbar,zxing项目地址为https://github.com/zxing/zxing,目前还有多个人在维护。zbar主要用C来写的,对速度有要求的可使用zbar,但目前没有在维护,项目地址:https://github.com/ZBar/ZBar。

1.引入jar包

%title插图%num

2.copy Zxing包到项目

%title插图%num

这里包名不一样肯定会报错,我们暂时不管,先把资源文件copy过来,后面来做处理。

3.导入相关资源文件

copy res底下的相关资源文件,如下:
drawable、drawable-hdpi和layout

%title插图%num

raw文件和values文件

%title插图%num

4.AndroidManifest.xml加入相关权限和扫描的Activity

<uses-permission android:name=”android.permission.CAMERA” />
<uses-permission android:name=”android.permission.INTERNET” />
<uses-permission android:name=”android.permission.VIBRATE” />
<uses-permission android:name=”android.permission.FLASHLIGHT” />
<activity
android:name=”.zxing.android.CaptureActivity”
android:screenOrientation=”portrait”
android:theme=”@android:style/Theme.NoTitleBar” />
6.capture.xml的ViewfinderView改成自己包名下的

%title插图%num

7.调起扫描界面 获取扫描结果

在需要打开扫描界面的地方直接跳转到CaptureActivity就OK(使用startActivityForResult)

/**
* 跳转到扫码界面扫码
*/
private void goScan() {
Intent intent = new Intent(MainActivity.this, CaptureActivity.class);
startActivityForResult(intent, REQUEST_CODE_SCAN);
}
在onActivityResult的回调中即可获取扫描内容

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
// 扫描二维码/条码回传
if (requestCode == REQUEST_CODE_SCAN && resultCode == RESULT_OK) {
if (data != null) {
//返回的文本内容
String content = data.getStringExtra(DECODED_CONTENT_KEY);
//返回的BitMap图像
Bitmap bitmap = data.getParcelableExtra(DECODED_BITMAP_KEY);
}
}
}
动态权限申请

由于扫描需要调用相机,打开摄像头属于敏感权限,所以需要进行权限的动态申请,如下

//动态权限申请
if (ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(MainActivity.this, new String[]{Manifest.permission.CAMERA}, 1);
} else {
//扫码
goScan();
}

@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
switch (requestCode) {
case 1:
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
//扫码
goScan();
} else {
Toast.makeText(this, “你拒*了权限申请,无法打开相机扫码哟!”, Toast.LENGTH_SHORT).show();
}
break;
default:
}
}
上面的代码就是动态申请权限的流程,首先判断用户是不是已经给我们权限授权了,使用ContextCompat.checkSelfPermission()方法,*个参数是Context,第二个参数是具体的权限名称,如果等于PackageManager.PERMISSION_GRANTED表明已授权,不等于就是没有授权。
如果已授权就直接做后面的操作,如果没有授权,需要调用ActivityCompat.requestPermissions()方法申请授权,*个参数是当前Activity实例,第二个参数是权限数组,第三个是请求码。
用户的选择将会回调到onRequestPermissionsResult()方法中,授权结果封装在grantResults参数中,如果grantResults长度大于0且grantResults[0]等于PackageManager.PERMISSION_GRANTED,也就是上面权限数组中加入的*个打开摄像头的权限被授权,则可跳转至扫描界面扫码,否则提示用户未打开权限无法使用。

二维码识别之Android完整编译Zbar

大概刚做Android开发的时候就做过二维码扫描,那时候懂的东西少,就搜出来了ZXing和Zbar两个库。ZXing是纯Java代码实现的,适用于Android平台;Zbar是C实现的,可以供很多语言和平台使用,比如Java、iOS平台、Android平台,Python等等。很明显Zbar的识别率和速度都是明显快于ZXing的,但是无奈那时候不会编译Zbar,只好下载了ZXing,但是由于当时技术能力不足,对于ZXing自定义剪切框也做不出来,只好下载了别人编译好的Zbar,可能由于别人修改了代码或者编译的不是很完整,后期bug层出,废了好大劲才完善好。

后来一直没有机会学习二维码扫描,直到前几天需要给我们平台的APP加上了二维码扫描功能,我决定使用ZBar,于是我完整的编译了一次,今天把这个过程记录下来,希望可以帮助到需要的同学。

比如微信使用的是ZXing,但是我肯定的说他们修改了不少源码,而且有很多地方应该改成了jni实现,所以微信的识别速率和准确率是相当高的,不过今天我编译后的封装也是秒秒钟就可以识别。

因为Zbar是基于LGPL-2.1开源的,因此我基于LGPL-2.1协议,我把一个完整的项目源码和sample放到Github上了,提供直接调用zbar的识别byte[]数据的功能和调用相机识别二维码的功能:
https://github.com/yanzhenjie/android-zbar-sdk

特别声明:本文已经修复了zbar识别中文乱码的问题!!!

编译Zbar
在正式编译之前要注意:编译Zbar需要先编译libiconv,编译libiconv需要linux环境,需要用到gcc。如果你没有linux环境也没有关系,我已经提供了编译好的libiconv。

其实在Zbar的官网也可以下载到他们已经编译好的so和jar,但是so文件他们只提供了armeabi、armeabi-v7a、x86平台:
https://sourceforge.net/projects/zbar/files/?source=navbar

所以我就抛弃了提供的编译包,自己编译了,下面是步骤。

首先在Zbar的开源主页下载Zbar源码:
https://github.com/ZBar/ZBar

顺便在开源主页点开android文件夹,发现编译Zbar需要libiconv,接下来下载libiconv:
http://www.gnu.org/software/libiconv

对于libiconv我是下载的在2017-02-02时发布的*新版1.15。

一、编译libiconv
如果你没有linux环境编译libiconv,那么你可以在这里下载我已经编译好的libiconv1.15:
http://download.csdn.net/detail/yanzhenjie1003/9833225,下好好文件后,你就可以直接跳过这一节,看下面Zbar和libiconv一起编译了。

如果你有linux环境可以编译libiconv,那么继续往下看。
下载好libiconv后,进入libiconv文件夹,如果报权限错误进不去的话执行sudo chmod 777 -R libiconv就可以了:

%title插图%num
进来后先执行:./configure,如果提示没权限那么执行:sudo chmod 777 configure,然后重新执行/.configure即可。

等./configure执行完后再执行make命令即可完成编译

编译时可能遇到以下错误:
1、configure: error: no acceptable C compiler found in $PATH
这个是说你没有安装gcc,安装gcc后再次执行未完成命令即可。

二、Zbar和libiconv一起编译
libiconv编译完成了,接下来把Zbar和libiconv放到一起,编译出我们需要的so文件。

把刚才编译好的libiconv放入我们项目的jni文件夹。
解压刚才下载好的Zbar,首先把Zbar的头文件所在文件夹zbar/include放入我们项目的jni文件夹下。
把Zbar对java的接口文件zbarjni.c放入我们项目的jni文件夹,zbrjni.c在zbar/java文件夹下。
把Zbar的核心库文件所在的文件夹zbar/zbar放到我们项目的jni文件夹下。
把Zbar编译时需要的Android.mk、Applicaiton.mk、config.h从zbar\android\jni下拷贝到我们项目的jni文件夹下。
此时我们项目的jni文件夹是这样的:

%title插图%num
理论上现在可以开始编译了吧,但是呢因为我们改动了zbar的文件夹结构,所以我们要对Android.mk进行改动,主要改的是文件夹路径和文件路径,修改后的Android.mk的内容如下:

MY_LOCAL_PATH := $(call my-dir)

# libiconv
include $(CLEAR_VARS)
LOCAL_PATH := $(MY_LOCAL_PATH)
LOCAL_MODULE := libiconv
LOCAL_CFLAGS := \
-Wno-multichar \
-D_ANDROID \
-DLIBDIR=”c” \
-DBUILDING_LIBICONV \
-DBUILDING_LIBCHARSET \
-DIN_LIBRARY

LOCAL_SRC_FILES := \
libiconv-1.15/lib/iconv.c \
libiconv-1.15/libcharset/lib/localcharset.c \
libiconv-1.15/lib/relocatable.c

LOCAL_C_INCLUDES := \
$(LOCAL_PATH)/libiconv-1.15/include \
$(LOCAL_PATH)/libiconv-1.15/libcharset \
$(LOCAL_PATH)/libiconv-1.15/libcharset/include

include $(BUILD_SHARED_LIBRARY)

LOCAL_LDLIBS := -llog -lcharset

# —————————————————–

# libzbar
include $(CLEAR_VARS)
LOCAL_PATH := $(MY_LOCAL_PATH)
LOCAL_MODULE := zbar
LOCAL_SRC_FILES := \
zbarjni.c \
zbar/img_scanner.c \
zbar/decoder.c \
zbar/image.c \
zbar/symbol.c \
zbar/convert.c \
zbar/config.c \
zbar/scanner.c \
zbar/error.c \
zbar/refcnt.c \
zbar/video.c \
zbar/video/null.c \
zbar/decoder/code128.c \
zbar/decoder/code39.c \
zbar/decoder/code93.c \
zbar/decoder/codabar.c \
zbar/decoder/databar.c \
zbar/decoder/ean.c \
zbar/decoder/i25.c \
zbar/decoder/qr_finder.c \
zbar/qrcode/bch15_5.c \
zbar/qrcode/binarize.c \
zbar/qrcode/isaac.c \
zbar/qrcode/qrdec.c \
zbar/qrcode/qrdectxt.c \
zbar/qrcode/rs.c \
zbar/qrcode/util.c

LOCAL_C_INCLUDES := \
$(LOCAL_PATH)/include \
$(LOCAL_PATH)/zbar \
$(LOCAL_PATH)/libiconv-1.15/include

LOCAL_SHARED_LIBRARIES := libiconv

include $(BUILD_SHARED_LIBRARY)

 

然后在Application.mk中填写你要编译的平台,如果想全部编译:

APP_ABI := all
1
如果要指定编译某几个平台,把平台名称依次空格隔开写上即可:

APP_ABI := armeabi armeabi-v7a x86 x86_64 mips mips_64 arm64_v8a
1
此时我们用命令行进入项目的jni文件夹的父母路,比如一般jni情况下jni文件夹位于ProjectName/ModuleName/src/main/jni,那么我们就进入这个main,然后此时执行ndk-build进行编译。

如果提示没有ndk-build这个命令,那么你需要从http://developer.android.com下载ndk并且在电脑上配置PATH。

等ndk-build执行完后会在libs下生成所有平台的so文件夹,文件夹里面是需要的libiconv和zbar的so文件。

编译Zbar和libiconv时遇到的错误解决
编译过程中可能发现如下错误,请按照修改方案修改即可。

1、libiconv-1.15/jni/libcharset/lib/localcharset.c:51:24: error: langinfo.h: No such file or directory
打开libiconv-1.15/libcharset/config.h文件,搜索#define HAVE_LANGINFO_CODESET,大概在14行,把这行注释了即可:

/* #define HAVE_LANGINFO_CODESET 1 */
1
2、…c undeclaired…
打开libiconv-1.15/libcharset/lib/localcharset.c,搜索到函数get_charset_aliases(),大概在124行。

大概在195行左右,有一个int c;(没有的话你可以搜索int c;),把这个一行代码移动到get_charset_aliases()开头:

%title插图%num
zbar的jar包
现在so文件有了,剩下的就是怎么调用so中的函数来识别条码/二维码了,首先把zbar/java下在net.sourceforge.zbar包和里边的java文件拷贝到你的项目的java目录下,大概结构如下:

%title插图%num
当然你也像这样使用源码,也可以把这几个类打包成jar包。

调用Zbar识别二维码
现在全部都编译好了,jar文件也有了,我们可以调用jar中封装的方法来识别二维码了:

byte[] imageData = …;

Image barcode = new Image(size.width, size.height, “Y800”);
barcode.setData(imageData);
// 指定二维码在图片中的区域,也可以不指定,识别全图。
// barcode.setCrop(startX, startY, width, height);

String qrCodeString = null;

int result = mImageScanner.scanImage(barcode);
if (result != 0) {
SymbolSet symSet = mImageScanner.getResults();
for (Symbol sym : symSet)
qrCodeString = sym.getData();
}

if (!TextUtils.isEmpty(qrCodeString)) {
// 成功识别二维码,qrCodeString就是数据。
}

如何和相机结合使用等复杂操作这里不再说了,一个完整的项目我放到Github上了:
https://github.com/yanzhenjie/android-zbar-sdk

山高水远,江湖再见!

 

linux之exec命令详解

1、介绍shell script:
有两种方法执行shell scripts。
一种是新产生一个shell,然后执行相应的shell scripts;新产生一个shell然后再执行scripts的方法是在scripts文件开头加入以下语句。一般的script文件(.sh)即是这种用法。这种方法先启用新的sub-shell(新的子进程),然后在其下执行命令

#!/bin/sh

一种是在当前shell下执行,不再启用其他shell。即source命令,不再产生新的shell,而在当前shell下执行一切命令。source命令即点(.)命令。

source:
1
2、exec命令:
系统调用exec是以新的进程去代替原来的进程,但进程的PID保持不变。因此,可以这样认为,exec系统调用并没有创建新的进程,只是替换了原来进程上下文的内容。原进程的代码段,数据段,堆栈段被新的进程所代替。

执行exec系统调用,一般都是这样,用fork()函数新建立一个进程,然后让进程去执行exec调用。我们知道,在fork()建立新进程之后,父进各与子进程共享代码段,但数据空间是分开的,但父进程会把自己数据空间的内容copy到子进程中去,还有上下文也会copy到子进程中去。而为了提高效率,采用一种写时copy的策略,即创建子进程的时候,并不copy父进程的地址空间,父子进程拥有共同的地址空间,只有当子进程需要写入数据时(如向缓冲区写入数据),这时候会复制地址空间,复制缓冲区到子进程中去。从而父子进程拥有独立的地址空间。而对于fork()之后执行exec后,这种策略能够很好的提高效率,如果一开始就copy,那么exec之后,子进程的数据会被放弃,被新的进程所代替。

3、 exec与system的区别:
(1) exec是直接用新的进程去代替原来的程序运行,运行完毕之后不回到原先的程序中去。
(2) system是调用shell执行你的命令,system=fork+exec+waitpid,执行完毕之后,回到原先的程序中去。继续执行下面的部分。
总之,如果你用exec调用,首先应该fork一个新的进程,然后exec. 而system不需要你fork新进程,已经封装好了。

4、exec I/O重定向:

exec ls //在shell中执行ls,ls结束后不返回原来的shell中了
exec //将file中的内容作为exec的标准输入
exec >file //将file中的内容作为标准写出
exec 3 //将file读入到fd3中
sort <&3 //fd3中读入的内容被分类
exec 4>file //将写入fd4中的内容写入file中
ls >&4 //Ls将不会有显示,直接写入fd4中了,即上面的file中
exec 5<&4 //创建fd4的拷贝fd5
exec 3<&- //关闭fd3

先上我们进如/dev/fd/目录下看一下:

%title插图%num

默认会有这四个项:
0是标准输入,默认是键盘。 stdin
1是标准输出,默认是屏幕/dev/tty stdout
2是标准错误,默认也是屏幕 stderr
255

对 2>&1详细说明一下 :2>&1 也就是 FD2=FD1 ,这里并不是说FD2 的值 等于FD1的值,因为 > 是改变送出的数据信道,也就是说把 FD2 的 “数据输出通道” 改为 FD1 的 “数据输出通道”。如果仅仅这样,这个改变好像没有什么作用,因为 FD2 的默认输出和 FD1的默认输出本来都是 monitor,一样的!

下面举例子说明:

liqian@liqian-Aspire-E1-471G:~$ exec>test
liqian@liqian-Aspire-E1-471G:~$ ls
liqian@liqian-Aspire-E1-471G:~$ pwd
liqian@liqian-Aspire-E1-471G:~$ echo “hello”
liqian@liqian-Aspire-E1-471G:~$ exec>/dev/tty
liqian@liqian-Aspire-E1-471G:~$ cat test
test
公共的
模板
视频
图片
文档
下载
音乐
桌面
/home/liqian
hello
liqian@liqian-Aspire-E1-471G:~$

说明:exec >text 是将当前shell的标准输出都打开到text文件中

root@liqian-Aspire-E1-471G:/home# cd /dev/fd
root@liqian-Aspire-E1-471G:/dev/fd# ls
0 1 2 255
root@liqian-Aspire-E1-471G:/dev/fd#
root@liqian-Aspire-E1-471G:/dev/fd# exec 3>/home/test
root@liqian-Aspire-E1-471G:/dev/fd# ls
0 1 2 255 3
root@liqian-Aspire-E1-471G:/dev/fd#

此处多了个3,也就是又增加了一个设备,这里也可以体会下linux设备即文件的理念。这时候fd3就相当于一个管道了,重定向到fd3中的文件会被写在test中。关闭这个重定向可以用exec 3>&-。

root@liqian-Aspire-E1-471G:/dev/fd# who >&3
root@liqian-Aspire-E1-471G:/dev/fd# ls >&3
root@liqian-Aspire-E1-471G:/dev/fd# exec 3>&-
root@liqian-Aspire-E1-471G:/dev/fd# cat /home/test
liqian :0 2015-12-09 21:20 (:0)
liqian pts/9 2015-12-09 22:11 (:0)

root@liqian-Aspire-E1-471G:/dev/fd#

android利用zbar二维码扫描

之前用zxing做开发,各种奇葩问题,横屏修等等,而且性能也不搞。被测试批了,没办法后来换了zbar。性能好多了。

/*********************重要更新*******************************/

有朋友提到两个问题:

1.扫描框目前只是做的假象,是全屏的图片进行解析

2.中文乱码现象

以上两个问题已经得到解决

/*************************/

直接上图,看看效果

%title插图%num

2.界面上的查找框

/**
* 2014-7-15 上午11:14:21
* Created By niexiaoqiang
*/

package com.example.qu;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.view.View;

/**
* 查找框
* @author niexiaoqiang
*/
public class FinderView extends View {
private static final long ANIMATION_DELAY = 30;
private Paint finderMaskPaint;
private int measureedWidth;
private int measureedHeight;

public FinderView(Context context) {
super(context);
init(context);
}

public FinderView(Context context, AttributeSet attrs) {
super(context, attrs);
init(context);
}

@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawRect(leftRect, finderMaskPaint);
canvas.drawRect(topRect, finderMaskPaint);
canvas.drawRect(rightRect, finderMaskPaint);
canvas.drawRect(bottomRect, finderMaskPaint);
//画框
zx_code_kuang.setBounds(middleRect);
zx_code_kuang.draw(canvas);
if (lineRect.bottom < middleRect.bottom) {
zx_code_line.setBounds(lineRect);
lineRect.top = lineRect.top + lineHeight / 2;
lineRect.bottom = lineRect.bottom + lineHeight / 2;
} else {
lineRect.set(middleRect);
lineRect.bottom = lineRect.top + lineHeight;
zx_code_line.setBounds(lineRect);
}
zx_code_line.draw(canvas);
postInvalidateDelayed(ANIMATION_DELAY, middleRect.left, middleRect.top, middleRect.right, middleRect.bottom);
}

private Rect topRect = new Rect();
private Rect bottomRect = new Rect();
private Rect rightRect = new Rect();
private Rect leftRect = new Rect();
private Rect middleRect = new Rect();

private Rect lineRect = new Rect();
private Drawable zx_code_kuang;
private Drawable zx_code_line;
private int lineHeight;

private void init(Context context) {
int finder_mask = context.getResources().getColor(R.color.finder_mask);
finderMaskPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
finderMaskPaint.setColor(finder_mask);
zx_code_kuang = context.getResources().getDrawable(R.drawable.zx_code_kuang);
zx_code_line = context.getResources().getDrawable(R.drawable.zx_code_line);
lineHeight = 30;
}

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
measureedWidth = MeasureSpec.getSize(widthMeasureSpec);
measureedHeight = MeasureSpec.getSize(heightMeasureSpec);
int borderWidth = measureedWidth / 2 + 100;
middleRect.set((measureedWidth – borderWidth) / 2, (measureedHeight – borderWidth) / 2, (measureedWidth – borderWidth) / 2 + borderWidth, (measureedHeight – borderWidth) / 2 + borderWidth);
lineRect.set(middleRect);
lineRect.bottom = lineRect.top + lineHeight;
leftRect.set(0, middleRect.top, middleRect.left, middleRect.bottom);
topRect.set(0, 0, measureedWidth, middleRect.top);
rightRect.set(middleRect.right, middleRect.top, measureedWidth, middleRect.bottom);
bottomRect.set(0, middleRect.bottom, measureedWidth, measureedHeight);
}
}

2.扫描的activity
package com.example.qu;

import net.sourceforge.zbar.Config;
import net.sourceforge.zbar.Image;
import net.sourceforge.zbar.ImageScanner;
import net.sourceforge.zbar.Symbol;
import net.sourceforge.zbar.SymbolSet;
import android.app.Activity;
import android.hardware.Camera;
import android.hardware.Camera.AutoFocusCallback;
import android.hardware.Camera.PreviewCallback;
import android.hardware.Camera.Size;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Handler;
import android.util.Log;
import android.view.SurfaceHolder;
import android.view.SurfaceView;

public class MainActivity extends Activity implements SurfaceHolder.Callback {
private Camera mCamera;
private SurfaceHolder mHolder;
private SurfaceView surface_view;
private ImageScanner scanner;
private Handler autoFocusHandler;
private AsyncDecode asyncDecode;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.ac_zbar_finder);
init();
}

private void init() {
surface_view = (SurfaceView) findViewById(R.id.surface_view);
mHolder = surface_view.getHolder();
mHolder.addCallback(this);
scanner = new ImageScanner();
scanner.setConfig(0, Config.X_DENSITY, 3);
scanner.setConfig(0, Config.Y_DENSITY, 3);
autoFocusHandler = new Handler();
asyncDecode = new AsyncDecode();
}

@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
if (mHolder.getSurface() == null) {
return;
}
try {
mCamera.stopPreview();
} catch (Exception e) {
}
try {
mCamera.setDisplayOrientation(90);
mCamera.setPreviewDisplay(mHolder);
mCamera.setPreviewCallback(previewCallback);
mCamera.startPreview();
mCamera.autoFocus(autoFocusCallback);
} catch (Exception e) {
Log.d(“DBG”, “Error starting camera preview: ” + e.getMessage());
}
}

/**
* 预览数据
*/
PreviewCallback previewCallback = new PreviewCallback() {
public void onPreviewFrame(byte[] data, Camera camera) {
if (asyncDecode.isStoped()) {
Camera.Parameters parameters = camera.getParameters();
Size size = parameters.getPreviewSize();
Image barcode = new Image(size.width, size.height, “Y800”);
barcode.setData(data);
asyncDecode = new AsyncDecode();
asyncDecode.execute(barcode);
}
}
};

private class AsyncDecode extends AsyncTask<Image, Void, Void> {
private boolean stoped = true;

@Override
protected Void doInBackground(Image… params) {
stoped = false;
Image barcode = params[0];
int result = scanner.scanImage(barcode);
if (result != 0) {
// mCamera.setPreviewCallback(null);
// mCamera.stopPreview();
SymbolSet syms = scanner.getResults();
System.out.println(syms);
for (Symbol sym : syms) {
Log.d(“xiaoqiang”, sym.getData());
}
}
return null;
}

@Override
protected void onPostExecute(Void result) {
super.onPostExecute(result);
stoped = true;
}

public boolean isStoped() {
return stoped;
}
}

/**
* 自动对焦回调
*/
AutoFocusCallback autoFocusCallback = new AutoFocusCallback() {
public void onAutoFocus(boolean success, Camera camera) {
autoFocusHandler.postDelayed(doAutoFocus, 1000);
}
};

//自动对焦
private Runnable doAutoFocus = new Runnable() {
public void run() {
if (null == mCamera || null == autoFocusCallback) {
return;
}
mCamera.autoFocus(autoFocusCallback);
}
};

@Override
public void surfaceCreated(SurfaceHolder holder) {
try {
mCamera = Camera.open();
} catch (Exception e) {
mCamera = null;
}
}

@Override
public void surfaceDestroyed(SurfaceHolder holder) {
if (mCamera != null) {
mCamera.setPreviewCallback(null);
mCamera.release();
mCamera = null;
}
}
}

 

Shell中exec、source和shell script的区别

exec和source都属于bash内部命令(builtins commands),在bash下输入man exec或man source可以查看所有的内部命令信息。
bash shell的命令分为两类:外部命令和内部命令。外部命令是通过系统调用或独立的程序实现的,如sed、awk等等。内部命令是由特殊的文件格式(.def)所实现,如cd、history、exec等等。

在说明exe和source的区别之前,先说明一下fork的概念。

fork是linux的系统调用,用来创建子进程(child process)。子进程是父进程(parent process)的一个副本,从父进程那里获得一定的资源分配以及继承父进程的环境。子进程与父进程唯一不同的地方在于pid(process id)。

环境变量(传给子进程的变量,遗传性是本地变量和环境变量的根本区别)只能单向从父进程传给子进程。不管子进程的环境变量如何变化,都不会影响父进程的环境变量。

shell script:

有两种方法执行shell scripts,一种是新产生一个shell,然后执行相应的shell scripts;一种是在当前shell下执行,不再启用其他shell。
新产生一个shell然后再执行scripts的方法是在scripts文件开头加入以下语句
#!/bin/sh
一般的script文件(.sh)即是这种用法。这种方法先启用新的sub-shell(新的子进程),然后在其下执行命令。
另外一种方法就是上面说过的source命令,不再产生新的shell,而在当前shell下执行一切命令。

source:

source命令即点(.)命令。
在bash下输入man source,找到source命令解释处,可以看到解释”Read and execute commands from filename in the current shell environment and …”。从中可以知道,source命令是在当前进程中执行参数文件中的各个命令,而不是另起子进程(或sub-shell)。

exec:

在bash下输入man exec,找到exec命令解释处,可以看到有”No new process is created.”这样的解释,这就是说exec命令不产生新的子进程。那么exec与source的区别是什么呢?
exec命令在执行时会把当前的shell process关闭,然后换到后面的命令继续执行。

三大操作系统比较,为什么开发要用Linux系统?

交流学习java大数据加QQ群460570824.

windows, macOS,linux是当今主流三大操作系统,普通用户一般是选择windows或macOS, linux主要是占据服务器领域市场。这三个操作系统给人的感觉是:windows经济适用,档次较低; macOS高端大气上档次; Linux是*客专用,一个黑乎乎的窗口,各种花花绿绿的指令在闪烁,对着键盘噼里啪啦一通乱敲,就能窃取各种机密…

普通用户对于操作系统的选择很简单,经济实用选windows,钱多讲究格调选macOS, 毕竟普通用户切换操作系统的成本并不大。而程序员就不一样了,换一个操作系统,就意味着所有开发环境都要一并更换。而且在程序员群体中,操作系统有以下的鄙视链:macOS–>linux–>windows。

其实操作系统的比较并不是简单粗暴的单一维度比较,用windows并不意味着low,用linux也不等同于你就是一个*客。关键在于你是如何使用。就好比编程语言的选择,php是*招人黑的语言(没有之一), 但黑php的人中有不少人写的代码同样不堪入目。

不过作为一个程序员,你很有必要学会linux,如果你的程序*终是跑在线上的Linux服务器上,那么你就更应该从现在开始投入linux的怀抱中,早日从windows脱坑。至于macOS,在命令行上与linux*大部分相同,都是类unix的操作系统。简单地说,macOS是一个比windows界面更加美观,同时又兼备linux强大命令行的操作系统. 要说macOS的缺点,那就是贵…

windows与linux的根本区别:自由
%title插图%num

windows与linux的设计理念有根本性的区别:

windows:用户不知道自己想要什么,也不明白自己在做什么,更不打算为自己的行为负责。

因此windows将所有操作都隐藏起来,只给用户提供封装好的功能,用户只能在操作系统限制的范围内操作,如果是普通用户,会觉得很windows很舒服,因为不需要思考。只需要按照指示去操作。但对于开发人员而言,这种设计理念是无法接受的,一旦要做出一些超越封装好的功能之外的事情,就会出现各种难以意料的情况,而且很多情况下,这些问题是无解的。或者只能用*其蹩脚扭曲的方式去勉强处理,然后瑟瑟发抖地期待着程序能正常运行。因为一旦程序崩溃,你也会为之崩溃。在windows下作开发,那种体验就好比在陪伴一个任性的女朋友,虽然长得清秀靓丽,但喜怒无常,她开心时,彼此相安无事,她不开心时,就直接哭闹,你问她:怎么了?她也不说原因,只是一直哭。你只能试着用各种方式去哄她开心,即使这次能哄好她,并不意味着下次同样的方法能奏效。这样的相处方式,很累。不是她不好,而是彼此不适合。即使终日相伴,却依然对其一无所知。

linux:用户知道自己想要什么,也明白自己在做什么,并且会为自己的行为负责。

linux将所有操作权都交给了用户,她相信用户是理性的聪明的,忠实地执行用户的指令,向用户暴露所有的细节。用户在拥有自主权的同时也拥有了破坏力,因此普通用户根本无法驾驭,可能一个指令就把操作系统弄崩溃了。对于开发者而言,linux的开放与自由给了我们无限的可能性,我们能看到程序是如何运行的,运行报错也会有友好的提示。根据报错指引往往能将问题解决。与之相处,好比与一个成熟的女性相处,历经风雨,已经不再关注浮夸的外表,而是关注内在的涵养。刚接触时,可能会觉得她高冷,慢慢地,会发现她很善解人意,心情不好时她会跟你说是什么原因造成的,彼此商量如何解决这个问题,而不是无来由地崩溃。与之相处,会感到很舒服,彼此坦诚,无需套路,你在想什么,我都知道。我在念什么,你也明白。

因此,我选择linux的原因在于自由、舒服,简言之:爽!

至于win10自带的WSL(linux子系统), 说起来都是泪,几个月前,我兴冲冲地折腾,以为windows可以完美地支持linux,结果,连个nginx都跑不起来,去wsl的github源码查看issue,才知道这就是wsl的一个bug!此外,还有其他各种坑,这感觉就像:分手后的任性前女友画风突变:善解人意,人情练达。你欣喜万分地准备复合,她马上又原形毕露。咱能不能多些真诚,少些套路…

迅哥说过:生命是以时间为单位的,浪费别人的时间等于谋财害命,浪费自己的时间等于慢性自杀。对于程序员而言,开发环境远离windows,就是珍惜生命的表现(开发windows程序的除外)

选择哪个发行版的linux

redhat,suse, Debian等各个发行版的linux都有各自的忠实拥护者,无法判断这些发行版孰优孰劣,毕竟“存在即合理”。我从实用的角度来看,选择的是redhat(红帽)系列的centos。

因为线上的服务器大多数是ceotos,我们的程序*终是在线上运行的,如果能将本地环境配置得跟线上一致,就可以减少很多莫名其妙的环境问题。而且熟悉使用centos,基础的运维能力也都掌握了

至于版本选择,肯定是要选择*新的centos7, centos7与centos6存在很多差异,性能有很大的提升

如何学linux

工作中,看到不少工作好几年的程序员,在操作linux时特别生疏,只会*基本的几个命令:cdls mkdir 等。vim编辑器的操作更是让人看了很着急,被誉为编辑器之神的vim在他们手中,完全没有半点灵气,感觉就像是编辑器之屎。

之所以会出现这种现象,很大原因在于他们的学习方式错了,不少人都知道linux很重要,不掌握linux,基本上就只能停留在*基本的增删改查功能上。他们学linux的方式可能是看书,也可能是看视频,甚至有些还会去背命令,但由于他们的操作环境是windows,linux的应用场景很少。根本没办法将所学的知识运用,更加感受不到linux的魅力。

学linux*好地方式,就是直接去用!直接将自己的开发环境都改成linux,一开始很蹩脚,很不适应,这很正常。如果你一直感到很舒服,只能说明你一直没有进步。想想我们学了那么多年英语,*大多数人还是无法掌握英语,看到英语文档就直接自动屏蔽。其原因都是:一直在学,但从来没在用。只学而不用,没有半点用。

 

原来 Linux 内核贡献第二是这么来的

原来 Linux 内核贡献第二是这么来的
27 labulaka521 · labulaka521 · 4 小时 38 分钟前 · 9803 次点击
%title插图%num

Linux 内核 贡献 原来139 条回复 • 2021-06-21 16:46:18 +08:00
1 2
2
❮ ❯
ohwind 101
ohwind 1 小时 47 分钟前 ❤️ 2
@Iserlohn44 这贴下面的不都在讨论这个事?贡献无用代码增加工作量不能嘲讽两句吗?我觉得这种不道德的行为应该被谴责。
Samuelcc 102
Samuelcc 1 小时 46 分钟前 via Android ❤️ 1
@hwdef 你一直让我回答你的问题,我如实回答,没有任何说我“高大上”的意思。是的,大部分开发者没有给 linux 这样的核心项目提交过代码,你就咬定这一点就可以随便喷人了是吗?
另外我发 issue 是有具体的解决方案的,不是只提出问题让开发者去解决。另外这也是开源社区的一部分,我也没有看到哪个大型项目维护者辱骂提出 issue 的人,称他们恶心。你又是在那个位置和立场在这里怒骂呢?
另外我一直觉得你阅读是不是有障碍,邮件中并不是说这类 pr 是违反规则的,而是说华为这种大型公司不应该一直以提大量这种没有技术含量的 pr 来刷 kpi 。你好像一直没理解,令人遗憾。
wuqingdzx 103
wuqingdzx 1 小时 43 分钟前 ❤️ 1
有一说一. 这人及类似行为确实问题很大, 可耻.

但我觉得吧, 上升到公司层面的话, 在注水比例不明确之前, 不至于这么高潮吧?
washbrain 104
washbrain 1 小时 43 分钟前
看了下知乎,有一种可能是华为内部在做代码质量优化,作者可能是修改了公司内部的 linux 版本然后同步到上游?当然提交的方式不太正确
感觉还是有受公司政策影响的
Iserlohn44 105
Iserlohn44 1 小时 40 分钟前
@ohwind 你是不是理解不了什么叫「就事论事」?还有你仿佛只会二分的逻辑,实在让人无语。
ladypxy 106
ladypxy 1 小时 38 分钟前
有一说一,这种明显就是刷 KPI 的。
像我在某血汗工厂,公司会看你写了多少 CR 。然后大家就就故意一个 CR 修复一个 typo…虽然明明是一个 CR 搞定的事
hwdef 107
hwdef 1 小时 34 分钟前
@Samuelcc

我觉得你先开喷的,你觉得我先开喷的,

我们对于“喷”的定义不同,没必要讨论了。
atwoodSoInterest 108
atwoodSoInterest 1 小时 31 分钟前
子贡赎牛,老典故了。利益不是罪恶,合理利用利益来导向世界才是正解。

其实题上这种拍桌而起的都是内心干净的人,但是这种干净却又和世界无序脏乱冲突,变成了清厉。
我能理解这种人,因为我也深陷其中,至今仍在挣扎。
但是为社会为国为世界,不是他们这种偏执的人能胜任的,需要更有容量更有眼界的人。
ohwind 109
ohwind 1 小时 28 分钟前 ❤️ 8
@Iserlohn44
>就事论事,读音是 jiù shì lùn shì,汉语成语,依据事情本身的情况来作评论或判断。现又指仅从事物的表面现象孤立、静止、片面地议
以及究竟是谁只会二分?
一,华为是否做了这件事?做了。
二,这种行为是道德的吗?不道德。
三,针对这种行为可以谴责嘲讽吗?不道德的行为我认为嘲讽几句无关紧要。
请问是谁没有就事论事?
yukiww233 110
yukiww233 1 小时 27 分钟前
之前的 commit 还挺正常的啊…
2021 之后就几乎全是 fix error return code, Remove redundant, fix spelling 和 reformat , 分布还很均匀, 是 kpi 要求没跑了

shuAS 111
shuAS 1 小时 21 分钟前 ❤️ 1
刚一打开帖子看评论,我还以为进到品葱了呢?
idealhs 112
idealhs 1 小时 20 分钟前
@atwoodSoInterest 赎人受牛。
你想表达的是,华为的 KPI 导向有问题是吗。
lakehylia 113
lakehylia 1 小时 19 分钟前
想起了新的 HR 以代码量来评价工作量,于是就有了各种斗法
GalaDOS 114
GalaDOS 1 小时 14 分钟前
already broken reputation 很刺眼呀
Iserlohn44 115
Iserlohn44 1 小时 12 分钟前
@ohwind 原来在你看来无意义的嘲讽,而不是针对事情本身发出有建设性的评论也是就事论事。那确实不用聊了。
其次,你居然真能问出「究竟是谁只会二分」?不会看看自己的发言 – 「只许夸了是吧」?
nicevar 116
nicevar 1 小时 10 分钟前 ❤️ 4
总有些人逮住华为一丁点东西就能嘲讽整个华为甚至上升到民族和国家,脑袋被驴踢过一样,这个地方很多人黑华为已经没有下限了。
phh 117
phh 1 小时 4 分钟前 ❤️ 24
说黑华为没有下限的,那是因为你们吹华为没有底线让人家反感了,遭到反噬了,什么东西都要吹嘘上升到民族高度,先有华为后有天的话都敢说。
ohwind 118
ohwind 1 小时 1 分钟前
@Iserlohn44 对这件事建设性的评论就是这种行为十分无耻以及十分可恶,不然要怎么评论?说他干的好?讨论它的合理性?
对基本是非没有自己的判断,见不得别人对自己维护的产品的批评,这和娱乐圈玩的那一套有何区别?
或者你认为把别人的批评说成“高潮了”是富有建设性的?
dustinth 119
dustinth 57 分钟前
比较好奇这个帖子里面有没有华为的. 哈哈.
做错事就认, 没有做错事, 没有刷 KPI, 理直气壮的就怼回去好了.
turi 120
turi 56 分钟前
提到华为,一帮人就想到国家。

厉害了
sunocean 121
sunocean 55 分钟前 ❤️ 10
@nicevar 华为跟国家和民族有什么关系?你这帖子是想表达什么呢?一个公司做错事了,大众批评他就叫黑吗?一般认为,有事实有依据的事儿叫批评指正,无端臆造才叫黑。你无端把一个没有道德底线的公司和一个国家和民族捆绑在一起,我们有理由怀疑你脑袋被驴踢了,在黑这个国家,黑这个民族。
baiyi 122
baiyi 55 分钟前
华为用这种方式刷成贡献第二也是真的丢人,这样刷 KPI,公司有问题,人也有问题
ohwind 123
ohwind 54 分钟前
@Iserlohn44 请您正面回答我的问题。
1,“有些人真的高潮了,终于找到了喷点了哈哈”这句话是否是在就事论事,是否是在提出”建设性的评论”?
2,对于这种不道德的行为楼下的嘲讽和批评是否是理所应当的?
3,对于[1]的不友好发言我反驳”只允许夸了是吧”是否是在就他的评论”就事论事”?
ugly 124
ugly 48 分钟前
我真的很烦说讲不买华为不爱国思想的人,这事本来就是一群爱国的人在网上表示自己支持中国支持国产,被一群不在乎爱不爱国的人放大成恶心,嘲讽。
Iserlohn44 125
Iserlohn44 47 分钟前
@ohwind 你看,指出你发言的问题就给说成了「对基本是非没有自己的判断」。稍微说句不好听的,你也太把自己当回事了。
twor2 126
twor2 43 分钟前
@ohwind 我知道哪里别扭了,逻辑错在哪里了
因为是华为的错(或者华为某些人),但你嘲讽的不是华为
yigecaiji 127
yigecaiji 41 分钟前
明明我们在嘲讽华为,怎么有人直接上升到民族和国家了
yunshansimon 128
yunshansimon 38 分钟前
华为干得好,什么道德之类的,都是西方国家制定的标准,作为强大如华为者,作为中国的代表,就是要打破西方制定的道德标准和底线,坚持填充垃圾,就是不改。看到华为这么流氓,我们中国人民就放心了!希望广大爱国程序员也学习华为,为华为的项目多多贡献垃圾!为建设伟大华为贡献自己的力量!
ohwind 129
ohwind 36 分钟前 ❤️ 1
@Iserlohn44 一边要求别人「就事论事」,一边回避我的提问,一边对我进行攻击。想必双重标准不外如是吧?我想孰是孰非已有结论,我们两个的讨论就此终止,我也不愿意与你浪费电力。
ioriwong 130
ioriwong 33 分钟前
无耻则无敌
polo3584 131
polo3584 29 分钟前
本来说的是 leizheng 这个开发者,然后就上升到了华为,然后上升到了国家和民族,真有意思。
libook 132
libook 27 分钟前
开源社区也是一个项目团队。

从项目管理的角度上来说,几乎所有问题都可以通过约定规则、流程来解决,所以有问题应该讨论一下:
1. 问题是否实际存在?
2. 影响范围如何?
3. 可以约定什么样的规则、流程来解决和避免这个问题?

华为员工有目的不纯的动机,邮件发送者也有“言外之意”的嫌疑(毕竟邮件里只有无证据的指控,而缺乏建设性)。

如果后续邮件没有人出来按照一个项目问题的正常解决流程来协调解决的话,那么项目管理本身可能是这个社区目前*大的问题。
zchlwj 133
zchlwj 27 分钟前 ❤️ 1
@phh 先有华为后有天的话 我在你这里*次看到
Iserlohn44 134
Iserlohn44 26 分钟前
@ohwind 不好意思,并非回避问题,而是没有看到你的第二条回复。我的上一条回复是针对你的 118 号评论。
针对你的问题:
1. 当然不是;
2. 无意义的嘲讽不应当,有道理的批评当然是应当的;
3. 并不是。用无逻辑去回应无逻辑,不代表你就是对的。

我发现你似乎把指出你的发言问题等价成了认为华为这件事做的对,甚至延伸到了「对基本是非没有自己的判断」,你不觉得太自以为是了吗?
tt67wq 135
tt67wq 22 分钟前
国内公司做开源的态度都有点问题,为了 KPI 一哄而上,KPI 有了就不维护了
miniwade514 136
miniwade514 21 分钟前
建议送进本站的精华版块(水深火热)
manzhiyong 137
manzhiyong 12 分钟前
这个作者真是丑陋,辱骂未开源提供贡献的人。
fanlis 138
fanlis 10 分钟前 ❤️ 1
进来看到前几排,以为进入 4V 社区
喷华为可以,随便喷,不要扯着你的破脸阴阳怪气中国,搁谁看不出来?
nmecury 139
nmecury 3 分钟前
@hwdef 我是某大型项目的使用者,用的时候发现了隐蔽的严重 bug,又不会修,只好提了个 issue,被打了 high-priority 的 tag,然后在大版本更新里被修复了。能不能请您说说我怎么恶心了?啥时候开源社区出了您这么一号大人物?

友情链接: SITEMAP | 旋风加速器官网 | 旋风软件中心 | textarea | 黑洞加速器 | jiaohess | 老王加速器 | 烧饼哥加速器 | 小蓝鸟 | tiktok加速器 | 旋风加速度器 | 旋风加速 | quickq加速器 | 飞驰加速器 | 飞鸟加速器 | 狗急加速器 | hammer加速器 | trafficace | 原子加速器 | 葫芦加速器 | 麦旋风 | 油管加速器 | anycastly | INS加速器 | INS加速器免费版 | 免费vqn加速外网 | 旋风加速器 | 快橙加速器 | 啊哈加速器 | 迷雾通 | 优途加速器 | 海外播 | 坚果加速器 | 海外vqn加速 | 蘑菇加速器 | 毛豆加速器 | 接码平台 | 接码S | 西柚加速器 | 快柠檬加速器 | 黑洞加速 | falemon | 快橙加速器 | anycast加速器 | ibaidu | moneytreeblog | 坚果加速器 | 派币加速器 | 飞鸟加速器 | 毛豆APP | PIKPAK | 安卓vqn免费 | 一元机场加速器 | 一元机场 | 老王加速器 | 黑洞加速器 | 白石山 | 小牛加速器 | 黑洞加速 | 迷雾通官网 | 迷雾通 | 迷雾通加速器 | 十大免费加速神器 | 猎豹加速器 | 蚂蚁加速器 | 坚果加速器 | 黑洞加速 | 银河加速器 | 猎豹加速器 | 海鸥加速器 | 芒果加速器 | 小牛加速器 | 极光加速器 | 黑洞加速 | movabletype中文网 | 猎豹加速器官网 | 烧饼哥加速器官网 | 旋风加速器度器 | 哔咔漫画 | PicACG | 雷霆加速