Hi all,
I'm working on an Android app for a client that is designed to run on the TC8000 and WT6000. The app is complete and all is working as expected when the app is debugged on emulator or device using Android Studio's instant run feature. But when instant run is disabled, or the APK is built and signed, the app crashes when the EMDK wrapper tries to get the barcode manager. The exact error is:
W/System.err: java.lang.ArrayIndexOutOfBoundsException: length=9; index=317143120
W/System.err: at com.symbol.emdk.EMDKManager.getInstance(EMDKManager.java:211)
W/System.err: at com.logistex.mobileclient.EMDKWrapper.onOpened(EMDKWrapper.java:74)
...
D/EMDKManager: length=9; index=317143120
The EMDKWrapper.java file looks like this:
package com.logistex.mobileclient;
import com.logistex.mobileclient.Comms.eventScannedData;
import com.symbol.emdk.EMDKManager;
import com.symbol.emdk.EMDKManager.EMDKListener;
import com.symbol.emdk.EMDKResults;
import com.symbol.emdk.barcode.BarcodeManager;
import com.symbol.emdk.barcode.ScanDataCollection;
import com.symbol.emdk.barcode.Scanner;
import com.symbol.emdk.barcode.ScannerConfig;
import com.symbol.emdk.barcode.ScannerException;
import com.symbol.emdk.barcode.ScannerInfo;
import com.symbol.emdk.barcode.ScannerResults;
import com.symbol.emdk.barcode.StatusData;
import android.content.Context;
import android.os.AsyncTask;
import android.widget.TextView;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public class EMDKWrapper implements EMDKListener, Scanner.StatusListener, Scanner.DataListener
{
EMDKManager emdkManager = null;
private BarcodeManager barcodeManager = null;
private Scanner scanner = null;
// List of supported scanner devices private List deviceList;
// Provides current scanner index in the device Selection Spinner private int scannerIndex = 0;
private TextView statusOutputView;
private eventScannedData scanTarget;
EMDKWrapper(Context appContext)
{
EMDKResults results = EMDKManager.getEMDKManager(appContext, this);
if (results.statusCode != EMDKResults.STATUS_CODE.SUCCESS)
{
//Failed to request the EMDKManager }
}
public void RegisterFragmentScanTarget(eventScannedData scanTarg)
{
scanTarget = scanTarg;
}
public void RegisterStatusOutputView(TextView statusOutput)
{
statusOutputView = statusOutput;
}
void release()
{
//Release the EMDKmanager on Application exit. if (emdkManager != null)
{
emdkManager.release();
emdkManager = null;
}
}
@Override public void onOpened(EMDKManager emdkManager)
{
this.emdkManager = emdkManager;
//The EMDK Manager is ready and now you can call other EMDK APIs. System.out.println("EMDK Manager = " + emdkManager);
barcodeManager = (BarcodeManager) this.emdkManager.getInstance(EMDKManager.FEATURE_TYPE.BARCODE);
System.out.println("Barcode Manager = " + barcodeManager);
// Get the supported scanner devices enumerateScannerDevices();
try {
deInitScanner();
initializeScanner();
setProfile();
} catch (ScannerException e)
{
System.out.println("EMDK Exception: " + e);
}
}
// Sets the user selected Barcode scanning Profile public void setProfile()
{
try {
// cancel any pending asynchronous read calls before applying profile // and start reading barcodes if (scanner.isReadPending())
scanner.cancelRead();
ScannerConfig config = scanner.getConfig();
config.decoderParams.code11.enabled = true;
config.decoderParams.code39.enabled = true;
config.decoderParams.code128.enabled = true;
config.decoderParams.upca.enabled = true;
config.decoderParams.ean8.enabled = true;
config.decoderParams.ean13.enabled = true;
config.readerParams.readerSpecific.cameraSpecific.illuminationMode = ScannerConfig.IlluminationMode.ON;
config.scanParams.decodeHapticFeedback = true;
// Set the Scan Tone selected from the Scan Tone Spinner config.scanParams.audioStreamType = ScannerConfig.AudioStreamType.RINGER;
scanner.setConfig(config);
// Starts an asynchronous Scan. The method will not turn // ON the // scanner. It will, however, put the scanner in a state // in which // the scanner can be turned ON either by pressing a // hardware // trigger or can be turned ON automatically. scanner.triggerType = Scanner.TriggerType.HARD;
} catch (ScannerException e)
{
e.printStackTrace();
}
}
public void ScannerOn()
{
if (scanner == null)
return;
try {
if (scanner.isReadPending())
scanner.cancelRead();
scanner.read();
}
catch (ScannerException e)
{
e.printStackTrace();
}
}
public void ScannerOff()
{
if (scanner == null)
return;
try {
if (scanner.isReadPending())
scanner.cancelRead();
}
catch (ScannerException e)
{
e.printStackTrace();
}
}
@Override public void onClosed()
{
/* EMDKManager is closed abruptly. * Call EmdkManager.release() to free the resources used by the * current EMDK instance. * */ deInitScanner();
if (emdkManager != null)
{
emdkManager.release();
emdkManager = null;
}
}
@Override public void onData(ScanDataCollection scanDataCollection)
{
new AsyncDataUpdate().execute(scanDataCollection);
}
public void processScan(ScanDataCollection scanDataCollection, TextView updateField)
{
new AsyncDataUpdate().execute(scanDataCollection);
}
@Override public void onStatus(StatusData statusData)
{
new AsyncStatusUpdate().execute(statusData);
}
private void enumerateScannerDevices()
{
if (barcodeManager != null)
{
List friendlyNameList = new ArrayList();
int Index = 0;
deviceList = barcodeManager.getSupportedDevicesInfo();
if (deviceList.size() != 0)
{
Iterator it = deviceList.iterator();
while (it.hasNext())
{
ScannerInfo scnInfo = it.next();
friendlyNameList.add(scnInfo.getFriendlyName());
if (scnInfo.isDefaultScanner())
{
scannerIndex = Index;
}
++Index;
}
} else {
System.out.println("Status: Failed to get the list of supported scanner devices! Please close and restart the application.");
}
}else {
System.out.println("Status: Barcode manager is null.");
}
}
private void initializeScanner() throws ScannerException
{
if (deviceList.size() != 0)
{
scanner = barcodeManager.getDevice(deviceList.get(scannerIndex));
} else {
Program.Toast("Status: Failed to get the specified scanner device! Please close and restart the application.", 1);
}
if (scanner != null)
{
// Add data and status listeners scanner.addDataListener(this);
scanner.addStatusListener(this);
try {
// Enable the scanner scanner.enable();
} catch (ScannerException e)
{
Program.Toast("Barcode scanner exception: " + e.getMessage(), 1);
}
}
}
// Disable the scanner instance private void deInitScanner()
{
if (scanner != null)
{
try {
scanner.cancelRead();
scanner.removeDataListener(this);
scanner.removeStatusListener(this);
scanner.disable();
} catch (ScannerException e)
{
}
scanner = null;
}
}
// Update the scan data on UI int dataLength = 0;
// AsyncTask that configures the scanned data on background // thread and updated the result on UI thread with scanned data and type of // label private class AsyncDataUpdate extends AsyncTask
{
@Override protected String doInBackground(ScanDataCollection... params)
{
// Status string that contains both barcode data and type of barcode // that is being scanned String statusStr = "";
ScanDataCollection scanDataCollection = params[0];
// The ScanDataCollection object gives scanning result and the // collection of ScanData. So check the data and its status if (scanDataCollection != null && scanDataCollection.getResult() == ScannerResults.SUCCESS)
{
ArrayList scanData = scanDataCollection.getScanData();
// Iterate through scanned data and prepare the statusStr for (ScanDataCollection.ScanData data : scanData)
{
// Get the scanned data String barcodeData = data.getData();
// Get the type of label being scanned ScanDataCollection.LabelType labelType = data.getLabelType();
// Concatenate barcode data and label type statusStr = barcodeData + " " + labelType;
}
}
// Return result to populate on UI thread return statusStr;
}
@Override protected void onPostExecute(String result)
{
// Update the dataView EditText on UI thread with barcode data and // its label type if (dataLength++ > 50)
{
// Clear the cache after 50 scans dataLength = 0;
}
scanTarget.ScannedDataDelegate(result);
setProfile();
}
@Override protected void onPreExecute()
{
}
@Override protected void onProgressUpdate(Void... values)
{
}
}
// AsyncTask that configures the current state of scanner on background // thread and updates the result on UI thread private class AsyncStatusUpdate extends AsyncTask
{
@Override protected String doInBackground(StatusData... params)
{
String statusStr = "";
// Get the current state of scanner in background StatusData statusData = params[0];
StatusData.ScannerStates state = statusData.getState();
// Different states of Scanner switch (state)
{
// Scanner is IDLE case IDLE:
statusStr = "The scanner enabled and its idle";
break;
// Scanner is SCANNING case SCANNING:
statusStr = "Scanning..";
break;
// Scanner is waiting for trigger press case WAITING:
statusStr = "Waiting for trigger press..";
break;
// Scanner is not enabled case DISABLED:
statusStr = "Scanner is not enabled";
break;
default:
break;
}
// Return result to populate on UI thread return statusStr;
}
@Override protected void onPostExecute(String result)
{
// Update the status text view on UI thread with current scanner // state if (statusOutputView != null)
statusOutputView.setText(result);
}
@Override protected void onPreExecute()
{
}
@Override protected void onProgressUpdate(Void... values)
{
}
}
}
The issue occurs at this line in onOpened:
barcodeManager = (BarcodeManager) this.emdkManager.getInstance(EMDKManager.FEATURE_TYPE.BARCODE);
The println after this line shows the barcodeManager is null and the program crashes when it is accessed later.
As I mentioned, this only happens when I'm installing a single APK. Instant run seems to split the APK into multiple parts which fixes the issue. However, it is a requirement that the app can be installed via a single APK, as the process will need to be repeated for many devices.
I've tried many ways of including the EMDK and changing the build.gradle to try and solve it, but the results are always the same - working via instant run but not otherwise. The only other logged line I've found that might be relevant is this:
W/ResourcesManager: Asset path '/system/framework/com.symbol.emdk.jar' does not exist or contains no resources.
Which is logged before the EMDK is connected, just before:
D/com.symbol.emdk.EMDKServiceConnection: The EMDK Service will be connected soon (asynchronus call)!
Is there something obvious I'm getting wrong here? How can I build a single APK that includes the EMDK?
Thanks
1 Replies
Hi, that is very strange, it almost sounds like a build error. Are you able to get one of the simple sample projects working successfully? Barcode APIs - Zebra Technologies TechDocs That could help narrow down the issue. I have not seen that particular error before. Also, please ensure the device runtime is deployed if your TC8000 is running KitKat (only necessary on KK, as explained here: Configuring A Device - Zebra Technologies Techdocs )