Java异步与Observable新手求助:如何确保异步执行完成后再继续?
嘿,刚接触Java异步编程和Observable确实容易踩这类坑——用Thread.sleep(100)来等待异步结果完全是碰运气,一旦网络延迟或者处理耗时超过100ms,你就拿不到想要的数据了。咱们来一步步拆解问题,找到靠谱的解决方案。
问题根源
你的代码里,Observable.subscribe()的回调是在异步线程执行的,而execute()方法里的callbackContext.success()是在调用线程(比如主线程)直接执行的,两者的执行顺序是不确定的。你用Thread.sleep()只是强行延迟后续代码,但根本不能保证异步操作已经完成,这才会出现数据未就绪的问题。
方案一:把后续逻辑放到Observable的回调里(推荐)
这是最符合RxJava异步设计思想的做法——既然异步操作的结果只有在subscribe的onNext回调里才能拿到,那直接把callbackContext.success()放到这个回调里就行,完全不需要用全局的allInfo来中转数据。
修改后的代码如下:
private void getGroupAllInfo(CallbackContext callbackContext){ Log.d("executing: ", "getGroupAllInfo1"); Observable observable = GsscFactory.executeGetZwaveAllInfo(this.sock, MationPlugin.gatewayId, MationPlugin.account, MationPlugin.password); observable.subscribe( // 异步操作成功时触发 t -> { System.out.print(t); // 直接在这里执行后续的成功回调 callbackContext.success(t.toString()); }, // 别忘记处理错误情况! error -> { callbackContext.error(error.getMessage()); } ); } public boolean execute(String action, JSONArray args, CallbackContext callbackContext) throws JSONException { this.getGroupAllInfo(callbackContext); // 这里不需要sleep和额外的success调用了 return true; }
这样做的好处是:只有当GsscFactory.executeGetZwaveAllInfo()的异步操作完成并返回结果时,才会触发callbackContext.success(),从根本上保证了数据的就绪性,也避免了线程阻塞。
方案二:阻塞式获取结果(仅适合后台线程)
如果你确实需要把数据存入allInfo再处理,可以用RxJava的阻塞式操作符(比如blockingSingle()),但要注意:这种方式会阻塞当前线程,绝对不能在主线程使用,否则会导致UI卡顿甚至ANR。
代码示例:
private String getGroupAllInfo() { Log.d("executing: ", "getGroupAllInfo1"); try { // 阻塞当前线程,直到Observable返回结果 Object result = GsscFactory.executeGetZwaveAllInfo(this.sock, MationPlugin.gatewayId, MationPlugin.account, MationPlugin.password) .blockingSingle(); System.out.print(result); return result.toString(); } catch (Exception e) { e.printStackTrace(); throw new RuntimeException("获取数据失败", e); } } public boolean execute(String action, JSONArray args, CallbackContext callbackContext) throws JSONException { try { String data = this.getGroupAllInfo(); MationPlugin.allInfo.put("data", data); callbackContext.success(data); } catch (RuntimeException e) { callbackContext.error(e.getMessage()); } return true; }
补充说明:为什么之前subscribe里赋值变量外部拿不到?
因为subscribe的回调是在独立的异步线程中执行的,当execute()方法里的Thread.sleep(100)结束时,异步回调可能还没执行,这时候allInfo里的data自然是空的。异步编程的核心就是不要等待,而是让后续逻辑依赖回调触发,这样才能保证时序的正确性。
内容的提问来源于stack exchange,提问作者zifan yan

