2 Replies Latest reply on Oct 31, 2017 9:01 AM by Sam Kay

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

    Sam Kay

      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<ScannerInfo> 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<String> friendlyNameList = new ArrayList<String>();

         int Index = 0;

       

         deviceList = barcodeManager.getSupportedDevicesInfo();

       

         if (deviceList.size() != 0)

        {

       

        Iterator<ScannerInfo> 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<ScanDataCollection, Void, String>

        {

       

         @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<ScanDataCollection.ScanData> 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<StatusData, Void, String>

        {

       

         @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