diff --git a/briar-android/src/org/briarproject/android/blogs/BlogActivity.java b/briar-android/src/org/briarproject/android/blogs/BlogActivity.java
index 866b191d96d38d6359aca975ae42196218f097df..7c3309010e1c85ccfabdcedab718e80637d61816 100644
--- a/briar-android/src/org/briarproject/android/blogs/BlogActivity.java
+++ b/briar-android/src/org/briarproject/android/blogs/BlogActivity.java
@@ -2,31 +2,36 @@ package org.briarproject.android.blogs;
 
 import android.content.Intent;
 import android.os.Bundle;
+import android.support.annotation.Nullable;
+import android.support.annotation.UiThread;
 import android.support.v4.app.Fragment;
 import android.support.v4.app.FragmentManager;
 import android.support.v4.app.FragmentStatePagerAdapter;
 import android.support.v4.view.ViewPager;
 import android.view.ViewGroup;
 import android.widget.ProgressBar;
-import android.widget.Toast;
 
 import org.briarproject.R;
 import org.briarproject.android.ActivityComponent;
 import org.briarproject.android.BriarActivity;
 import org.briarproject.android.blogs.BlogController.BlogPostListener;
 import org.briarproject.android.blogs.BlogPostAdapter.OnBlogPostClickListener;
-import org.briarproject.android.controller.handler.UiResultHandler;
+import org.briarproject.android.controller.handler.UiResultExceptionHandler;
 import org.briarproject.android.fragment.BaseFragment.BaseFragmentListener;
+import org.briarproject.api.blogs.BlogPostHeader;
+import org.briarproject.api.db.DbException;
 import org.briarproject.api.sync.GroupId;
 import org.briarproject.api.sync.MessageId;
 
+import java.util.ArrayList;
 import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
 
 import javax.inject.Inject;
 
 import static android.view.View.GONE;
 import static android.view.View.VISIBLE;
