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)
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.