从两个活动运行相同的后台任务
在我的应用程序中,我有一个后台任务(使用 AsyncTask)从网站下载内容。
该任务可以从两个单独的活动中调用,并且可以调用它两次。因此,在“activity1”中,后台任务“update”被调用,并运行一段时间(通常需要 5-10 秒)。 然后,当它运行时,用户切换到“activity2”并再次运行“update”。 这会带来问题:当两者同时尝试清除数据库(命令:DELETE FROM table)时会崩溃,从而导致“数据库锁定”错误。或者他们尝试将相同的项目放入数据库中,从而导致重复。
我尝试通过在任务处于活动状态时将静态布尔标志设置为 true 来解决此问题。 当任务被调用时,它将检查此标志,如果为真(即在另一个线程上运行的同一任务),它将使用处理程序进入等待循环,直到清除此标志,然后返回。这是为了确保当后台任务返回时,更新已完成。为此,我必须使用 Looper:这有时会失败,并出现错误“每个线程只能创建一个 Looper”。我确实以一种方式只能启动一个循环程序,这是有问题的代码,它出现在后台任务开始时:
if (active) {
Looper.prepare();
handler = new Handler();
handler.postDelayed(new Runnable() {
int count = 0;
@Override
public void run() {
if (active) {
count++;
if (count < 1000)
handler.postDelayed(this, 100);
}
}
}, 100);
Looper.loop();
active = false;
return "done";
}
更糟糕的是,它似乎经常挂在这个循环中,而不返回。
遇到这样的情况怎么解决呢?
In my app I have a background task (using AsyncTask) that downloads stuff from a web site.
This task can be called from two separate activities, and it is possible to call it twice. So in "activity1" the background task "update" is called, and runs for a while (it takes something like 5-10 seconds usually).
Then while it's running, user switches to "activity2" and runs "update" again.
This gives problems: either a crash when both try to clear the database (command: DELETE FROM table) at the same time, causing a "database locked" error. Or they try to put the same item in the database causing a duplicate.
I've tried to solve this by setting a static boolean flag to true when a task is active.
When the task is called, it will check for this flag, and if true (i.e. the same task running on another thread) it goes into a wait loop using a handler until this flag clears, and then returns. This to make sure that when the background task returns, the update has been done. I have to use a Looper for that: this sometimes fails with an error "can create only one looper per thread". And I really have it in a way that only one looper can be started, this is the offending code, which appears at the start of the background task:
if (active) {
Looper.prepare();
handler = new Handler();
handler.postDelayed(new Runnable() {
int count = 0;
@Override
public void run() {
if (active) {
count++;
if (count < 1000)
handler.postDelayed(this, 100);
}
}
}, 100);
Looper.loop();
active = false;
return "done";
}
And to make matters worse it often seems to hang in this loop, without returning.
How to solve such a situation?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
为什么不使用同步呢?这听起来像是一个并发问题。为什么不确保如果第一个后台任务正在运行,那么第二个后台任务正在休眠,直到第一个后台任务完成。
或者以某种方式确保,如果用户切换到活动编号 2,活动编号 1 的后台任务将被取消。
Why don't use synchronization instead? It sounds like a concurrency issue. Why don't you make sure that if the first background task is running then the second background task is sleeping until the first one is finished.
Or ensure somehow, that if the user switches to Activity number 2, the background task from activity number 1 is cancelled.
除了 AsyncTask,您还可以考虑使用 IntentService。查看 Android 服务概念。 IntentService 类确保一次仅处理一个请求。
我发现这个答案在使用 Activity 回调通信实现 IntentService 期间非常有用。
Instead of the AsyncTask you can consider to use IntentService. Have a look at the Android Service concept. The IntentService class ensures that only one request will be processed at one time.
I found this answer very useful during implementing IntentService with Activity callback communication.
通过将数据库包装到 ContentProvider 中解决了数据库锁定问题。除了在前一个实例完成之前再次调用方法的问题之外,我还遇到了在不同后台线程中运行的不同方法在尝试写入数据库时发生冲突的问题。
官方设计为允许在应用程序之间共享数据,它也非常适合在单个应用程序中的线程之间共享数据。 ContentProvider 将确保不会发生锁定问题。
Database locking issues solved by wrapping it into a ContentProvider. Besides problems with a method being called again before the previous instance was finished, I had the issue of different methods running in different background threads clashing while trying to write to the database.
Officially designed to allow for sharing data between apps, it also works great for sharing data between threads in a single app. The ContentProvider will make sure that no locking issues occur.