mirror of
https://github.com/moparisthebest/k-9
synced 2024-11-24 02:12:15 -05:00
Clone Message object to avoid ConcurrentModificationExceptions
Fixes issue 3953
This commit is contained in:
parent
4b5189973b
commit
71174417ce
@ -1123,12 +1123,28 @@ public class MessageView extends K9Activity implements OnClickListener {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
MessageView.this.mMessage = message;
|
MessageView.this.mMessage = message;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Clone the message object because the original could be modified by
|
||||||
|
* MessagingController later. This could lead to a ConcurrentModificationException
|
||||||
|
* when that same object is accessed by the UI thread (below).
|
||||||
|
*
|
||||||
|
* See issue 3953
|
||||||
|
*
|
||||||
|
* This is just an ugly hack to get rid of the most pressing problem. A proper way to
|
||||||
|
* fix this is to make Message thread-safe. Or, even better, rewriting the UI code to
|
||||||
|
* access messages via a ContentProvider.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
final Message clonedMessage = message.clone();
|
||||||
|
|
||||||
runOnUiThread(new Runnable() {
|
runOnUiThread(new Runnable() {
|
||||||
public void run() {
|
public void run() {
|
||||||
if (!message.isSet(Flag.X_DOWNLOADED_FULL) && !message.isSet(Flag.X_DOWNLOADED_PARTIAL)) {
|
if (!clonedMessage.isSet(Flag.X_DOWNLOADED_FULL) &&
|
||||||
|
!clonedMessage.isSet(Flag.X_DOWNLOADED_PARTIAL)) {
|
||||||
mMessageView.loadBodyFromUrl("file:///android_asset/downloading.html");
|
mMessageView.loadBodyFromUrl("file:///android_asset/downloading.html");
|
||||||
}
|
}
|
||||||
mMessageView.setHeaders(message, account);
|
mMessageView.setHeaders(clonedMessage, account);
|
||||||
mMessageView.setOnFlagListener(new OnClickListener() {
|
mMessageView.setOnFlagListener(new OnClickListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onClick(View v) {
|
public void onClick(View v) {
|
||||||
|
@ -224,4 +224,32 @@ public abstract class Message implements Part, Body {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copy the contents of this object into another {@code Message} object.
|
||||||
|
*
|
||||||
|
* @param destination
|
||||||
|
* The {@code Message} object to receive the contents of this instance.
|
||||||
|
*/
|
||||||
|
protected void copy(Message destination) {
|
||||||
|
destination.mUid = mUid;
|
||||||
|
destination.mInternalDate = mInternalDate;
|
||||||
|
destination.mFolder = mFolder;
|
||||||
|
destination.mReference = mReference;
|
||||||
|
|
||||||
|
// mFlags contents can change during the object lifetime, so copy the Set
|
||||||
|
destination.mFlags = new HashSet<Flag>(mFlags);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new {@code Message} object with the same content as this object.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* <strong>Note:</strong>
|
||||||
|
* This method was introduced as a hack to prevent {@code ConcurrentModificationException}s. It
|
||||||
|
* shouldn't be used unless absolutely necessary. See the comment in
|
||||||
|
* {@link com.fsck.k9.activity.MessageView.Listener#loadMessageForViewHeadersAvailable(com.fsck.k9.Account, String, String, Message)}
|
||||||
|
* for more information.
|
||||||
|
* </p>
|
||||||
|
*/
|
||||||
|
public abstract Message clone();
|
||||||
}
|
}
|
||||||
|
@ -133,9 +133,9 @@ public class MimeHeader {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static class Field {
|
static class Field {
|
||||||
String name;
|
final String name;
|
||||||
|
|
||||||
String value;
|
final String value;
|
||||||
|
|
||||||
public Field(String name, String value) {
|
public Field(String name, String value) {
|
||||||
this.name = name;
|
this.name = name;
|
||||||
@ -153,4 +153,13 @@ public class MimeHeader {
|
|||||||
public void setCharset(String charset) {
|
public void setCharset(String charset) {
|
||||||
mCharset = charset;
|
mCharset = charset;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public MimeHeader clone() {
|
||||||
|
MimeHeader header = new MimeHeader();
|
||||||
|
header.mCharset = mCharset;
|
||||||
|
|
||||||
|
header.mFields = new ArrayList<Field>(mFields);
|
||||||
|
|
||||||
|
return header;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -553,4 +553,38 @@ public class MimeMessage extends Message {
|
|||||||
throw new UnsupportedOperationException("Not supported");
|
throw new UnsupportedOperationException("Not supported");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copy the contents of this object into another {@code MimeMessage} object.
|
||||||
|
*
|
||||||
|
* @param message
|
||||||
|
* The {@code MimeMessage} object to receive the contents of this instance.
|
||||||
|
*/
|
||||||
|
protected void copy(MimeMessage message) {
|
||||||
|
super.copy(message);
|
||||||
|
|
||||||
|
message.mHeader = mHeader.clone();
|
||||||
|
|
||||||
|
message.mBody = mBody;
|
||||||
|
message.mMessageId = mMessageId;
|
||||||
|
message.mSentDate = mSentDate;
|
||||||
|
message.mDateFormat = mDateFormat;
|
||||||
|
message.mSize = mSize;
|
||||||
|
|
||||||
|
// These arrays are not supposed to be modified, so it's okay to reuse the references
|
||||||
|
message.mFrom = mFrom;
|
||||||
|
message.mTo = mTo;
|
||||||
|
message.mCc = mCc;
|
||||||
|
message.mBcc = mBcc;
|
||||||
|
message.mReplyTo = mReplyTo;
|
||||||
|
message.mReferences = mReferences;
|
||||||
|
message.mInReplyTo = mInReplyTo;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public MimeMessage clone() {
|
||||||
|
MimeMessage message = new MimeMessage();
|
||||||
|
copy(message);
|
||||||
|
return message;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -3310,6 +3310,25 @@ public class LocalStore extends Store implements Serializable {
|
|||||||
loadHeaders();
|
loadHeaders();
|
||||||
return super.getHeaderNames();
|
return super.getHeaderNames();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public LocalMessage clone() {
|
||||||
|
LocalMessage message = new LocalMessage();
|
||||||
|
super.copy(message);
|
||||||
|
|
||||||
|
message.mId = mId;
|
||||||
|
message.mAttachmentCount = mAttachmentCount;
|
||||||
|
message.mSubject = mSubject;
|
||||||
|
message.mPreview = mPreview;
|
||||||
|
message.mToMeCalculated = mToMeCalculated;
|
||||||
|
message.mCcMeCalculated = mCcMeCalculated;
|
||||||
|
message.mToMe = mToMe;
|
||||||
|
message.mCcMe = mCcMe;
|
||||||
|
message.mHeadersLoaded = mHeadersLoaded;
|
||||||
|
message.mMessageDirty = mMessageDirty;
|
||||||
|
|
||||||
|
return message;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class LocalAttachmentBodyPart extends MimeBodyPart {
|
public static class LocalAttachmentBodyPart extends MimeBodyPart {
|
||||||
|
Loading…
Reference in New Issue
Block a user