Android ReCaptcha: Может ли создание экземпляра (синхронного!) Объекта Executor в моей активности вызвать его поток (и пользовательский интерфейс) к сбою? - PullRequest
0 голосов
/ 08 мая 2019

Контекст

Я использую Android SafetyNet ReCaptcha , чтобы отобразить капчу Google в моем приложении для Android.

Пользователи моего приложения могут зарегистрироваться, войти, выйти. Когда пользователь запускает мое приложение, появляется заставка. Если пользователь не подключен, ему предлагается нажать кнопку ...:

  • Если он касается кнопки, запускается ReCaptcha. a) Если ReCaptcha успешно завершен, то пользователь может зарегистрироваться и войти в систему со своей учетной записью Google (я использую Google Firebase Auth и даже AuthUI). b) В противном случае ничего не происходит: ему придется повторить попытку завершения ReCaptcha.

Как я реализовал ReCaptcha

В резюме: я прикрепляю слушателя onClick к кнопке. Если щелкнуть по последнему, то я вызываю verifyWithRecaptcha в (синхронно! И это добровольно) Executor. Затем я звоню на серверы Google, чтобы убедиться, что капча была завершена человеком, а не ботом, благодаря моему классу NetworkUseRecaptcha, который предоставляет результаты серверов Google.

Мой вопрос

Поскольку я использую Executor с AppCompatActivity (с именем SplashScreen), не в асинхронном режиме, а на самом деле синхронном (, поэтому в том же потоке, что и действие и его пользовательский интерфейс ), есть ли риск выбросить Exception (утечки памяти или что-то другое)?

Ресурсы

Я следовал этой документации: https://developer.android.com/training/safetynet/recaptcha#send-request, но я не нашел никакой полезной информации о своей проблеме.

Я не нашел ни одного вопроса Stackoverflow о моей проблеме.

SplashScreen.java (класс AppCompatActivity): обработчик события "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: Мой класс, который позволяет мне связаться с серверами 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);
    }
}

1 Ответ

1 голос
/ 08 мая 2019

Нет, нет риска исключения при вызове reCaptcha из основного потока.

Причина в том, что, хотя вы сделали вызов reCaptcha API из основного потока синхронно, это безопасно, потому что сам SafetyNet API работает асинхронно и порождает свои собственные рабочие потоки, чтобы избежать NetworkOnMainThread Исключение и доставить его результат обратно MainThread.

...