在 delphi android 应用程序中使用 java .jar 库时出现问题



我正在尝试在Delphi android应用程序中使用java API来访问和控制android PDT设备扫描仪服务。前段时间,我已经在不同的设备上遇到了类似的问题,但后来它变得更加简单和直接。接口部分中公开的所有函数和属性。这次不一样了,我使用java和接口的知识太差了,无法弄清楚这一点。这一次,所有过程都在类定义中公开,而在接口部分没有,我不知道如何访问和使用它们。我花了几天时间寻找类似问题和其他设备或 API 的线索和解决方案。有一些,但所有的过程已经在接口部分中公开,或者必须编写自定义 java 类以公开基于 android studio 演示应用程序的 api 的功能。

使用其他设备,我只需要此代码即可访问扫描仪

var 
ScanDevice: JScanDevice;
begin
ScanDevice := TJScanDevice.Create;
ScanDevice.setScanCodeEnterKey;
ScanDevice.setOutScanMode(0);
ScanDevice.openScan;
.......

带有包含接口的桥接文件:

type
JScanDevice = interface; { android/device/ScanDevice }
JScanDeviceClass = interface(JObjectClass)
['{58979737-4A26-4134-A604-C6FAB0D224CA}']
{ Methods }
function init: JScanDevice; cdecl;
function toHexString(s: JString): JString; cdecl;
end;
[JavaSignature('android/device/ScanDevice')]
JScanDevice = interface(JObject)
['{A11B33C8-394C-4180-9003-9C66326B4A2B}']
{ Methods }
function closeScan: Boolean; cdecl;
.......
function stopScan: Boolean; cdecl;
function openScan: Boolean; cdecl;
end;
TJScanDevice = class(TJavaGenericImport<JScanDeviceClass, JScanDevice>)

但是AUTOID6L不同,桥接文件是这样的:

Jscanner_ScannerClass = interface(JObjectClass)
['{84F85EBE-57DD-48C5-B2AE-38D3A5E31355}']
{ class } procedure close; cdecl;
........
{ class } procedure stopScan; cdecl;
end;
[JavaSignature('com/seuic/scanner/Scanner')]
Jscanner_Scanner = interface(JObject)
['{692FF5AE-6C4B-4222-B858-DA029630F7A5}']
end;
TJscanner_Scanner = class(TJavaGenericImport<Jscanner_ScannerClass, Jscanner_Scanner>)
end;

我不知道如何正确使用该接口和类,无法访问类函数。

PDT设备是Seuic AUTOID6L,它带有用于其多个设备的通用SDK。对于android扫描仪应用程序,有ScannerAPI.jar带有com\seuic\scannerScanner.class,com\seuic\ScannerFactory.class类文件的库。

java2op.exe 到生成的桥文件:

