Фоновое задание выполняется перед предыдущим заданием (как предотвратить) - PullRequest
0 голосов
/ 06 ноября 2018

У меня проблема при отправке электронной почты в фоновом режиме. Я пытаюсь поместить текстовый набор внутри EditText в качестве сообщения электронной почты, но в мой почтовый ящик всегда приходит ноль, потому что AsyncTask всегда вызывается перед тем, как я что-то наберу в EditText, и нажимаю «ок» в диалоге.

final int partsCount = imageKeeperList.size();

                        class PhotoSend extends AsyncTask <Void, Void, Void>{

                            @Override
                            protected void onPreExecute() {


                            }


                            @Override
                            protected Void doInBackground(Void... voids) {

                                final String username = "sampleemail@gmail.com";
                                final String password = "somepassword";

                                Properties props = new Properties();
                                props.put("mail.smtp.host", "smtp.gmail.com");
                                props.put("mail.smtp.socketFactory.port", "465");
                                props.put("mail.smtp.socketFactory.class", "javax.net.ssl.SSLSocketFactory");
                                props.put("mail.smtp.auth", "true");
                                props.put("mail.smtp.port", "465");

                                Session session = Session.getDefaultInstance(props,

                                        new javax.mail.Authenticator() {

                                            protected PasswordAuthentication getPasswordAuthentication() {
                                                return new PasswordAuthentication(username,password);

                                            }
                                        });

                                try {

                                    Message message = new MimeMessage(session);
                                    message.setFrom(new InternetAddress("sampleemail@gmail.com"));
                                    message.setRecipients(Message.RecipientType.TO, InternetAddress.parse("targetemail@gmail.pl"));
                                    message.setSubject("Subject of email");
                                    message.setText("Some text.");
                                    Transport.send(message);
                                    Log.d(TAG, "onInput: background");

                                } catch (MessagingException e) {

                                    throw new RuntimeException(e);

                                }
                                return null;
                            }

                            @Override
                            protected void onPostExecute(Void aVoid) {
                                Toast.makeText(getContext(), "Sent.", Toast.LENGTH_SHORT).show();
                                Log.d(TAG, "onInput: postExecute");
                            }
                        }

                        new MaterialDialog.Builder(getContext())

                                .content("Set description")
                                .inputType(InputType.TYPE_TEXT_FLAG_IME_MULTI_LINE)
                                .input("short description", "", new MaterialDialog.InputCallback() {
                                    @Override
                                    public void onInput(MaterialDialog dialog, CharSequence input) {

                                        if (input.length() == 0) partPicturesDescription = "No description";
                                        else partPicturesDescription = dialog.getInputEditText().getText().toString();
                                        dialog.dismiss();
                                        Log.d(TAG, "onInput: preExecute");
                                    }
                                }).show();

                        PhotoSend ps = new PhotoSend();
                        ps.execute(partPicturesDescription);
                    } 

У меня был диалог внутри моего метода onPreExecute (), но он остался прежним, doInBackground идет первым.

1 Ответ

0 голосов
/ 06 ноября 2018

Посмотрим, смогу ли я вам помочь. Итак, сначала у вас есть небольшое неправильное использование asyncTasks.

AsyncTask предназначен для выполнения короткой «фоновой» операции, которая возвращает результаты в поток пользовательского интерфейса. Поток пользовательского интерфейса является единственным потоком, который может касаться элементов пользовательского интерфейса без нарушений или сбоев должным образом.

Таким образом, типичное поведение будет

UI-Thread -> Получить пользовательский ввод

AsyncTask (Фоновая нить) -> Отправка или обработка данных, переданных в

UI-Thread -> Уведомлять пользователя об успехе / неудаче

Итак, у вас есть несколько вариантов.

1) У вашего диалогового окна «До» запускается AsyncTask

    private void getUserInput(){
         Session session = Session.getDefaultInstance(props,
                              new javax.mail.Authenticator() {
                                  protected PasswordAuthentication getPasswordAuthentication() {
                                      doBackgroundProcessing(new PasswordAuthentication(username,password));

                                 }
                             });
      }

