问题描述
我正在学习 android 开发,我正在使用 RXJava 进行改造以与 API 进行交互.所以我已经成功地从我的 API 获取数据.现在的问题是程序在获取数据之前继续运行.如何等待从 API 下载数据然后运行某些函数?
I'm learning android development I'm using RXJava with retrofit to interact with an API. So i've successfully managed to GET Data from my API. The problem now is that the program continues before the data has been fetched. How can I wait for the data to be downloaded from the API and then run some function?
我有一个改装客户端,如图所示
I have a retrofit client as shown
public class RetrofitClient {
private static Retrofit retrofit = null;
private RetrofitClient() {
}
public static Retrofit getClient(String baseUrl) {
if (retrofit == null) {
retrofit = new Retrofit.Builder()
.baseUrl(baseUrl)
.addConverterFactory(SimpleXmlConverterFactory.create())
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.build();
}
return retrofit;
}
}
我进行了这样的 API 调用.
I do an API call as such.
public void getData(MyFragment Fragment) {
mAPIService.getData()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Subscriber<Data>() {
@Override
public void onCompleted() {
}
@Override
public void onError(Throwable e) {
Log.i("ERROR IN GET DATA", e.toString());
}
@Override
public void onNext(Data response) {
Log.i("MY DATA", response.toString());
fragment.downloadData(response);
}
});
}
问题是我的 android 应用程序没有等待 fragment.downloadData(response) 完成,而是继续执行代码,然后因为响应为空而崩溃.
the problem is my android application does not wait for fragment.downloadData(response) to finish but instead continues executing code and then crashes because response is null.
我在一个按钮上有一个监听器,当点击它时从 API 获取数据
I have a listener on a button that when clicked gets data from the API
button.setOnClickListener(v ->{
APICaller.getData(this);
Log.i("TEST", data.ToString()); //THIS IS NULL
});
这是我从 APICaller 运行的 downloadData 函数
This is the downloadData function that I run from the APICaller
public void downloadData(Data data) {
this.data = data;
}
推荐答案
您需要等待 RxJava 流发出值(错误或响应).
You need to be waiting for your RxJava stream to emit a value (either error or response).
首先,为此,如果您期望单一"发射,成功或失败,我会使用 Single
.目前看起来您的 mAPIService.getData()
方法正在返回一个 Observable
.这些适用于将发出多个值的流,在您的原因中,我假设不会发生什么.您只有一项将被发出,因此我会考虑返回一个 Single
.不是你问题的一部分,但仅供参考.
Firstly, for this, if you're expecting a "single" emission, success or failure, I would use a Single
. At the moment it looks your mAPIService.getData()
method is returning an Observable
. These are meant for streams that are going to emit multiple values which in your cause I am assuming is not what is going to happen. You only have one item that is going to be emitted so I would look at returning a Single
. Not part of your question but FYI.
我喜欢做的是告诉我的 UI 我正在做的任何事情都是加载",通常在 doOnSubscribe
中.然后 UI 知道显示加载图标或不允许用户交互或其他东西.像这样的事情(注意它是如何在 observeOn(AndroidSchedulers.mainThread)
之后的.任何时候你与 UI 元素交互时,在主线程上进行.我认为这会做到):>
What I like to do is to tell my UI that whatever I'm doing is "loading", normally in the doOnSubscribe
. Then the UI knows to show a loading icon or to not allow user interactions or something. Something like this (nb. notice how its after the observeOn(AndroidSchedulers.mainThread)
. Any time you interact with UI elements, do it on the main thread. I think this will do it):
mAPIService.getData()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.doOnSubscribe(new Consumer<Disposable>() {
@Override
public void accept(Disposable disposable) throws Exception {
fragment.loading();
}
})
然后,当 onError
或 onSuccess
在您的订阅中返回时,您就会告诉 UI 它已准备好继续.然后,您要么有一个有效的响应,要么可以向用户显示错误.
Then when either the onError
or onSuccess
returns in your subscription, that is where you tell the UI it's ready to proceed. You then either have a valid response or can show the error to the user.
响应式编程
在您发表评论之后,您似乎不了解反应式编程.它的学习曲线有点陡峭,我今天仍然在努力.
After your comments it looks like you do not understand reactive programming. It has a bit of a steep learning curve and I still struggle with it today.
你的 APICaller
类,不管它是什么,都应该返回 Observable
本身.您不应该将 Fragment
传递给它并在其中处理它,因为您将自己暴露在内存泄漏和代码气味中.更好的选择是只返回 mAPIService.getData()
返回的 Observable
.就是这样.目前,您正在使用 Schedulers.io()
将 Observable
推送到另一个线程,该线程对您的主线程说,继续,不要等我.然后,当使用代码 .observeOn(AndroidSchedulers.mainThread())
Your APICaller
class, whatever it is, should return the Observable
itself. You shouldn't be passing in a Fragment
to this and handling it within there as you're opening yourself up to memory leaks and its a bit of a code smell. A better option is to just return the Observable
returned by mAPIService.getData()
. That's it. At the moment you are pushing the Observable
to another thread using Schedulers.io()
which says to your main thread, carry on and don't wait for me. You then come back to this thread when a response is emitted using the code .observeOn(AndroidSchedulers.mainThread())
然后在您的片段中处理值或错误的发射.您的片段代码将变为:
In your fragment is then where you handle the emission of a value or an error. Your fragment code then becomes:
button.setOnClickListener(v ->{
APICaller.getData()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Subscriber<Data>() {
@Override
public void onCompleted() {
}
@Override
public void onError(Throwable e) {
Log.i("ERROR IN GET DATA", e.toString());
}
@Override
public void onNext(Data response) {
Log.i("MY DATA", response.toString());
this.data = response
Log.i("TEST: "+data.toString());
}
});
});
我想推荐你看一些教程,做一些阅读,我也想在开始时让你回到我关于Single
s的观点
I would like to recommend you watch some tutorials, do some reading and I'd also like to refer you back to my point about Single
s at the start
这篇关于RXJava 改造等待 UI 等待从 API 获取数据的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!