EMDK for Android in Multi-Activity App

Peter Arcuri -
11 MIN READ

While Line of Business applications have the ability to leverage Zebra's unique device capabilities through EMDK-for-Android APIs, use and practice of such APIs are typically in complex apps with multiple activities. Since most sample and tutorial posted on the web that illustrate these APIs are in abbreviated single-activity app, incorporating code snippets and dev techniques from these into multi-activity applications can lead to excessive redundant code and management of EMDK instances can become challenging given the Android Lifecycle flow.

 

To get an appreciation of amount of code required for a use case of simply enabling the scanner and capture decoded barcode data through EMDK APIs, one would typically have to code for the following steps.

  • Initialize the EMDK Manager
  • Get the Barcode Manager
  • Select and enable the scanner for scanning
  • Configure the scanner settings and enable/disable barcode symbologies
  • Register for receiving the scanner status and scanned data via callback
  • Receive the scanner status and scanned data.

 

Given the level of work involved, most would tend to agree that writing code for the EMDK interface in every UI Activity, that requires barcode scanning, is not good practice. It would hence result in a convolutely structured app with high proportion of redundant code, which in turn becomes laborious to support and performance negatively impacted. Since most developer aspires creating clean, well architected and streamlined apps, first impression of EMDK-for-Android can be viewed as impractical.

 

Oddly enough, developers who encounter these types of issues are often suggested to take the easier route, such as using the DataWedge utility instead of incorporating the EMDK libraries. Essentially, DataWedge is a background service that manages the scanner on your behalf and can be invoked with minimal to no code at all. While this can be a viable practice for some applications, others may need use of a more robust scheme allowing for full scanner control. EMDK-for-Android offers this as well as granular control of other hardware capabilities, specifically through EMDK features such as; SimulScanManager, ProfileManager and Scan&PairManager.

 

Developers looking to implement the full feature-set of EMDK-for-Android in an efficient manner typically considers using a separate individual Class. Also, referenced as a Wrapper Class. This blog helps describe how to employ EMDK libraries in such a fashion. The approach to take is not to implement the EMDK interface in the MainActivity or any UI Activities, rather create a separate class (for example, BarcodeScanner). An Interface class can be used to enable the UI Activities to make bidirectional calls to and from the BarcodeScanner class. The diagram below attempts to illustrate a simple but viable structure. We will breakdown and discuss this arrangement in greater detail.

 

Screen Shot 2017-12-31 at 12.43.01 PM.png

Scanner Event Interface Class

As illustrated in the diagram, an interface class is created to work as a go between the BarcodeScanner class and UI activities. For reference, the sample below uses IOnScannerEvent as the interface class name. It essentially captures 3 events; onDataScanned(), onStatusUpdate() and onError(). These pass-through function-calls contain information about Scanner Status and Data from Scanned Barcodes. These reflect the 2 main functions of the EMDK.

 

Since we don’t display errors in the UI activity, we're not passing error data. While we do log errors in logcat, one can for intuitive purposes, add an argument to the onError() event so to pass through specific error related data.

 

package com.BarcodeClassSample;

public interface IOnScannerEvent {

    //Function is called to pass scanned data
    void onDataScanned(String scanData);

    //Function is called to pass scanner status
    void onStatusUpdate(String scanStatus);

   //Function to be called upon error or exception
    void onError();
}

 

 

UI Activity Enabling the Scanner

For all UI activities requiring barcode data and scanner status captured and populated, should implement the IOnScannerEvent interface class that was just referenced above. By way of the UI activity "implements" statement, the 3 events (onDataScanned(), onStatusUpdate() and onError()) from the Interface class are made available here. These methods are called automatically upon their respective events occurring, which are controlled by the BarcodeScanner class. We will cover the BarcodeScanner class in the next segment.

 

@Override
public void onDataScanned(String scanData) {
    DataView.append(scanData + "\n");
}

@Override
public void onStatusUpdate(String scanStatus) {
    StatusView.setText(scanStatus);
}

@Override
public void onError() {
    System.
out.println (TAG + "onError() ");
}

 