unit SeuicScanner;
interface
uses
Androidapi.JNIBridge,
Androidapi.JNI.GraphicsContentViewText,
Androidapi.JNI.JavaTypes,
Androidapi.JNI.Os;
type
// ===== Forward declarations =====
JAccount             = interface; // android.accounts.Account
Jscanner_BuildConfig = interface; // com.seuic.scanner.BuildConfig
JDecodeInfo          = interface; // com.seuic.scanner.DecodeInfo
JDecodeInfoCallBack  = interface; // com.seuic.scanner.DecodeInfoCallBack
Jscanner_Scanner     = interface; // com.seuic.scanner.Scanner
JScannerFactory      = interface; // com.seuic.scanner.ScannerFactory
JScannerKey          = interface; // com.seuic.scanner.ScannerKey
JStatusCallBack      = interface; // com.seuic.scanner.StatusCallBack
JVideoCallBack       = interface; // com.seuic.scanner.VideoCallBack
// JStringBuffer = interface;//java.lang.StringBuffer
// JStringBuilder = interface;//java.lang.StringBuilder
// ===== Interface declarations =====
JAccountClass = interface(JObjectClass)
['{94EE6861-F326-489F-8919-E20B39E3D9C1}']
{ class } function _GetCREATOR: JParcelable_Creator; cdecl;
{ class } function _Getname: JString; cdecl;
{ class } function _Gettype: JString; cdecl;
{ class } function init(name: JString; type_: JString): JAccount; cdecl; overload; // Deprecated
{ class } function init(in_: JParcel): JAccount; cdecl; overload; // Deprecated
{ class } function describeContents: Integer; cdecl; // Deprecated
{ class } function equals(o: JObject): Boolean; cdecl; // Deprecated
{ class } property CREATOR: JParcelable_Creator read _GetCREATOR;
{ class } property name: JString read _Getname;
{ class } property &type: JString read _Gettype;
end;
[JavaSignature('android/accounts/Account')]
JAccount = interface(JObject)
['{71476381-8B6E-471F-9189-9857ECD7508C}']
function hashCode: Integer; cdecl; // Deprecated
function toString: JString; cdecl; // Deprecated
procedure writeToParcel(dest: JParcel; flags: Integer); cdecl; // Deprecated
end;
TJAccount = class(TJavaGenericImport<JAccountClass, JAccount>)
end;
Jscanner_BuildConfigClass = interface(JObjectClass)
['{6AD54B95-FC70-418E-AA52-5846F17E981F}']
{ class } function _GetAPPLICATION_ID: JString; cdecl;
{ class } function _GetBUILD_TYPE: JString; cdecl;
{ class } function _GetDEBUG: Boolean; cdecl;
{ class } function _GetFLAVOR: JString; cdecl;
{ class } function _GetVERSION_CODE: Integer; cdecl;
{ class } function _GetVERSION_NAME: JString; cdecl;
{ class } function init: Jscanner_BuildConfig; cdecl;
{ class } property APPLICATION_ID: JString read _GetAPPLICATION_ID;
{ class } property BUILD_TYPE: JString read _GetBUILD_TYPE;
{ class } property DEBUG: Boolean read _GetDEBUG;
{ class } property FLAVOR: JString read _GetFLAVOR;
{ class } property VERSION_CODE: Integer read _GetVERSION_CODE;
{ class } property VERSION_NAME: JString read _GetVERSION_NAME;
end;
[JavaSignature('com/seuic/scanner/BuildConfig')]
Jscanner_BuildConfig = interface(JObject)
['{1584DFE9-2B25-481A-8AF3-0E512A80A042}']
end;
TJscanner_BuildConfig = class(TJavaGenericImport<Jscanner_BuildConfigClass, Jscanner_BuildConfig>)
end;
JDecodeInfoClass = interface(JObjectClass)
['{D37439A1-F763-4798-8291-30E01196985A}']
{ class } function _Getbarcode: JString; cdecl;
{ class } function init: JDecodeInfo; cdecl;
{ class } property barcode: JString read _Getbarcode;
end;
[JavaSignature('com/seuic/scanner/DecodeInfo')]
JDecodeInfo = interface(JObject)
['{0169284C-55E4-45BF-B648-87471A0AB873}']
function _Getcodetype: JString; cdecl;
function _Getlength: Integer; cdecl;
property codetype: JString read _Getcodetype;
property length: Integer read _Getlength;
end;
TJDecodeInfo = class(TJavaGenericImport<JDecodeInfoClass, JDecodeInfo>)
end;
JDecodeInfoCallBackClass = interface(IJavaClass)
['{81EA2AA6-F12D-41D0-ADA4-61EEB0F3FB9D}']
{ class } procedure onDecodeComplete(P1: JDecodeInfo); cdecl;
end;
[JavaSignature('com/seuic/scanner/DecodeInfoCallBack')]
JDecodeInfoCallBack = interface(IJavaInstance)
['{CE9270B9-CD79-4FE7-8C29-7BFA9C2D8804}']
end;
TJDecodeInfoCallBack = class(TJavaGenericImport<JDecodeInfoCallBackClass, JDecodeInfoCallBack>)
end;
Jscanner_ScannerClass = interface(JObjectClass)
['{84F85EBE-57DD-48C5-B2AE-38D3A5E31355}']
{ class } procedure close; cdecl;
{ class } procedure disable; cdecl;
{ class } procedure enable; cdecl;
{ class } function getLastImage: TJavaArray<Byte>; cdecl;
{ class } function getParams(P1: Integer): Integer; cdecl;
{ class } function open: Boolean; cdecl;
{ class } procedure setDecodeInfoCallBack(P1: JDecodeInfoCallBack); cdecl;
{ class } function setParams(P1: Integer; P2: Integer): Boolean; cdecl;
{ class } procedure setStatusCallBack(P1: JStatusCallBack); cdecl;
{ class } procedure setVideoCallBack(P1: JVideoCallBack); cdecl;
{ class } procedure startScan; cdecl;
{ class } function startVideo(P1: Integer): Integer; cdecl;
{ class } procedure stopScan; cdecl;
{ class } procedure stopVideo; cdecl;
end;
[JavaSignature('com/seuic/scanner/Scanner')]
Jscanner_Scanner = interface(JObject)
['{692FF5AE-6C4B-4222-B858-DA029630F7A5}']
end;
TJscanner_Scanner = class(TJavaGenericImport<Jscanner_ScannerClass, Jscanner_Scanner>)
end;
JScannerFactoryClass = interface(JObjectClass)
['{00796D3B-F41D-4FC1-9F68-475CE7DB738E}'] { class }
function init: JScannerFactory; cdecl;
function getScanner(P1: JContext): Jscanner_Scanner; cdecl;
end;
[JavaSignature('com/seuic/scanner/ScannerFactory')]
JScannerFactory = interface(JObject)
['{77AE2625-EEFB-4EF2-838E-D8178AF6A9BD}']
end;
TJScannerFactory = class(TJavaGenericImport<JScannerFactoryClass, JScannerFactory>)
end;
JScannerKeyClass = interface(JObjectClass)
['{00D92230-CBC1-42EF-A1BA-7A84E4E70AB9}']
{ class } function _GetKEY_DOWN: Integer; cdecl;
{ class } function _GetKEY_UP: Integer; cdecl;
{ class } procedure close; cdecl;
{ class } function getKeyEvent: Integer; cdecl;
{ class } function init: JScannerKey; cdecl;
{ class } function open: Integer; cdecl;
{ class } property KEY_DOWN: Integer read _GetKEY_DOWN;
{ class } property KEY_UP: Integer read _GetKEY_UP;
end;
[JavaSignature('com/seuic/scanner/ScannerKey')]
JScannerKey = interface(JObject)
['{CE5ADF91-7ED1-46B5-B51D-694277A743AD}']
end;
TJScannerKey = class(TJavaGenericImport<JScannerKeyClass, JScannerKey>)
end;
JStatusCallBackClass = interface(IJavaClass)
['{58DA1A43-E11A-4F47-82FF-15DB0D65750E}']
{ class } procedure onStatusCallBack(P1: Integer; P2: Integer); cdecl;
end;
[JavaSignature('com/seuic/scanner/StatusCallBack')]
JStatusCallBack = interface(IJavaInstance)
['{75C0B88C-71DC-4645-A11D-76CFE299F28C}']
end;
TJStatusCallBack = class(TJavaGenericImport<JStatusCallBackClass, JStatusCallBack>)
end;
JVideoCallBackClass = interface(IJavaClass)
['{2C57576F-B3EC-4784-B190-C36924D62BAC}']
{ class } function onVideoCallBack(P1: Integer; P2: Integer; P3: TJavaArray<Byte>): Boolean; cdecl;
end;
[JavaSignature('com/seuic/scanner/VideoCallBack')]
JVideoCallBack = interface(IJavaInstance)
['{A8CB0355-4451-4FCD-B6D6-9E718CA500D0}']
end;
TJVideoCallBack = class(TJavaGenericImport<JVideoCallBackClass, JVideoCallBack>)
end;
// java.lang.StringBuffer
// java.lang.StringBuilder
implementation
procedure RegisterTypes;
begin
TRegTypes.RegisterType('Android.JNI.SeuicScanner.JAccount', TypeInfo(SeuicScanner.JAccount));
TRegTypes.RegisterType('Android.JNI.SeuicScanner.Jscanner_BuildConfig', TypeInfo(SeuicScanner.Jscanner_BuildConfig));
TRegTypes.RegisterType('Android.JNI.SeuicScanner.JDecodeInfo', TypeInfo(SeuicScanner.JDecodeInfo));
TRegTypes.RegisterType('Android.JNI.SeuicScanner.JDecodeInfoCallBack', TypeInfo(SeuicScanner.JDecodeInfoCallBack));
TRegTypes.RegisterType('Android.JNI.SeuicScanner.Jscanner_Scanner', TypeInfo(SeuicScanner.Jscanner_Scanner));
TRegTypes.RegisterType('Android.JNI.SeuicScanner.JScannerFactory', TypeInfo(SeuicScanner.JScannerFactory));
TRegTypes.RegisterType('Android.JNI.SeuicScanner.JScannerKey', TypeInfo(SeuicScanner.JScannerKey));
TRegTypes.RegisterType('Android.JNI.SeuicScanner.JStatusCallBack', TypeInfo(SeuicScanner.JStatusCallBack));
TRegTypes.RegisterType('Android.JNI.SeuicScanner.JVideoCallBack', TypeInfo(SeuicScanner.JVideoCallBack));
// TRegTypes.RegisterType('Android.JNI.SeuicScanner.JStringBuffer', TypeInfo(Android.JNI.SeuicScanner.JStringBuffer));
// TRegTypes.RegisterType('Android.JNI.SeuicScanner.JStringBuilder', TypeInfo(Android.JNI.SeuicScanner.JStringBuilder));
end;
initialization
RegisterTypes;
end.

