Android如何像iOS一样将异步任务组合在一起
我在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) } } }
正如您从我的代码中看到的,我可以在同article
获取所有comments
,然后在每个comment
下取回相应的replies
。 所有请求完成后,我调用我的success
回调。 这可以通过使用GCD的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(); }
在答复块,但似乎非常繁琐。
在普通的Java或Android中没有dispatch_group
直接模拟。 如果你准备投入一些额外的时间,我可以推荐一些相当复杂的技术来产生一个非常干净和优雅的解决方案。 不幸的是,这不会是一两行代码。
-
使用
RxJava
并行化 。RxJava
提供了一个干净的方式来分派多个任务,但默认情况下,它依次运行。 请参阅本文以使其同时执行任务。 -
虽然这不完全是预期的用例,但您可以尝试使用ForkJoinPool执行您的任务组,然后收到单个结果。
你可以简单地用这个类来模仿iOS的行为。 使用dispatch_group_enter和dispatch_group_leave调用enter()和leave(),就像在dispatch_group_leave中调用notify()一样,然后调用notify(),就像dispatch_group_notify一样。 它也使用与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(); } } }
希望它有帮助;)
你的“毛茸茸的”实现在所有imho上都不是“毛茸茸”的。
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) {} }
你可以使用Thread.join()
和Thread.join()
和Handler
作为一个选项。
引自: https : //docs.oracle.com/javase/tutorial/essential/concurrency/join.html
连接方法允许一个线程等待另一个线程的完成。 如果t是线程当前正在执行的线程对象,
t.join(); 导致当前线程暂停执行,直到t的线程终止。 连接超载允许程序员指定一个等待期。 但是,与睡眠一样,连接依赖于操作系统进行计时,因此您不应该认为连接将按照您指定的时间长度等待。
像睡眠一样,连接通过退出InterruptedException来响应中断。
编辑 :你也应该检查我的事件调度的要点。 你可能会喜欢它。
尝试优先级作业队列: https : //github.com/yigit/android-priority-jobqueue
优先级作业队列是为Android专门编写的作业队列的一个实现,可轻松调度在后台运行的作业(任务),从而提高UX和应用程序的稳定性。
(……)
如有必要,您可以将作业分组以确保其串行执行。 例如,假设您有一个消息客户端,并且您的用户在他们的电话没有网络覆盖时发送了一堆消息。 创建这些SendMessageToNetwork作业时,可以按对话ID对它们进行分组。 通过这种方法,同一对话中的消息将按照它们排队的顺序发送,而不同对话之间的消息仍然是并行发送的。 这使您可以毫不费力地最大化网络利用率并确保数据完整性。
不要使用Java
给Kotlin一个尝试,它有承诺
task { //some (long running) operation, or just: 1 + 1 } then { i -> "result: $i" } success { msg -> println(msg) }
你不必,但最简单的方法来解决这个问题是通过使用rxJava而不是凌空的改造。
也许你正在寻找像Retrofit或Volley的东西。
- Kotlin Higher Order Function以可变数量的参数作为parameter passing一个函数
- sharedpreferences上的nullPointerException helper类运行测试时
- 如何使用共享元素转换来缩放textviews?
- Gradle无法在Windows上读取正确的本地Maven存储库
- 许多小模块,或几个大型模块的最佳构建性能?
- 在库模块中使用Kotlin,而不在app模块中使用它
- Android架构组件LiveData
- 如何通过setOnEditorActionListener关闭android alert对话框本身?
- Android,Espresso仪器化测试仅适用于Android 5.0+