Following the onCreate() method of the UI activity, the onResume() method calls .getInstance() method from within the BarcodeScanner class followed by the .registerUIobject() method. In both cases, the UI activity object (this) is passed along to the BarcodeScanner class so to reference with when passing along scanner data to the calling UI activity.

 

 

@Override
protected void onResume(){
   
super.onResume();
    BarcodeScanner.getInstance(
this);
    BarcodeScanner.registerUIobject(
this);
}

 

The onPause() method just calls .unregisterUIobject() method in the BarcodeScanner class. This method simply resets the variable holding the current UI activity object by setting it to null.

 

 @Override
 public void onPause() {
     super.onPause();
     BarcodeScanner.unregisterUIobject();
 }

 

A noteworthy piece of information here is the necessity of managing the EMDK instance. Since only one instance of EMDKManager can be used in an application, all features including EMDKManager must be released before exiting calling activities or the application. The event typically used for releasing resources is onDestroy(). However, this event is not triggered upon a suspend/resume condiion so the better choice would be to use the onStop() event. In the onStop() event, specifically, we go ahead and de-initialize the scanner and release the EMDK resources. The methods that perform this are called from within the BarcodeScanner class, .deInitScanner() and .releaseEmdk().

 

 

@Override
public void onStop() {
    super.onStop();
    BarcodeScanner.deInitScanner();

    BarcodeScanner.releaseEmdk();
}

 

BarcodeScanner Class

The purpose for the BarcodeScanner class is to enable UI activities within an application with barcode scanning. All EMDK methods associated with capturing barcode data and scanner status events reside here. Since all necessary EMDK calls are performed in this class and nowhere else, it provides a single location for whenever code changes or refinements are desired. Thus, producing a more efficient and better architected app with little to no code redundancy.

 

As noted above, when BarcodeScanner.getInstance() is called from within the UI Activity, its context is passed on to the BarcodeScanner class. Essentially, this is the entry point to the class. A new BarcodeScanner object is created using the current UI Activity context and gets held in the mBarcodeScanner variable.

 

 public static BarcodeScanner getInstance(Context context) {
     if (mBarcodeScanner == null) {
         mBarcodeScanner = new BarcodeScanner(context); 
     }
     return mBarcodeScanner;
 }

 

Within the BarcodeScanner() method we then call EMDKManager so that the EMDK service can be initialized and checked to see if it is ready. We’re also creating a new Handler(Looper.getMainLooper()) object for receiving and handling messages. This enables us to move data from scanner object to an object in the UI thread. The Handler object is stored in mScanHandler variable. We’re also creating a new IOnScannerEventRunnable() object to support a Runnable class, which is designed for background processing. This object will be stored in mEventRunnable variable.

 

 private BarcodeScanner(Context context) {
    EMDKResults results = EMDKManager.getEMDKManager(context, this);
     if (results.statusCode != EMDKResults.STATUS_CODE.SUCCESS) {
         System.out.println (TAG + "EMDKManager Request Failed");
     }
     mScanHandler = new Handler(Looper.getMainLooper());
     mEventRunnable = new IOnScannerEventRunnable();
 }

 

By now the EMDK service should be connected and as a result it will automatically trigger the EMDK onOpened() method. Here we get a reference to the EMDKManager. This event is used to pass the EMDKManager instance to a global variable, emdkManager. We’ll use that instance to create the BarcodeManager object which in turn is used to enable scanning. This work is done in the initializeScanner() method called from here.

 

 public void onOpened(EMDKManager emdkManager) {
     this.emdkManager = emdkManager;
     initializeScanner();
 }

 

