Android 在上一个任务之前执行后台任务(如何防止)
我在后台发送电子邮件时遇到问题。我试图将文本集作为电子邮件放入EditText中,但我的邮箱中总是出现空值,因为在我在EditText中键入任何内容之前,始终会调用AsyncTask,然后在对话框中按“ok”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>{
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方法时带走该对象,所以这是一个非常糟糕的主意
希望这能有所帮助,如果有问题,可以提问
快乐公司