我对Android,Java和Azure有点新手,我正在使用Azure MobileServiceClient类尝试在后端调用各种API。我遇到的问题是,使用ListenableFutures,MobileServiceClient的方法似乎都是异步的。
这样就好了,除了我想在一个helper类中使用这些方法,这个类也可以执行其他应该是异步的东西(因此它扩展了AsyncTask)。但是,由于MobileServiceClient调用是异步的,因此会导致AsyncTask过早返回。我希望AsyncTask在调用MobileServiceClient方法之前不要调用它的onPostExecute方法。
如何避免此问题?我需要改变我的架构吗?是否可以将MobileServiceClient调用放在另一个异步任务中并让它阻止它?
@Override
protected Boolean doInBackground(Void... params) {
Log.i(TAG, "Doing background task");
if(mTaskType==tTaskType.LOGIN_TASK){
login();
//do other stuff here that should be async
}
return true;
}
private void login(){
Log.i(TAG, "Doing login task...");
ListenableFuture<JsonElement> result = mClient.invokeApi("login", mJSONHelper.makeLoginObject(thisUser));
Futures.addCallback(result, new FutureCallback<JsonElement>() {
@Override
public void onFailure(Throwable exc) {
error.setError(ErrorHelper.Error.NETWORK_ERROR);
}
@Override
public void onSuccess(JsonElement result) {
}
});
}
答案 0 :(得分:2)
我将在前面加上警告,我对Android也不是很熟悉。但是根据我在其他平台上的经验以及对API的快速搜索,这是我认为你应该采取的方法。我也不承诺代码片段会编译,因为我没有检查过,但他们应该接近这样做。
您的login
方法应返回ListenableFuture<T>
,然后doInBackground
方法可以添加自己的回调,该回调在登录完成时执行。
如果您希望其他内容能够等待doInBackground
任务完成,那么还应返回ListenableFuture<T>
,这可以通过使用Futures.transform
方法链接到一起来完成一系列异步调用。
以下是我认为应该是这样的:
protected void doInBackground(Void... params) {
Log.i(TAG, "Doing background task");
if(mTaskType==tTaskType.LOGIN_TASK){
var loginFuture = ListenableFuture<UserDetail> login();
Futures.addCallback(loginFuture, new FutureCallback<UserDetail>() {
@Override
public void onSuccess(UserDetail userDetail)
{
// do other stuff here that should be async
// also optionally you could implement this as a transform
// style thing to and return another future from this `doInBackground`
// method so other parts of your code could know when it is completed.
}
@Override
public void onFailure(Throwable exc) {
// I'd quite likely move the error handling from the login method here
// as that way it can also handle any exceptions caused by the transform
// from json to user detail as well.
}
})
}
}
private ListenableFuture<UserDetail> login(){
Log.i(TAG, "Doing login task...");
ListenableFuture<JsonElement> loginFutureResult = mClient.invokeApi("login", mJSONHelper.makeLoginObject(thisUser));
Futures.addCallback(loginFutureResult, new FutureCallback<JsonElement>() {
@Override
public void onFailure(Throwable exc) {
// This is just to keep with what your style is, for recording the error
// I think you might be better off handling it at a higher level and
// also you might want to check `exc` to see if it was an actual network
// error and not for example just failed credentials or something.
error.setError(ErrorHelper.Error.NETWORK_ERROR);
}
@Override
public void onSuccess(JsonElement result) {
Log.i(TAG, "The login was successful");
}
});
// lets pretend that instead of returning the JSON response
// you wanted to map it to a user detail before returning, just to show how to do that.
AsyncFunction<JsonElement, UserDetail> transformUserJsonFunction =
new AsyncFunction<JsonElement, UserDetail>() {
public ListenableFuture<UserDetail> apply(JsonElement userJson) {
// some code to map the json element to user detail
UserDetail userDetail = new UserDetail(userJson);
return Futures.immediateFuture(userDetail);
}
};
return Futures.transform(loginFutureResult, transformUserJsonFunction);
}
我希望指出你正确的方向。
答案 1 :(得分:0)
嗯,我用一面旗帜做了 - 但它并不漂亮。我仍然对任何更优雅或更正确的方法感兴趣。或者这实际上是正确的方法吗?
private void login(){
Log.i(TAG, "Doing login task...");
isLoginFinished= false;
ListenableFuture<JsonElement> result = mClient.invokeApi("login", mJSONHelper.makeLoginObject(thisUser));
Futures.addCallback(result, new FutureCallback<JsonElement>() {
@Override
public void onFailure(Throwable exc) {
error.setError(ErrorHelper.Error.NETWORK_ERROR);
isLoginFinished= true;
}
@Override
public void onSuccess(JsonElement result) {
Log.i(TAG, "Login call was successful, parsing result:" + result.toString());
isLoginFinished= true;
}
});
while(!isLoginFinished);
}