The EMDK Data/Status listeners to support their respective callbacks are added in the  initializeScanner() method. Here we also enable the scanner by using the barcodeManager object. First, the EMDK BarcodeManager.DeviceIdentifier is used to create the scanner object. Then we enable the scan engine (scanner hardware) by running the scanner.enable() method. In this sample, we use the default scanner of the device to scan barcodes and set the hard (physical) buttons to trigger scanning action. This method simply gets the scanner ready for use.

 

 
 private void initializeScanner() {
     try {
         barcodeManager = (BarcodeManager) emdkManager.getInstance(EMDKManager.FEATURE_TYPE.BARCODE);
         scanner = barcodeManager.getDevice(BarcodeManager.DeviceIdentifier.DEFAULT); 
         scanner.addDataListener(this);
         scanner.addStatusListener(this);
         scanner.triggerType = Scanner.TriggerType.HARD;
         scanner.enable();
     } catch (ScannerException e) {
         System.out.println (TAG + "initializeScanner() - ScannerException " + e.getMessage());
         e.printStackTrace();
     }
 }

 

Once the scanner is initialized or the scan trigger is pressed subsequently, the EMDK onStatus() method is automatically triggered through the StatusListener. The purpose for this callback method is to return status of the scanner. In order to scan a barcode, a scanner.read() must be submitted. A read can be submitted from within onData() or onStatus() events. If called within onStatus(), as illustrated below, it should be called only when IDLE status is received. If submitted while another read is pending, the method call will fail, so checking for isReadPending() is recommended.

 

Since EMDK callbacks are asynchronous by nature and since scanner status change occurs rapidly, it can create a condition whereby scanner status get misaligned with the order in progress of populating the UI thread. So we introduce a brief delay immediately before the next read() is submitted. This has little to no impact on barcode decoding performance. This applies only to apps that display or process scanner status.

 

With every status change we go ahead and populate the current UI activity with the updated status. This is done by calling the callIOnScannerEvent() method which is created to manage background threads.

 

 @Override
 public void onStatus(StatusData statusData) {
     String statusStr = "";
     StatusData.ScannerStates state = statusData.getState();
     switch (state) {
         case IDLE: //Scanner is IDLE - this is when to request a read
             statusStr = "Scanner enabled and idle";
             try {
                 if (scanner.isEnabled() && !scanner.isReadPending()) {
                     try {
                         Thread.sleep(100);
                     } catch (InterruptedException e) {
                         e.printStackTrace();
                     }
                     scanner.read();
                 }
             } catch (ScannerException e) {
                 System.out.println (TAG + "onStatus() - ScannerException " + e.getMessage());
                 e.printStackTrace();
                 statusStr = e.getMessage();
             }
             break;
         case SCANNING: //Scanner is SCANNING
             statusStr = "Scanner beam is on, aim at the barcode";
             break;
         case WAITING: //Scanner is waiting for trigger press
             statusStr = "Waiting for trigger, press to scan barcode";
             break;
         case DISABLED: //Scanner is disabled
             statusStr = "Scanner is not enabled";
             break;
         case ERROR: //Error occurred
             statusStr = "Error occurred during scanning";
             break;
     }
     //Return result to populate UI thread
     callIOnScannerEvent(I_ON_STATUS, null, statusStr);
 }

 

Whenever a barcode is scanned, its data will be received in the onData() callback method. Conversely to the scanner status event, this callback is facilitated through EMDK’s DataListener. Here is where we obtain decoded barcode data and process it in the desirable format then populate the UI thread. As we did for the scanner status event, we will send the barcode data in a background thread managed by the callIOnScannerEvent() method.

 

 
 @Override
 public void onData(ScanDataCollection scanDataCollection) {
     if (scanDataCollection != null && scanDataCollection.getResult() == ScannerResults.SUCCESS) {
         ArrayList scanData = scanDataCollection.getScanData();
         if (scanData != null && scanData.size() > 0) {
             final ScanDataCollection.ScanData data = scanData.get(0);
             callIOnScannerEvent(I_ON_DATA, data.getData(), null);
         }
     }
 }

 

As mentioned earlier, barcode data and scanner status will populate the UI thread in a background thread. We achieve this by implementing the Runnable interface. In preparation, we need to setup the details, such as the interfaceID, barcode data and scanned status. We declared several int variables for calling the Interface class with the proper function and respective data. There are as follows: int I_ON_DATA = 0; int I_ON_STATUS = 1; int I_ON_ERROR = 2;

