QT for android 获取PDA扫码的广播数据

前记

笔者接触到的PDA扫码枪,有三种, 一种是蓝牙枪,连接到手机或者电脑上之后,可以直接把扫描到的数据传送到焦点所在的文本框中;另外两种是普通的掌上扫码枪,笔者手中的一款也可以和蓝牙枪一样,操作系统中有设置,可以把扫描到的数据直接送到焦点所在的文本框中;还有一款是操作系统中没并有这个设置,而是通过Android的广播机制来进行发送,而前一种实际也是利用广播机制。
在Android Studio中,这一机制还是比较容易实现的,下面这一段Kotlin代码可以实现:
在这里插入图片描述
而在Qt中,如果要利用Android的广播,C++和QML都无法直接实现,需要用到Java。Qt官方有两个实例:Jni Messenger,Android Service with BroadcastReceiver,在Qt的欢迎界面搜索就可以查看源代码。这两个实例比较好的解释了广播机制的运用。

参考

笔者这里参考了一篇文章:QT for android 获取PDA扫码的广播数据并在QML中显示,这篇文章中实现了通过广播获取扫描到的数据的过程,但是这个数据只停留在Java中,C++页面如果想获取数据,必须要C++自己去Java中拿,Java不能直接把数据推给C++。博主的方法可能是设置个时间,然后不停地刷新向Java要数据。

实现

笔者这里的实现是以Qt的实例Android Service with BroadcastReceiver为基础,作了下修改。在QT for android 获取PDA扫码的广播数据并在QML中显示这篇博文中,还有QtAndroid详解(1):QAndroidJniObject这一篇中,都有介绍怎么让C++获取Java的数据,所以笔者这里加入这一块,代码只有Java主动推送给C++的功能。

AndroidManifest.xml

上面两篇博文中好像都没有写到对这个文件的修改,实际上这个文件很重要,在Qt6.0版本之后,QtCreater默认显示的界面,只展示了可以让开发者修改的部分,避免修改不必要的部分。QT for android 获取PDA扫码的广播数据并在QML中显示,这篇文章中的MainActivity.java是扩展了Activity类,所以有一处修改:
在这里插入图片描述
而Qt官方的实例Android Service with BroadcastReceiver,这里没有修改,但是也对AndroidManifest.xml文件有要求,修改的地方如下:
在这里插入图片描述
原本是false,需要改为true。

包名

Qt官方的实例Android Service with BroadcastReceiver中引入了两个Java文件,ActivityUtils.java和QtAndroidService.java,这两个文件的头一行均是package org.qtproject.example.qtandroidservice;
如果要自己使用这两个文件的话,这里需要更改为自己的包名,同样,也是在AndroidManifest.xml一起修改:
在这里插入图片描述
需要注意的是,这两个Java文件不是放在.pro文件同级目录中,Qtt生成AndroidManifest.xml文件时会生成一个android文件夹,在这下面新建一个src文件夹,然后根据包名往下新建文件夹:包名是org.qtproject.example的话,就依次新建org->qtproject->example,然后把两个Java文件放在example文件夹中,最后在QtCreater中添加这两个文件。

源码

工程项目源文件不附了,这里附4个文件的源码,如前述,主功能为Java通过广播获取PDA扫描的数据,然后把数据主动推给C++,笔者这里没有用QML,C++与QML通信请参考其他。
mainwindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <QObject>
#include <QtAndroid>
#include <QAndroidIntent>

QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    MainWindow(QWidget *parent = nullptr);
    ~MainWindow();
    static MainWindow *instance() { return m_instance; }
public slots:
    void slot_test(QString);

private:
    Ui::MainWindow *ui;
private:
    void registerNatives();
    void registerBroadcastReceiver();

    static MainWindow *m_instance;
};
#endif // MAINWINDOW_H

mainwindow.cpp

#include "mainwindow.h"
#include "ui_mainwindow.h"

#include <QAndroidJniEnvironment>
#include <QAndroidIntent>
#include <QtDebug>
MainWindow *MainWindow::m_instance = nullptr;

