我在UI线程上调用方法时是否可以阻止我的服务线程?

时间:2017-03-07 01:49:07

标签: android multithreading service concurrency

我有一个看似愚蠢的要求:

我需要在运行必须从UI线程访问的单个方法时阻止我的IntentService线程。我怎么能这样做?

显然,我可以通过带有Handler的{​​{1}}运行UI方法,但当然我的其余服务处理会继续。

更详细一点:

我的服务同步内容,同时使用活页夹回调逐步更新带有新项目的用户界面。影响项列表的所有方法都是UI-Thread绑定,以避免我的StaggeredGridLayout抛出ConcurrentModificationExceptions。

但是,当我的服务启动时,我想在我同步每个内容源之前调用列表来获取当前ID,这就是需要UI线程访问的内容。

我无法在启动服务时提供此列表的原因是应用必须保持响应(意味着可以在我们同步时删除项目),并且需要在每个其他来源之前检查列表内容已同步

解决方案:

我提出的最佳解决方案是创建两个处理程序,一个在主循环器上用于UI方法,另一个用于其他所有内容并在它们之间发送消息。它并不像一个可接受的,干净的解决方案

另一种方法是制作相同UI方法的线程安全版本,首先对内容进行数组复制并使用副本循环。我不确定arraycopy操作是否也不安全(容易出现ConcurrentModificationException),因为它很难触发bug。所以我不确定这是否可以接受。

1 个答案:

答案 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中的原因。