Since the call handles both barcode data and scanner status and because they are mutually exclusive, one passing arguments will contain a value of null. To ellaborate, if the interfaceID is I_ON_DATA, the “data” argument would contain barcode data but “status” would contain null. Conversely, if interfaceID is I_ON_STATUS, the “status” argument would contain scanner status but “data” would contain null. However, in the event interfaceID is I_ON_ERROR, both the “data” and “status” arguments will contain null.

 

private void callIOnScannerEvent(int interfaceId, String data, String status) {
     if (mUIactivity != null) {
         mEventRunnable.setDetails(interfaceId, data, status);
         mScanHandler.post(mEventRunnable);
     }
 }

 

As referenced earlier, a Runnable interface is implemented by the IOnScannerEventRunnable class. The setDetails()method is called by callIOnScannerEvent() method upon every scanner event, be it status or barcode data.  The class also defines the no arguments run() method that connects to the current UI thread and executes the appropriate scanner event with its associated data.

 

private static class IOnScannerEventRunnable implements Runnable {
     private int mInterfaceId = 0;
     private String mBarcodeData = "";
     private String mBarcodeStatus = "";
 
     public void setDetails(int id, String data, String statusStr) {
         mInterfaceId = id;
         mBarcodeData = data;
         mBarcodeStatus = statusStr;
     }
 
     @Override
     public void run() {
         if(mUIactivity!=null) {
             switch (mInterfaceId) {
                 case I_ON_DATA:
                     mUIactivity.onDataScanned(mBarcodeData);
                     break;
                 case I_ON_STATUS:
                     mUIactivity.onStatusUpdate(mBarcodeStatus);
                     break;
                 case I_ON_ERROR:
                     mUIactivity.onError();
                     break;
             }
         }
     }
 }

 

Other Considerations

Amalgamation of the above-noted code snippets do not compose a complete Android project. Its purpose is to highlight key components and considerations for properly implementing EMDK libraries and use of the BarcodeManager API in a multi-activity application.

 

Sample Application

To help illustrate use of the BarcodeManager APIs as discussed in this blog, I have created and attached below a sample app for reference. To note, this sample app is purely for demonstration purposes only. Any comments/questions can be posted here.

profile

Peter Arcuri

Please register or login to post a reply

21 Replies

P Peter Arcuri

There may be something, perhaps an old app lingering on your device, that may be interfering with EMDK runtime. I would suggest performing a Factory reset on your device. This would reset the device to default values.

If you're not at the latest version on Marshmallow you may want to update the OS build as well. The latest build for Marshmallow for the TC51 is <a href="https://www.zebra.com/us/en/support-downloads/software/operating-system…; BSP 21.04.01</a>

P Peter Arcuri

As noted in the article, a read is typically submitted from within the <strong>onData()</strong> or <strong>onStatus()</strong> events.&nbsp; Regardless where you submit it if a read is pending the method call will fail, so checking for <strong>isReadPending()</strong>is recommended. For instance:

if (scanner.isEnabled() &amp;&amp; !scanner.isReadPending()) {
&nbsp;&nbsp; scanner.read();
}

A Anas Garwal

Thanks. I actually solved it differently.
One more question. When i try to change the "ScannerConfig" I am getting a exception. How can i solve this?

A Anas Garwal

Thanks for the quick answer. But my problem is not that he schould start reading. The problem is, that he is still reading.
I get the exception by scanner.setConfig(config); And the exception is saying "Already Scanning" so i need to stop scanning before setting the configs. Im confused :/

A Anas Garwal

thanks, it got me a bit to figure out how to change that. My mistake was, that i called the scanner.Read in the Scanner Status. so it updated fasted then it closed, Thank you very much

D Darryn Campbell

Great article. Just had to integrate our Android App with a TC20 and this one example was exactly what i was looking for! Should defiantly be in the samples section.

One extra aspect i had to cover was moving from one Activity to another Activity that both use the scanner functionality.

When finishing one activity and starting a new one e.g.
finish();
startActivity(intent);

i ibrahim nehme

An issue I came across with your code in a multi activity Android application is when user logout (means "finish current activity and get forwarded to the login activity") from the application and get send to the login activity. The infrared scanner is not working in the login activity. I think the issue is Activity1 is destroyed last.

