Access MessageTree only on UiThread and improve code in the process

This commit is contained in:
Torsten Grote
2021-01-27 15:18:09 -03:00
parent 998c435b13
commit d670179e30
3 changed files with 70 additions and 92 deletions

View File

@@ -94,8 +94,9 @@ class ForumViewModel extends ThreadListViewModel<ForumPostItem> {
ForumPostReceivedEvent f = (ForumPostReceivedEvent) e; ForumPostReceivedEvent f = (ForumPostReceivedEvent) e;
if (f.getGroupId().equals(groupId)) { if (f.getGroupId().equals(groupId)) {
LOG.info("Forum post received, adding..."); LOG.info("Forum post received, adding...");
ForumPostItem item = buildItem(f.getHeader(), f.getText()); ForumPostItem item =
addItem(item); new ForumPostItem(f.getHeader(), f.getText());
addItem(item, false);
} }
} else if (e instanceof ForumInvitationResponseReceivedEvent) { } else if (e instanceof ForumInvitationResponseReceivedEvent) {
ForumInvitationResponseReceivedEvent f = ForumInvitationResponseReceivedEvent f =
@@ -140,10 +141,23 @@ class ForumViewModel extends ThreadListViewModel<ForumPostItem> {
List<ForumPostHeader> headers = List<ForumPostHeader> headers =
forumManager.getPostHeaders(txn, groupId); forumManager.getPostHeaders(txn, groupId);
logDuration(LOG, "Loading headers", start); logDuration(LOG, "Loading headers", start);
return createItems(txn, headers, this::buildItem); start = now();
List<ForumPostItem> items = new ArrayList<>();
for (ForumPostHeader header : headers) {
items.add(loadItem(txn, header));
}
logDuration(LOG, "Loading bodies and creating items", start);
return items;
}, this::setItems); }, this::setItems);
} }
private ForumPostItem loadItem(Transaction txn, PostHeader header)
throws DbException {
if (!(header instanceof ForumPostHeader)) throw new AssertionError();
String text = forumManager.getPostText(txn, header.getId());
return new ForumPostItem((ForumPostHeader) header, text);
}
@Override @Override
public void createAndStoreMessage(String text, public void createAndStoreMessage(String text,
@Nullable MessageId parentId) { @Nullable MessageId parentId) {
@@ -171,26 +185,15 @@ class ForumViewModel extends ThreadListViewModel<ForumPostItem> {
} }
private void storePost(ForumPost msg, String text) { private void storePost(ForumPost msg, String text) {
runOnDbThread(() -> { runOnDbThread(false, txn -> {
try { long start = now();
long start = now(); ForumPostHeader header = forumManager.addLocalPost(txn, msg);
ForumPostHeader header = forumManager.addLocalPost(msg); logDuration(LOG, "Storing forum post", start);
addItemAsync(buildItem(header, text)); txn.attach(() -> {
logDuration(LOG, "Storing forum post", start); ForumPostItem item = new ForumPostItem(header, text);
} catch (DbException e) { addItem(item, true);
logException(LOG, WARNING, e); });
} }, e -> logException(LOG, WARNING, e));
});
}
private ForumPostItem buildItem(ForumPostHeader header, String text) {
return new ForumPostItem(header, text);
}
@Override
protected String loadMessageText(Transaction txn, PostHeader header)
throws DbException {
return forumManager.getPostText(txn, header.getId());
} }
@Override @Override

View File

@@ -27,7 +27,6 @@ import org.briarproject.briar.android.viewmodel.MutableLiveEvent;
import org.briarproject.briar.api.android.AndroidNotificationManager; import org.briarproject.briar.api.android.AndroidNotificationManager;
import org.briarproject.briar.api.client.MessageTracker; import org.briarproject.briar.api.client.MessageTracker;
import org.briarproject.briar.api.client.MessageTracker.GroupCount; import org.briarproject.briar.api.client.MessageTracker.GroupCount;
import org.briarproject.briar.api.client.PostHeader;
import org.briarproject.briar.api.privategroup.GroupMember; import org.briarproject.briar.api.privategroup.GroupMember;
import org.briarproject.briar.api.privategroup.GroupMessage; import org.briarproject.briar.api.privategroup.GroupMessage;
import org.briarproject.briar.api.privategroup.GroupMessageFactory; import org.briarproject.briar.api.privategroup.GroupMessageFactory;
@@ -105,7 +104,7 @@ class GroupViewModel extends ThreadListViewModel<GroupMessageItem> {
if (!g.isLocal() && g.getGroupId().equals(groupId)) { if (!g.isLocal() && g.getGroupId().equals(groupId)) {
LOG.info("Group message received, adding..."); LOG.info("Group message received, adding...");
GroupMessageItem item = buildItem(g.getHeader(), g.getText()); GroupMessageItem item = buildItem(g.getHeader(), g.getText());
addItem(item); addItem(item, false);
// In case the join message comes from the creator, // In case the join message comes from the creator,
// we need to reload the sharing contacts // we need to reload the sharing contacts
// in case it was delayed and the sharing count is wrong (#850). // in case it was delayed and the sharing count is wrong (#850).
@@ -171,10 +170,28 @@ class GroupViewModel extends ThreadListViewModel<GroupMessageItem> {
List<GroupMessageHeader> headers = List<GroupMessageHeader> headers =
privateGroupManager.getHeaders(txn, groupId); privateGroupManager.getHeaders(txn, groupId);
logDuration(LOG, "Loading headers", start); logDuration(LOG, "Loading headers", start);
return createItems(txn, headers, this::buildItem); start = now();
List<GroupMessageItem> items = new ArrayList<>();
for (GroupMessageHeader header : headers) {
items.add(loadItem(txn, header));
}
logDuration(LOG, "Loading bodies and creating items", start);
return items;
}, this::setItems); }, this::setItems);
} }
private GroupMessageItem loadItem(Transaction txn,
GroupMessageHeader header) throws DbException {
String text;
if (header instanceof JoinMessageHeader) {
// will be looked up later
text = "";
} else {
text = privateGroupManager.getMessageText(txn, header.getId());
}
return buildItem(header, text);
}
private GroupMessageItem buildItem(GroupMessageHeader header, String text) { private GroupMessageItem buildItem(GroupMessageHeader header, String text) {
if (header instanceof JoinMessageHeader) { if (header instanceof JoinMessageHeader) {
return new JoinMessageItem((JoinMessageHeader) header, text); return new JoinMessageItem((JoinMessageHeader) header, text);
@@ -182,16 +199,6 @@ class GroupViewModel extends ThreadListViewModel<GroupMessageItem> {
return new GroupMessageItem(header, text); return new GroupMessageItem(header, text);
} }
@Override
protected String loadMessageText(
Transaction txn, PostHeader header) throws DbException {
if (header instanceof JoinMessageHeader) {
// will be looked up later
return "";
}
return privateGroupManager.getMessageText(txn, header.getId());
}
@Override @Override
public void createAndStoreMessage(String text, public void createAndStoreMessage(String text,
@Nullable MessageId parentId) { @Nullable MessageId parentId) {
@@ -222,17 +229,15 @@ class GroupViewModel extends ThreadListViewModel<GroupMessageItem> {
} }
private void storePost(GroupMessage msg, String text) { private void storePost(GroupMessage msg, String text) {
runOnDbThread(() -> { runOnDbThread(false, txn -> {
try { long start = now();
long start = now(); GroupMessageHeader header =
GroupMessageHeader header = privateGroupManager.addLocalMessage(txn, msg);
privateGroupManager.addLocalMessage(msg); logDuration(LOG, "Storing group message", start);
addItemAsync(buildItem(header, text)); txn.attach(() ->
logDuration(LOG, "Storing group message", start); addItem(buildItem(header, text), true)
} catch (DbException e) { );
logException(LOG, WARNING, e); }, e -> logException(LOG, WARNING, e));
}
});
} }
@Override @Override

View File

@@ -5,7 +5,6 @@ import android.app.Application;
import org.briarproject.bramble.api.crypto.CryptoExecutor; import org.briarproject.bramble.api.crypto.CryptoExecutor;
import org.briarproject.bramble.api.db.DatabaseExecutor; import org.briarproject.bramble.api.db.DatabaseExecutor;
import org.briarproject.bramble.api.db.DbException; import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.db.Transaction;
import org.briarproject.bramble.api.db.TransactionManager; import org.briarproject.bramble.api.db.TransactionManager;
import org.briarproject.bramble.api.event.Event; import org.briarproject.bramble.api.event.Event;
import org.briarproject.bramble.api.event.EventBus; import org.briarproject.bramble.api.event.EventBus;
@@ -26,11 +25,8 @@ import org.briarproject.briar.android.viewmodel.LiveResult;
import org.briarproject.briar.api.android.AndroidNotificationManager; import org.briarproject.briar.api.android.AndroidNotificationManager;
import org.briarproject.briar.api.client.MessageTracker; import org.briarproject.briar.api.client.MessageTracker;
import org.briarproject.briar.api.client.MessageTree; import org.briarproject.briar.api.client.MessageTree;
import org.briarproject.briar.api.client.PostHeader;
import org.briarproject.briar.client.MessageTreeImpl; import org.briarproject.briar.client.MessageTreeImpl;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List; import java.util.List;
import java.util.concurrent.Executor; import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicReference; import java.util.concurrent.atomic.AtomicReference;
@@ -46,9 +42,7 @@ import androidx.lifecycle.MutableLiveData;
import static java.util.logging.Level.INFO; import static java.util.logging.Level.INFO;
import static java.util.logging.Level.WARNING; import static java.util.logging.Level.WARNING;
import static java.util.logging.Logger.getLogger; import static java.util.logging.Logger.getLogger;
import static org.briarproject.bramble.util.LogUtils.logDuration;
import static org.briarproject.bramble.util.LogUtils.logException; import static org.briarproject.bramble.util.LogUtils.logException;
import static org.briarproject.bramble.util.LogUtils.now;
@MethodsNotNullByDefault @MethodsNotNullByDefault
@ParametersNotNullByDefault @ParametersNotNullByDefault
@@ -67,6 +61,7 @@ public abstract class ThreadListViewModel<I extends ThreadItem>
private final MessageTracker messageTracker; private final MessageTracker messageTracker;
private final EventBus eventBus; private final EventBus eventBus;
// UIThread
private final MessageTree<I> messageTree = new MessageTreeImpl<>(); private final MessageTree<I> messageTree = new MessageTreeImpl<>();
private final MutableLiveData<LiveResult<List<I>>> items = private final MutableLiveData<LiveResult<List<I>>> items =
new MutableLiveData<>(); new MutableLiveData<>();
@@ -175,55 +170,34 @@ public abstract class ThreadListViewModel<I extends ThreadItem>
* Loads the ContactIds of all contacts the group is shared with * Loads the ContactIds of all contacts the group is shared with
* and adds them to {@link SharingController}. * and adds them to {@link SharingController}.
*/ */
public abstract void loadSharingContacts(); protected abstract void loadSharingContacts();
@UiThread @UiThread
protected void setItems(LiveResult<List<I>> items) { protected void setItems(LiveResult<List<I>> items) {
this.items.setValue(items); if (items.hasError()) {
} this.items.setValue(items);
} else {
@DatabaseExecutor messageTree.clear();
protected <H extends PostHeader> List<I> createItems( // not null, because hasError() is false
Transaction txn, Collection<H> headers, ItemGetter<H, I> itemGetter) messageTree.add(items.getResultOrNull());
throws DbException { LiveResult<List<I>> result =
long start = now(); new LiveResult<>(messageTree.depthFirstOrder());
List<I> items = new ArrayList<>(); this.items.setValue(result);
for (H header : headers) {
String text = loadMessageText(txn, header);
items.add(itemGetter.getItem(header, text));
} }
logDuration(LOG, "Loading bodies and creating items", start);
messageTree.clear();
messageTree.add(items);
return messageTree.depthFirstOrder();
} }
/** /**
* Add a remote item on the UI thread. * Add a remote item on the UI thread.
* The list will not scroll, but show an unread indicator. *
* @param scrollToItem whether the list will scroll to the newly added item
*/ */
@UiThread @UiThread
protected void addItem(I item) { protected void addItem(I item, boolean scrollToItem) {
messageTree.add(item); messageTree.add(item);
if (scrollToItem) this.scrollToItem.set(item.getId());
items.setValue(new LiveResult<>(messageTree.depthFirstOrder())); items.setValue(new LiveResult<>(messageTree.depthFirstOrder()));
} }
/**
* Add a local item from the DB thread.
* The list will scroll to the new item.
*/
@DatabaseExecutor
protected void addItemAsync(I item) {
messageTree.add(item);
scrollToItem.set(item.getId());
items.postValue(new LiveResult<>(messageTree.depthFirstOrder()));
}
@DatabaseExecutor
protected abstract String loadMessageText(Transaction txn,
PostHeader header) throws DbException;
@UiThread @UiThread
void setReplyId(@Nullable MessageId id) { void setReplyId(@Nullable MessageId id) {
replyId = id; replyId = id;
@@ -271,8 +245,4 @@ public abstract class ThreadListViewModel<I extends ThreadItem>
return scrollToItem.getAndSet(null); return scrollToItem.getAndSet(null);
} }
public interface ItemGetter<H extends PostHeader, I> {
I getItem(H header, String text);
}
} }