Android如何将异步任务组合在一起,就像在iOS

时间:2016-01-19 08:16:00

标签: java android ios swift android-asynctask

我在iOS应用中有一个功能,它使用dispatch_group对多个休息请求进行分组:

static func fetchCommentsAndTheirReplies(articleId: String, failure: ((NSError)->Void)?, success: (comments: [[String: AnyObject]], replies: [[[String: AnyObject]]], userIds: Set<String>)->Void) {
    var retComments = [[String: AnyObject]]()
    var retReplies = [[[String: AnyObject]]]()
    var retUserIds = Set<String>()

    let queue = dispatch_get_global_queue(QOS_CLASS_USER_INITIATED, 0)
    Alamofire.request(.GET, API.baseUrl + API.article.listCreateComment, parameters: [API.article.articleId: articleId]).responseJSON {
        response in

        dispatch_async(queue) {

            guard let comments = response.result.value as? [[String: AnyObject]] else {
                failure?(Helper.error())
                return
            }
            print(comments)
            retComments = comments

            let group = dispatch_group_create()

            for (commentIndex, comment) in comments.enumerate() {
                guard let id = comment["_id"] as? String else {continue}

                let relevantUserIds = helperParseRelaventUserIdsFromEntity(comment)
                for userId in relevantUserIds {
                    retUserIds.insert(userId)
                }

                retReplies.append([[String: AnyObject]]())

                dispatch_group_enter(group)
                Alamofire.request(.GET, API.baseUrl + API.article.listCreateReply, parameters: [API.article.commentId: id]).responseJSON {
                    response in

                    dispatch_async(queue) {
                        if let replies = response.result.value as? [[String: AnyObject]] {
                            for (_, reply) in replies.enumerate() {

                                let relevantUserIds = helperParseRelaventUserIdsFromEntity(reply)
                                for userId in relevantUserIds {
                                    retUserIds.insert(userId)
                                }
                            }
                            retReplies[commentIndex] = replies
                        }
                        dispatch_group_leave(group)
                    }

                }
            }

            dispatch_group_wait(group, DISPATCH_TIME_FOREVER)
            success(comments: retComments, replies: retReplies, userIds: retUserIds)

        }

    }
}

从我的代码中可以看出,我在同一comments下获取了所有article,然后在每个replies下获取相应的comment。完成所有请求后,我调用success回调。这可以使用GCD&#39; dispatch_group来实现。

现在我正在将相同的功能迁移到android。

public static void fetchCommentsAndTheirReplies(Context context, String articleId, final StringBuffer outErrorMessage, final Runnable failure, final ArrayList<JSONObject> outComments, final ArrayList<ArrayList<JSONObject>> outReplies, final HashSet<String> outUserIds, final Runnable success) {
    final RequestQueue queue = Volley.newRequestQueue(context);
    HashMap<String, String> commentParams = new HashMap<>();
    commentParams.put(API.article.articleId, articleId);
    JsonArrayRequest commentRequest = new JsonArrayRequest(Request.Method.GET, API.baseUrl + API.article.listCreateComment, new JSONObject(commentParams), new Response.Listener<JSONArray>() {
        @Override
        public void onResponse(JSONArray response) {
            try {
                for (int i = 0; i < response.length(); i++) {
                    JSONObject comment = response.getJSONObject(i);
                    outComments.add(comment);

                    outUserIds.addAll(helperParseRelaventUserIdsFromEntity(comment));
                    outReplies.add(new ArrayList<JSONObject>());

                    //TODO: DISPATCH_GROUP?
                    String id = comment.getString("_id");
                    HashMap<String, String> replyParams = new HashMap<>();
                    replyParams.put(API.article.commentId, id);
                    final int finalI = i;
                    JsonArrayRequest replyRequest = new JsonArrayRequest(Request.Method.GET, API.baseUrl + API.article.listCreateReply, new JSONObject(replyParams), new Response.Listener<JSONArray>() {
                        @Override
                        public void onResponse(JSONArray response) {
                            try {
                                for (int j = 0; j < response.length(); j++) {
                                    JSONObject reply = response.getJSONObject(j);
                                    outUserIds.addAll(helperParseRelaventUserIdsFromEntity(reply));
                                    outReplies.get(finalI).add(reply);
                                }
                            } catch (JSONException ex) {}
                        }
                    }, new Response.ErrorListener() {
                        @Override
                        public void onErrorResponse(VolleyError error) {}
                    });
                    queue.add(replyRequest);
                }
                success.run();

            } catch (JSONException ex) {}
        }
    }, new Response.ErrorListener() {
        @Override
        public void onErrorResponse(VolleyError error) {
            outErrorMessage.append(error.getMessage());
            failure.run();
        }
    });
    queue.add(commentRequest);
}