LoginActivity ---&gt; MainActivity ----&gt; Activity1 (logout go back to LoginActivity)

Logout code&nbsp; (Activity1 where the logout method is located implements IOnScannerEvent and override onStop, OnDestroy, onResume and onPause as the LoginActivity below with the same code as below )

public void logout(final Context mContext) {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; AlertDialog.Builder builder = new AlertDialog.Builder(mContext);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; builder.setTitle("Logout Confirmation");
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; builder.setMessage("Are you sure you want to logout?");
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; builder.setCancelable(false);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; builder.setPositiveButton("Yes", new DialogInterface.OnClickListener() {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; @Override
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public void onClick(DialogInterface dialog, int which) {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // Clear login user
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Globals globals = (Globals) getApplication();
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (globals != null) {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; globals.setUser_name(null);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; globals.setEmployee_number(null);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Intent intent = new Intent(mContext, LoginActivity.class);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; intent.putExtra("finish", true);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP |
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Intent.FLAG_ACTIVITY_CLEAR_TASK |
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Intent.FLAG_ACTIVITY_NEW_TASK);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mContext.startActivity(intent);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; finish();
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; });

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; builder.setNegativeButton("No", new DialogInterface.OnClickListener() {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; @Override
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public void onClick(DialogInterface dialog, int which) {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; });
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; builder.show();
&nbsp;&nbsp;&nbsp; }

Code for Login Activity
-------------------------------
public class LoginActivity extends BaseActivity implements IOnScannerEvent {
&nbsp;&nbsp;&nbsp;
&nbsp;&nbsp;&nbsp; @Override
&nbsp;&nbsp;&nbsp; public void onCreate(Bundle savedInstanceState) {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; super.onCreate(savedInstanceState);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; setContentView(R.layout.activity_login_in);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
&nbsp;&nbsp;&nbsp; }

&nbsp;&nbsp;&nbsp; @Override
&nbsp;&nbsp;&nbsp; public void onStop() {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; super.onStop();&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; BarcodeScanner.deInitScanner();
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; BarcodeScanner.releaseEmdk();&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
&nbsp;&nbsp;&nbsp; }

&nbsp;&nbsp;&nbsp; @Override
&nbsp;&nbsp;&nbsp; protected void onDestroy() {&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; super.onDestroy();&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; BarcodeScanner.releaseEmdk();&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
&nbsp;&nbsp;&nbsp; }

&nbsp;&nbsp;&nbsp; @Override
&nbsp;&nbsp;&nbsp; protected void onResume()
&nbsp;&nbsp;&nbsp; {&nbsp;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; super.onResume();&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; BarcodeScanner.getInstance(this);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; BarcodeScanner.registerUIobject(this);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
&nbsp;&nbsp;&nbsp; }

&nbsp;&nbsp;&nbsp; @Override
&nbsp;&nbsp;&nbsp; protected void onPause() {&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; super.onPause();&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; BarcodeScanner.unregisterUIobject();&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
&nbsp;&nbsp;&nbsp; }

&nbsp;&nbsp;&nbsp; @Override
&nbsp;&nbsp;&nbsp; public void onDataScanned(String scanData) {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // some code
&nbsp;&nbsp;&nbsp; }
}

i ibrahim nehme

I added the code provided in this article to an application that I am working on. I have an issue inside my activity with EditText controls. When I scan a barcode and the cursor is on an EditText control (EditText has focus) the 'onDataScanned' method is not called and the barcode data get display on the EditText with the cursor on.&nbsp; In some instance the 'onDataScanned' is getting called, but in other scan the barcode data (PF|56565|67889) is getting displayed on EditText control. Is this the correct behior when the activity has EditText? Do I need to disable the cursor and make all the EditText as readonly? The EMDK version on my TC56 device is 6.5.12. Thanks

P Peter Arcuri

Hi Ibrahim,

I added an EditText field to the sample app and observed it to behave as expected. I'm able to manually update the field then auto populated via a scan - also in reverse order. Scanned quickly and many times, didn't skip a beat. Tested on a couple of devices, TC25 and TC51, both running EMDK service 6.7.10.

If you can you may want to update the BSP on your TC56 to Android N. I suspect you are running Android M. Not a requirement but you may want to test it. Besides its best to deploy your app on devices with the latest OS build. The BSP update files can be downloaded from <a href="https://www.zebra.com/us/en/support-downloads/mobile-computers/handheld…;.

P Phil Thoennissen

(Maybe a solution, because it works for me)
I am approaching the same problem. My problem approached after switching between Activities (UI_Activity1 -&gt; UI_Activity2 -&gt; UI_Activity1). After starting the App, the first Activiy (with scanner) was 100% functional, but after the switching, 1-&gt;2-&gt;1, the scanner was functional but not connected to my UI anymore, so the public void onDataScanned(String scanData) was not triggered.

<a href></a> is right, you have to deInitialize the scanner and release EMDK first. After that you can do:
&nbsp;&nbsp;&nbsp;&nbsp; BarcodeScanner.getInstance(MainActivity.this);&nbsp;&nbsp;&nbsp;&nbsp; BarcodeScanner.registerUIobject(MainActivity.this);

Its all a matter of Time!
When I switch my Activity i do:

&nbsp;&nbsp;&nbsp;&nbsp; BarcodeScanner.deInitScanner();&nbsp;&nbsp;&nbsp;&nbsp; BarcodeScanner.releaseEmdk();&nbsp;&nbsp;&nbsp;&nbsp; Intent intent= new Intent(this, .class);&nbsp;&nbsp;&nbsp;&nbsp; startActivity(intent);

It´s the code from onStop().

When you get back from UI_Activity2 to UI_Actvity1 (in my case it´s the&nbsp; (Hardware-)ArrowLeft-Button "Back") you have to wait to get the scanner object, because onResume() is called earlier from A.1 than onStop() from A.2 .
My onResume() looks like:

@Overrideprotected void onResume() {
&nbsp;&nbsp; super.onResume();&nbsp;&nbsp; BarcodeScanner.getInstance(this);&nbsp;&nbsp; BarcodeScanner.registerUIobject(this); &nbsp;&nbsp; // Only needed when the Activity was already created&nbsp;&nbsp; if (!firstStart){ // boolean firstStart is set to true in onCreate()
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Timer timer2 = new Timer();&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; timer2.schedule(new TimerTask() {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; @Override&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public void run() {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // You get a "new" Scanner&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; BarcodeScanner.getInstance(MainActivity.this);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; BarcodeScanner.registerUIobject(MainActivity.this);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Timer timer2 = new Timer();&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; timer2.schedule(new TimerTask() {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; @Override&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public void run() {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; setDecoders(); // need to configure the "new" scanner&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }, 2000); // 2000 is safe, less is risky&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }, 2000); //2000 is safe, less is risky&nbsp;&nbsp; }
&nbsp;&nbsp; firstStart=false;}

It´s kinda workaround, but at least it works (for me)

Phil

P Peter Arcuri

The log shows LoginActivity resuming before Activity1 had the chance to deInitScanner and release Emdk. This may be causing scanner lockup in LoginActivity. As Activity1 is in the process of closing per the Android lifecycle of onPause, onStop and onDestroy -&nbsp; LoginActivity has already encountered onResume before Activity1 reached the onStop. Resulting in multiple EMDK sessions.

In Activity1, would suggest explicitly calling "onStop" method or its content before transitioning to LoginActivity.

J Joachim Unger

That's normal Android behaviour because startActivity() is called before finish().

My suggestion:
<ul>
<li>Do not use the onStop ()-method for freeing EMDK resources. A stopped activity could be reactivated by navigation.</li>
<li>Because of the nested implementation, I would completely abandon unregisterUIobject(). The last resume activity will regsiter an is on top and gets scan input.</li>
<li>The EMDK resources should be freed when the app is closed.</li>
</ul>
Jo

A Anas Garwal

Same here, I´m using Android 6.0 and emdk v6.6. I´m using a TC51 with an Android Version 6.01. The four tutorials about "Basic Data Capture" dosent even work on my device. I cant use the feature of Braodcast Intent

K Kevin Lollock

Great writeup and thanks for the sample app! Your insight is well appreciated in helping to better understand the behavior the EMDK for scanner control.

A Anas Garwal

First of all thanks for the sample.
Is it me or is the app/scanning very slow?

P Peter Arcuri

Which device and Android version are you seeing delay with? The sample app is using EMDKv6.6 library for marshmallow and higher. You can change the library by replacing that from within BarcodeClassSample/app/libs/com.symbol.emdk.jar.

P Peter Arcuri

Setting the scanner configuration is preferably done before any read is submitted. The error you're encountering is highly likely due to the fact that a read was submitted before setting Scanner config. This is typically done in the tail end of scanner initialization, as illustrated in the attached sample app.

Should you wish to keep your design as is, then try cancelling the pending read by calling the scanner.cancelRead() method. Should be called just before calling scanner.setConfig(). <code> </code>

V Vedsatx Saddvv

Great stuff... such a frequent question, it would be good if this was an official sample

P Peter Arcuri

It appears as LogonActivity implements EMDK and initializes the scanner. Is there any other activities using the scanner? All activities implementing EMDK, needs to release resources before another activity created or its self is recreated. No showing of MainActivity but assuming MainActivity is using the scanner, is it possible it isn't de-initiallizing the scanner and the releasing EMDK resources? You may want to add "onStop" method to MainActivity or simply run the 2 methods before calling Activity1.

Note, from the article:
In the <strong>onStop()</strong> event, specifically, we go ahead and de-initialize the scanner and release the EMDK resources. The methods that perform this are called from within the BarcodeScanner class, .<em>deInitScanner</em>() and .<em>releaseEmdk</em>().

i ibrahim nehme

Only LoginActivity and Activity1 implement EMDK and initializes the scanner. The MainActiviy display a menu list only. I had added loggoing in LoginActivity and Activity1 and I captured the logs below when the logout method is called from Activity1.

I sign in into LoginActivity ---&gt; MainActivitry ---&gt; Activity1 (logout from here) Why Activity1 is destroyed after LoginActivity resume?

05-30 20:29:12.665 19468-19468/app.sample.testing I/MT: Activity1: logout - BEGIN
05-30 20:29:12.726 19468-19468/app.sample.testing I/MT: LoginActivity: onDestroy - BEGIN
05-30 20:29:12.726 19468-19468/app.sample.testing I/MT: releaseEmdk
05-30 20:29:12.726 19468-19468/app.sample.testing I/MT: LoginActivity: onDestroy - END
05-30 20:29:12.755 19468-19468/app.sample.testing I/MT: Activity1: onPause - BEGIN
05-30 20:29:12.755 19468-19468/app.sample.testing I/MT: unregisterUIobject
05-30 20:29:12.755 19468-19468/app.sample.testing I/MT: Activity1: onPause - END
05-30 20:29:12.788 19468-19468/app.sample.testing I/MT: LoginActivity: onResume - BEGIN
05-30 20:29:12.788 19468-19468/app.sample.testing I/MT: registerUIobject
05-30 20:29:12.788 19468-19468/app.sample.testing I/MT: LoginActivity: onResume - END
05-30 20:29:13.035 19468-19468/app.sample.testing I/MT: Activity1: onStop - BEGIN
05-30 20:29:13.035 19468-19468/app.sample.testing I/MT: deInitScanner
05-30 20:29:13.035 19468-19468/app.sample.testing I/MT: releaseEmdk
05-30 20:29:13.035 19468-19468/app.sample.testing I/MT: Activity1: onStop - END
05-30 20:29:13.035 19468-19468/app.sample.testing I/MT: Activity1: onDestroy - BEGIN
05-30 20:29:13.035 19468-19468/app.sample.testing I/MT: releaseEmdk
05-30 20:29:13.035 19468-19468/app.sample.testing I/MT: Activity1: onDestroy - END

J Johan GRONDIN

Hi, Sorry i do not find the link to the sample. Is it a complete CodeLab (kind of) or it's just parts ? I think i'm missing some parts. Thanks for your help :)