我有一个看似愚蠢的要求:
我需要在运行必须从UI线程访问的单个方法时阻止我的IntentService线程。我怎么能这样做?
显然,我可以通过带有Handler
的{{1}}运行UI方法,但当然我的其余服务处理会继续。
更详细一点:
我的服务同步内容,同时使用活页夹回调逐步更新带有新项目的用户界面。影响项列表的所有方法都是UI-Thread绑定,以避免我的StaggeredGridLayout抛出ConcurrentModificationExceptions。
但是,当我的服务启动时,我想在我同步每个内容源之前调用列表来获取当前ID,这就是需要UI线程访问的内容。
我无法在启动服务时提供此列表的原因是应用必须保持响应(意味着可以在我们同步时删除项目),并且需要在每个其他来源之前检查列表内容已同步
解决方案:
我提出的最佳解决方案是创建两个处理程序,一个在主循环器上用于UI方法,另一个用于其他所有内容并在它们之间发送消息。它并不像一个可接受的,干净的解决方案
另一种方法是制作相同UI方法的线程安全版本,首先对内容进行数组复制并使用副本循环。我不确定arraycopy操作是否也不安全(容易出现ConcurrentModificationException),因为它很难触发bug。所以我不确定这是否可以接受。
答案 0 :(得分:0)
我写了一个使用AsyncTask
的解决方案。
这是调用任务的Activity
:
public class CallingActivity extends Activity {
// method that does all the synchronization
private void doSync() {
// for each item you want to sync
int itemId;
new ItemUpdateAsyncTask(itemId, this).execute();
}
// this method is called for each synced item
public void syncItem(int itemId, Object syncedItem) {
// update list etc.
}
}
现在,更新后台项目的AsyncTask
:
public class ItemUpdateAsyncTask extends AsyncTask<Void, Void, Object> {
// the id of the item to be updated
private final int mItemId;
// reference to the activity that will be notified
// of the item update
private final WeakReference<CallingActivity> mCallingActivity;
public ItemUpdateAsyncTask(int itemId, CallingActivity callingActivity) {
super();
mItemId = itemId;
mCallingActivity = new WeakReference<>(callingActivity);
}
@Override
protected Object doInBackground(Void... params) {
// sync item in background
Object syncedItem;
return syncedItem;
}
@Override
protected void onPostExecute(Object syncedItem) {
CallingActivity callingActivity = mCallingActivity.get();
if (callingActivity != null) {
// update item in main thread
callingActivity.syncItem(mItemId, syncedItem);
}
}
}
我认为由于回调对象的传递,使用AsyncTask
而不是Service
来设计此解决方案更容易。服务和活动只能通过Intents相互通信,Intents只能携带可序列化的数据。如您所见,asynctasks可以引用回调对象,这使事情变得更容易。否则,您必须在活动中注册广播接收器,并且服务必须发送包含更新项目的广播,该更新项目需要是可序列化的。不过,这也是一个可行的解决方案。
用户可以在运行期间在多个活动之间进行操作
当您设计这样的架构时,值得注意的是,它在后台执行某些操作并将结果推送给调用者。呼叫者可以是例如一个Activity
,可能已经完成并在后台工作完成时进行垃圾收集。这就是我将调用者包裹在WeakReference
中的原因。