-import static android.widget.Toast.LENGTH_SHORT;
 
 public class BlogActivity extends BriarActivity implements BlogPostListener,
 		OnBlogPostClickListener, BaseFragmentListener {
@@ -37,17 +42,17 @@ public class BlogActivity extends BriarActivity implements BlogPostListener,
 	static final String IS_MY_BLOG = "briar.IS_MY_BLOG";
 	static final String IS_NEW_BLOG = "briar.IS_NEW_BLOG";
 
-	private static final String BLOG_PAGER_ADAPTER = "briar.BLOG_PAGER_ADAPTER";
+	private static final String POST_ID = "briar.POST_ID";
 
+	private GroupId groupId;
 	private ProgressBar progressBar;
 	private ViewPager pager;
 	private BlogPagerAdapter blogPagerAdapter;
 	private BlogPostPagerAdapter postPagerAdapter;
 	private String blogName;
 	private boolean myBlog, isNew;
+	private MessageId savedPostId;
 
-	// Fields that are accessed from background threads must be volatile
-	private volatile GroupId groupId = null;
 	@Inject
 	BlogController blogController;
 
@@ -58,8 +63,9 @@ public class BlogActivity extends BriarActivity implements BlogPostListener,
 		// GroupId from Intent
 		Intent i = getIntent();
 		byte[] b = i.getByteArrayExtra(GROUP_ID);
-		if (b == null) throw new IllegalStateException("No Group in intent.");
+		if (b == null) throw new IllegalStateException("No group ID in intent");
 		groupId = new GroupId(b);
+		blogController.setGroupId(groupId);
 
 		// Name of the Blog from Intent
 		blogName = i.getStringExtra(BLOG_NAME);
@@ -73,30 +79,46 @@ public class BlogActivity extends BriarActivity implements BlogPostListener,
 
 		pager = (ViewPager) findViewById(R.id.pager);
 		progressBar = (ProgressBar) findViewById(R.id.progressBar);
-		hideLoadingScreen();
 
 		blogPagerAdapter = new BlogPagerAdapter(getSupportFragmentManager());
-		if (state == null || state.getBoolean(BLOG_PAGER_ADAPTER, true)) {
+		postPagerAdapter = new BlogPostPagerAdapter(
+				getSupportFragmentManager());
+
+		if (state == null || state.getByteArray(POST_ID) == null) {
+			// The blog fragment has its own progress bar
+			hideLoadingScreen();
 			pager.setAdapter(blogPagerAdapter);
+			savedPostId = null;
 		} else {
-			// this initializes and restores the postPagerAdapter
-			loadBlogPosts();
+			// Adapter will be set in selectPostInPostPager()
+			savedPostId = new MessageId(state.getByteArray(POST_ID));
+		}
+	}
+
+	@Override
+	public void onResume() {
+		super.onResume();
+		if (savedPostId == null) {
+			MessageId selected = getSelectedPostInPostPager();
+			if (selected != null) loadBlogPosts(selected);
+		} else {
+			loadBlogPosts(savedPostId);
 		}
 	}
 
 	@Override
 	public void onSaveInstanceState(Bundle outState) {
 		super.onSaveInstanceState(outState);
-
-		// remember which adapter we had active
-		outState.putBoolean(BLOG_PAGER_ADAPTER,
-				pager.getAdapter() == blogPagerAdapter);
+		MessageId selected = getSelectedPostInPostPager();
+		if (selected != null)
+			outState.putByteArray(POST_ID, selected.getBytes());
 	}
 
 	@Override
 	public void onBackPressed() {
 		if (pager.getAdapter() == postPagerAdapter) {
 			pager.setAdapter(blogPagerAdapter);
+			savedPostId = null;
 		} else {
 			super.onBackPressed();
 		}
@@ -112,10 +134,6 @@ public class BlogActivity extends BriarActivity implements BlogPostListener,
 		progressBar.setVisibility(VISIBLE);
 	}
 
-	private void showLoadingScreen() {
-		showLoadingScreen(false, 0);
-	}
-
 	@Override
 	public void hideLoadingScreen() {
 		progressBar.setVisibility(GONE);
@@ -127,60 +145,79 @@ public class BlogActivity extends BriarActivity implements BlogPostListener,
 	}
 
 	@Override
-	public void onBlogPostClick(int position, BlogPostItem post) {
-		loadBlogPosts(position, true);
+	public void onBlogPostClick(BlogPostItem post) {
+		loadBlogPosts(post.getId());
 	}
 
-	private void loadBlogPosts() {
-		loadBlogPosts(0, false);
-	}
+	private void loadBlogPosts(final MessageId select) {
+		blogController.loadBlogPosts(
+				new UiResultExceptionHandler<Collection<BlogPostItem>, DbException>(
+						this) {
+					@Override
+					public void onResultUi(Collection<BlogPostItem> posts) {
+						hideLoadingScreen();
+						savedPostId = null;
+						postPagerAdapter.setPosts(posts);
+						selectPostInPostPager(select);
+					}
 
-	private void loadBlogPosts(final int position, final boolean setItem) {
-		showLoadingScreen();
-		blogController.loadBlog(groupId, false,
-				new UiResultHandler<Boolean>(this) {
 					@Override
-					public void onResultUi(Boolean result) {
-						if (result) {
-							Collection<BlogPostItem> posts =
-									blogController.getBlogPosts();
-
-							if (postPagerAdapter == null) {
-								postPagerAdapter = new BlogPostPagerAdapter(
-										getSupportFragmentManager(),
-										posts.size());
-							} else {
-								postPagerAdapter.setSize(posts.size());
-							}
-							pager.setAdapter(postPagerAdapter);
-							if (setItem) pager.setCurrentItem(position);
-						} else {
-							Toast.makeText(BlogActivity.this,
-									R.string.blogs_blog_post_failed_to_load,
-									LENGTH_SHORT).show();
-						}
+					public void onExceptionUi(DbException exception) {
+						// TODO: Decide how to handle errors in the UI
+						finish();
 					}
 				});
 	}
 
 	@Override
-	public void onBlogPostAdded(final BlogPostItem post, final boolean local) {
-		runOnUiThread(new Runnable() {
-			@Override
-			public void run() {
-				if (blogPagerAdapter != null) {
-					BlogFragment f = blogPagerAdapter.getFragment();
-					if (f != null && f.isVisible()) {
-						f.onBlogPostAdded(post, local);
+	public void onBlogPostAdded(BlogPostHeader header, boolean local) {
+		if (pager.getAdapter() == postPagerAdapter) {
+			loadBlogPost(header);
+		} else {
+			BlogFragment f = blogPagerAdapter.getFragment();
+			if (f != null && f.isVisible()) f.onBlogPostAdded(header, local);
+		}
+	}
+
+	private void loadBlogPost(BlogPostHeader header) {
+		blogController.loadBlogPost(header,
+				new UiResultExceptionHandler<BlogPostItem, DbException>(this) {
+					@Override
+					public void onResultUi(BlogPostItem post) {
+						addPostToPostPager(post);
 					}
-				}
 
-				if (postPagerAdapter != null) {
-					postPagerAdapter.onBlogPostAdded();
-					postPagerAdapter.notifyDataSetChanged();
-				}
+					@Override
+					public void onExceptionUi(DbException exception) {
+						// TODO: Decide how to handle errors in the UI
+						finish();
+					}
+				});
+	}
+
+	@Nullable
+	private MessageId getSelectedPostInPostPager() {
+		if (pager.getAdapter() != postPagerAdapter) return null;
+		if (postPagerAdapter.getCount() == 0) return null;
+		int position = pager.getCurrentItem();
+		return postPagerAdapter.getPost(position).getId();
+	}
+
+	private void selectPostInPostPager(MessageId m) {
+		int count = postPagerAdapter.getCount();
+		for (int i = 0; i < count; i++) {
+			if (postPagerAdapter.getPost(i).getId().equals(m)) {
+				pager.setAdapter(postPagerAdapter);
+				pager.setCurrentItem(i);
+				return;
 			}
-		});
+		}
+	}
+
+	private void addPostToPostPager(BlogPostItem post) {
+		MessageId selected = getSelectedPostInPostPager();
+		postPagerAdapter.addPost(post);
+		if (selected != null) selectPostInPostPager(selected);
 	}
 
 	@Override
@@ -191,18 +228,22 @@ public class BlogActivity extends BriarActivity implements BlogPostListener,
 		// The BlogPostAddedEvent arrives when the controller is not listening,
 		// so we need to manually reload the blog posts :(
 		if (requestCode == REQUEST_WRITE_POST && resultCode == RESULT_OK) {
-			BlogFragment f = blogPagerAdapter.getFragment();
-			if (f != null && f.isVisible()) {
-				f.reload();
+			if (pager.getAdapter() == postPagerAdapter) {
+				MessageId selected = getSelectedPostInPostPager();
+				if (selected != null) loadBlogPosts(selected);
+			} else {
+				BlogFragment f = blogPagerAdapter.getFragment();
+				if (f != null && f.isVisible()) f.loadBlogPosts(true);
 			}
 		}
 	}
 
-
+	@UiThread
 	private class BlogPagerAdapter extends FragmentStatePagerAdapter {
+
 		private BlogFragment fragment = null;
 
-		BlogPagerAdapter(FragmentManager fm) {
+		private BlogPagerAdapter(FragmentManager fm) {
 			super(fm);
 		}
 
@@ -224,36 +265,46 @@ public class BlogActivity extends BriarActivity implements BlogPostListener,
 			return fragment;
 		}
 
-		BlogFragment getFragment() {
+		private BlogFragment getFragment() {
 			return fragment;
 		}
 	}
 
-	private class BlogPostPagerAdapter extends FragmentStatePagerAdapter {
-		private int size;
+	@UiThread
+	private static class BlogPostPagerAdapter
+			extends FragmentStatePagerAdapter {
+
+		private final List<BlogPostItem> posts = new ArrayList<>();
 
-		BlogPostPagerAdapter(FragmentManager fm, int size) {
+		private BlogPostPagerAdapter(FragmentManager fm) {
 			super(fm);
-			this.size = size;
 		}
 
 		@Override
 		public int getCount() {
-			return size;
+			return posts.size();
 		}
 
 		@Override
 		public Fragment getItem(int position) {
-			MessageId postIdOfPos = blogController.getBlogPostId(position);
-			return BlogPostFragment.newInstance(groupId, postIdOfPos);
+			return BlogPostFragment.newInstance(posts.get(position).getId());
+		}
+
+		private BlogPostItem getPost(int position) {
+			return posts.get(position);
 		}
 
-		void onBlogPostAdded() {
-			size++;
+		private void setPosts(Collection<BlogPostItem> posts) {
+			this.posts.clear();
+			this.posts.addAll(posts);
+			Collections.sort(this.posts);
+			notifyDataSetChanged();
 		}
 
-		void setSize(int size) {
-			this.size = size;
+		private void addPost(BlogPostItem post) {
+			posts.add(post);
+			Collections.sort(posts);
+			notifyDataSetChanged();
 		}
 	}
 
diff --git a/briar-android/src/org/briarproject/android/blogs/BlogController.java b/briar-android/src/org/briarproject/android/blogs/BlogController.java
index 8436813de64e2505bd8fa27eea954199282ba867..90fbb61cf1ae4ab2428674fadca842608a92da97 100644
--- a/briar-android/src/org/briarproject/android/blogs/BlogController.java
+++ b/briar-android/src/org/briarproject/android/blogs/BlogController.java
@@ -1,36 +1,36 @@
 package org.briarproject.android.blogs;
 
-import android.support.annotation.Nullable;
+import android.support.annotation.UiThread;
 
 import org.briarproject.android.controller.ActivityLifecycleController;
 import org.briarproject.android.controller.handler.ResultExceptionHandler;
-import org.briarproject.android.controller.handler.ResultHandler;
+import org.briarproject.api.blogs.BlogPostHeader;
 import org.briarproject.api.db.DbException;
 import org.briarproject.api.sync.GroupId;
 import org.briarproject.api.sync.MessageId;
 
-import java.util.SortedSet;
+import java.util.Collection;
 
 public interface BlogController extends ActivityLifecycleController {
 
-	void loadBlog(GroupId groupId, boolean reload,
-			ResultHandler<Boolean> resultHandler);
+	void setGroupId(GroupId g);
 
-	SortedSet<BlogPostItem> getBlogPosts();
+	void loadBlogPosts(
+			ResultExceptionHandler<Collection<BlogPostItem>, DbException> handler);
 
-	@Nullable
-	BlogPostItem getBlogPost(MessageId postId);
+	void loadBlogPost(BlogPostHeader header,
+			ResultExceptionHandler<BlogPostItem, DbException> handler);
 
-	@Nullable
-	MessageId getBlogPostId(int position);
+	void loadBlogPost(MessageId m,
+			ResultExceptionHandler<BlogPostItem, DbException> handler);
 
-	void canDeleteBlog(GroupId groupId,
-			ResultExceptionHandler<Boolean, DbException> resultHandler);
+	void canDeleteBlog(ResultExceptionHandler<Boolean, DbException> handler);
 
-	void deleteBlog(ResultHandler<Boolean> resultHandler);
+	void deleteBlog(ResultExceptionHandler<Void, DbException> handler);
 
 	interface BlogPostListener {
-		void onBlogPostAdded(BlogPostItem post, boolean local);
+		@UiThread
+		void onBlogPostAdded(BlogPostHeader header, boolean local);
 	}
 
 }
diff --git a/briar-android/src/org/briarproject/android/blogs/BlogControllerImpl.java b/briar-android/src/org/briarproject/android/blogs/BlogControllerImpl.java
index 0095b4beeca507d5e02e055a2cff57603e921f89..fbed89b0c9943656fa845cb1856c410dde1d35c9 100644
--- a/briar-android/src/org/briarproject/android/blogs/BlogControllerImpl.java
+++ b/briar-android/src/org/briarproject/android/blogs/BlogControllerImpl.java
@@ -1,11 +1,9 @@
 package org.briarproject.android.blogs;
 
 import android.app.Activity;
-import android.support.annotation.Nullable;
 
 import org.briarproject.android.controller.DbControllerImpl;
 import org.briarproject.android.controller.handler.ResultExceptionHandler;
-import org.briarproject.android.controller.handler.ResultHandler;
 import org.briarproject.api.blogs.Blog;
 import org.briarproject.api.blogs.BlogManager;
 import org.briarproject.api.blogs.BlogPostHeader;
@@ -20,8 +18,9 @@ import org.briarproject.api.sync.MessageId;
 
 import java.util.ArrayList;
 import java.util.Collection;
-import java.util.SortedSet;
-import java.util.TreeSet;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
 import java.util.logging.Logger;
 
 import javax.inject.Inject;
@@ -38,19 +37,27 @@ public class BlogControllerImpl extends DbControllerImpl
 	@Inject
 	protected Activity activity;
 	@Inject
-	protected volatile BlogManager blogManager;
+	protected EventBus eventBus;
+
 	@Inject
-	protected volatile EventBus eventBus;
+	protected volatile BlogManager blogManager;
+
+	private final Map<MessageId, byte[]> bodyCache = new ConcurrentHashMap<>();
+	private final Map<MessageId, BlogPostHeader> headerCache =
+			new ConcurrentHashMap<>();
 
 	private volatile BlogPostListener listener;
 	private volatile GroupId groupId = null;
-	// FIXME: This collection isn't thread-safe, isn't updated atomically
-	private volatile TreeSet<BlogPostItem> posts = null;
 
 	@Inject
 	BlogControllerImpl() {
 	}
 
+	@Override
+	public void setGroupId(GroupId g) {
+		groupId = g;
+	}
+
 	@Override
 	public void onActivityCreate() {
 		if (activity instanceof BlogPostListener) {
@@ -78,26 +85,17 @@ public class BlogControllerImpl extends DbControllerImpl
 
 	@Override
 	public void eventOccurred(Event e) {
+		if (groupId == null) throw new IllegalStateException();
 		if (e instanceof BlogPostAddedEvent) {
-			BlogPostAddedEvent m = (BlogPostAddedEvent) e;
+			final BlogPostAddedEvent m = (BlogPostAddedEvent) e;
 			if (m.getGroupId().equals(groupId)) {
 				LOG.info("New blog post added");
-				if (posts == null) {
-					LOG.info("Posts have not loaded, yet");
-					// FIXME: Race condition, new post may not get loaded
-					return;
-				}
-				final BlogPostHeader header = m.getHeader();
-				// FIXME: Don't make blocking calls in event handlers
-				try {
-					byte[] body = blogManager.getPostBody(header.getId());
-					BlogPostItem post = new BlogPostItem(groupId, header, body);
-					posts.add(post);
-					listener.onBlogPostAdded(post, m.isLocal());
-				} catch (DbException ex) {
-					if (LOG.isLoggable(WARNING))
-						LOG.log(WARNING, ex.toString(), ex);
-				}
+				activity.runOnUiThread(new Runnable() {
+					@Override
+					public void run() {
+						listener.onBlogPostAdded(m.getHeader(), m.isLocal());
+					}
+				});
 			}
 		} else if (e instanceof GroupRemovedEvent) {
 			GroupRemovedEvent s = (GroupRemovedEvent) e;
@@ -106,6 +104,7 @@ public class BlogControllerImpl extends DbControllerImpl
 				activity.runOnUiThread(new Runnable() {
 					@Override
 					public void run() {
+						// TODO: Not the controller's job, add a listener method
 						activity.finish();
 					}
 				});
@@ -114,106 +113,149 @@ public class BlogControllerImpl extends DbControllerImpl
 	}
 
 	@Override
-	public void loadBlog(final GroupId g, final boolean reload,
-			final ResultHandler<Boolean> resultHandler) {
-
+	public void loadBlogPosts(
+			final ResultExceptionHandler<Collection<BlogPostItem>, DbException> handler) {
+		if (groupId == null) throw new IllegalStateException();
 		runOnDbThread(new Runnable() {
 			@Override
 			public void run() {
 				try {
-					if (reload || posts == null) {
-						groupId = g;
-						posts = new TreeSet<>();
-						// load blog posts
-						long now = System.currentTimeMillis();
-						Collection<BlogPostItem> newPosts = new ArrayList<>();
-						Collection<BlogPostHeader> header =
-								blogManager.getPostHeaders(g);
-						for (BlogPostHeader h : header) {
-							byte[] body = blogManager.getPostBody(h.getId());
-							newPosts.add(new BlogPostItem(g, h, body));
-						}
-						posts.addAll(newPosts);
-						long duration = System.currentTimeMillis() - now;
-						if (LOG.isLoggable(INFO))
-							LOG.info("Loading blog took " + duration + " ms");
+					long now = System.currentTimeMillis();
+					Collection<BlogPostHeader> headers =
+							blogManager.getPostHeaders(groupId);
+					long duration = System.currentTimeMillis() - now;
+					if (LOG.isLoggable(INFO))
+						LOG.info("Loading headers took " + duration + " ms");
+					List<BlogPostItem> items = new ArrayList<>(headers.size());
+					now = System.currentTimeMillis();
+					for (BlogPostHeader h : headers) {
+						headerCache.put(h.getId(), h);
+						byte[] body = getPostBody(h.getId());
+						items.add(new BlogPostItem(groupId, h, body));
 					}
-					resultHandler.onResult(true);
+					duration = System.currentTimeMillis() - now;
+					if (LOG.isLoggable(INFO))
+						LOG.info("Loading bodies took " + duration + " ms");
+					handler.onResult(items);
 				} catch (DbException e) {
 					if (LOG.isLoggable(WARNING))
 						LOG.log(WARNING, e.toString(), e);
-					resultHandler.onResult(false);
+					handler.onException(e);
 				}
 			}
 		});
 	}
 
 	@Override
-	@Nullable
-	public SortedSet<BlogPostItem> getBlogPosts() {
-		return posts;
+	public void loadBlogPost(final BlogPostHeader header,
+			final ResultExceptionHandler<BlogPostItem, DbException> handler) {
+		if (groupId == null) throw new IllegalStateException();
+		byte[] body = bodyCache.get(header.getId());
+		if (body != null) {
+			LOG.info("Loaded body from cache");
+			handler.onResult(new BlogPostItem(groupId, header, body));
+			return;
+		}
+		runOnDbThread(new Runnable() {
+			@Override
+			public void run() {
+				try {
+					long now = System.currentTimeMillis();
+					byte[] body = getPostBody(header.getId());
+					long duration = System.currentTimeMillis() - now;
+					if (LOG.isLoggable(INFO))
+						LOG.info("Loading body took " + duration + " ms");
+					handler.onResult(new BlogPostItem(groupId, header, body));
+				} catch (DbException e) {
+					if (LOG.isLoggable(WARNING))
+						LOG.log(WARNING, e.toString(), e);
+					handler.onException(e);
+				}
+			}
+		});
 	}
 
 	@Override
-	@Nullable
-	public BlogPostItem getBlogPost(MessageId id) {
-		if (posts == null) return null;
-		for (BlogPostItem item : posts) {
-			if (item.getId().equals(id)) return item;
+	public void loadBlogPost(final MessageId m,
+			final ResultExceptionHandler<BlogPostItem, DbException> handler) {
+		if (groupId == null) throw new IllegalStateException();
+		BlogPostHeader header = headerCache.get(m);
+		if (header != null) {
+			LOG.info("Loaded header from cache");
+			loadBlogPost(header, handler);
+			return;
+		}
+		runOnDbThread(new Runnable() {
+			@Override
+			public void run() {
+				try {
+					long now = System.currentTimeMillis();
+					BlogPostHeader header = getPostHeader(m);
+					byte[] body = getPostBody(m);
+					long duration = System.currentTimeMillis() - now;
+					if (LOG.isLoggable(INFO))
+						LOG.info("Loading post took " + duration + " ms");
+					handler.onResult(new BlogPostItem(groupId, header, body));
+				} catch (DbException e) {
+					if (LOG.isLoggable(WARNING))
+						LOG.log(WARNING, e.toString(), e);
+					handler.onException(e);
+				}
+			}
+		});
+	}
+
+	private BlogPostHeader getPostHeader(MessageId m) throws DbException {
+		BlogPostHeader header = headerCache.get(m);
+		if (header == null) {
+			header = blogManager.getPostHeader(m);
+			headerCache.put(m, header);
 		}
-		return null;
+		return header;
 	}
 
-	@Override
-	@Nullable
-	public MessageId getBlogPostId(int position) {
-		if (posts == null) return null;
-		int i = 0;
-		for (BlogPostItem post : posts) {
-			if (i == position) return post.getId();
-			i++;
+	private byte[] getPostBody(MessageId m) throws DbException {
+		byte[] body = bodyCache.get(m);
+		if (body == null) {
+			body = blogManager.getPostBody(m);
+			if (body != null) bodyCache.put(m, body);
 		}
-		return null;
+		return body;
 	}
 
 	@Override
-	public void canDeleteBlog(final GroupId g,
-			final ResultExceptionHandler<Boolean, DbException> resultHandler) {
+	public void canDeleteBlog(
+			final ResultExceptionHandler<Boolean, DbException> handler) {
+		if (groupId == null) throw new IllegalStateException();
 		runOnDbThread(new Runnable() {
 			@Override
 			public void run() {
-				if (groupId == null) {
-					resultHandler.onResult(false);
-					return;
-				}
 				try {
-					resultHandler.onResult(blogManager.canBeRemoved(groupId));
+					handler.onResult(blogManager.canBeRemoved(groupId));
 				} catch (DbException e) {
 					if (LOG.isLoggable(WARNING))
 						LOG.log(WARNING, e.toString(), e);
-					resultHandler.onException(e);
+					handler.onException(e);
 				}
 			}
 		});
 	}
 
 	@Override
-	public void deleteBlog(final ResultHandler<Boolean> resultHandler) {
+	public void deleteBlog(
+			final ResultExceptionHandler<Void, DbException> handler) {
+		if (groupId == null) throw new IllegalStateException();
 		runOnDbThread(new Runnable() {
 			@Override
 			public void run() {
-				if (groupId == null) {
-					resultHandler.onResult(false);
-					return;
-				}
 				try {
 					Blog b = blogManager.getBlog(groupId);
 					blogManager.removeBlog(b);
-					resultHandler.onResult(true);
+					handler.onResult(null);
 				} catch (DbException e) {
 					if (LOG.isLoggable(WARNING))
 						LOG.log(WARNING, e.toString(), e);
-					resultHandler.onResult(false);
+					handler.onException(e);
 				}
 			}
 		});
diff --git a/briar-android/src/org/briarproject/android/blogs/BlogFragment.java b/briar-android/src/org/briarproject/android/blogs/BlogFragment.java
index 0bd866284320f3347e6d576757e228305dc0aeb2..143fc1b10d0a3dca946783c4a0ccb7c6ed204c79 100644
--- a/briar-android/src/org/briarproject/android/blogs/BlogFragment.java
+++ b/briar-android/src/org/briarproject/android/blogs/BlogFragment.java
@@ -22,11 +22,11 @@ import org.briarproject.android.ActivityComponent;
 import org.briarproject.android.blogs.BlogController.BlogPostListener;
 import org.briarproject.android.blogs.BlogPostAdapter.OnBlogPostClickListener;
 import org.briarproject.android.controller.handler.UiResultExceptionHandler;
-import org.briarproject.android.controller.handler.UiResultHandler;
 import org.briarproject.android.fragment.BaseFragment;
 import org.briarproject.android.sharing.ShareBlogActivity;
 import org.briarproject.android.sharing.SharingStatusBlogActivity;
 import org.briarproject.android.util.BriarRecyclerView;
+import org.briarproject.api.blogs.BlogPostHeader;
 import org.briarproject.api.db.DbException;
 import org.briarproject.api.sync.GroupId;
 
@@ -59,7 +59,7 @@ public class BlogFragment extends BaseFragment implements BlogPostListener {
 	private boolean myBlog;
 	private BlogPostAdapter adapter;
 	private BriarRecyclerView list;
-	private MenuItem deleteButton = null;
+	private MenuItem deleteButton;
 
 	static BlogFragment newInstance(GroupId groupId, String name,
 			boolean myBlog, boolean isNew) {
@@ -85,7 +85,7 @@ public class BlogFragment extends BaseFragment implements BlogPostListener {
 
 		Bundle args = getArguments();
 		byte[] b = args.getByteArray(GROUP_ID);
-		if (b == null) throw new IllegalStateException("No Group found.");
+		if (b == null) throw new IllegalStateException("No group ID in args");
 		groupId = new GroupId(b);
 		blogName = args.getString(BLOG_NAME);
 		myBlog = args.getBoolean(IS_MY_BLOG);
@@ -98,6 +98,7 @@ public class BlogFragment extends BaseFragment implements BlogPostListener {
 		list = (BriarRecyclerView) v.findViewById(R.id.postList);
 		list.setLayoutManager(new LinearLayoutManager(getActivity()));
 		list.setAdapter(adapter);
+		list.showProgressBar();
 		if (myBlog) {
 			list.setEmptyText(
 					getString(R.string.blogs_my_blogs_blog_empty_state));
@@ -126,13 +127,13 @@ public class BlogFragment extends BaseFragment implements BlogPostListener {
 	@Override
 	public void onStart() {
 		super.onStart();
-		loadData(false);
 		if (!myBlog) checkIfBlogCanBeDeleted();
 	}
 
 	@Override
 	public void onResume() {
 		super.onResume();
+		loadBlogPosts(false);
 		list.startPeriodicUpdate();
 	}
 
@@ -208,41 +209,49 @@ public class BlogFragment extends BaseFragment implements BlogPostListener {
 	}
 
 	@Override
-	public void onBlogPostAdded(BlogPostItem post, boolean local) {
-		adapter.add(post);
-		if (local) list.scrollToPosition(0);
+	public void onBlogPostAdded(BlogPostHeader header, final boolean local) {
+		blogController.loadBlogPost(header,
+				new UiResultExceptionHandler<BlogPostItem, DbException>(
+						getActivity()) {
+					@Override
+					public void onResultUi(BlogPostItem post) {
+						adapter.add(post);
+						if (local) list.scrollToPosition(0);
+					}
+
+					@Override
+					public void onExceptionUi(DbException exception) {
+						// TODO: Decide how to handle errors in the UI
+						getActivity().finish();
+					}
+				}
+		);
 	}
 
-	private void loadData(final boolean reload) {
-		blogController.loadBlog(groupId, reload,
-				new UiResultHandler<Boolean>(getActivity()) {
+	void loadBlogPosts(final boolean reload) {
+		blogController.loadBlogPosts(
+				new UiResultExceptionHandler<Collection<BlogPostItem>, DbException>(
+						getActivity()) {
 					@Override
-					public void onResultUi(Boolean result) {
-						if (result) {
-							Collection<BlogPostItem> posts =
-									blogController.getBlogPosts();
-							if (posts.size() > 0) {
-								adapter.addAll(posts);
-								if (reload) list.scrollToPosition(0);
-							} else {
-								list.showData();
-							}
+					public void onResultUi(Collection<BlogPostItem> posts) {
+						if (posts.size() > 0) {
+							adapter.addAll(posts);
+							if (reload) list.scrollToPosition(0);
 						} else {
-							Toast.makeText(getActivity(),
-									R.string.blogs_blog_failed_to_load,
-									LENGTH_SHORT).show();
-							getActivity().supportFinishAfterTransition();
+							list.showData();
 						}
 					}
-				});
-	}
 
-	void reload() {
-		loadData(true);
+					@Override
+					public void onExceptionUi(DbException exception) {
+						// TODO: Decide how to handle errors in the UI
+						getActivity().finish();
+					}
+				});
 	}
 
 	private void checkIfBlogCanBeDeleted() {
-		blogController.canDeleteBlog(groupId,
+		blogController.canDeleteBlog(
 				new UiResultExceptionHandler<Boolean, DbException>(
 						getActivity()) {
 					@Override
@@ -251,9 +260,11 @@ public class BlogFragment extends BaseFragment implements BlogPostListener {
 							showDeleteButton();
 						}
 					}
+
 					@Override
 					public void onExceptionUi(DbException exception) {
-						// nothing to do here, delete button is already hidden
+						// TODO: Decide how to handle errors in the UI
+						getActivity().finish();
 					}
 				});
 	}
@@ -290,15 +301,20 @@ public class BlogFragment extends BaseFragment implements BlogPostListener {
 
 	private void deleteBlog() {
 		blogController.deleteBlog(
-				new UiResultHandler<Boolean>(getActivity()) {
+				new UiResultExceptionHandler<Void, DbException>(getActivity()) {
 					@Override
-					public void onResultUi(Boolean result) {
-						if (!result) return;
+					public void onResultUi(Void result) {
 						Toast.makeText(getActivity(),
 								R.string.blogs_blog_removed, LENGTH_SHORT)
 								.show();
 						getActivity().supportFinishAfterTransition();
 					}
+
+					@Override
+					public void onExceptionUi(DbException exception) {
+						// TODO: Decide how to handle errors in the UI
+						getActivity().finish();
+					}
 				});
 	}
 
diff --git a/briar-android/src/org/briarproject/android/blogs/BlogPostAdapter.java b/briar-android/src/org/briarproject/android/blogs/BlogPostAdapter.java
index a0060a1e73783cdd449bd8721107682c2c118cc8..44d971c83f0d23d5dcdaacb22835fba44cc89b02 100644
--- a/briar-android/src/org/briarproject/android/blogs/BlogPostAdapter.java
+++ b/briar-android/src/org/briarproject/android/blogs/BlogPostAdapter.java
@@ -3,11 +3,9 @@ package org.briarproject.android.blogs;
 import android.content.Context;
 import android.support.v7.util.SortedList;
 import android.support.v7.widget.RecyclerView;
-import android.text.format.DateUtils;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
-import android.widget.ImageView;
 import android.widget.TextView;
 
 import org.briarproject.R;
@@ -97,7 +95,7 @@ class BlogPostAdapter extends
 		ui.layout.setOnClickListener(new View.OnClickListener() {
 			@Override
 			public void onClick(View v) {
-				listener.onBlogPostClick(ui.getAdapterPosition(), post);
+				listener.onBlogPostClick(post);
 			}
 		});
 	}
@@ -132,15 +130,12 @@ class BlogPostAdapter extends
 	}
 
 	static class BlogPostHolder extends RecyclerView.ViewHolder {
+
 		private final ViewGroup layout;
 		private final CircleImageView avatar;
 		private final TextView author;
 		private final TrustIndicatorView trust;
 		private final TextView date;
-		private final TextView unread;
-		private final ImageView chat;
-		private final ImageView comment;
-		private final TextView title;
 		private final TextView body;
 
 		BlogPostHolder(View v) {
@@ -151,16 +146,12 @@ class BlogPostAdapter extends
 			author = (TextView) v.findViewById(R.id.authorName);
 			trust = (TrustIndicatorView) v.findViewById(R.id.trustIndicator);
 			date = (TextView) v.findViewById(R.id.dateView);
-			unread = (TextView) v.findViewById(R.id.newView);
-			chat = (ImageView) v.findViewById(R.id.chatView);
-			comment = (ImageView) v.findViewById(R.id.commentView);
-			title = (TextView) v.findViewById(R.id.titleView);
 			body = (TextView) v.findViewById(R.id.bodyView);
 		}
 	}
 
 	interface OnBlogPostClickListener {
-		void onBlogPostClick(int position, BlogPostItem post);
+		void onBlogPostClick(BlogPostItem post);
 	}
 
 }
diff --git a/briar-android/src/org/briarproject/android/blogs/BlogPostFragment.java b/briar-android/src/org/briarproject/android/blogs/BlogPostFragment.java
index 89670292e36691c4428fd0b4ed5c683439da036c..9c36a73a50c54d3f6276fa30bfe7deb0408ccdcc 100644
--- a/briar-android/src/org/briarproject/android/blogs/BlogPostFragment.java
+++ b/briar-android/src/org/briarproject/android/blogs/BlogPostFragment.java
@@ -1,6 +1,5 @@
 package org.briarproject.android.blogs;
 
-import android.app.Activity;
 import android.content.Context;
 import android.os.Bundle;
 import android.support.annotation.Nullable;
@@ -10,16 +9,15 @@ import android.view.View;
 import android.view.ViewGroup;
 import android.widget.ImageView;
 import android.widget.TextView;
-import android.widget.Toast;
 
 import org.briarproject.R;
 import org.briarproject.android.ActivityComponent;
-import org.briarproject.android.controller.handler.UiResultHandler;
+import org.briarproject.android.controller.handler.UiResultExceptionHandler;
 import org.briarproject.android.fragment.BaseFragment;
 import org.briarproject.android.util.AndroidUtils;
 import org.briarproject.android.util.TrustIndicatorView;
+import org.briarproject.api.db.DbException;
 import org.briarproject.api.identity.Author;
-import org.briarproject.api.sync.GroupId;
 import org.briarproject.api.sync.MessageId;
 import org.briarproject.util.StringUtils;
 
@@ -30,31 +28,27 @@ import javax.inject.Inject;
 import im.delight.android.identicons.IdenticonDrawable;
 
 import static android.view.View.GONE;
-import static android.widget.Toast.LENGTH_SHORT;
-import static org.briarproject.android.BriarActivity.GROUP_ID;
 import static org.briarproject.android.util.AndroidUtils.MIN_RESOLUTION;
 
 public class BlogPostFragment extends BaseFragment {
 
 	public final static String TAG = BlogPostFragment.class.getName();
-	private static final Logger LOG = Logger.getLogger(TAG);
 
-	private final static String BLOG_POST_ID = "briar.BLOG_NAME";
+	private static final Logger LOG = Logger.getLogger(TAG);
+	private static final String BLOG_POST_ID = "briar.BLOG_POST_ID";
 
-	private GroupId groupId;
 	private MessageId postId;
 	private BlogPostViewHolder ui;
-	private BlogPostItem post = null;
-	private Runnable refresher = null;
+	private BlogPostItem post;
+	private Runnable refresher;
 
 	@Inject
 	BlogController blogController;
 
-	static BlogPostFragment newInstance(GroupId groupId, MessageId postId) {
+	static BlogPostFragment newInstance(MessageId postId) {
 		BlogPostFragment f = new BlogPostFragment();
 
 		Bundle bundle = new Bundle();
-		bundle.putByteArray(GROUP_ID, groupId.getBytes());
 		bundle.putByteArray(BLOG_POST_ID, postId.getBytes());
 
 		f.setArguments(bundle);
@@ -65,15 +59,11 @@ public class BlogPostFragment extends BaseFragment {
 	@Override
 	public View onCreateView(LayoutInflater inflater, ViewGroup container,
 			Bundle savedInstanceState) {
-
 		setHasOptionsMenu(true);
 
-		byte[] b = getArguments().getByteArray(GROUP_ID);
-		if (b == null) throw new IllegalStateException("No Group found.");
-		groupId = new GroupId(b);
-		byte[] p = getArguments().getByteArray(BLOG_POST_ID);
-		if (p == null) throw new IllegalStateException("No MessageId found.");
-		postId = new MessageId(p);
+		byte[] b = getArguments().getByteArray(BLOG_POST_ID);
+		if (b == null) throw new IllegalStateException("No post ID in args");
+		postId = new MessageId(b);
 
 		View v = inflater.inflate(R.layout.fragment_blog_post, container,
 				false);
@@ -89,21 +79,20 @@ public class BlogPostFragment extends BaseFragment {
 	@Override
 	public void onStart() {
 		super.onStart();
-		blogController.loadBlog(groupId, false,
-				new UiResultHandler<Boolean>((Activity) listener) {
+		blogController.loadBlogPost(postId,
+				new UiResultExceptionHandler<BlogPostItem, DbException>(
+						getActivity()) {
 					@Override
-					public void onResultUi(Boolean result) {
+					public void onResultUi(BlogPostItem post) {
 						listener.hideLoadingScreen();
-						if (result) {
-							post = blogController.getBlogPost(postId);
-							if (post != null) {
-								bind();
-							}
-						} else {
-							Toast.makeText(getActivity(),
-									R.string.blogs_blog_post_failed_to_load,
-									LENGTH_SHORT).show();
-						}
+						BlogPostFragment.this.post = post;
+						bind();
+					}
+
+					@Override
+					public void onExceptionUi(DbException exception) {
+						// TODO: Decide how to handle errors in the UI
+						getActivity().finish();
 					}
 				});
 	}
@@ -157,14 +146,15 @@ public class BlogPostFragment extends BaseFragment {
 	}
 
 	private static class BlogPostViewHolder {
-		private ImageView avatar;
-		private TextView authorName;
-		private TrustIndicatorView trust;
-		private TextView date;
-		private TextView title;
-		private TextView body;
-
-		BlogPostViewHolder(View v) {
+
+		private final ImageView avatar;
+		private final TextView authorName;
+		private final TrustIndicatorView trust;
+		private final TextView date;
+		private final TextView title;
+		private final TextView body;
+
+		private BlogPostViewHolder(View v) {
 			avatar = (ImageView) v.findViewById(R.id.avatar);
 			authorName = (TextView) v.findViewById(R.id.authorName);
 			trust = (TrustIndicatorView) v.findViewById(R.id.trustIndicator);
diff --git a/briar-android/src/org/briarproject/android/blogs/BlogPostItem.java b/briar-android/src/org/briarproject/android/blogs/BlogPostItem.java
index 10a76b511f92cad3231ef2894af33a4e8c6f7d21..c3aa850af251b88bb5b39092f014d3c57b33cdc8 100644
--- a/briar-android/src/org/briarproject/android/blogs/BlogPostItem.java
+++ b/briar-android/src/org/briarproject/android/blogs/BlogPostItem.java
@@ -1,6 +1,7 @@
 package org.briarproject.android.blogs;
 
 import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
 
 import org.briarproject.api.blogs.BlogPostHeader;
 import org.briarproject.api.identity.Author;
@@ -31,14 +32,11 @@ class BlogPostItem implements Comparable<BlogPostItem> {
 		return groupId;
 	}
 
+	@Nullable
 	public String getTitle() {
 		return header.getTitle();
 	}
 
-	public byte[] getBody() {
-		return body;
-	}
-
 	public long getTimestamp() {
 		return header.getTimestamp();
 	}
@@ -55,18 +53,22 @@ class BlogPostItem implements Comparable<BlogPostItem> {
 		return header.getAuthorStatus();
 	}
 
-	public void setRead(boolean read) {
-		this.read = read;
+	public byte[] getBody() {
+		return body;
 	}
 
 	public boolean isRead() {
 		return read;
 	}
 
+	public void setRead(boolean read) {
+		this.read = read;
+	}
+
 	@Override
 	public int compareTo(@NonNull BlogPostItem other) {
 		if (this == other) return 0;
-		// The blog with the newest message comes first
+		// The newest post comes first
 		long aTime = getTimeReceived(), bTime = other.getTimeReceived();
 		if (aTime > bTime) return -1;
 		if (aTime < bTime) return 1;
diff --git a/briar-android/src/org/briarproject/android/blogs/FeedFragment.java b/briar-android/src/org/briarproject/android/blogs/FeedFragment.java
index a337f5a2f6b411e0c42bf40f81a1abd1d679ce89..a83f9beb056b87d5d41ba441c63f08e3c5d0e2c7 100644
--- a/briar-android/src/org/briarproject/android/blogs/FeedFragment.java
+++ b/briar-android/src/org/briarproject/android/blogs/FeedFragment.java
@@ -182,7 +182,7 @@ public class FeedFragment extends BaseFragment implements
 	}
 
 	@Override
-	public void onBlogPostClick(int position, BlogPostItem post) {
+	public void onBlogPostClick(BlogPostItem post) {
 		byte[] groupId = post.getGroupId().getBytes();
 		String name = getString(R.string.blogs_personal_blog,
 				post.getAuthor().getName());
diff --git a/briar-android/src/org/briarproject/android/controller/handler/UiResultExceptionHandler.java b/briar-android/src/org/briarproject/android/controller/handler/UiResultExceptionHandler.java
index 89659b71d09965fbe1767360e7d99d30a0fc855a..ba2eab195ade5bd7c2ded6a9445041b025d70aec 100644
--- a/briar-android/src/org/briarproject/android/controller/handler/UiResultExceptionHandler.java
+++ b/briar-android/src/org/briarproject/android/controller/handler/UiResultExceptionHandler.java
@@ -1,6 +1,7 @@
 package org.briarproject.android.controller.handler;
 
 import android.app.Activity;
+import android.support.annotation.UiThread;
 
 public abstract class UiResultExceptionHandler<R, E extends Exception>
 		implements ResultExceptionHandler<R, E> {
@@ -31,7 +32,9 @@ public abstract class UiResultExceptionHandler<R, E extends Exception>
 		});
 	}
 
+	@UiThread
 	public abstract void onResultUi(R result);
 
+	@UiThread
 	public abstract void onExceptionUi(E exception);
 }
diff --git a/briar-android/src/org/briarproject/android/controller/handler/UiResultHandler.java b/briar-android/src/org/briarproject/android/controller/handler/UiResultHandler.java
index 5aa32e25a7cae4fef97910fe83c054980c1ffab8..0616b82a2bf2ad263575803c79f1d765a4826315 100644
--- a/briar-android/src/org/briarproject/android/controller/handler/UiResultHandler.java
+++ b/briar-android/src/org/briarproject/android/controller/handler/UiResultHandler.java
@@ -1,6 +1,7 @@
 package org.briarproject.android.controller.handler;
 
 import android.app.Activity;
+import android.support.annotation.UiThread;
 
 public abstract class UiResultHandler<R> implements ResultHandler<R> {
 
@@ -20,5 +21,6 @@ public abstract class UiResultHandler<R> implements ResultHandler<R> {
 		});
 	}
 
+	@UiThread
 	public abstract void onResultUi(R result);
 }
diff --git a/briar-api/src/org/briarproject/api/blogs/BlogManager.java b/briar-api/src/org/briarproject/api/blogs/BlogManager.java
index f0f4bc0a12528f17933167a5029f44b1b4570c5b..1d4f0eaeebee2cf20fc018851c8aa1b6b6fe8f23 100644
--- a/briar-api/src/org/briarproject/api/blogs/BlogManager.java
+++ b/briar-api/src/org/briarproject/api/blogs/BlogManager.java
@@ -7,7 +7,6 @@ import org.briarproject.api.identity.LocalAuthor;
 import org.briarproject.api.sync.ClientId;
 import org.briarproject.api.sync.GroupId;
 import org.briarproject.api.sync.MessageId;
-import org.jetbrains.annotations.Nullable;
 
 import java.util.Collection;
 
@@ -47,8 +46,10 @@ public interface BlogManager {
 	/** Returns all blogs to which the user subscribes. */
 	Collection<Blog> getBlogs() throws DbException;
 
+	/** Returns the header of the blog post with the given ID. */
+	BlogPostHeader getPostHeader(MessageId m) throws DbException;
+
 	/** Returns the body of the blog post with the given ID. */
-	@Nullable
 	byte[] getPostBody(MessageId m) throws DbException;
 
 	/** Returns the headers of all posts in the given blog. */
diff --git a/briar-core/src/org/briarproject/blogs/BlogManagerImpl.java b/briar-core/src/org/briarproject/blogs/BlogManagerImpl.java
index c58a2d0d84453ef6f325c22b3f0bca2e7743dc40..097e594f46c53dc8ad614cca33475b610b94678e 100644
--- a/briar-core/src/org/briarproject/blogs/BlogManagerImpl.java
+++ b/briar-core/src/org/briarproject/blogs/BlogManagerImpl.java
@@ -33,7 +33,6 @@ import org.briarproject.api.sync.Message;
 import org.briarproject.api.sync.MessageId;
 import org.briarproject.clients.BdfIncomingMessageHook;
 import org.briarproject.util.StringUtils;
-import org.jetbrains.annotations.Nullable;
 
 import java.util.ArrayList;
 import java.util.Collection;
@@ -341,10 +340,26 @@ class BlogManagerImpl extends BdfIncomingMessageHook implements BlogManager,
 	}
 
 	@Override
-	@Nullable
+	public BlogPostHeader getPostHeader(MessageId m) throws DbException {
+		Transaction txn = db.startTransaction(true);
+		try {
+			BdfDictionary meta =
+					clientHelper.getMessageMetadataAsDictionary(txn, m);
+			BlogPostHeader h = getPostHeaderFromMetadata(txn, m, meta);
+			txn.setComplete();
+			return h;
+		} catch (FormatException e) {
+			throw new DbException(e);
+		} finally {
+			db.endTransaction(txn);
+		}
+	}
+
+	@Override
 	public byte[] getPostBody(MessageId m) throws DbException {
 		try {
 			BdfList message = clientHelper.getMessageAsList(m);
+			if (message == null) throw new DbException();
 			return getPostBody(message);
 		} catch (FormatException e) {
 			throw new DbException(e);
@@ -362,24 +377,23 @@ class BlogManagerImpl extends BdfIncomingMessageHook implements BlogManager,
 	public Collection<BlogPostHeader> getPostHeaders(GroupId g)
 			throws DbException {
 
-		Map<MessageId, BdfDictionary> metadata;
+		Transaction txn = db.startTransaction(true);
 		try {
-			metadata = clientHelper.getMessageMetadataAsDictionary(g);
-		} catch (FormatException e) {
-			throw new DbException(e);
-		}
-		Collection<BlogPostHeader> headers = new ArrayList<BlogPostHeader>();
-		for (Entry<MessageId, BdfDictionary> entry : metadata.entrySet()) {
-			try {
-				BdfDictionary meta = entry.getValue();
-				BlogPostHeader h =
-						getPostHeaderFromMetadata(null, entry.getKey(), meta);
+			Map<MessageId, BdfDictionary> metadata =
+					clientHelper.getMessageMetadataAsDictionary(txn, g);
+			List<BlogPostHeader> headers = new ArrayList<BlogPostHeader>();
+			for (Entry<MessageId, BdfDictionary> entry : metadata.entrySet()) {
+				BlogPostHeader h = getPostHeaderFromMetadata(txn,
+						entry.getKey(), entry.getValue());
 				headers.add(h);
-			} catch (FormatException e) {
-				throw new DbException(e);
 			}
+			txn.setComplete();
+			return headers;
+		} catch (FormatException e) {
+			throw new DbException(e);
+		} finally {
+			db.endTransaction(txn);
 		}
-		return headers;
 	}
 
 	@Override
@@ -404,7 +418,7 @@ class BlogManagerImpl extends BdfIncomingMessageHook implements BlogManager,
 		return d.getString(KEY_DESCRIPTION, "");
 	}
 
-	private BlogPostHeader getPostHeaderFromMetadata(@Nullable Transaction txn,
+	private BlogPostHeader getPostHeaderFromMetadata(Transaction txn,
 			MessageId id, BdfDictionary meta)
 			throws DbException, FormatException {
 
@@ -418,11 +432,7 @@ class BlogManagerImpl extends BdfIncomingMessageHook implements BlogManager,
 		byte[] publicKey = d.getRaw(KEY_PUBLIC_KEY);
 		Author author = new Author(authorId, name, publicKey);
 		Status authorStatus;
-		if (txn == null)
-			authorStatus = identityManager.getAuthorStatus(authorId);
-		else {
-			authorStatus = identityManager.getAuthorStatus(txn, authorId);
-		}
+		authorStatus = identityManager.getAuthorStatus(txn, authorId);
 
 		String contentType = meta.getString(KEY_CONTENT_TYPE);
 		boolean read = meta.getBoolean(KEY_READ);