Deploying app with EMDK as a single APK (instant run disabled) causes a crash?

S Sam Kay 3 years 7 months ago
101 1 0

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

Please Register or Login to post a reply

1 Replies

Y Yanis Dalabiras

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  )

CONTACT
Can’t find what you’re looking for?