同步 Volley 请求锁定
Synchronous Volley request locks up
我正在尝试实施 "live" 输入验证。我一直面临的问题是如何实时检查用户名是否已存在于数据库中。
对于所有数据库请求,我使用 Volley POST 发送到 PHP API,结果是:
private boolean checkTaken(String username, Context context){
boolean taken;
Volley.getInstance(context).addToRequestQueue(new StringRequest(Request.Method.POST, url, output -> taken = output.contains("taken"), error -> {}) {
@Override
protected Map<String, String> getParams() {
Map<String, String> params = new HashMap<>();
params.put("username", username);
return params;
}
@Override
public Map<String, String> getHeaders() {
Map<String, String> params = new HashMap<>();
params.put("Content-Type", "application/x-www-form-urlencoded");
return params;
}
});
return taken;
}
这本来不错,但行不通,因为 return 语句在 API 响应到达之前很久就已到达。那是我发现 RequestFutures 并尝试实现它们的时候,正如我看到的 here:
private boolean checkTaken(String username, Context context){
RequestFuture<String> taken = RequestFuture.newFuture();
Volley.getInstance(context).addToRequestQueue(new StringRequest(Request.Method.POST, url, taken, error -> {}) {
@Override
protected Map<String, String> getParams() {
Map<String, String> params = new HashMap<>();
params.put("username", username);
return params;
}
@Override
public Map<String, String> getHeaders() {
Map<String, String> params = new HashMap<>();
params.put("Content-Type", "application/x-www-form-urlencoded");
return params;
}
});
try {
return taken.get(500, TimeUnit.MILLISECONDS).contains("taken");
} catch (InterruptedException | ExecutionException | TimeoutException e) {
return false;
}
}
不幸的是,即使我将超时增加到无法忍受的数量,它也不起作用,taken.get()
永远不会得到解决。我认为这是因为请求中使用的 taken
被解释为输出的新变量,而不是实际的 FutureRequest。
如果能提示为什么这不起作用或为我的问题提供其他解决方案,我将不胜感激。
方法调用:
if (!email.matches(regex)){
return "Username has an invalid format";
} else if (checkTaken(username, context)){
return "Username already taken";
} else{
return null;
}
private void checkTaken(String username, Context context, Callback callback){
boolean taken;
Volley.getInstance(context).addToRequestQueue(new StringRequest(Request.Method.POST, url,
output -> callback.done(output.contains("taken")),
error -> {
// TODO handle this error
}) {
@Override
protected Map<String, String> getParams() {
Map<String, String> params = new HashMap<>();
params.put("username", username);
return params;
}
@Override
public Map<String, String> getHeaders() {
Map<String, String> params = new HashMap<>();
params.put("Content-Type", "application/x-www-form-urlencoded");
return params;
}
});
}
定义您的回调接口并从您调用的任何地方传入 lambda 表达式 checkTaken
:
// This method wherever this is defined also needs to accept a callback so it can be asynchronous
public void whatever(StringCallback callback) {
// ...
if (!email.matches(regex)){
callback.done("Username has an invalid format");
} else {
checkTaken(username, context, taken ->
callback.done(taken ? "Username already taken" : null));
}
然后无论你在哪里调用它:
whatever(error -> {
if(error == null) {
handleSuccess();
}
else {
handleError(error);
}
);
这似乎是您必须进行的一系列烦人且复杂的更改,但是当您从同步转到异步时,通常会产生很多连锁反应,而且由于您无法在主线程上进行网络操作在 Android 中,您经常会发现自己在编写异步方法。
我正在尝试实施 "live" 输入验证。我一直面临的问题是如何实时检查用户名是否已存在于数据库中。
对于所有数据库请求,我使用 Volley POST 发送到 PHP API,结果是:
private boolean checkTaken(String username, Context context){
boolean taken;
Volley.getInstance(context).addToRequestQueue(new StringRequest(Request.Method.POST, url, output -> taken = output.contains("taken"), error -> {}) {
@Override
protected Map<String, String> getParams() {
Map<String, String> params = new HashMap<>();
params.put("username", username);
return params;
}
@Override
public Map<String, String> getHeaders() {
Map<String, String> params = new HashMap<>();
params.put("Content-Type", "application/x-www-form-urlencoded");
return params;
}
});
return taken;
}
这本来不错,但行不通,因为 return 语句在 API 响应到达之前很久就已到达。那是我发现 RequestFutures 并尝试实现它们的时候,正如我看到的 here:
private boolean checkTaken(String username, Context context){
RequestFuture<String> taken = RequestFuture.newFuture();
Volley.getInstance(context).addToRequestQueue(new StringRequest(Request.Method.POST, url, taken, error -> {}) {
@Override
protected Map<String, String> getParams() {
Map<String, String> params = new HashMap<>();
params.put("username", username);
return params;
}
@Override
public Map<String, String> getHeaders() {
Map<String, String> params = new HashMap<>();
params.put("Content-Type", "application/x-www-form-urlencoded");
return params;
}
});
try {
return taken.get(500, TimeUnit.MILLISECONDS).contains("taken");
} catch (InterruptedException | ExecutionException | TimeoutException e) {
return false;
}
}
不幸的是,即使我将超时增加到无法忍受的数量,它也不起作用,taken.get()
永远不会得到解决。我认为这是因为请求中使用的 taken
被解释为输出的新变量,而不是实际的 FutureRequest。
如果能提示为什么这不起作用或为我的问题提供其他解决方案,我将不胜感激。
方法调用:
if (!email.matches(regex)){
return "Username has an invalid format";
} else if (checkTaken(username, context)){
return "Username already taken";
} else{
return null;
}
private void checkTaken(String username, Context context, Callback callback){
boolean taken;
Volley.getInstance(context).addToRequestQueue(new StringRequest(Request.Method.POST, url,
output -> callback.done(output.contains("taken")),
error -> {
// TODO handle this error
}) {
@Override
protected Map<String, String> getParams() {
Map<String, String> params = new HashMap<>();
params.put("username", username);
return params;
}
@Override
public Map<String, String> getHeaders() {
Map<String, String> params = new HashMap<>();
params.put("Content-Type", "application/x-www-form-urlencoded");
return params;
}
});
}
定义您的回调接口并从您调用的任何地方传入 lambda 表达式 checkTaken
:
// This method wherever this is defined also needs to accept a callback so it can be asynchronous
public void whatever(StringCallback callback) {
// ...
if (!email.matches(regex)){
callback.done("Username has an invalid format");
} else {
checkTaken(username, context, taken ->
callback.done(taken ? "Username already taken" : null));
}
然后无论你在哪里调用它:
whatever(error -> {
if(error == null) {
handleSuccess();
}
else {
handleError(error);
}
);
这似乎是您必须进行的一系列烦人且复杂的更改,但是当您从同步转到异步时,通常会产生很多连锁反应,而且由于您无法在主线程上进行网络操作在 Android 中,您经常会发现自己在编写异步方法。