Question

I was using iBeacon library version 0.6 and everything was fine except monitoring. In that version monitoring was happening but the monitoring notifier callback was not getting called because library used an "implicit intent" to pass this information. see Issue IBeacon getting warning Implcit intents are not safe on callback of MonitorNotifier

Recently I upgraded to the version 0.7.6 of the iBeacon android library and it solved the problem of monitoring callback but I found a new issue with this upgrade. I am getting an Exception which is android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.

I am invoking my app'S method inside the ranging notifier callback and If I am wrapping it inside UI thread than that exception is not coming but it slowing down the performance of the app.

here is the code

BackgroundRangingFragment.java

 @Override
 public void onIBeaconServiceConnect() {
   //Ranging Notifier
   iBeaconManager.setRangeNotifier(new RangeNotifier() {
      @Override 
      public void didRangeBeaconsInRegion(Collection<IBeacon> iBeacons, Region region) {
        //If beacon found in the specified Region.
        if (iBeacons.size() > 0) {
            MainActivity main = (MainActivity)MainActivity.mainActivityInstance;  
            main.setActiveBeaconsFromBackgroundRangingFragment(iBeacons);
        }
     }
 }

MainActivity.java

public void setActiveBeaconsFromBackgroundRangingFragment(List<Beacon> tempActiveBeaconArrayList){
    this.activeBeaconArrayList.clear();
    activeBeaconArrayList.addAll(tempActiveBeaconArrayList);
    MainRangingFragmennt fragment = (MainRangingFragmennt)getSupportFragmentManager().findFragmentByTag("beaconLogFragment");
    if(fragment != null){
        fragment.setActiveBeaconListForBeaconLogFromBackgroundRangingData(this.activeBeaconArrayList);
    }

MainRangingFragment.java

//Refreshes Beacon log 
public void setActiveBeaconListForBeaconLogFromBackgroundRangingData(List<Beacon> activeBeacons){
    //Here I am updating view with currently active beacons.
    LinearLayout progressBar = (LinearLayout)this.view.findViewById(R.id.beacon_log_list_progress);
        progressBar.setVisibility(View.VISIBLE);  //This line is raising exception .
        //code continues ...
}

Exception message.

04-22 18:10:15.546: E/isNull(1435): MainRangingFragmennt{421408c0 #2 id=0x7f0c0021 beaconLogFragment}
04-22 18:10:15.546: W/dalvikvm(1435): threadid=19: thread exiting with uncaught exception (group=0x4186fba8)
04-22 18:10:15.546: E/AndroidRuntime(1435): FATAL EXCEPTION: IntentService[IBeaconIntentProcessor]
04-22 18:10:15.546: E/AndroidRuntime(1435): Process: com.example.ranging, PID: 1435
04-22 18:10:15.546: E/AndroidRuntime(1435): android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
04-22 18:10:15.546: E/AndroidRuntime(1435):     at android.view.ViewRootImpl.checkThread(ViewRootImpl.java:6094)
04-22 18:10:15.546: E/AndroidRuntime(1435):     at android.view.ViewRootImpl.requestLayout(ViewRootImpl.java:824)
04-22 18:10:15.546: E/AndroidRuntime(1435):     at android.view.View.requestLayout(View.java:16431)
04-22 18:10:15.546: E/AndroidRuntime(1435):     at android.view.View.requestLayout(View.java:16431)
04-22 18:10:15.546: E/AndroidRuntime(1435):     at android.view.View.requestLayout(View.java:16431)
04-22 18:10:15.546: E/AndroidRuntime(1435):     at android.view.View.requestLayout(View.java:16431)
04-22 18:10:15.546: E/AndroidRuntime(1435):     at android.support.v4.widget.DrawerLayout.requestLayout(DrawerLayout.java:762)
04-22 18:10:15.546: E/AndroidRuntime(1435):     at android.view.View.requestLayout(View.java:16431)
04-22 18:10:15.546: E/AndroidRuntime(1435):     at android.view.View.requestLayout(View.java:16431)
04-22 18:10:15.546: E/AndroidRuntime(1435):     at android.view.View.requestLayout(View.java:16431)
04-22 18:10:15.546: E/AndroidRuntime(1435):     at android.widget.RelativeLayout.requestLayout(RelativeLayout.java:352)
04-22 18:10:15.546: E/AndroidRuntime(1435):     at android.view.View.requestLayout(View.java:16431)
04-22 18:10:15.546: E/AndroidRuntime(1435):     at android.view.View.setFlags(View.java:8908)
04-22 18:10:15.546: E/AndroidRuntime(1435):     at android.view.View.setVisibility(View.java:6036)
04-22 18:10:15.546: E/AndroidRuntime(1435):     at com.example.ranging.MainRangingFragmennt.setActiveBeaconListForBeaconLogFromBackgroundRangingData(MainRangingFragmennt.java:309)
04-22 18:10:15.546: E/AndroidRuntime(1435):     at com.example.ranging.MainActivity.setActiveBeaconsFromBackgroundRangingFragment(MainActivity.java:497)
04-22 18:10:15.546: E/AndroidRuntime(1435):     at com.example.ranging.BackgroundRanging$4.didRangeBeaconsInRegion(BackgroundRanging.java:489)
04-22 18:10:15.546: E/AndroidRuntime(1435):     at com.radiusnetworks.ibeacon.IBeaconIntentProcessor.onHandleIntent(IBeaconIntentProcessor.java:73)
04-22 18:10:15.546: E/AndroidRuntime(1435):     at android.app.IntentService$ServiceHandler.handleMessage(IntentService.java:65)
04-22 18:10:15.546: E/AndroidRuntime(1435):     at android.os.Handler.dispatchMessage(Handler.java:102)
04-22 18:10:15.546: E/AndroidRuntime(1435):     at android.os.Looper.loop(Looper.java:136)
04-22 18:10:15.546: E/AndroidRuntime(1435):     at android.os.HandlerThread.run(HandlerThread.java:61)

I am using fragments to load the beacon data in the UI.

Was it helpful?

Solution

When receiving asynchronous calls from the AndroidIBeaconLibrary, you must wrap any code that updates the user interface inside blocks like below:

    runOnUiThread(new Runnable() {
        public void run() {
           ...
        }
     }

This is a general rule for building Android apps, and isn't something that specifically changed from the 0.6 to 0.7.6 version of the Android iBeacon Library. Sometimes thread handling is indeterminate, so it is certainly possible that this happened to work most or all the time in version 0.6. The fact remains is that you need to make sure this processing is run on the UI thread. It should not affect performance.

I think the most likely explanation for why you see a "delay" and some beacons "getting ignored" is because of the way the setActiveBeaconListForBeaconLogFromBackgroundRangingData method is designed. You must write it to take into account the fact that you WILL NOT get a complete list of all visible beacons on every callback. You must maintain a separate list in your UI, and merge in any newly visible iBeacons passed to this method. Likewise, you must programmatically drop off any iBeacons from this list that have not been reported to be visible within a few seconds.

Again, this is not something that specifically changed between the library versions, but I don't doubt that subtle timing differences could make the issue more acute after an upgrade.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top