我确实成功地尝试运行了 sdk 中为 android 工作室提供的演示应用程序。

服务脚本:

package com.seuic.scannerapitest.service;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.os.IBinder;
import android.os.Message;
import android.util.Log;
import com.seuic.scanner.DecodeInfo;
import com.seuic.scanner.DecodeInfoCallBack;
import com.seuic.scanner.Scanner;
import com.seuic.scanner.ScannerFactory;
import com.seuic.scanner.ScannerKey;
import com.seuic.scanner.VideoCallBack;
import com.seuic.scannerapitest.activity.MainActivity;
@SuppressWarnings("unused")
public class ScannerService extends Service implements DecodeInfoCallBack,VideoCallBack {
static final String TAG = "ScannerApiTest";
Scanner scanner;
private static  MainActivity mcontext = null;
private boolean mScanRunning = false;
private  void log(String  string){
Log.i(TAG, string);
}
public static  void MyService(Context context){
mcontext = (MainActivity)context;
}
@Override
public void onCreate() {
super.onCreate();
scanner = ScannerFactory.getScanner(this);
scanner.open();
scanner.setDecodeInfoCallBack(this);
scanner.setVideoCallBack(this);
scanner.enable();
mScanRunning = true;
new Thread(runnable).start();
};
Runnable runnable = new Runnable() {
@Override
public void run() {
int ret1 = ScannerKey.open();
if (ret1 > -1) {
while (mScanRunning) {
int ret = ScannerKey.getKeyEvent();
if (ret > -1) {
switch (ret) {
case ScannerKey.KEY_DOWN:
if (scanner != null && mScanRunning) {
scanner.startScan();
}
break;
case ScannerKey.KEY_UP:
if (scanner != null && mScanRunning) {
scanner.stopScan();
}
break;
}
}
}
}
}
};
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
super.onStartCommand(intent, flags, startId);
return Service.START_STICKY;
};
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public void onDestroy() {
mScanRunning = false;
ScannerKey.close();
scanner.setDecodeInfoCallBack(null);
scanner.setVideoCallBack(null);
scanner.close();
scanner = null;
super.onDestroy();
}
public static final String BAR_CODE = "barcode";
public static final String CODE_TYPE = "codetype";
public static final String LENGTH = "length";
// this is a custom broadcast receiver action
public static final String ACTION = "seuic.android.scanner.scannertestreciever";
@Override
public void onDecodeComplete(DecodeInfo info) {
Intent intent = new Intent(ACTION);
Bundle bundle = new Bundle();
bundle.putString(BAR_CODE, info.barcode);
bundle.putString(CODE_TYPE, info.codetype);
bundle.putInt(LENGTH, info.length);
intent.putExtras(bundle);
sendBroadcast(intent);
}
@Override
public boolean onVideoCallBack(int width, int height, byte[] img) {
if (img == null||width == 0||height == 0||img.length == 0){
return false;
}
log("onVideCallBack E");
Message video_msg = mcontext.mHandler.obtainMessage(img.length, width, height, img);
mcontext.mHandler.sendMessage(video_msg);
try {
Thread.sleep(80);
} catch (InterruptedException e) {
e.printStackTrace();
}
log("onVideCallBack X");
return false;
}
}