static void receivedFromAndroidService(JNIEnv *env, jobject /*thiz*/, jstring value)
{
    emit MainWindow::instance()->slot_test(env->GetStringUTFChars(value, nullptr));
}
MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    m_instance = this;

    registerNatives();
    registerBroadcastReceiver();
}
void MainWindow::slot_test(QString x)
{
    ui->textBrowser->append(x);
}
MainWindow::~MainWindow()
{
    delete ui;
}

void MainWindow::registerNatives()
{
    JNINativeMethod methods[] {
        {"sendToQt", "(Ljava/lang/String;)V", reinterpret_cast<void *>(receivedFromAndroidService)}};
    QAndroidJniObject javaClass("org/qtproject/example/ActivityUtils");

    QAndroidJniEnvironment env;
    jclass objectClass = env->GetObjectClass(javaClass.object<jobject>());
    env->RegisterNatives(objectClass,
                         methods,
                         sizeof(methods) / sizeof(methods[0]));
    env->DeleteLocalRef(objectClass);
}

void MainWindow::registerBroadcastReceiver()
{
    QAndroidJniEnvironment env;
    jclass javaClass = env.findClass("org/qtproject/example/ActivityUtils");
    QAndroidJniObject classObject(javaClass);

    classObject.callMethod<void>("registerServiceBroadcastReceiver",
                                 "(Landroid/content/Context;)V",
                                 QtAndroid::androidContext().object());
}

ActivityUtils.java

package org.qtproject.example;

import android.content.Context;
import android.content.Intent;
import android.util.Log;
import android.content.BroadcastReceiver;
import android.content.IntentFilter;

public class ActivityUtils {
    private static native void sendToQt(String message);
    private static final String TAG = "ActivityUtils";
    public static final String BROADCAST_NAME_ACTION = "android.intent.ACTION_DECODE_DATA";

    public void registerServiceBroadcastReceiver(Context context) {
        IntentFilter intentFilter = new IntentFilter();
        intentFilter.addAction(BROADCAST_NAME_ACTION);
        context.registerReceiver(serviceMessageReceiver, intentFilter);
        Log.i(TAG, "Registered broadcast receiver");
    }
    private BroadcastReceiver serviceMessageReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            Log.i(TAG, "In OnReceive broadcast receiver");
            if (BROADCAST_NAME_ACTION.equals(intent.getAction())) {
                String name = intent.getStringExtra("barcode_string");
                Log.i(TAG, "Service received name: " + name);
                String message = "Hello " + name;
                sendToQt(message);
                Log.i(TAG, "Service sent back message: " + message);
            }
        }
    };
}

QtAndroidService.java

package org.qtproject.example;

import android.content.Context;
import android.content.Intent;
import android.util.Log;
import android.os.IBinder;
import org.qtproject.qt5.android.bindings.QtService;
import android.content.IntentFilter;

public class QtAndroidService extends QtService
{
    private static final String TAG = "QtAndroidService";
    @Override
    public void onCreate() {
        super.onCreate();
        Log.i(TAG, "Creating Service");
    }
    @Override
    public void onDestroy() {
        super.onDestroy();
        Log.i(TAG, "Destroying Service");
    }
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        int ret = super.onStartCommand(intent, flags, startId);

        String name = new String(intent.getByteArrayExtra("barcode_string"));
        Intent sendToUiIntent = new Intent();
        sendToUiIntent.setAction(ActivityUtils.BROADCAST_NAME_ACTION);
        Log.i(TAG, "Service sending broadcast");
        return ret;
    }
    @Override
    public IBinder onBind(Intent intent) {
        return super.onBind(intent);
    }
}

笔者的Qt版本更新到了6.2.4,但是官方的例程是5.15.2,在6.0版本之后,Qt取消了androidextras模块,Android相关的QAndroidJniObject、QAndroidJniEnvironment等都并入了core中,所以如果使用6.0之后版本尝试的话,在.pro文件中不需要再添加QT+=androidextras。
更新日期:2022-03-25,此外,整个过程并没有研究透,所以博文写的也不透亮,稍后会写更详细。

优博讯i6200s,安卓4.1版本

private String barcodeStr;
public static final String BROADCAST_NAME_ACTION = "urovo.rcv.message";、
byte[] barcode = intent.getByteArrayExtra("barcode");
int barocodelen = intent.getIntExtra("length", 0);
barcodeStr = new String(barcode, 0, barocodelen);
Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