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

Android 在上一个任务之前执行后台任务(如何防止),android,multithreading,asynchronous,android-asynctask,Android,Multithreading,Asynchronous,Android Asynctask,我在后台发送电子邮件时遇到问题。我试图将文本集作为电子邮件放入EditText中,但我的邮箱中总是出现空值,因为在我在EditText中键入任何内容之前,始终会调用AsyncTask,然后在对话框中按“ok” final int partsCount = imageKeeperList.size(); class PhotoSend extends AsyncTask <Void, Void, Void>{

我在后台发送电子邮件时遇到问题。我试图将文本集作为电子邮件放入EditText中,但我的邮箱中总是出现空值,因为在我在EditText中键入任何内容之前,始终会调用AsyncTask,然后在对话框中按“ok”

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);
                    } 
final int partscont=imageKeeperList.size();
类PhotoSend扩展异步任务{
@凌驾
受保护的void onPreExecute(){
}
@凌驾
受保护的空位背景(空位…空位){
最终字符串用户名=”sampleemail@gmail.com";
最终字符串password=“somepassword”;
Properties props=新属性();
put(“mail.smtp.host”、“smtp.gmail.com”);
props.put(“mail.smtp.socketFactory.port”,“465”);
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,
新的javax.mail.Authenticator(){
受保护的密码身份验证getPasswordAuthentication(){
返回新密码身份验证(用户名、密码);
}
});
试一试{
Message Message=新的mimessage(会话);
message.setFrom(新的InternetAddress(“sampleemail@gmail.com"));
message.setRecipients(message.RecipientType.TO,InternetAddress.parse(“targetemail@gmail.pl"));
message.setSubject(“电子邮件主题”);
message.setText(“一些文本”);
传输。发送(消息);
Log.d(标签“onInput:background”);
}捕获(消息异常e){
抛出新的运行时异常(e);
}
返回null;
}
@凌驾
受保护的void onPostExecute(void避免){
Toast.makeText(getContext(),“Sent.”,Toast.LENGTH_SHORT.show();
Log.d(标记“onInput:postExecute”);
}
}
new MaterialDialog.Builder(getContext())
.内容(“集合描述”)
.inputType(inputType.TYPE\文本\标志\输入\多行)
.input(“简短描述”,“新材料对话框.InputCallback()){
@凌驾
public void onInput(MaterialDialog对话框,字符序列输入){
如果(input.length()==0)partPicturesDescription=“无描述”;
else partPicturesDescription=dialog.GetInputItemText().getText().toString();
dialog.dismise();
Log.d(标记“onInput:preExecute”);
}
}).show();
PhotoSend ps=新的PhotoSend();
ps.execute(零件图片说明);
} 

我的onPreExecute()方法中有一个对话框,但它保持不变,首先是doInBackground。

让我们看看我是否可以帮助您。因此,首先您对异步任务有一点误用

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

所以典型的行为是

UI线程->获取用户输入

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

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

所以你有两个选择

1) 在启动异步任务之前执行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检索,这将导致内存泄漏、生命周期问题和糟糕的代码管理

通过尝试传入侦听器并将处理放入asynctask中会话验证器的对话框回调中,确实可能会让事情变得糟糕,但这可能会失败,因为垃圾收集会在对象退出execute方法时带走该对象,所以这是一个非常糟糕的主意

希望这能有所帮助,如果有问题,可以提问

快乐公司