请注意,在我获得所有success之后,以及获取所有comments之前,我正在使用replies

那么如何对它们进行分组并延迟响应呢?

我正在研究毛茸茸的实施,比如

taskCount++;
if (taskCount == totalCount) {
    success.run();
} 

回复阻止,但似乎很乏味。

7 个答案:

答案 0 :(得分:19)

您可以使用我为模仿iOS行为而创建的这个类来完成它。调用enter()和leave(),就像你在iOS中使用dispatch_group_enter和dispatch_group_leave一样,并在你想要分组的请求之后调用notify(),就像dispatch_group_notify一样。它也像iOS使用块一样使用runnable:

public class DispatchGroup {

    private int count = 0;
    private Runnable runnable;

    public DispatchGroup()
    {
        super();
        count = 0;
    }

    public synchronized void enter(){
        count++;
    }

    public synchronized void leave(){
        count--;
        notifyGroup();
    }

    public void notify(Runnable r) {
        runnable = r;
        notifyGroup();
    }

    private void notifyGroup(){
        if (count <=0 && runnable!=null) {
             runnable.run();
        }
    }
}

希望它有所帮助;)

答案 1 :(得分:4)

普通Java或Android中没有dispatch_group的直接模拟。如果您准备在其中投入一些额外的时间,我可以推荐一些相当复杂的技术来制作一个非常干净和优雅的解决方案。不幸的是,它不会是一行或两行代码。

  1. Use RxJava with parallelizationRxJava提供了一种分发多个任务的简洁方法,但默认情况下它按顺序工作。请参阅此文章以使其同时执行任务。

  2. 虽然这不完全是预期的用例,但您可以尝试ForkJoinPool执行您的任务组,然后再收到一个结果。

答案 2 :(得分:3)

你的“毛茸茸”的实施并非“毛茸茸”。

public void onResponse(JSONArray response) {
                try {
                    final int[] taskFinished = {0};
                    final int taskTotal = response.length();
                    for (int i = 0; i < response.length(); i++) {
                        JSONObject comment = response.getJSONObject(i);
                        outComments.add(comment);

                        outUserIds.addAll(helperParseRelaventUserIdsFromEntity(comment));
                        outReplies.add(new ArrayList<JSONObject>());

                        //TODO: DISPATCH_GROUP?
                        String id = comment.getString("_id");
                        HashMap<String, String> replyParams = new HashMap<>();
                        replyParams.put(API.article.commentId, id);
                        final int finalI = i;
                        JsonArrayRequest replyRequest = new JsonArrayRequest(Request.Method.GET, API.baseUrl + API.article.listCreateReply, new JSONObject(replyParams), new Response.Listener<JSONArray>() {
                            @Override
                            public void onResponse(JSONArray response) {
                                taskFinished[0]++;
                                try {
                                    for (int j = 0; j < response.length(); j++) {
                                        JSONObject reply = response.getJSONObject(j);
                                        outUserIds.addAll(helperParseRelaventUserIdsFromEntity(reply));
                                        outReplies.get(finalI).add(reply);
                                    }
                                } catch (JSONException ex) {}
                                if (taskFinished[0] == taskTotal) {
                                    success.run();
                                }
                            }
                        }, new Response.ErrorListener() {
                            @Override
                            public void onErrorResponse(VolleyError error) {
                                taskFinished[0]++;
                                if (taskFinished[0] == taskTotal) {
                                    success.run();
                                }
                            }
                        });
                        queue.add(replyRequest);
                    }


                } catch (JSONException ex) {}
            }

答案 3 :(得分:3)

您可以使用ThreadThread.join() Handler作为选项。

引自:https://docs.oracle.com/javase/tutorial/essential/concurrency/join.html

  

join方法允许一个线程等待完成   另一个。如果t是其当前正在执行其线程的Thread对象,则

     

