1
0
mirror of https://github.com/moparisthebest/k-9 synced 2024-11-27 19:52:17 -05:00

Optimize setting flags for whole threads

This commit is contained in:
cketti 2013-01-12 02:28:12 +01:00
parent 3ec623c174
commit 421558c148
3 changed files with 95 additions and 47 deletions

View File

@ -2533,17 +2533,28 @@ public class MessagingController implements Runnable {
} }
public void setFlag(final Account account, final List<Long> messageIds, final Flag flag, public void setFlag(final Account account, final List<Long> messageIds, final Flag flag,
final boolean newState, final boolean threadedList) { final boolean newState) {
threadPool.execute(new Runnable() { threadPool.execute(new Runnable() {
@Override @Override
public void run() { public void run() {
setFlagSynchronous(account, messageIds, flag, newState, threadedList); setFlagSynchronous(account, messageIds, flag, newState, false);
} }
}); });
} }
private void setFlagSynchronous(final Account account, final List<Long> messageIds, public void setFlagForThreads(final Account account, final List<Long> threadRootIds,
final Flag flag, final boolean newState) {
threadPool.execute(new Runnable() {
@Override
public void run() {
setFlagSynchronous(account, threadRootIds, flag, newState, true);
}
});
}
private void setFlagSynchronous(final Account account, final List<Long> ids,
final Flag flag, final boolean newState, final boolean threadedList) { final Flag flag, final boolean newState, final boolean threadedList) {
LocalStore localStore; LocalStore localStore;
@ -2557,7 +2568,11 @@ public class MessagingController implements Runnable {
// Update affected messages in the database. This should be as fast as possible so the UI // Update affected messages in the database. This should be as fast as possible so the UI
// can be updated with the new state. // can be updated with the new state.
try { try {
localStore.setFlag(messageIds, flag, newState, threadedList); if (threadedList) {
localStore.setFlagForThreads(ids, flag, newState);
} else {
localStore.setFlag(ids, flag, newState);
}
} catch (MessagingException e) { } catch (MessagingException e) {
Log.e(K9.LOG_TAG, "Couldn't set flags in local database", e); Log.e(K9.LOG_TAG, "Couldn't set flags in local database", e);
} }
@ -2565,7 +2580,7 @@ public class MessagingController implements Runnable {
// Read folder name and UID of messages from the database // Read folder name and UID of messages from the database
Map<String, List<String>> folderMap; Map<String, List<String>> folderMap;
try { try {
folderMap = localStore.getFoldersAndUids(messageIds, threadedList); folderMap = localStore.getFoldersAndUids(ids, threadedList);
} catch (MessagingException e) { } catch (MessagingException e) {
Log.e(K9.LOG_TAG, "Couldn't get folder name and UID of messages", e); Log.e(K9.LOG_TAG, "Couldn't get folder name and UID of messages", e);
return; return;

View File

@ -2056,10 +2056,16 @@ public class MessageListFragment extends SherlockFragment implements OnItemClick
Cursor cursor = (Cursor) mAdapter.getItem(adapterPosition); Cursor cursor = (Cursor) mAdapter.getItem(adapterPosition);
Account account = mPreferences.getAccount(cursor.getString(ACCOUNT_UUID_COLUMN)); Account account = mPreferences.getAccount(cursor.getString(ACCOUNT_UUID_COLUMN));
long id = cursor.getLong(ID_COLUMN);
mController.setFlag(account, Collections.singletonList(Long.valueOf(id)), flag, newState, if (mThreadedList && cursor.getInt(THREAD_COUNT_COLUMN) > 1) {
mThreadedList); long threadRootId = cursor.getLong(THREAD_ROOT_COLUMN);
mController.setFlagForThreads(account,
Collections.singletonList(Long.valueOf(threadRootId)), flag, newState);
} else {
long id = cursor.getLong(ID_COLUMN);
mController.setFlag(account, Collections.singletonList(Long.valueOf(id)), flag,
newState);
}
computeBatchDirection(); computeBatchDirection();
} }
@ -2069,7 +2075,8 @@ public class MessageListFragment extends SherlockFragment implements OnItemClick
return; return;
} }
Map<Account, List<Long>> accountMapping = new HashMap<Account, List<Long>>(); Map<Account, List<Long>> messageMap = new HashMap<Account, List<Long>>();
Map<Account, List<Long>> threadMap = new HashMap<Account, List<Long>>();
Set<Account> accounts = new HashSet<Account>(); Set<Account> accounts = new HashSet<Account>();
for (int position = 0, end = mAdapter.getCount(); position < end; position++) { for (int position = 0, end = mAdapter.getCount(); position < end; position++) {
@ -2079,21 +2086,39 @@ public class MessageListFragment extends SherlockFragment implements OnItemClick
if (mSelected.contains(uniqueId)) { if (mSelected.contains(uniqueId)) {
String uuid = cursor.getString(ACCOUNT_UUID_COLUMN); String uuid = cursor.getString(ACCOUNT_UUID_COLUMN);
Account account = mPreferences.getAccount(uuid); Account account = mPreferences.getAccount(uuid);
accounts.add(account); accounts.add(account);
List<Long> messageIdList = accountMapping.get(account);
if (messageIdList == null) {
messageIdList = new ArrayList<Long>();
accountMapping.put(account, messageIdList);
}
messageIdList.add(cursor.getLong(ID_COLUMN)); if (mThreadedList && cursor.getInt(THREAD_COUNT_COLUMN) > 1) {
List<Long> threadRootIdList = threadMap.get(account);
if (threadRootIdList == null) {
threadRootIdList = new ArrayList<Long>();
threadMap.put(account, threadRootIdList);
}
threadRootIdList.add(cursor.getLong(THREAD_ROOT_COLUMN));
} else {
List<Long> messageIdList = messageMap.get(account);
if (messageIdList == null) {
messageIdList = new ArrayList<Long>();
messageMap.put(account, messageIdList);
}
messageIdList.add(cursor.getLong(ID_COLUMN));
}
} }
} }
for (Account account : accounts) { for (Account account : accounts) {
List<Long> messageIds = accountMapping.get(account); List<Long> messageIds = messageMap.get(account);
mController.setFlag(account, messageIds, flag, newState, mThreadedList); List<Long> threadRootIds = threadMap.get(account);
if (messageIds != null) {
mController.setFlag(account, messageIds, flag, newState);
}
if (threadRootIds != null) {
mController.setFlagForThreads(account, threadRootIds, flag, newState);
}
} }
computeBatchDirection(); computeBatchDirection();

View File

@ -118,6 +118,13 @@ public class LocalStore extends Store implements Serializable {
*/ */
private static final int FLAG_UPDATE_BATCH_SIZE = 500; private static final int FLAG_UPDATE_BATCH_SIZE = 500;
/**
* Number of threads to perform flag updates on at once.
*
* @see #setFlagForThreads(List, Flag, boolean)
*/
private static final int THREAD_FLAG_UPDATE_BATCH_SIZE = 400;
public static final int DB_VERSION = 47; public static final int DB_VERSION = 47;
protected String uUid = null; protected String uUid = null;
@ -4169,10 +4176,7 @@ public class LocalStore extends Store implements Serializable {
* *
* <p> * <p>
* The goal of this method is to be fast. Currently this means using as few SQL UPDATE * The goal of this method is to be fast. Currently this means using as few SQL UPDATE
* statements as possible.<br> * statements as possible.
* Current benchmarks show that updating 1000 messages takes about 8 seconds on a Nexus 7. So
* there should be room for further improvement.
* </p>
* *
* @param messageIds * @param messageIds
* A list of primary keys in the "messages" table. * A list of primary keys in the "messages" table.
@ -4180,23 +4184,9 @@ public class LocalStore extends Store implements Serializable {
* The flag to change. This must be a flag with a separate column in the database. * The flag to change. This must be a flag with a separate column in the database.
* @param newState * @param newState
* {@code true}, if the flag should be set. {@code false}, otherwise. * {@code true}, if the flag should be set. {@code false}, otherwise.
* @param threadRootIds
* If this is {@code true}, {@code messageIds} contains the IDs of the messages at the
* root of a thread. In that case the flag is changed for all messages in these threads.
* If this is {@code false} only the messages in {@code messageIds} are changed.
* *
* @throws MessagingException * @throws MessagingException
*/ */
public void setFlag(List<Long> messageIds, Flag flag, boolean newState, boolean threadRootIds)
throws MessagingException {
if (threadRootIds) {
setFlagForThreads(messageIds, flag, newState);
} else {
setFlag(messageIds, flag, newState);
}
}
public void setFlag(final List<Long> messageIds, final Flag flag, final boolean newState) public void setFlag(final List<Long> messageIds, final Flag flag, final boolean newState)
throws MessagingException { throws MessagingException {
@ -4251,7 +4241,23 @@ public class LocalStore extends Store implements Serializable {
}, FLAG_UPDATE_BATCH_SIZE); }, FLAG_UPDATE_BATCH_SIZE);
} }
public void setFlagForThreads(final List<Long> messageIds, Flag flag, final boolean newState) /**
* Change the state of a flag for a list of threads.
*
* <p>
* The goal of this method is to be fast. Currently this means using as few SQL UPDATE
* statements as possible.
*
* @param threadRootIds
* A list of root thread IDs.
* @param flag
* The flag to change. This must be a flag with a separate column in the database.
* @param newState
* {@code true}, if the flag should be set. {@code false}, otherwise.
*
* @throws MessagingException
*/
public void setFlagForThreads(final List<Long> threadRootIds, Flag flag, final boolean newState)
throws MessagingException { throws MessagingException {
final String flagColumn; final String flagColumn;
@ -4281,35 +4287,37 @@ public class LocalStore extends Store implements Serializable {
@Override @Override
public int getListSize() { public int getListSize() {
return messageIds.size(); return threadRootIds.size();
} }
@Override @Override
public String getListItem(int index) { public String getListItem(int index) {
return Long.toString(messageIds.get(index)); return Long.toString(threadRootIds.get(index));
} }
@Override @Override
public void doDbWork(SQLiteDatabase db, String selectionSet, String[] selectionArgs) public void doDbWork(SQLiteDatabase db, String selectionSet, String[] selectionArgs)
throws UnavailableStorageException { throws UnavailableStorageException {
int len = selectionArgs.length;
String[] args = new String[len * 2];
System.arraycopy(selectionArgs, 0, args, 0, len);
System.arraycopy(selectionArgs, 0, args, len, len);
db.execSQL("UPDATE messages SET " + flagColumn + " = " + ((newState) ? "1" : "0") + db.execSQL("UPDATE messages SET " + flagColumn + " = " + ((newState) ? "1" : "0") +
" WHERE id IN (" + " WHERE id IN (" +
"SELECT m.id FROM messages h " + "SELECT m.id FROM threads t " +
"JOIN threads t1 ON (t1.message_id = h.id) " + "LEFT JOIN messages m ON (t.message_id = m.id) " +
"JOIN threads t2 ON " +
"(t1.root IN (t2.id, t2.root) OR t1.id IN (t2.id, t2.root)) " +
"JOIN messages m ON (t2.message_id = m.id) " +
"WHERE (m.empty IS NULL OR m.empty != 1) AND m.deleted = 0 " + "WHERE (m.empty IS NULL OR m.empty != 1) AND m.deleted = 0 " +
"AND h.id" + selectionSet + ")", "AND (t.id" + selectionSet + " OR t.root" + selectionSet + "))",
selectionArgs); args);
} }
@Override @Override
public void postDbWork() { public void postDbWork() {
notifyChange(); notifyChange();
} }
}, FLAG_UPDATE_BATCH_SIZE); }, THREAD_FLAG_UPDATE_BATCH_SIZE);
} }
/** /**