[java] Android, как объединить задачи async вместе, как в iOS



3 Answers

Вы можете просто сделать это с помощью этого класса, который я сделал, чтобы имитировать поведение iOS. Вызовите enter () и оставьте () так же, как вы делали в iOS, с dispatch_group_enter и dispatch_group_leave и вызывают notify () сразу после запросов, которые вы хотите сгруппировать, например dispatch_group_notify. Он также использует runnable так же, как iOS использует блоки:

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();
        }
    }
}

Надеюсь, поможет ;)

Question

У меня есть функция в приложении 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 . После того, как все запросы выполнены, я вызываю свой обратный вызов. Это может быть достигнуто с помощью dispatch_group GCD.

Теперь я переношу ту же функциональность в 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();
} 

в блоке ответа, но это кажется очень утомительным.




Не использовать Java

Попросите Котлина попробовать, у него есть обещания

task {
    //some (long running) operation, or just:
    1 + 1
} then {
    i -> "result: $i"
} success {
    msg -> println(msg)
}

https://github.com/mplatvoet/kovenant




Возможно, вы ищете что-то вроде Retrofit или Volley .




Вы можете использовать Thread s и Thread.join() с Handler s в качестве опции.

цитата из: https://docs.oracle.com/javase/tutorial/essential/concurrency/join.html

Метод join позволяет одному потоку ждать завершения другого. Если t - объект Thread, поток которого в настоящее время выполняется,

t.join (); заставляет текущий поток приостанавливать выполнение до тех пор, пока поток t не завершится. Перегрузки соединения позволяют программисту указать период ожидания. Однако, как и во время сна, соединение зависит от ОС для синхронизации, поэтому вы не должны предполагать, что соединение будет ждать столько, сколько вы укажете.

Как и сон, соединение реагирует на прерывание, выходя из InterruptedException.

EDIT : вы также должны проверить мой диспетчер событий . Вам это может понравиться.






Related