private void doBackgroundProcessing(PasswordAuthentication passAuth){
    if(passAuth == null || !passAuth.isSuccessful()){ //or whatever success flag they have
        Log.e(TAG, "Failed to get credential token");
        return;
    }

    //else we send it to the server it appears based on your code\
    showBusyIndicator() //IF you need to block the UI from interacting while you send, NOTE* showBusyIndicator is just a method YOU would create to show one.
    new PhotoSend()() {
        @Override
        public void onPostTask(Boolean wasSuccessful) {
             //I recommend actually returning a valid result rather then Void so you know whether or not it succeeded.
             if(wasSuccessful){
                 //close busy indicator, and notify of success
             }else{
                 //close busy indicator, and notify of error
             }
        }
    }
}

Другим вариантом было бы перейти к сопрограммам. Это очень удобно, так как вы можете использовать async и ожидать приостановки вашего действия.

Вот пример ожидания в диалоговом окне, чтобы вернуть имя, прежде чем двигаться вперед.

protected suspend fun getNameFromDialog(): String? = suspendCancellableCoroutine { c ->
    A35Log.v(mClassTag, "getNameFromDialog")
    GetTextEntryDialog.newInstance(getParamsForTextDialog(), object : ITextEntryDialogListener {
        override fun onTextEntered(text: String) {
            if(!c.isCompleted) {
                A35Log.v(mClassTag, "User entered name: $text")
                c.resume(text)
            }
        }
        override fun onCancel(){
            if(!c.isCompleted) {
                A35Log.v(mClassTag, "User canceled name entry")
                c.resume(null)
            }
        }
    }).show(supportFragmentManager, mClassTag)
}

Вариант использования просто для этого примера - получение имени, затем сохранение в базе данных. Это выглядит так:

  private fun saveAsDuplicateConfiguration(){
    launch(UI){
        setIsActionInProgress(true)
        val configName = withContext(DefaultDispatcher) { getNameFromDialog() }
        if(configName == null){
            showFancyToast(getString(R.string.canceled), true, FancyToast.INFO)
        }else{
            withContext(DefaultDispatcher){
                try{
                    withTimeout(TIMEOUT_FOR_DB_INTERACTION_MS){
                        A35Log.v(mClassTag, "inserting copy of config with name :$configName")
                        val configCopy = DeviceAndConfigurationHelper.getCopyOfConfigurationModel(mSelectedConfiguration!!)
                        configCopy.setConfigName(configName)
                        configCopy.setDeviceType(FeatureHelper.PRO_DEVICE_KEY) //todo should come from BLE eventually
                        if(SSDBHelper.insertConfiguration(configCopy) > 0){
                            showFancyToast(getString(R.string.successfully_saved), true, FancyToast.SUCCESS)
                            finishCurrentActivity(SSGlobals.TimeOuts.TOAST_DISPLAY_DELAY_CLOSE_MS, true)
                        }else{
                            showFancyToast(getString(R.string.error_saving_copy))
                        }
                    }
                }catch (e: TimeoutCancellationException) {
                    showFancyToast(getString(R.string.error_timed_out) + ", " + getString(R.string.error_please_try_again_or_press_back), true, FancyToast.ERROR, "TimedOut with error: ${e.message}")
                }catch(ex: JobCancellationException){
                    showFancyToast(getString(R.string.canceled))
                }catch(ex: Exception){
                    showFancyToast(getString(R.string.error_saving_copy) + ", " + getString(R.string.error_please_try_again_or_press_back), true, FancyToast.ERROR, "Error deleting: ${ex.message}")
                }finally {
                    setIsActionInProgress(false)
                }
            }
        }
    }
}

Как вы можете видеть, GetNameFromDialog - это метод блокировки и ожидания, поэтому saveAsDuplicateConfiguration не перемещается до тех пор, пока не завершится получение имени, а затем пытается использовать его.

Итак, мораль этой истории в том, что сопрограммы удивительны и допускают чистый асинхронный код, но кривая обучения крутая. Так что делай то, что тебе удобно, но «и я не могу этого подчеркнуть». Не пытайтесь выполнять диалоги и извлечение пользовательского интерфейса из AsyncTask, который будет запрашивать утечки памяти, проблемы жизненного цикла и плохое управление кодом.

Вы могли бы действительно сделать это плохо, попытавшись передать слушателя и поместить обработку в обратный вызов диалога вашего аутентификатора сеанса внутри вашей асинхронной задачи, но это могло бы с треском провалиться, потому что сборка мусора может получить этот объект, когда он выходит из метода execute так что это будет ОЧЕНЬ плохая идея.

Надеюсь, это поможет, задавайте вопросы, если у вас есть.

Счастливое кодирование.

...