t.join();导致当前线程暂停执行直到t   线程终止。连接过载允许程序员指定一个   等待期。但是,与睡眠一样,join依赖于OS   对于计时,所以你不应该假设联接将完全等待   只要你指定。

     

就像睡眠一样,join通过退出来响应中断   InterruptedException的。

修改: 您还应该检查我的event dispatcher要点。你可能会喜欢它。

答案 4 :(得分:3)

这是Damien Praca的答案的Kotlin版本。这将允许你像这样使用Kotlin lambdas。

val dispatchGroup = DispatchGroup()
dispatchGroup.enter()
// Some long running task
dispatchGroup.leave()

dispatchGroup.notify {
// Some code to run after all dispatch groups complete
}

class DispatchGroup {
    private var count = 0
    private var runnable: (() -> Unit)? = null

    init {
        count = 0
    }

    @Synchronized
    fun enter() {
        count++
    }

    @Synchronized
    fun leave() {
        count--
        notifyGroup()
    }

    fun notify(r: () -> Unit) {
        runnable = r
        notifyGroup()
    }

    private fun notifyGroup() {
        if (count <= 0 && runnable != null) {
            runnable!!()
        }
    }
}

答案 5 :(得分:2)

尝试优先作业队列:https://github.com/yigit/android-priority-jobqueue

  

优先级作业队列是具体的作业队列实现   为Android编写,可以轻松安排在中运行的作业(任务)   背景,改善用户体验和应用程序稳定性。

     

(...)

     

如有必要,您可以对作业进行分组以确保其序列执行。对于   假设您有一个消息传递客户端,并且您的用户发送了一堆消息   他们的手机没有网络覆盖的消息。创建时   这些SendMessageToNetwork作业,您可以通过对话对它们进行分组   ID。通过这种方法,同一个对话中的消息将发送   在他们入队的顺序,而不同之间的消息   对话仍然是并行发送的。这让你毫不费力   最大化网络利用率并确保数据完整性。

答案 6 :(得分:1)

我使用 java.util.concurrent。 CountDownLatch 来实现目标。
首先,我为每个任务创建了一个界面。

interface MyDisptchGroupObserver {
    void onAllGroupTaskFinish();
}
class MyDisptchGroup {
    private static final int MSG_ALLTASKCOMPLETED = 300;
    private CountDownLatch latch;
    private MyDisptchGroupObserver observer;

    private MsgHandler msgHandler;
    private class MsgHandler extends Handler {
        MsgHandler(Looper looper) {
            super(looper);
        }
        @Override
        public void handleMessage(Message msg) {
            switch(msg.what) {
                case MSG_ALLTASKCOMPLETED:
                    observer.onAllGroupTaskFinish();
                    break;
                default:
                    break;
            }
        }
    }

    MyDisptchGroup(List<GroupTask> tasks, MyDisptchGroupObserver obj) {
        latch = new CountDownLatch(tasks.size());
        observer = obj;
        msgHandler = new MsgHandler(getActivity().getMainLooper())

        new Thread( new Runnable() {
            @Override
            public void run() {
                try {
                    latch.await();
                    Log.d(TAG, "========= All Tasks Completed =========");
                    msgHandler.sendEmptyMessage(MSG_ALLTASKCOMPLETED);
                } catch() {
                    e.printStackTrace();
                }
            }
        }).start();

        for( GroupTask task : tasks ) {
            task.onProcessing(latch);
        }
    }
}

然后我创建一个类来处理分组任务。

class Task1 implements GroupTask {
    @Override
    public void onProcessing(final CountDownLatch latch) {
        new Thread( new Runnable() {
            @Override
            public void run() {
                // Just implement my task1 stuff here


                // The end of the Task1 remember to countDown
                latch.countDown();
            }
        }).start();
    }
}

当然,我有多个任务实现,如下所示。 Task1

class Task2 implements GroupTask {
    @Override
    public void onProcessing(final CountDownLatch latch) {
        new Thread( new Runnable() {
            @Override
            public void run() {
                // Just implement my task2 stuff here


                // The end of the Task2 remember to countDown
                latch.countDown();
            }
        }).start();
    }
}

和Task2

ArrayList<GroupTask> allTasks = new ArrayList<GroupTask>();
allTasks.add(new Task1());
allTasks.add(new Task2());
new MyDisptchGroup(allTasks, this);

现在一切都准备好了。

{{1}}