Android ReCaptcha:在我的 Activity 中实例化一个(同步!)Executor 对象是否会导致其线程(和 UI)崩溃?
Android ReCaptcha: Does instanciating a (synchronous!) Executor object in my Activity could cause its thread (and UI) to crash?
上下文
我使用 Android SafetyNet ReCaptcha 在我的 Android 应用程序中显示 Google 验证码。
我的应用程序的用户可以注册、登录、注销。当用户启动我的应用程序时,会出现启动画面。如果用户未连接,请他触摸一个按钮...:[=27=]
- 如果他触摸按钮,ReCaptcha 就会启动。 a) 如果 ReCaptcha 成功完成,则用户可以使用他的 Google 帐户注册并登录(我使用 Google Firebase Auth 甚至授权UI)。 b) 否则,什么也不会发生:他将不得不重新尝试完成 ReCaptcha。
我是如何实现 ReCaptcha
在简历中:我将 onClick
侦听器附加到按钮。因此,如果单击后者,我将调用 verifyWithRecaptcha
(同步!这是自愿的)Executor
。然后我调用 Google 的服务器来确保验证码是由人类完成的,而不是由机器人完成的,多亏了我的 class NetworkUseRecaptcha
它提供了 Google的服务器。
我的问题
由于我将 Executor
与 AppCompatActivity
(名为 SplashScreen
)一起使用,所以在非异步模式下实际上是同步的(,所以在与 activity 及其 UI 相同的线程)是否存在抛出 Exception
(内存泄漏或其他问题)的风险?
资源
我已遵循此文档: https://developer.android.com/training/safetynet/recaptcha#send-request 但我没有找到有关我的问题的任何有用信息。
我没有找到关于我的问题的任何 Whosebug 问题。
SplashScreen.java(一个 AppCompatActivity class):"onClick" 事件处理程序 listeni
注意:final Context that = this
是当前 AppCompatActivity
SplashScreen
对象。
final Context that = this;
button_splash_screen_recaptcha.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
final Executor executor = new Executor() {
@Override
public void execute(@NonNull Runnable command) {
command.run();
}
};
executor.execute(new Runnable() {
@Override
public void run() {
SafetyNet.getClient(that).verifyWithRecaptcha("PUBLIC KEY")
.addOnSuccessListener(executor,
new OnSuccessListener<SafetyNetApi.RecaptchaTokenResponse>() {
@Override
public void onSuccess(final SafetyNetApi.RecaptchaTokenResponse response) {
String userResponseToken = response.getTokenResult();
if (!userResponseToken.isEmpty()) {
String[] parameters = new String[2];
parameters[0] = "SECRET KEY";
parameters[1] = userResponseToken;
new NetworkUseRecaptcha(new RecaptchaPostExecuteCallback() {
@Override
public void onTaskCompleted(String result, boolean background_error) {
if(background_error) {
runOnUiThread(new Runnable() {
public void run() {
Toast.makeText(that,"Error N°2: Unable to check the captcha.", Toast.LENGTH_SHORT).show();
}
});
return;
}
try {
final JSONObject json_response = new JSONObject(result);
if(!json_response.isNull("success") && json_response.getBoolean("success")) {
final List<AuthUI.IdpConfig> providers = ImmutableList.of(
new AuthUI.IdpConfig.GoogleBuilder().build()
);
startActivityForResult(
AuthUI.getInstance()
.createSignInIntentBuilder()
.setAvailableProviders(providers)
.setAlwaysShowSignInMethodScreen(true)
.setLogo(R.drawable.yellow_logo)
.setTheme(R.style.LoginTheme)
.build(),
REQUEST_CODE_SIGN_IN
);
} else {
Toast.makeText(that,"Error N°4: Unable to check the captcha.", Toast.LENGTH_SHORT).show();
}
} catch (JSONException e) {
runOnUiThread(new Runnable() {
public void run() {
Toast.makeText(that,"Error N°3: Unable to check the captcha.", Toast.LENGTH_SHORT).show();
}
});
}
}
}).execute(parameters);
}
}
})
.addOnFailureListener(executor, new OnFailureListener() {
@Override
public void onFailure(@NonNull Exception e) {
System.err.println(e);
runOnUiThread(new Runnable() {
public void run() {
Toast.makeText(that,"Error N°1: Unable to check the captcha.", Toast.LENGTH_SHORT).show();
}
});
}
});
}
});
}
});
NetworkUseRecaptcha.java:我的class允许我联系Google的服务器来验证验证码
class NetworkUseRecaptcha extends AsyncTask<String, Void, String> {
private final RecaptchaPostExecuteCallback post_execute_callback;
private boolean background_error;
NetworkUseRecaptcha(RecaptchaPostExecuteCallback post_execute_callback) {
this.post_execute_callback = post_execute_callback;
background_error = false;
}
@Override
protected String doInBackground(String[] parameters) {
StringBuilder string_builder = new StringBuilder();
try {
URL url = new URL("https://www.google.com/recaptcha/api/siteverify");
HttpsURLConnection https_url_connection = (HttpsURLConnection) url.openConnection();
https_url_connection.setRequestMethod("POST");
https_url_connection.setDoOutput(false);
https_url_connection.setUseCaches(false);
OutputStream os = https_url_connection.getOutputStream();
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(os, StandardCharsets.UTF_8));
writer.write("secret=" + parameters[0] + "&response=" + parameters[1]);
writer.flush();
writer.close();
os.close();
InputStream input_stream = https_url_connection.getInputStream();
BufferedReader buffered_reader = new BufferedReader(new InputStreamReader(input_stream));
String line;
while((line = buffered_reader.readLine()) != null) {
string_builder.append(line);
}
buffered_reader.close();
} catch (Exception e) {
background_error = true;
}
return string_builder.toString();
}
@Override
protected void onPostExecute(String result) {
post_execute_callback.onTaskCompleted(result, background_error);
}
}
不,从主线程调用 reCaptcha 时没有异常风险。
原因是,虽然您已经从主线程同步调用 reCaptcha API,但它是安全的,因为 SafetyNet
API 本身异步工作并生成它自己的工作线程来避免 NetworkOnMainThread
异常并在 MainThread
返回结果。
上下文
我使用 Android SafetyNet ReCaptcha 在我的 Android 应用程序中显示 Google 验证码。
我的应用程序的用户可以注册、登录、注销。当用户启动我的应用程序时,会出现启动画面。如果用户未连接,请他触摸一个按钮...:[=27=]
- 如果他触摸按钮,ReCaptcha 就会启动。 a) 如果 ReCaptcha 成功完成,则用户可以使用他的 Google 帐户注册并登录(我使用 Google Firebase Auth 甚至授权UI)。 b) 否则,什么也不会发生:他将不得不重新尝试完成 ReCaptcha。
我是如何实现 ReCaptcha
在简历中:我将 onClick
侦听器附加到按钮。因此,如果单击后者,我将调用 verifyWithRecaptcha
(同步!这是自愿的)Executor
。然后我调用 Google 的服务器来确保验证码是由人类完成的,而不是由机器人完成的,多亏了我的 class NetworkUseRecaptcha
它提供了 Google的服务器。
我的问题
由于我将 Executor
与 AppCompatActivity
(名为 SplashScreen
)一起使用,所以在非异步模式下实际上是同步的(,所以在与 activity 及其 UI 相同的线程)是否存在抛出 Exception
(内存泄漏或其他问题)的风险?
资源
我已遵循此文档: https://developer.android.com/training/safetynet/recaptcha#send-request 但我没有找到有关我的问题的任何有用信息。
我没有找到关于我的问题的任何 Whosebug 问题。
SplashScreen.java(一个 AppCompatActivity class):"onClick" 事件处理程序 listeni
注意:final Context that = this
是当前 AppCompatActivity
SplashScreen
对象。
final Context that = this;
button_splash_screen_recaptcha.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
final Executor executor = new Executor() {
@Override
public void execute(@NonNull Runnable command) {
command.run();
}
};
executor.execute(new Runnable() {
@Override
public void run() {
SafetyNet.getClient(that).verifyWithRecaptcha("PUBLIC KEY")
.addOnSuccessListener(executor,
new OnSuccessListener<SafetyNetApi.RecaptchaTokenResponse>() {
@Override
public void onSuccess(final SafetyNetApi.RecaptchaTokenResponse response) {
String userResponseToken = response.getTokenResult();
if (!userResponseToken.isEmpty()) {
String[] parameters = new String[2];
parameters[0] = "SECRET KEY";
parameters[1] = userResponseToken;
new NetworkUseRecaptcha(new RecaptchaPostExecuteCallback() {
@Override
public void onTaskCompleted(String result, boolean background_error) {
if(background_error) {
runOnUiThread(new Runnable() {
public void run() {
Toast.makeText(that,"Error N°2: Unable to check the captcha.", Toast.LENGTH_SHORT).show();
}
});
return;
}
try {
final JSONObject json_response = new JSONObject(result);
if(!json_response.isNull("success") && json_response.getBoolean("success")) {
final List<AuthUI.IdpConfig> providers = ImmutableList.of(
new AuthUI.IdpConfig.GoogleBuilder().build()
);
startActivityForResult(
AuthUI.getInstance()
.createSignInIntentBuilder()
.setAvailableProviders(providers)
.setAlwaysShowSignInMethodScreen(true)
.setLogo(R.drawable.yellow_logo)
.setTheme(R.style.LoginTheme)
.build(),
REQUEST_CODE_SIGN_IN
);
} else {
Toast.makeText(that,"Error N°4: Unable to check the captcha.", Toast.LENGTH_SHORT).show();
}
} catch (JSONException e) {
runOnUiThread(new Runnable() {
public void run() {
Toast.makeText(that,"Error N°3: Unable to check the captcha.", Toast.LENGTH_SHORT).show();
}
});
}
}
}).execute(parameters);
}
}
})
.addOnFailureListener(executor, new OnFailureListener() {
@Override
public void onFailure(@NonNull Exception e) {
System.err.println(e);
runOnUiThread(new Runnable() {
public void run() {
Toast.makeText(that,"Error N°1: Unable to check the captcha.", Toast.LENGTH_SHORT).show();
}
});
}
});
}
});
}
});
NetworkUseRecaptcha.java:我的class允许我联系Google的服务器来验证验证码
class NetworkUseRecaptcha extends AsyncTask<String, Void, String> {
private final RecaptchaPostExecuteCallback post_execute_callback;
private boolean background_error;
NetworkUseRecaptcha(RecaptchaPostExecuteCallback post_execute_callback) {
this.post_execute_callback = post_execute_callback;
background_error = false;
}
@Override
protected String doInBackground(String[] parameters) {
StringBuilder string_builder = new StringBuilder();
try {
URL url = new URL("https://www.google.com/recaptcha/api/siteverify");
HttpsURLConnection https_url_connection = (HttpsURLConnection) url.openConnection();
https_url_connection.setRequestMethod("POST");
https_url_connection.setDoOutput(false);
https_url_connection.setUseCaches(false);
OutputStream os = https_url_connection.getOutputStream();
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(os, StandardCharsets.UTF_8));
writer.write("secret=" + parameters[0] + "&response=" + parameters[1]);
writer.flush();
writer.close();
os.close();
InputStream input_stream = https_url_connection.getInputStream();
BufferedReader buffered_reader = new BufferedReader(new InputStreamReader(input_stream));
String line;
while((line = buffered_reader.readLine()) != null) {
string_builder.append(line);
}
buffered_reader.close();
} catch (Exception e) {
background_error = true;
}
return string_builder.toString();
}
@Override
protected void onPostExecute(String result) {
post_execute_callback.onTaskCompleted(result, background_error);
}
}
不,从主线程调用 reCaptcha 时没有异常风险。
原因是,虽然您已经从主线程同步调用 reCaptcha API,但它是安全的,因为 SafetyNet
API 本身异步工作并生成它自己的工作线程来避免 NetworkOnMainThread
异常并在 MainThread
返回结果。