主要活动:

package scannertest.test2;
import android.content.Intent;
import android.os.Bundle;
import android.support.design.widget.FloatingActionButton;
import android.support.design.widget.Snackbar;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.util.Log;
import android.view.View;
import android.view.Menu;
import android.view.MenuItem;

import com.seuic.scanner.Scanner;
import com.seuic.scanner.ScannerFactory;

public class MainActivity extends AppCompatActivity {
public static final String TAG;
static final int SCANNER_KEYCODE;
static {
TAG = "ScannerApiTest";
SCANNER_KEYCODE = 142;
}
Scanner scanner;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);

// Intent intent = new Intent(this, ScannerService.  class);
// startService(intent);
scanner = ScannerFactory.getScanner(this);
setContentView(R.layout.activity_main);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
fab.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG)
.setAction("Action", null).show();
}
});
}
void init() {
scanner = ScannerFactory.getScanner(this);
if (scanner == null){
log("scanner(NULL)");
}


}
private  void log(String  string){
Log.i(TAG, string);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.menu_main, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
//noinspection SimplifiableIfStatement
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}
}

编辑:

scanner = TJScannerFactory.JavaClass.getScanner(TAndroidHelper.Context)

引发异常,调用堆栈:

System._DbgExcNotify(-506670960,0xe0015d30,0xe20e463d,0xe0015d30,0xe20e463d)
System.NotifyReRaise(0xe0015d30,0xe20e463d)
System._RaiseAtExcept(0xe0015d30,0xe20e463d)
System._RaiseExcept(0xe0015d30)
System.Internal.Excutils.DoRaiseJNIExceptionCallBack('class java.lang.RuntimeException','java.lang.RuntimeException: Stub!')
Androidapi.Jni.HandleJNIException(0xef826b20)
Androidapi.Jnimarshal.ExecJNI(0xe1ccd540,0xe13fa138)
:E27BFA70 DispatchToImport
:E27CBE5C dispatch_first_stage_intercept
Unit1.TForm1.Button1Click(0xef89d800,0xef8b4a00)
Fmx.Controls.TControl.Click(0xef8b4a00)
Fmx.Stdctrls.TCustomButton.Click(0xef8b4a00)
Fmx.Controls.TControl.MouseClick(0xef8b4a00,System.Uitypes.mbLeft,0x430a0088,66.3783722,5.73217773)
:E25182D2 __stub_in864v88__ZN3Fmx8Controls8TControl10MouseClickEN6System7Uitypes12TMouseButtonENS2_3SetINS2_7Classes17System_Classes__1ELS7_0ELS7_10EEEff
Fmx.Forms.TCommonCustomForm.MouseUp(0xef89d800,System.Uitypes.mbLeft,0xe1cc0088,138.378372,359.732178,true)
Fmx.Platform.Android.TWindowManager.MouseUp(0xe1698a20,System.Uitypes.mbLeft,0x88,138.378372,384.732178,true)
Fmx.Platform.Android.TPlatformAndroid.ProcessAndroidMouseEvents(0xefa59740)
Fmx.Platform.Android.TPlatformAndroid.HandleAndroidMotionEvent(0xefa59740,0xf4a48d00)
Fmx.Platform.Android.TPlatformAndroid.HandleAndroidInputEvent(0xefa59740,0xf4a48d00)
Fmx.Platform.Android.HandleAndroidInputEvent(@0xf4acbec0: {userData = nil, onAppCmd = 0xe264cf69 <Fmx.Platform.Android.HandleAndroidCmd(Androidapi.Appglue.TAndroid_app&, int)>, onInputEvent = 0xe264cf31 <Fmx.Platform.Android.HandleAndroidInputEvent(Androidapi.Appglue.TAndroid_app&, AInputEvent*)>, activity = 0xf4acb4c0, config = 0xef82f550, savedState = nil, savedStateSize = 0, looper = 0xef82e2e0, inputQueue = 0xf4ae6200, window = 0xf49a8e08, contentRect = {left = 0, top = -496709783, right = -496709839, bottom = -190008128}, activityState = 11, destroyRequested = 0, mutex = {value = 0}, cond = {value = 0}, msgread = 29, msgwrite = 30, thread = 0, cmdPollSource = {id = 0, app = 0xe264cf69, process = 0xe264cf31 <Fmx.Platform.Android.HandleAndroidInputEvent(Androidapi.Appglue.TAndroid_app&, AInputEvent*)>}, inputPollSource = {id = 0, app = 0xe264cf69, process = 0xe264cf31 <Fmx.Platform.Android.HandleAndroidInputEvent(Androidapi.Appglue.TAndroid_app&, AInputEvent*)>}, running = 1, stateSaved = 0, destroyed = 0, redrawNeeded = 0, pendingInputQueue = 0xf4ae6200, pendingWindow = 0xf49a8e08, pendingContentRect = {left = 0, top = -496709783, right = -496709839, bottom = -190008128}},0xf4a48d00)
Androidapi.Appglue.process_input(0xf4acbec0,0xf4acbf20)
Fmx.Platform.Android.TPlatformAndroid.InternalProcessMessages(0xefa59740)
Fmx.Platform.Android.TPlatformAndroid.Run(0xefa59740)
:E2637FF2 __stub_in272s__ZN3Fmx8Platform7Android16TPlatformAndroid3RunEv
Fmx.Forms.TApplication.Run(0xefac7940)
_NativeMain
Androidapi.Appglue.android_app_entry(void*).SystemEntry(void*)(@0x0: {})
Androidapi.Appglue.android_app_entry(0xf4acbec0)
:F71950C4 __pthread_start(void*)
:F71930B0 __start_thread
:00000000 ??

类方法的访问方式如下:

TJScannerKey.JavaClass.open 

从Java代码的角度来看,我会说Java2OP再次错误地导入了一些方法,特别是在Scanner类中,因为以下代码:

@Override
public void onCreate() {
super.onCreate();
scanner = ScannerFactory.getScanner(this);
scanner.open();
scanner.setDecodeInfoCallBack(this);
scanner.setVideoCallBack(this);
scanner.enable();
mScanRunning = true;
new Thread(runnable).start();
};

在实例(即扫描器(上调用一堆在类 (Jscanner_ScannerClass( 中声明的方法。您应该参考文档(或者可能只是示例 Java 代码(来确定哪些方法是类方法,哪些是实例方法

最新更新