适配器的内容已更改,但ListView未收到通知。

时间:2016-07-09 11:51:28

标签: multithreading listview ibeacon-android altbeacon

我想在列表视图中显示检测到的beacon UUID,Major和Minor。当我的onServiceConnect()方法被调用时,它将在列表中添加项(UUID,Major,Minor值)。虽然它在我调试我的代码时显示了列表项,但我的应用程序在几秒钟后终止,让我发现此错误:

  

适配器的内容已更改,但ListView未收到   通知。确保未修改适配器的内容   来自后台线程,但仅来自UI线程。确保你的   适配器在其内容发生变化时调用notifyDataSetChanged()。

这是我的代码

public class NotifyDemo_Sc4 extends Activity implements BeaconConsumer {
    private ListView list;
    private ArrayList<String> arrayList;
    Button buttonNotify,b2;
    private BeaconManager beaconManager;
    private BluetoothAdapter mBluetoothAdapter;
    private static final String TAG = "MainActivity";
    private int nos_calls=0;
    private boolean run_call=true;
    Beacon beacon;
    ArrayAdapter<String> adapter;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_notifydemo);
        WebView view = (WebView) findViewById(R.id.myWebView3);
        buttonNotify = (Button) findViewById(R.id.buttonViewNotific);
        b2 = (Button) findViewById(R.id.buttondemo);
        b2.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent i = new Intent(NotifyDemo_Sc4.this, HomeScreen.class);
                startActivity(i);
            }
        });
        view.loadUrl("file:///android_asset/name.html");
        list = (ListView) findViewById(R.id.listview2);
        arrayList = new ArrayList<String>();
        adapter = new ArrayAdapter<String>(getApplicationContext(), android.R.layout.simple_spinner_item, arrayList);
        list.setAdapter(adapter);
        adapter.notifyDataSetChanged();
        runOnUiThread(new Runnable() {
            public void run() {
                adapter.notifyDataSetChanged();
            }
        });

        // requestLayout();
        beaconManager = org.altbeacon.beacon.BeaconManager.getInstanceForApplication(this);
        beaconManager.getBeaconParsers().add(new BeaconParser().setBeaconLayout("m:2-3=0215,i:4-19,i:20-21,i:22-23,p:24-24,d:25-25"));
        beaconManager.bind(this);

        beaconManager.setDebug(true);
        beaconManager.setBackgroundScanPeriod(1100l);
        beaconManager.setAndroidLScanningDisabled(false);

        Log.d(TAG, "didEnterRegion" + beaconManager);
        mBluetoothAdapter = ((BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE)).getAdapter();

        // Create the adapter to convert the array to views
      //  ArrayAdapter adapter = new ArrayAdapter<>(this, arrayOfDevices);
        // Attach the adapter to a ListView

    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        beaconManager.unbind(this);
    }

    @Override
    public void onBeaconServiceConnect() {
        blefunction();
        // nos_calls++;
        final Region region = new Region("myBeaons", Identifier.parse("E2C56DB5-DFFB-48D2-B060-D0F5A71096E0"), null, null);
        // runOnUiThread(new Runnable() {
        final Handler handler = new Handler();
        handler.postDelayed(new Runnable() {
            @Override
            public void run() {
                beaconManager.setRangeNotifier(new RangeNotifier() {

                    @Override
                    public void didRangeBeaconsInRegion(Collection<Beacon> beacons, Region region) {
                        for (Beacon beacon : beacons) {
                            beacon = beacons.iterator().next();
                            String b = beacon.toString();
                            double b2 = beacon.getDistance();
                            Log.i(TAG, "The first beacon I see is about " + beacon.toString() + " Distance " + beacon.getDistance() + " meters away.");
                            Log.i(TAG, "Beacon Detected" + beacon.toString());
                            arrayList.add("Beacon UUID, Major, Minor:" + b + "\n");
                            Toast.makeText(getApplicationContext(), " The beacon " + "The distance " + beacon.getDistance() + " meters away.", Toast.LENGTH_SHORT);
                        }
                    }
                });
            }}, 3000);

            try
            {
                beaconManager.startRangingBeaconsInRegion(new Region("myRangingUniqueId", Identifier.parse("E2C56DB5-DFFB-48D2-B060-D0F5A71096E0"), Identifier.parse("0"), Identifier.parse("1")));
            }
            catch(RemoteException e){}
    }
    void blefunction()
    {if(run_call = true){
        mBluetoothAdapter.startLeScan(new BluetoothAdapter.LeScanCallback() {
            @Override
            public void onLeScan(final BluetoothDevice device, int rssi, byte[] scanRecord) {
                Log.d(TAG, "Scanned BLE device with mac: " + device.getAddress());
                String add = "C4:BE84:05:EE:BF";
                long e = System.currentTimeMillis();
                Log.i(TAG, "in " + (e) + "ms");
            //    Toast.makeText(getApplicationContext(), "BLE Scanned Device Address: " + device.getAddress() + "/n" + "Time in milliseconds:" + e + " ms", Toast.LENGTH_SHORT).show();
                run_call = false;  }
        });}}

}

1 个答案:

答案 0 :(得分:0)

问题是由于任何非UI线程的线程操纵它包含的adapterarrayList引起的。至少有一行更改了信标回调中的arrayList,这不在UI线程上:

arrayList.add("Beacon UUID, Major, Minor:" + b + "\n");

要解决此问题,您需要将该行包装在如下所示的块中。为了获得最佳效率,您应该将整个信标迭代循环包装在这样的块中,这样您就不会在循环内重复创建新的Runnable

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

您还需要从UI线程调用adapter.notifyDataSetChanged();以使更改生效。你应该把它添加到块的末尾。

虽然这不是问题的一部分,但值得注意的是,迭代beacons的循环内的以下行会将beacon的值设置为列表中的第一项。这可能会违背迭代beacons的目的,因此您可能希望删除该行:

beacon = beacons.iterator().next();