我正在重写一些Android应用程序来调用我用Java编写的REST-Server,使用Spring-Boot Framework。
总而言之,假设在应用程序中,函数A调用我的MainController中的函数B(它应该管理客户端 - 服务器通信)。功能B调用(带一些额外步骤)调用我的服务器。当函数B进行调用时,它使用匿名内部类重写方法来定义如何处理响应。我很难将匿名内部类的响应从函数B中获取,因此它可以返回到函数A.可悲的是,涉及到一些异步,只是为了让事情变得更有趣。
我的第一次尝试是使用Map来存储具有唯一ID的调用结果,以使其可以在内部类之外访问。在理论上运作得相当好,但在同步性下崩溃
其次,我直截了当地添加了一个循环,它应该等待结果在继续之前放入Map中,确保有一些东西可以返回。
不知何故,循环没有正确执行(我相信),当我使用循环时,对我的服务器的调用永远不会完成(当没有循环时它会执行)
请参阅以下代码
客户:MainController
class MainController
{
private final AtomicLong counter;
private final HashMap<Long, Object> callResults;
public MainController(){
counter = new AtomicLong();
callResults = new HashMap<>();
}
public Login getLoginByUsername(String username)
{
long callID = counter.incrementAndGet();
System.out.println("Start call");
ServerConnection.get("myURL",null, new JsonHttpResponseHandler(){
@Override
public void onSuccess(int statusCode, Header[] headers, JSONObject response){
try {
System.out.println("Call success, enter result:");
callResults.put(callID, new CallResult(Login.parseFromJson(response)));
System.out.println(callResults.get(callID));
} catch (JSONException e) {
e.printStackTrace();
}
}
});
System.out.println("start waiting");
while(callResults.get(callID) == null){
System.out.print(".");
}
System.out.println("waiting over, return result:");
System.out.println(callResults.get(callID));
//return (Login) callResults.remove(callID).content;
return null;
}
}
客户端:ServerConnection
import com.loopj.android.http.*;
import cz.msebera.android.httpclient.HttpEntity;
public class ServerConnection {
private static final String BASE_URL = "http://1.3.3.7:8080/";
private static AsyncHttpClient client = new AsyncHttpClient();
public static void get(String url, RequestParams params, JsonHttpResponseHandler responseHandler) {
client.get(BASE_URL+url, params, responseHandler);
}
}
这就是我所知道的:
这就是我所假设的问题:循环阻止了异步REST调用,因此无法终止。
我的问题:A)如何将我从服务器收到的登录对象返回到首先调用MainController.getLoginByUsername-function的函数?
B)我试图用来等待的while循环是怎么回事?为什么不执行正文(print(".");
),更重要的是,为什么它会停止执行/完成REST调用表单?
(也可以随意留下关于我的发布风格的反馈,这是我第一次在这里提问)
编辑:添加问题B,可能提供一种更简单的方法来查找快速解决方案
编辑更新:很抱歉没有说清楚:任何需要我调整调用函数的建议(例如观察者模式,如Livedata
或Rxjava
或Leo Pelozo提供的其他非常好的建议)都是不可行的。我测试了至少Leo Pelozos的建议,而底层代码根本不允许这样做。我无法访问参数,无法再使用函数返回。我知道,重新设计底层系统将是最好的行动方案,但现在只是超出范围......
你有没有使用谷歌的GSON,它将JSON对象转换为java对象,它的语法很简单你会喜欢它,我一直都在使用它。我使用volley进行HTTP请求响应。只需使用GSON就好
GSON gson = new GSON();
LoginObject login = new LoginObject();
login=gson.fromJson(responce,LoginObject.class);
瞧,你做完了。
如果您不想使用Livedata
或Rxjava
,您可以重构该方法以接受JsonHttpResponseHandler
或自定义回调,它不是很漂亮,但它的工作原理:
1.
public void getLoginByUsername(String username, JsonHttpResponseHandler callback)
{
long callID = counter.incrementAndGet();
System.out.println("Start call");
ServerConnection.get("myURL",null, callback);
}
然后像这样调用它:
getLoginByUsername("myUsername", new JsonHttpResponseHandler(){
@Override
public void onSuccess(int statusCode, Header[] headers, JSONObject response){
try {
Login myLogin = Login.parseFromJson(response);
//do stuff
} catch (JSONException e) {
e.printStackTrace();
}
}
});
要么
2.
class MainController {
private final AtomicLong counter;
private final HashMap<Long, Object> callResults;
interface OnLogin{
void onSuccessLogin(Login login);
}
public MainController() {
counter = new AtomicLong();
callResults = new HashMap<>();
}
public void getLoginByUsername(String username, OnLogin callback)
{
ServerConnection.get("myURL",null, new JsonHttpResponseHandler(){
@Override
public void onSuccess(int statusCode, Header[] headers, JSONObject response){
try {
Login loginObject = Login.parseFromJson(response);
callback.onSuccessLogin(loginObject);
} catch (JSONException e) {
e.printStackTrace();
}
}
});
}
}
你这样称呼它:
MainController controller = new MainController();
controller.getLoginByUsername("myUsername", new MainController.OnLogin() {
@Override
public void onSuccessLogin(Login login) {
//do whatever here
}
});
您可以使用CompletableFuture<?>
轻松实现此目的。它与JavaScript中的Promise类似。如果您在API Level 24之前需要兼容性,请检查this answer here。
所以你的getLoginByUsername
看起来像这样:
public CompletableFuture<Login> loginByUsername(String username) {
CompletableFuture<Login> promise = new CompletableFuture<Login>();
System.out.println("Start call");
ServerConnection.get("myURL",null, new JsonHttpResponseHandler(){
@Override
public void onSuccess(int statusCode, Header[] headers, JSONObject response){
try {
System.out.println("Call success, enter result:");
promise.complete(Login.parseFromJson(response));
} catch (JSONException e) {
promise.completeExceptionally(e);
}
}
});
return promise;
}
你的电话网站是这样的:
Login login = loginByUsername("user").get();
// Or wait a maximum time
Login login = loginByUsername("user").get(30, TimeUnit.SECONDS);
永远不要在UIThread上调用get()
。这将阻止UI并为用户提供糟糕的体验
感谢你的帮助!我找到了适合我案例的解决方案。
首先,因为这只是一个原型,我真的不需要我的请求是异步的,所以我改变ServerConnection
使用SyncHttpClient
代替:
public class ServerConnection {
private static final String BASE_URL = "http://10.203.58.11:8080/";
private static SyncHttpClient client = new SyncHttpClient();
public static void get(String url, RequestParams params, JsonHttpResponseHandler responseHandler) {
client.get(getAbsoluteUrl(url), params, responseHandler);
}
}
然后,Android将不允许我在主线程上执行请求。这不是AsyncHttpClient
的问题,因为async-part是在它自己的一个线程中处理的。所以我不得不为我的请求添加线程。因此,我的MainController
现在看起来像这样:
public class MainController
{
private final AtomicLong counter;
private final HashMap<Long, CallResult> callResults;
public MainController(){
counter = new AtomicLong();
callResults = new HashMap<>();
}
public Login getLoginByUsername(String username)
{
long callID = counter.incrementAndGet();
new Thread(new Runnable() {
@Override
public void run() {
ServerConnection.get("person-login-relations?byUsername="+username,null, new JsonHttpResponseHandler(){
@Override
public void onSuccess(int statusCode, Header[] headers, JSONObject response){
try {
callResults.put(callID, new CallResult(Login.parseFromJson(response)));
System.out.println(callResults.get(callID));
} catch (JSONException e) {
e.printStackTrace();
}
}
});
}
}) .start();
while(callResults.get(callID) == null){
//wait
}
return (Login) callResults.remove(callID).content;
}
}
我想我现在用来等待的while循环有效,因为它没有阻塞负责执行和处理请求的线程。虽然不完全确定。
不可否认,这可能不是理想的解决方案,但考虑到我的具体情况,它是最可行的。
我几个月后,我将负责重新编写我现在扩展到连接到客户端的应用程序。那时我会确保实施你的大部分建议!所以对于阅读此解决方案的任何人来说:这就是一个解决方法,因为我不需要异步请求,我不应该更改调用您在此处看到的方法的函数!我建议你仍然阅读其他回复,因为它们在质量上更优越!