后台任务在上一个任务之前执行(如何防止)

时间:2018-11-06 13:19:34

标签: android multithreading asynchronous android-asynctask

我在后台发送电子邮件时遇到问题。我试图将在SetText中设置的文本作为电子邮件发送,但是总是有空值进入我的邮箱,因为在我在EditText中键入任何内容之前,总是会先调用AsyncTask,然后在Dialog上按“确定”。

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 :(得分:1)

让我们看看我是否可以帮助您。因此,首先,您会稍微滥用asyncTasks。

AsyncTask用于执行简短的“背景”操作,该操作将结果返回到UI线程。 UI线程是唯一可以触摸UI元素而不会发生冲突或正常崩溃的线程。

所以典型的行为是

  

UI-Thread->获取用户输入

     

AsyncTask(背景线程)->发送或处理传入的数据

     

UI-Thread->通知用户成功/失败

因此,您有两种选择。

1)在启动AsyncTask之前,先执行UI对话框

    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内部进行对话框和UI检索,因为这会询问内存泄漏,生命周期问题和不良的代码管理。

通过尝试传递一个侦听器并将处理放入异步任务中会话身份验证器的对话框回调中,您可能真的很糟糕,但这可能会失败,因为垃圾回收在退出execute方法时会占用该对象。 ,所以这是一个非常糟糕的主意。

希望有帮助,请问是否有问题。

快乐编码。