0

Using cling library for upnp discovery. Whenever deviceDiscovered fires up, calling the below code to update device list and hence forth, calling notifyDataSetChanged of ViewPager

public void deviceAdded(Registry registry, Device device) {
        // TODO Auto-generated method stub
        super.deviceAdded(registry, device);

        String name = device.getDisplayString();
        String manufac = device.getDetails().getManufacturerDetails()
                .getManufacturer();
        if (!manufac.contains("Microsoft")) {
            if (!deviceNames.contains(name)) {
                if (!devices.contains(device)) {
                    devices.add(device);
                    deviceNames.add(name);
                    Log.e(TAG,"\t\tdevice.size() = "+String.valueOf(devices.size()));
                //  Log.e(TAG,"Calling notify runnable after adding device "+deviceNames.get(deviceNames.size()-1));
                    runOnUiThread(notifyAdapterDataChanged);
                }

            }
        }
}

Field declarations

ArrayList/*CopyOnWriteArrayList*/<Device> devices;
    ArrayList/*CopyOnWriteArrayList*/<String> deviceNames;

notifyAdapterDataChanged is the runnable, as below

@Override
        public void run() {
            if(adapter != null){
                adapter.notifyDataSetChanged();
             }
       }

Getting below exception

07-25 02:58:36.185: E/AndroidRuntime(11404): FATAL EXCEPTION: main
07-25 02:58:36.185: E/AndroidRuntime(11404): java.lang.IllegalStateException: The application's PagerAdapter changed the adapter's contents without calling PagerAdapter#notifyDataSetChanged! Expected adapter item count: 5, found: 6 Pager id: com.example.upnpclient/myviewpager Pager class: class android.support.v4.view.ViewPager Problematic adapter: class com.example.upnpclient.MyPagerAdapter
07-25 02:58:36.185: E/AndroidRuntime(11404):    at android.support.v4.view.ViewPager.populate(ViewPager.java:967)
07-25 02:58:36.185: E/AndroidRuntime(11404):    at android.support.v4.view.ViewPager.populate(ViewPager.java:919)
07-25 02:58:36.185: E/AndroidRuntime(11404):    at android.support.v4.view.ViewPager.onMeasure(ViewPager.java:1441)
07-25 02:58:36.185: E/AndroidRuntime(11404):    at android.view.View.measure(View.java:15473)
07-25 02:58:36.185: E/AndroidRuntime(11404):    at android.widget.RelativeLayout.measureChildHorizontal(RelativeLayout.java:617)
07-25 02:58:36.185: E/AndroidRuntime(11404):    at android.widget.RelativeLayout.onMeasure(RelativeLayout.java:399)
07-25 02:58:36.185: E/AndroidRuntime(11404):    at android.view.View.measure(View.java:15473)
07-25 02:58:36.185: E/AndroidRuntime(11404):    at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5056)
07-25 02:58:36.185: E/AndroidRuntime(11404):    at android.widget.FrameLayout.onMeasure(FrameLayout.java:310)
07-25 02:58:36.185: E/AndroidRuntime(11404):    at android.view.View.measure(View.java:15473)
07-25 02:58:36.185: E/AndroidRuntime(11404):    at android.widget.LinearLayout.measureVertical(LinearLayout.java:833)
07-25 02:58:36.185: E/AndroidRuntime(11404):    at android.widget.LinearLayout.onMeasure(LinearLayout.java:574)
07-25 02:58:36.185: E/AndroidRuntime(11404):    at android.view.View.measure(View.java:15473)
07-25 02:58:36.185: E/AndroidRuntime(11404):    at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5056)
07-25 02:58:36.185: E/AndroidRuntime(11404):    at android.widget.FrameLayout.onMeasure(FrameLayout.java:310)
07-25 02:58:36.185: E/AndroidRuntime(11404):    at com.android.internal.policy.impl.PhoneWindow$DecorView.onMeasure(PhoneWindow.java:2361)
07-25 02:58:36.185: E/AndroidRuntime(11404):    at android.view.View.measure(View.java:15473)
07-25 02:58:36.185: E/AndroidRuntime(11404):    at android.view.ViewRootImpl.performMeasure(ViewRootImpl.java:1974)
07-25 02:58:36.185: E/AndroidRuntime(11404):    at android.view.ViewRootImpl.measureHierarchy(ViewRootImpl.java:1217)
07-25 02:58:36.185: E/AndroidRuntime(11404):    at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:1390)
07-25 02:58:36.185: E/AndroidRuntime(11404):    at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1112)
07-25 02:58:36.185: E/AndroidRuntime(11404):    at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:4472)
07-25 02:58:36.185: E/AndroidRuntime(11404):    at android.view.Choreographer$CallbackRecord.run(Choreographer.java:725)
07-25 02:58:36.185: E/AndroidRuntime(11404):    at android.view.Choreographer.doCallbacks(Choreographer.java:555)
07-25 02:58:36.185: E/AndroidRuntime(11404):    at android.view.Choreographer.doFrame(Choreographer.java:525)
07-25 02:58:36.185: E/AndroidRuntime(11404):    at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:711)
07-25 02:58:36.185: E/AndroidRuntime(11404):    at android.os.Handler.handleCallback(Handler.java:615)
07-25 02:58:36.185: E/AndroidRuntime(11404):    at android.os.Handler.dispatchMessage(Handler.java:92)
07-25 02:58:36.185: E/AndroidRuntime(11404):    at android.os.Looper.loop(Looper.java:137)
07-25 02:58:36.185: E/AndroidRuntime(11404):    at android.app.ActivityThread.main(ActivityThread.java:4898)
07-25 02:58:36.185: E/AndroidRuntime(11404):    at java.lang.reflect.Method.invokeNative(Native Method)
07-25 02:58:36.185: E/AndroidRuntime(11404):    at java.lang.reflect.Method.invoke(Method.java:511)
07-25 02:58:36.185: E/AndroidRuntime(11404):    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1006)
07-25 02:58:36.185: E/AndroidRuntime(11404):    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:773)
07-25 02:58:36.185: E/AndroidRuntime(11404):    at dalvik.system.NativeStart.main(Native Method)
nmxprime
  • 1,506
  • 3
  • 25
  • 52

