【问题标题】:ConcurrentModificationException in Android AsyncTaskAndroid AsyncTask 中的 ConcurrentModificationException
【发布时间】:2016-04-08 12:46:12
【问题描述】:

我有一个奇怪的问题,我希望我能解释得足够清楚。我的应用程序有两个活动 - MainActivity 和 SearchActivity。我在 MainActivity 上有一个按钮,它触发从设备上的数据库上传到我的 Web 服务器上的远程数据库。如果我在第一次启动应用程序时单击按钮,没问题,工作正常。如果我切换到 SearchActivity,不执行任何操作,然后切换回来,然后尝试使用按钮,应用程序将崩溃并出现 ConcurrentModificationException。 我有一个 AsyncTask,它发送本地数据库的内容(已经从数据库中拉出并通过参数作为 ArrayList 发送到线程)。我已经花了几个小时调试它,但仍然无法确定它在哪里。任何建议将不胜感激。

这是按下按钮时触发的代码,用于从单独的 Databaser 线程请求数据库的内容

Button btnRemoteSync = (Button)findViewById(R.id.btnSync);
    btnRemoteSync.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            Intent startUpload = new Intent(getString(R.string.broadcast_search_database));
            startUpload.putExtra("type-id",1);
            LocalBroadcastManager.getInstance(getApplicationContext()).sendBroadcast(startUpload);
        }
    });

这是 BroadcastReceiver 中的代码,它从数据库中获取每个响应并将它们添加到自定义响应对象的 ArrayList 中。当数据库线程发送一个 Bssid 值 DONE 时,将启动 AsyncTask,并将 ArrayList 作为参数传入。

@Override
    public void onReceive(Context context, Intent intent) {
        String bssid = intent.getStringExtra(getString(R.string.data_bssid));
       if (bssid.equals("DONE")) {
            RemoteDatabaseUploader rdb = new RemoteDatabaseUploader(getApplicationContext());
            rdb.execute(databases);
       } else {
           databases.add(new ResponseObject(getApplicationContext(),
                   bssid,
                   intent.getStringExtra(getString(R.string.data_ssid)),
                   intent.getStringExtra(getString(R.string.data_capabilities)),
                   intent.getIntExtra(getString(R.string.data_level), 0),
                   intent.getIntExtra(getString(R.string.data_frequency), 0),
                   intent.getStringExtra(getString(R.string.data_timestamp)),
                   intent.getDoubleExtra(getString(R.string.data_latitude), 0),
                   intent.getDoubleExtra(getString(R.string.data_longitude), 0)));

       }
    }

下面是 AsyncTask 的 doInBackground 代码

@Override
protected Integer doInBackground(ArrayList<ResponseObject>... params) {
    ArrayList<ResponseObject> entries = params[0];
    try {
        URL url = new URL(insertURL);
        for (Iterator<ResponseObject> it = entries.iterator(); it.hasNext();) {
            ResponseObject ro = it.next(); // THIS IS WHERE THE EXCEPTION REFERENCES IN THE DEBUG OUTPUT
            HttpURLConnection urlConnection = (HttpURLConnection)url.openConnection();

            urlConnection.setRequestMethod("POST");
            urlConnection.setRequestProperty("USER-AGENT", "Mozilla/5.0");
            urlConnection.setRequestProperty("ACCEPT-LANGUAGE", "en-US,en;0.5");
            urlConnection.setDoOutput(true);
            String postParams = "bssid=" + ro.BSSID
                    + "&ssid=" + ro.SSID
                    + "&capabilities=" + ro.CAPABILITIES
                    + "&level=" + String.valueOf(ro.LEVEL)
                    + "&frequency=" + String.valueOf(ro.FREQUENCY)
                    + "&timestamp=" + ro.TIMESTAMP
                    + "&lat=" + String.valueOf(ro.LAT)
                    + "&long=" + String.valueOf(ro.LON);
            DataOutputStream wr = new DataOutputStream(urlConnection.getOutputStream());
            wr.writeBytes(postParams);
            wr.flush();
            wr.close();
            Log.d("RemoteDatabase : ", "Post sent " + ro.BSSID + " || " + String.valueOf(urlConnection.getResponseCode()));
        }
    } catch (MalformedURLException e) {
        e.printStackTrace();
    } catch (ProtocolException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    }

    entries.clear();

    return null;
}


编辑——我似乎已经将问题追溯到另一​​段代码,点击按钮就会发送广播。该按钮肯定只发送一次(一直在使用 Log.d 检查),但在数据库中接收到的是两次。现在正试图解决这个问题。

【问题讨论】:

  • 你知道ConcurrentModificationException 是什么吗?您知道为什么在一个线程中将元素添加到另一个线程中的列表时可能会出现问题吗?
  • @AndyTurner 我确实知道那个错误是什么,但我不应该在一个线程中修改它并在另一个线程中迭代——只有在数据库将“完成”发送到主线程后,迭代才应该开始,表示列表完整。

标签: java android arraylist android-asynctask concurrentmodification


【解决方案1】:

已经有一段时间了,但我意识到我忘了添加我是如何解决这个问题的,以防万一它对其他人有帮助。

我发现这里的问题实际上是某些 Android 设备会发送多个广播副本。我正在使用 HTC 手机进行测试,显然出于某种原因,他们发送了所有广播的 2 个副本。我的代码的工作方式是从广播中生成一个线程,这导致 2 个相同的线程在相同的数据上运行。当这些完成时,他们每个人都发送他们的“完成”广播,这导致其中 4 个被主线程接收。一团糟。 我最终不得不为每个广播添加一个唯一的 ID 令牌并在接收端记录值,因此如果两次收到相同的 ID,第二次就不会采取任何行动。

【讨论】:

    【解决方案2】:

    请在异步任务的 onPreExecute() 方法中插入进度条,并在 onPostExecute() 中将其关闭。我认为完成异步任务需要太多时间,并且您在完成之前再次点击按钮异步任务。

    【讨论】:

    • 当我第一次按下它时,异常几乎发生了。出于这个确切原因,我一直小心不要按两次。无论如何,我可能应该包括一个进度条,但我没有打扰,因为这只是针对网络编程的单课程作业。从技术上讲,我不需要修复这个错误,因为如果我在正确的时间这样做,网络部分就可以工作,但这让我很恼火
    • 我猜,你从searchActivity回来后点击了按钮?我们如何确保之前的异步任务已经完成。所以为了避免用户操作直到异步任务完成我们通常使用进度条。
    • 是的,但在按下按钮之前不应该创建另一个 AsyncTask 实例(事实上我知道没有,因为我一直在使用 Log.d 检查)。我刚刚对原始帖子进行了编辑 - 我想我几乎已经找到了这个问题。尽管发送了一次,但似乎检测到数据库请求的 BroadcastReceiver 两次
    猜你喜欢
    • 2019-08-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-11-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多