Attached data to DB events to avoid DB lookups; refactored UI code.

Fields in Android UI objects that are accessed from background threads
must be declared volatile. UI objects use data attached to DB events to
avoid DB lookups, which complicates the UI code but should improve
performance.
This commit is contained in:
akwizgran
2013-03-14 20:58:20 +00:00
parent c783958d94
commit 23ab23a931
31 changed files with 909 additions and 424 deletions

View File

@@ -202,6 +202,13 @@ interface Database<T> {
*/
TransportConfig getConfig(T txn, TransportId t) throws DbException;
/**
* Returns the contact with the given ID.
* <p>
* Locking: contact read, window read.
*/
Contact getContact(T txn, ContactId c) throws DbException;
/**
* Returns the IDs of all contacts.
* <p>
@@ -230,6 +237,11 @@ interface Database<T> {
*/
long getFreeSpace() throws DbException;
/**
* Returns the group with the given ID, if the user subscribes to it.
*/
Group getGroup(T txn, GroupId g) throws DbException;
/**
* Returns the parent of the given group message, or null if either the
* message has no parent, or the parent is absent from the database, or the

View File

@@ -41,11 +41,12 @@ import net.sf.briar.api.db.event.ContactAddedEvent;
import net.sf.briar.api.db.event.ContactRemovedEvent;
import net.sf.briar.api.db.event.DatabaseEvent;
import net.sf.briar.api.db.event.DatabaseListener;
import net.sf.briar.api.db.event.GroupMessageAddedEvent;
import net.sf.briar.api.db.event.LocalSubscriptionsUpdatedEvent;
import net.sf.briar.api.db.event.LocalTransportsUpdatedEvent;
import net.sf.briar.api.db.event.MessageAddedEvent;
import net.sf.briar.api.db.event.MessageExpiredEvent;
import net.sf.briar.api.db.event.MessageReceivedEvent;
import net.sf.briar.api.db.event.PrivateMessageAddedEvent;
import net.sf.briar.api.db.event.RatingChangedEvent;
import net.sf.briar.api.db.event.RemoteRetentionTimeUpdatedEvent;
import net.sf.briar.api.db.event.RemoteSubscriptionsUpdatedEvent;
@@ -286,7 +287,7 @@ DatabaseCleaner.Callback {
} finally {
contactLock.readLock().unlock();
}
if(added) callListeners(new MessageAddedEvent());
if(added) callListeners(new GroupMessageAddedEvent(m, false));
}
/**
@@ -399,7 +400,7 @@ DatabaseCleaner.Callback {
} finally {
contactLock.readLock().unlock();
}
if(added) callListeners(new MessageAddedEvent());
if(added) callListeners(new PrivateMessageAddedEvent(m, c, false));
}
public void addSecrets(Collection<TemporarySecret> secrets)
@@ -844,6 +845,30 @@ DatabaseCleaner.Callback {
}
}
public Contact getContact(ContactId c) throws DbException {
contactLock.readLock().lock();
try {
windowLock.readLock().lock();
try {
T txn = db.startTransaction();
try {
if(!db.containsContact(txn, c))
throw new NoSuchContactException();
Contact contact = db.getContact(txn, c);
db.commitTransaction(txn);
return contact;
} catch(DbException e) {
db.abortTransaction(txn);
throw e;
}
} finally {
windowLock.readLock().unlock();
}
} finally {
contactLock.readLock().unlock();
}
}
public Collection<Contact> getContacts() throws DbException {
contactLock.readLock().lock();
try {
@@ -866,6 +891,25 @@ DatabaseCleaner.Callback {
}
}
public Group getGroup(GroupId g) throws DbException {
subscriptionLock.readLock().lock();
try {
T txn = db.startTransaction();
try {
if(!db.containsSubscription(txn, g))
throw new NoSuchSubscriptionException();
Group group = db.getGroup(txn, g);
db.commitTransaction(txn);
return group;
} catch(DbException e) {
db.abortTransaction(txn);
throw e;
}
} finally {
subscriptionLock.readLock().unlock();
}
}
public TransportProperties getLocalProperties(TransportId t)
throws DbException {
transportLock.readLock().lock();
@@ -1301,8 +1345,12 @@ DatabaseCleaner.Callback {
} finally {
contactLock.readLock().unlock();
}
callListeners(new MessageReceivedEvent());
if(added) callListeners(new MessageAddedEvent());
callListeners(new MessageReceivedEvent(c));
if(added) {
if(m.getGroup() == null)
callListeners(new PrivateMessageAddedEvent(m, c, true));
else callListeners(new GroupMessageAddedEvent(m, true));
}
}
/**
@@ -1795,7 +1843,7 @@ DatabaseCleaner.Callback {
} finally {
subscriptionLock.writeLock().unlock();
}
if(added) callListeners(new SubscriptionAddedEvent(g.getId()));
if(added) callListeners(new SubscriptionAddedEvent(g));
return added;
}

View File

@@ -1047,6 +1047,31 @@ abstract class JdbcDatabase implements Database<Connection> {
}
}
public Contact getContact(Connection txn, ContactId c) throws DbException {
PreparedStatement ps = null;
ResultSet rs = null;
try {
String sql = "SELECT name, lastConnected"
+ " FROM contacts AS c"
+ " JOIN connectionTimes AS ct"
+ " ON c.contactId = ct.contactId"
+ " WHERE c.contactId = ?";
ps = txn.prepareStatement(sql);
ps.setInt(1, c.getInt());
rs = ps.executeQuery();
if(!rs.next()) throw new DbStateException();
String name = rs.getString(1);
long lastConnected = rs.getLong(2);
rs.close();
ps.close();
return new Contact(c, name, lastConnected);
} catch(SQLException e) {
tryToClose(rs);
tryToClose(ps);
throw new DbException(e);
}
}
public Collection<ContactId> getContactIds(Connection txn)
throws DbException {
PreparedStatement ps = null;
@@ -1124,6 +1149,27 @@ abstract class JdbcDatabase implements Database<Connection> {
}
}
public Group getGroup(Connection txn, GroupId g) throws DbException {
PreparedStatement ps = null;
ResultSet rs = null;
try {
String sql = "SELECT name, key FROM groups WHERE groupId = ?";
ps = txn.prepareStatement(sql);
ps.setBytes(1, g.getBytes());
rs = ps.executeQuery();
if(!rs.next()) throw new DbStateException();
String name = rs.getString(1);
byte[] publicKey = rs.getBytes(2);
rs.close();
ps.close();
return new Group(g, name, publicKey);
} catch(SQLException e) {
tryToClose(rs);
tryToClose(ps);
throw new DbException(e);
}
}
public MessageId getGroupMessageParent(Connection txn, MessageId m)
throws DbException {
PreparedStatement ps = null;
@@ -1228,10 +1274,12 @@ abstract class JdbcDatabase implements Database<Connection> {
PreparedStatement ps = null;
ResultSet rs = null;
try {
String sql = "SELECT messageId, parentId, authorId, authorName,"
String sql = "SELECT messageId, parentId, m.authorId, authorName,"
+ " authorKey, contentType, subject, timestamp, read,"
+ " starred"
+ " FROM messages"
+ " starred, rating"
+ " FROM messages AS m"
+ " LEFT OUTER JOIN ratings AS r"
+ " ON m.authorId = r.authorId"
+ " WHERE groupId = ?";
ps = txn.prepareStatement(sql);
ps.setBytes(1, g.getBytes());
@@ -1242,13 +1290,18 @@ abstract class JdbcDatabase implements Database<Connection> {
MessageId id = new MessageId(rs.getBytes(1));
byte[] b = rs.getBytes(2);
MessageId parent = b == null ? null : new MessageId(b);
Author author = null;
Author author;
Rating rating;
b = rs.getBytes(3);
if(b != null) {
if(b == null) {
author = null;
rating = UNRATED;
} else {
AuthorId authorId = new AuthorId(b);
String authorName = rs.getString(4);
byte[] authorKey = rs.getBytes(5);
author = new Author(authorId, authorName, authorKey);
rating = Rating.values()[rs.getByte(11)];
}
String contentType = rs.getString(6);
String subject = rs.getString(7);
@@ -1256,7 +1309,7 @@ abstract class JdbcDatabase implements Database<Connection> {
boolean read = rs.getBoolean(9);
boolean starred = rs.getBoolean(10);
headers.add(new GroupMessageHeader(id, parent, contentType,
subject, timestamp, read, starred, g, author));
subject, timestamp, read, starred, g, author, rating));
}
rs.close();
ps.close();

View File

@@ -30,11 +30,12 @@ import net.sf.briar.api.db.DbException;
import net.sf.briar.api.db.event.ContactRemovedEvent;
import net.sf.briar.api.db.event.DatabaseEvent;
import net.sf.briar.api.db.event.DatabaseListener;
import net.sf.briar.api.db.event.GroupMessageAddedEvent;
import net.sf.briar.api.db.event.LocalSubscriptionsUpdatedEvent;
import net.sf.briar.api.db.event.LocalTransportsUpdatedEvent;
import net.sf.briar.api.db.event.MessageAddedEvent;
import net.sf.briar.api.db.event.MessageExpiredEvent;
import net.sf.briar.api.db.event.MessageReceivedEvent;
import net.sf.briar.api.db.event.PrivateMessageAddedEvent;
import net.sf.briar.api.db.event.RatingChangedEvent;
import net.sf.briar.api.db.event.RemoteRetentionTimeUpdatedEvent;
import net.sf.briar.api.db.event.RemoteSubscriptionsUpdatedEvent;
@@ -134,6 +135,9 @@ abstract class DuplexConnection implements DatabaseListener {
if(e instanceof ContactRemovedEvent) {
ContactRemovedEvent c = (ContactRemovedEvent) e;
if(contactId.equals(c.getContactId())) dispose(false, true);
} else if(e instanceof GroupMessageAddedEvent) {
if(canSendOffer.getAndSet(false))
dbExecutor.execute(new GenerateOffer());
} else if(e instanceof MessageExpiredEvent) {
dbExecutor.execute(new GenerateRetentionUpdate());
} else if(e instanceof LocalSubscriptionsUpdatedEvent) {
@@ -143,11 +147,15 @@ abstract class DuplexConnection implements DatabaseListener {
dbExecutor.execute(new GenerateSubscriptionUpdate());
} else if(e instanceof LocalTransportsUpdatedEvent) {
dbExecutor.execute(new GenerateTransportUpdates());
} else if(e instanceof MessageAddedEvent) {
if(canSendOffer.getAndSet(false))
dbExecutor.execute(new GenerateOffer());
} else if(e instanceof MessageReceivedEvent) {
dbExecutor.execute(new GenerateAcks());
if(((MessageReceivedEvent) e).getContactId().equals(contactId))
dbExecutor.execute(new GenerateAcks());
} else if(e instanceof PrivateMessageAddedEvent) {
PrivateMessageAddedEvent p = (PrivateMessageAddedEvent) e;
if(!p.isIncoming() && p.getContactId().equals(contactId)) {
if(canSendOffer.getAndSet(false))
dbExecutor.execute(new GenerateOffer());
}
} else if(e instanceof RatingChangedEvent) {
RatingChangedEvent r = (RatingChangedEvent) e;
if(r.getRating() == GOOD && canSendOffer.getAndSet(false))