3 Answers3

3

First of all: After every time you add an item to the list referenced in an adapter, you have to call notifyDataSetChanged.

Your situation usually happens when a second item is added before the UI thread is done executing notifyDataChanged. You can prevent this by adding the items using an adapter.

If you rather want to add the items directly to the list and not to the the adapter, you can use a mutex to wait for runOnUIThread to finish - like this:

final Semaphore mutex = new Semaphore(0);
runOnUiThread(new Runnable() {
    @Override
    public void run() {
        adapter.notifyDataSetChanged();
        mutex.release();
    }
});

try {
    // wait for the line mutex.release(); to be executed
    mutex.acquire();
} catch (InterruptedException e) {
    e.printStackTrace();
}

Now, if you add an item to the list and start off the UI thread, the thread that is adding the item waits (via the mutex) for the UI thread to be done with notifying the adapter. This way the case that an additional item slips into the list before the adapter has been updated will be prevented.

Edit: Another solution would be adding the items in the UI thread, too. In your case like so:

runOnUiThread(new Runnable() {
        @Override
        public void run() {
            devices.add(device);
            deviceNames.add(name);
            adapter.notifyDataSetChanged();
        }
    });

This way you are avoiding a mutex while still only adding one item per adapter.notifyDataSetChanged(); call.

ntv1000
  • 626
  • 6
  • 16
  • The constraint is `mutex` should not be used, so, what you mean by ` adding the items using an adapter.`? I am already using an adapter(MyPagerAdapter) – nmxprime Jul 25 '14 at 07:58
  • Why is that a constraint and why don't you say that in your question? I mean use `adapter.Add(name)` instead of `yourlist.Add(name)` like Orion suggested. – ntv1000 Jul 25 '14 at 08:07
  • Because this delayed the discovery of the device, – nmxprime Jul 25 '14 at 08:09
  • `public class MyPagerAdapter extends FragmentPagerAdapter implements ViewPager.OnPageChangeListener`, which doesn't have a `add/remove` method – nmxprime Jul 25 '14 at 08:13
  • Sorry, I overlooked the detail that you are working with a `PageAdapter`. See my edited answer. – ntv1000 Jul 25 '14 at 08:23
0

If you look here: https://stackoverflow.com/a/5092426/1235505

Quote:

For an ArrayAdapter, notifyDataSetChanged only works if you use the add(), insert(), remove(), and clear() on the Adapter.

So if you add the device to the collection in the adapter manually, it's likely you will get an exception after modifying the data.

I can't really tell if you use these methods already from your question, if you do so, please show us the field declarations next time too.

Community
  • 1
  • 1
Orion
  • 1,258
  • 2
  • 14
  • 32
0

If you are Leaving Your activity for some reason(like via Intent to other apps) you have to add below line in your menifest under your Activity.

   android:configChanges="keyboardHidden|orientation|screenSize"
ADM
  • 20,406
  • 11
  • 52
  • 83