如何return响应调用函数? (Android-应用程序通过 JSON 调用 Java REST 服务器)
How to return response to calling function? (Android-App calling Java REST-Server via JSON)
我正在使用 Spring-Boot Framework 重写 Android 应用程序的某些部分以调用我在 Java 中编写的 REST 服务器。
总而言之,假设在应用程序中,函数 A 在我的 MainController(应该管理客户端-服务器通信)中调用函数 B。函数 B 调用(通过一些额外的步骤)调用我的服务器。当函数 B 进行调用时,它使用匿名内部 class 覆盖方法来定义如何处理响应。
我努力从匿名内部 class 获得对函数 B 的响应,以便它可以 returned 到函数 A。
可悲的是,涉及到一些异步性,只是为了让事情变得更有趣。
我的第一个尝试是使用一个 Map 来存储具有唯一 ID 的调用结果,以使其可以在内部 class 之外访问。理论上工作得很好,但在异步下会崩溃
其次,我直接添加了一个循环,该循环应该等待结果放入 Map 中,然后再继续,确保有东西可以 return。
不知何故,循环没有正确执行(我相信),当我使用循环时,对我的服务器的调用永远不会完成(当没有循环时它会完成)
看下面代码
客户端:主控制器
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);
}
}
这是我所知道的:
预期的响应最终会出现。当我不使用循环等待而只是 return null 时,控制台将显示消息 "Call success, enter result:" 和对象 ID
在最后使用 while 循环时,它从不打印“。” - 它什么都不做。我知道应用程序仍然是 运行,因为 AndroidStudio 不断更新我的 space 分配
尝试应用观察者模式可能完全超出范围。我将不得不深入挖掘我不想接触的客户端代码。老实说,我提供的代码并不容易使用
这是我认为的问题所在:
该循环阻止进行异步 REST 调用,因此无法终止。
我的问题:
A) 我怎样才能 return 我从服务器收到的登录对象到首先调用 MainController.getLoginByUsername 函数的函数?
B) 我试图用于等待的 while 循环发生了什么?为什么主体 (print(".");
) 不会被执行,更重要的是,为什么它会停止正在执行/完成的 REST 调用表单?
(也欢迎大家对我的发帖风格留下反馈,这是我第一次在这里提问)
编辑:添加了问题 B,可能会提供一种更简单的方法来快速找到解决方案
编辑更新:
很抱歉没有说清楚:任何需要我调整调用函数的建议(例如 Livedata
或 Rxjava
之类的观察者模式或 Leo Pelozo 提供的其他非常好的建议)都不是可行的。
我至少测试了 Leo Pelozos 的建议,而底层代码根本不允许这样做。我无法访问参数并且无法再使用函数 returns。
我知道,重新设计底层系统将是最好的行动方案,但现在这完全超出了范围...
你用过google的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 级别 24 之前的兼容性,请选中 。
所以你的 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);
切勿在 UI 线程上调用 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
的问题,因为异步部分是在它自己的线程中处理的。所以我不得不在我的请求中添加线程。因此,我的 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 循环现在可以工作了,因为它不会阻塞负责执行和处理请求的线程。不过不完全确定。
诚然,这可能不是理想的解决方案,但在我的具体情况下这是最可行的。
几个月后,我将负责返工我现在正在扩展以连接到客户端的应用程序。届时我将确保实施您的大部分建议!
所以对于阅读此解决方案的任何人:这是一种解决方法,因为我不需要异步请求,而且我不应该更改调用您在此处看到的方法的函数!我建议你还是看看其他的回复,因为它们质量上乘!
我正在使用 Spring-Boot Framework 重写 Android 应用程序的某些部分以调用我在 Java 中编写的 REST 服务器。
总而言之,假设在应用程序中,函数 A 在我的 MainController(应该管理客户端-服务器通信)中调用函数 B。函数 B 调用(通过一些额外的步骤)调用我的服务器。当函数 B 进行调用时,它使用匿名内部 class 覆盖方法来定义如何处理响应。 我努力从匿名内部 class 获得对函数 B 的响应,以便它可以 returned 到函数 A。 可悲的是,涉及到一些异步性,只是为了让事情变得更有趣。
我的第一个尝试是使用一个 Map 来存储具有唯一 ID 的调用结果,以使其可以在内部 class 之外访问。理论上工作得很好,但在异步下会崩溃
其次,我直接添加了一个循环,该循环应该等待结果放入 Map 中,然后再继续,确保有东西可以 return。
不知何故,循环没有正确执行(我相信),当我使用循环时,对我的服务器的调用永远不会完成(当没有循环时它会完成)
看下面代码
客户端:主控制器
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);
}
}
这是我所知道的:
预期的响应最终会出现。当我不使用循环等待而只是 return null 时,控制台将显示消息 "Call success, enter result:" 和对象 ID
在最后使用 while 循环时,它从不打印“。” - 它什么都不做。我知道应用程序仍然是 运行,因为 AndroidStudio 不断更新我的 space 分配
尝试应用观察者模式可能完全超出范围。我将不得不深入挖掘我不想接触的客户端代码。老实说,我提供的代码并不容易使用
这是我认为的问题所在: 该循环阻止进行异步 REST 调用,因此无法终止。
我的问题: A) 我怎样才能 return 我从服务器收到的登录对象到首先调用 MainController.getLoginByUsername 函数的函数?
B) 我试图用于等待的 while 循环发生了什么?为什么主体 (print(".");
) 不会被执行,更重要的是,为什么它会停止正在执行/完成的 REST 调用表单?
(也欢迎大家对我的发帖风格留下反馈,这是我第一次在这里提问)
编辑:添加了问题 B,可能会提供一种更简单的方法来快速找到解决方案
编辑更新:
很抱歉没有说清楚:任何需要我调整调用函数的建议(例如 Livedata
或 Rxjava
之类的观察者模式或 Leo Pelozo 提供的其他非常好的建议)都不是可行的。
我至少测试了 Leo Pelozos 的建议,而底层代码根本不允许这样做。我无法访问参数并且无法再使用函数 returns。
我知道,重新设计底层系统将是最好的行动方案,但现在这完全超出了范围...
你用过google的GSON吗,它把JSON对象转换成java对象,它的语法很简单,你会喜欢的,我一直在用它。我使用 volley 进行 HTTP 请求响应。 像
这样使用 GSONGSON 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 级别 24 之前的兼容性,请选中
所以你的 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);
切勿在 UI 线程上调用 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
的问题,因为异步部分是在它自己的线程中处理的。所以我不得不在我的请求中添加线程。因此,我的 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 循环现在可以工作了,因为它不会阻塞负责执行和处理请求的线程。不过不完全确定。
诚然,这可能不是理想的解决方案,但在我的具体情况下这是最可行的。
几个月后,我将负责返工我现在正在扩展以连接到客户端的应用程序。届时我将确保实施您的大部分建议! 所以对于阅读此解决方案的任何人:这是一种解决方法,因为我不需要异步请求,而且我不应该更改调用您在此处看到的方法的函数!我建议你还是看看其他的回复,因为它们质量上乘!