我正在使用Spring-Boot框架重写一些Android应用程序,以调用我用Java编写的REST服务器。
总而言之,让我们在应用程序中说,函数A在我的MainController中调用了函数B(它应该管理客户端与服务器之间的通信)。函数B调用(还有一些额外的步骤)将调用我的服务器。当函数B进行调用时,它将使用匿名内部类重写方法来定义如何处理响应。 我很难从匿名内部类获得对函数B的响应,因此可以将其返回给函数A。 可悲的是,其中包含一些异步性,只是使事情变得更加有趣。
我的第一个尝试是使用一个Map,该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);
}
}
这就是我所知道的:
预期的响应最终会到来。当我不使用循环等待并仅返回null时,控制台将显示消息“呼叫成功,输入结果:”和一个对象ID
在末尾使用while循环时,它永远不会输出“”。 -它什么也没做。我知道应用程序仍在运行,因为AndroidStudio不断在空间分配方面更新我的信息
尝试应用观察者模式可能完全超出范围。我将不得不深入研究我不想接触的客户端代码。老实说,我提供的代码并不容易使用
这是我假设的问题: 该循环会阻止进行异步REST调用,从而使其无法终止。
我的问题: A)如何将从服务器收到的登录对象返回到首先调用MainController.getLoginByUsername-function的函数?
B)我试图用于等待的while循环是怎么回事?为什么不执行正文(print(".");
),更重要的是,为什么它会阻止REST调用表单的执行/完成?
(也可以随时对我的发帖风格发表反馈,这是我第一次在这里提出问题)
编辑:添加了问题B,可能会提供一种更简便的方法来找到快速解决方案
编辑为UPDATE:
不好意思,我很抱歉:任何也需要我改编调用函数的建议(例如Livedata
或Rxjava
之类的观察者模式或Leo Pelozo提供的其他非常好的建议)可行。
我至少测试了Leo Pelozos的建议,而底层代码根本不允许这样做。我无法访问参数,无法再使用函数返回。
我知道,对底层系统进行返工将是最好的方法,但是现在这已经超出了范围...
答案 0 :(得分:0)
您使用过Google的GSON,它将JSON对象转换为Java对象,其语法很简单,您会喜欢的,我一直都在使用它。我将凌空性用于HTTP请求响应。 像
一样使用GSONGSON gson = new GSON();
LoginObject login = new LoginObject();
login=gson.fromJson(responce,LoginObject.class);
瞧,你完成了。
答案 1 :(得分:0)
如果您不想使用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
}
});
答案 2 :(得分:0)
您可以使用CompletableFuture<?>
轻松实现这一目标。它的工作方式类似于JavaScript中的Promise。如果需要在API级别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()
。这样会阻塞用户界面,并给用户带来糟糕的体验
答案 3 :(得分:0)
感谢您的帮助!我找到了适合我情况的解决方案。
首先,因为这只是一个原型,所以我真的不需要异步请求,所以我将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
无关,因为异步部分是在自己的线程中处理的。因此,我不得不在请求中添加线程。因此,我的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循环现在可以工作了,因为它没有阻塞负责执行和处理请求的线程。对此,我并不完全确定。
诚然,这可能不是理想的解决方案,但在我的特定情况下,这是最可行的解决方案。
几个月后,我将负责重新设计我现在扩展的应用程序以连接到客户端。届时,我将确保落实您的大部分建议! 所以对任何阅读此解决方案的人来说:这是一种解决方法,因为我不需要异步请求,而且我不应该更改调用您在此处看到的方法的函数!我建议您仍然阅读其他答复,因为它们在质量上要好!