improved database cache trimming

close #405
This commit is contained in:
Mariotaku Lee 2016-02-25 22:25:03 +08:00
parent 1e8c6233d3
commit 8069111e51
5 changed files with 151 additions and 31 deletions

View File

@ -64,6 +64,7 @@ public interface TwidereDataStore {
CachedHashtags.CONTENT_URI, CachedTrends.Local.CONTENT_URI}; CachedHashtags.CONTENT_URI, CachedTrends.Local.CONTENT_URI};
Uri[] DIRECT_MESSAGES_URIS = new Uri[]{DirectMessages.Inbox.CONTENT_URI, Uri[] DIRECT_MESSAGES_URIS = new Uri[]{DirectMessages.Inbox.CONTENT_URI,
DirectMessages.Outbox.CONTENT_URI}; DirectMessages.Outbox.CONTENT_URI};
Uri[] ACTIVITIES_URIS = new Uri[]{Activities.AboutMe.CONTENT_URI};
interface InsertedDateColumns { interface InsertedDateColumns {
String INSERTED_DATE = "inserted_date"; String INSERTED_DATE = "inserted_date";

View File

@ -111,7 +111,7 @@ dependencies {
compile 'org.attoparser:attoparser:1.4.0.RELEASE' compile 'org.attoparser:attoparser:1.4.0.RELEASE'
compile 'com.github.mariotaku.MediaViewerLibrary:base:0.9.10' compile 'com.github.mariotaku.MediaViewerLibrary:base:0.9.10'
compile 'com.github.mariotaku.MediaViewerLibrary:subsample-image-view:0.9.10' compile 'com.github.mariotaku.MediaViewerLibrary:subsample-image-view:0.9.10'
compile 'com.github.mariotaku:SQLiteQB:0.9.4' compile 'com.github.mariotaku.SQLiteQB:library:0.9.5-SNAPSHOT'
compile 'com.github.mariotaku.ObjectCursor:core:0.9.4' compile 'com.github.mariotaku.ObjectCursor:core:0.9.4'
compile project(':twidere.component.common') compile project(':twidere.component.common')
compile project(':twidere.component.nyan') compile project(':twidere.component.nyan')

View File

@ -82,6 +82,7 @@ import org.mariotaku.twidere.model.ParcelableAccount;
import org.mariotaku.twidere.model.SupportTabSpec; import org.mariotaku.twidere.model.SupportTabSpec;
import org.mariotaku.twidere.model.message.TaskStateChangedEvent; import org.mariotaku.twidere.model.message.TaskStateChangedEvent;
import org.mariotaku.twidere.model.message.UnreadCountUpdatedEvent; import org.mariotaku.twidere.model.message.UnreadCountUpdatedEvent;
import org.mariotaku.twidere.provider.TwidereDataStore;
import org.mariotaku.twidere.provider.TwidereDataStore.Accounts; import org.mariotaku.twidere.provider.TwidereDataStore.Accounts;
import org.mariotaku.twidere.provider.TwidereDataStore.Activities; import org.mariotaku.twidere.provider.TwidereDataStore.Activities;
import org.mariotaku.twidere.provider.TwidereDataStore.Statuses; import org.mariotaku.twidere.provider.TwidereDataStore.Statuses;
@ -917,7 +918,8 @@ public class HomeActivity extends BaseAppCompatActivity implements OnClickListen
} }
} }
private static class UpdateUnreadCountTask extends AsyncTask<Object, Object, SparseIntArray> { private static class UpdateUnreadCountTask extends AsyncTask<Object, UpdateUnreadCountTask.TabBadge,
SparseIntArray> {
private final Context mContext; private final Context mContext;
private final ReadStateManager mReadStateManager; private final ReadStateManager mReadStateManager;
private final TabPagerIndicator mIndicator; private final TabPagerIndicator mIndicator;
@ -934,17 +936,22 @@ public class HomeActivity extends BaseAppCompatActivity implements OnClickListen
@Override @Override
protected SparseIntArray doInBackground(final Object... params) { protected SparseIntArray doInBackground(final Object... params) {
final SparseIntArray result = new SparseIntArray(); final SparseIntArray result = new SparseIntArray();
for (int i = 0, count = mTabs.size(); i < count; i++) { for (int i = 0, j = mTabs.size(); i < j; i++) {
SupportTabSpec spec = mTabs.get(i); SupportTabSpec spec = mTabs.get(i);
if (spec.type == null) continue; if (spec.type == null) {
publishProgress(new TabBadge(i, -1));
continue;
}
switch (spec.type) { switch (spec.type) {
case CustomTabType.HOME_TIMELINE: { case CustomTabType.HOME_TIMELINE: {
final long[] accountIds = Utils.getAccountIds(spec.args); final long[] accountIds = Utils.getAccountIds(spec.args);
final String tagWithAccounts = Utils.getReadPositionTagWithAccounts(mContext, final String tagWithAccounts = Utils.getReadPositionTagWithAccounts(mContext,
true, spec.tag, accountIds); true, spec.tag, accountIds);
final long position = mReadStateManager.getPosition(tagWithAccounts); final long position = mReadStateManager.getPosition(tagWithAccounts);
result.put(i, DataStoreUtils.getStatusesCount(mContext, Statuses.CONTENT_URI, final int count = DataStoreUtils.getStatusesCount(mContext, Statuses.CONTENT_URI,
position, accountIds)); position, accountIds);
result.put(i, count);
publishProgress(new TabBadge(i, count));
break; break;
} }
case CustomTabType.NOTIFICATIONS_TIMELINE: { case CustomTabType.NOTIFICATIONS_TIMELINE: {
@ -955,20 +962,29 @@ public class HomeActivity extends BaseAppCompatActivity implements OnClickListen
Expression extraWhere = null; Expression extraWhere = null;
String[] extraWhereArgs = null; String[] extraWhereArgs = null;
boolean followingOnly = false;
if (spec.args != null) { if (spec.args != null) {
Bundle extras = spec.args.getBundle(EXTRA_EXTRAS); Bundle extras = spec.args.getBundle(EXTRA_EXTRAS);
if (extras != null && extras.getBoolean(EXTRA_MENTIONS_ONLY)) { if (extras != null) {
if (extras.getBoolean(EXTRA_MENTIONS_ONLY)) {
extraWhere = Expression.inArgs(Activities.ACTION, 3); extraWhere = Expression.inArgs(Activities.ACTION, 3);
extraWhereArgs = new String[]{Activity.Action.MENTION, extraWhereArgs = new String[]{Activity.Action.MENTION,
Activity.Action.REPLY, Activity.Action.QUOTE}; Activity.Action.REPLY, Activity.Action.QUOTE};
} }
if (extras.getBoolean(EXTRA_MY_FOLLOWING_ONLY)) {
followingOnly = true;
} }
result.put(i, DataStoreUtils.getActivitiesCount(mContext, }
}
final int count = DataStoreUtils.getActivitiesCount(mContext,
Activities.AboutMe.CONTENT_URI, extraWhere, extraWhereArgs, Activities.AboutMe.CONTENT_URI, extraWhere, extraWhereArgs,
position, accountIds)); position, followingOnly, accountIds);
publishProgress(new TabBadge(i, count));
result.put(i, count);
break; break;
} }
case CustomTabType.DIRECT_MESSAGES: { default: {
publishProgress(new TabBadge(i, -1));
break; break;
} }
} }
@ -984,6 +1000,22 @@ public class HomeActivity extends BaseAppCompatActivity implements OnClickListen
} }
} }
@Override
protected void onProgressUpdate(TabBadge... values) {
for (TabBadge value : values) {
mIndicator.setBadge(value.index, value.count);
}
}
static class TabBadge {
int index;
int count;
public TabBadge(int index, int count) {
this.index = index;
this.count = count;
}
}
} }

View File

@ -0,0 +1,24 @@
package org.mariotaku.twidere.model;
import com.bluelinelabs.logansquare.annotation.JsonField;
import com.bluelinelabs.logansquare.annotation.JsonObject;
import com.hannesdorfmann.parcelableplease.annotation.ParcelableThisPlease;
import org.mariotaku.library.objectcursor.annotation.CursorField;
import org.mariotaku.twidere.provider.TwidereDataStore;
/**
* Created by mariotaku on 16/2/25.
*
* This is a subset of {@link ParcelableUser}, which only has two fields, id and is_following,
* to minimize processing speed.
*/
@JsonObject
public class UserFollowState {
@JsonField(name = "id")
public long id;
@JsonField(name = "is_following")
public boolean is_following;
}

View File

@ -29,6 +29,9 @@ import android.provider.BaseColumns;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
import android.support.annotation.Nullable; import android.support.annotation.Nullable;
import android.support.v4.util.LongSparseArray; import android.support.v4.util.LongSparseArray;
import android.text.TextUtils;
import com.bluelinelabs.logansquare.JsonMapper;
import org.apache.commons.lang3.ArrayUtils; import org.apache.commons.lang3.ArrayUtils;
import org.mariotaku.sqliteqb.library.ArgsArray; import org.mariotaku.sqliteqb.library.ArgsArray;
@ -48,6 +51,7 @@ import org.mariotaku.twidere.model.ParcelableAccount;
import org.mariotaku.twidere.model.ParcelableAccountCursorIndices; import org.mariotaku.twidere.model.ParcelableAccountCursorIndices;
import org.mariotaku.twidere.model.ParcelableCredentials; import org.mariotaku.twidere.model.ParcelableCredentials;
import org.mariotaku.twidere.model.ParcelableCredentialsCursorIndices; import org.mariotaku.twidere.model.ParcelableCredentialsCursorIndices;
import org.mariotaku.twidere.model.UserFollowState;
import org.mariotaku.twidere.provider.TwidereDataStore; import org.mariotaku.twidere.provider.TwidereDataStore;
import org.mariotaku.twidere.provider.TwidereDataStore.Accounts; import org.mariotaku.twidere.provider.TwidereDataStore.Accounts;
import org.mariotaku.twidere.provider.TwidereDataStore.Activities; import org.mariotaku.twidere.provider.TwidereDataStore.Activities;
@ -72,12 +76,14 @@ import org.mariotaku.twidere.provider.TwidereDataStore.Suggestions;
import org.mariotaku.twidere.provider.TwidereDataStore.Tabs; import org.mariotaku.twidere.provider.TwidereDataStore.Tabs;
import org.mariotaku.twidere.provider.TwidereDataStore.UnreadCounts; import org.mariotaku.twidere.provider.TwidereDataStore.UnreadCounts;
import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import static android.text.TextUtils.isEmpty; import static android.text.TextUtils.isEmpty;
import static org.mariotaku.twidere.provider.TwidereDataStore.ACTIVITIES_URIS;
import static org.mariotaku.twidere.provider.TwidereDataStore.CACHE_URIS; import static org.mariotaku.twidere.provider.TwidereDataStore.CACHE_URIS;
import static org.mariotaku.twidere.provider.TwidereDataStore.DIRECT_MESSAGES_URIS; import static org.mariotaku.twidere.provider.TwidereDataStore.DIRECT_MESSAGES_URIS;
import static org.mariotaku.twidere.provider.TwidereDataStore.STATUSES_URIS; import static org.mariotaku.twidere.provider.TwidereDataStore.STATUSES_URIS;
@ -395,7 +401,7 @@ public class DataStoreUtils implements Constants {
public static int getActivitiesCount(final Context context, final Uri uri, public static int getActivitiesCount(final Context context, final Uri uri,
final Expression extraWhere, final String[] extraWhereArgs, final Expression extraWhere, final String[] extraWhereArgs,
final long sinceTimestamp, final long... accountIds) { final long sinceTimestamp, boolean followingOnly, final long... accountIds) {
if (context == null) return 0; if (context == null) return 0;
final RawItemArray idsIn; final RawItemArray idsIn;
if (accountIds == null || accountIds.length == 0 || (accountIds.length == 1 && accountIds[0] < 0)) { if (accountIds == null || accountIds.length == 0 || (accountIds.length == 1 && accountIds[0] < 0)) {
@ -414,6 +420,45 @@ public class DataStoreUtils implements Constants {
expressions[1] = Expression.greaterThan(Activities.TIMESTAMP, sinceTimestamp); expressions[1] = Expression.greaterThan(Activities.TIMESTAMP, sinceTimestamp);
expressions[2] = buildActivityFilterWhereClause(getTableNameByUri(uri), null); expressions[2] = buildActivityFilterWhereClause(getTableNameByUri(uri), null);
final Expression selection = Expression.and(expressions); final Expression selection = Expression.and(expressions);
// If followingOnly option is on, we have to iterate over items
if (followingOnly) {
final ContentResolver resolver = context.getContentResolver();
final String[] projection = new String[]{Activities.SOURCES};
final Cursor cur = resolver.query(uri, projection, selection.getSQL(), extraWhereArgs, null);
if (cur == null) return -1;
try {
final JsonMapper<UserFollowState> mapper;
try {
mapper = LoganSquareMapperFinder.mapperFor(UserFollowState.class);
} catch (LoganSquareMapperFinder.ClassLoaderDeadLockException e) {
return -1;
}
int total = 0;
cur.moveToFirst();
while (cur.isAfterLast()) {
final String string = cur.getString(0);
if (TextUtils.isEmpty(string)) continue;
boolean hasFollowing = false;
try {
for (UserFollowState state : mapper.parseList(string)) {
if (state.is_following) {
hasFollowing = true;
break;
}
}
} catch (IOException e) {
continue;
}
if (hasFollowing) {
total++;
}
cur.moveToNext();
}
return total;
} finally {
cur.close();
}
}
return queryCount(context, uri, selection.getSQL(), extraWhereArgs); return queryCount(context, uri, selection.getSQL(), extraWhereArgs);
} }
@ -648,24 +693,41 @@ public class DataStoreUtils implements Constants {
continue; continue;
} }
final String table = getTableNameByUri(uri); final String table = getTableNameByUri(uri);
final Expression account_where = new Expression(Statuses.ACCOUNT_ID + " = " + accountId); final Expression accountWhere = new Expression(Statuses.ACCOUNT_ID + " = " + accountId);
final SQLSelectQuery.Builder qb = new SQLSelectQuery.Builder(); final SQLSelectQuery.Builder qb = new SQLSelectQuery.Builder();
qb.select(new Column(Statuses._ID)).from(new Tables(table)); qb.select(new Column(Statuses._ID))
qb.where(Expression.equals(Statuses.ACCOUNT_ID, accountId)); .from(new Tables(table))
qb.orderBy(new OrderBy(Statuses.STATUS_ID, false)); .where(Expression.equals(Statuses.ACCOUNT_ID, accountId))
qb.limit(itemLimit); .orderBy(new OrderBy(Statuses.STATUS_ID, false))
final Expression where = Expression.and(Expression.notIn(new Column(Statuses._ID), qb.build()), account_where); .limit(itemLimit);
final Expression where = Expression.and(Expression.lesserThan(new Column(Statuses._ID),
SQLQueryBuilder.select(SQLFunctions.MIN(new Column(Statuses._ID))).from(qb.build()).build()), accountWhere);
resolver.delete(uri, where.getSQL(), null);
}
for (final Uri uri : ACTIVITIES_URIS) {
final String table = getTableNameByUri(uri);
final Expression accountWhere = new Expression(Activities.ACCOUNT_ID + " = " + accountId);
final SQLSelectQuery.Builder qb = new SQLSelectQuery.Builder();
qb.select(new Column(Activities._ID))
.from(new Tables(table))
.where(Expression.equals(Activities.ACCOUNT_ID, accountId))
.orderBy(new OrderBy(Activities.TIMESTAMP, false))
.limit(itemLimit);
final Expression where = Expression.and(Expression.lesserThan(new Column(Activities._ID),
SQLQueryBuilder.select(SQLFunctions.MIN(new Column(Activities._ID))).from(qb.build()).build()), accountWhere);
resolver.delete(uri, where.getSQL(), null); resolver.delete(uri, where.getSQL(), null);
} }
for (final Uri uri : DIRECT_MESSAGES_URIS) { for (final Uri uri : DIRECT_MESSAGES_URIS) {
final String table = getTableNameByUri(uri); final String table = getTableNameByUri(uri);
final Expression account_where = new Expression(DirectMessages.ACCOUNT_ID + " = " + accountId); final Expression accountWhere = new Expression(DirectMessages.ACCOUNT_ID + " = " + accountId);
final SQLSelectQuery.Builder qb = new SQLSelectQuery.Builder(); final SQLSelectQuery.Builder qb = new SQLSelectQuery.Builder();
qb.select(new Column(DirectMessages._ID)).from(new Tables(table)); qb.select(new Column(DirectMessages._ID))
qb.where(Expression.equals(DirectMessages.ACCOUNT_ID, accountId)); .from(new Tables(table))
qb.orderBy(new OrderBy(DirectMessages.MESSAGE_ID, false)); .where(Expression.equals(DirectMessages.ACCOUNT_ID, accountId))
qb.limit(itemLimit * 10); .orderBy(new OrderBy(DirectMessages.MESSAGE_ID, false))
final Expression where = Expression.and(Expression.notIn(new Column(DirectMessages._ID), qb.build()), account_where); .limit(itemLimit * 10);
final Expression where = Expression.and(Expression.lesserThan(new Column(DirectMessages._ID),
SQLQueryBuilder.select(SQLFunctions.MIN(new Column(DirectMessages._ID))).from(qb.build()).build()), accountWhere);
resolver.delete(uri, where.getSQL(), null); resolver.delete(uri, where.getSQL(), null);
} }
} }
@ -674,11 +736,12 @@ public class DataStoreUtils implements Constants {
final String table = getTableNameByUri(uri); final String table = getTableNameByUri(uri);
if (table == null) continue; if (table == null) continue;
final SQLSelectQuery.Builder qb = new SQLSelectQuery.Builder(); final SQLSelectQuery.Builder qb = new SQLSelectQuery.Builder();
qb.select(new Column(BaseColumns._ID)); qb.select(new Column(BaseColumns._ID))
qb.from(new Tables(table)); .from(new Tables(table))
qb.orderBy(new OrderBy(BaseColumns._ID, false)); .orderBy(new OrderBy(BaseColumns._ID, false))
qb.limit(itemLimit * 20); .limit(itemLimit * 20);
final Expression where = Expression.notIn(new Column(BaseColumns._ID), qb.build()); final Expression where = Expression.lesserThan(new Column(BaseColumns._ID),
SQLQueryBuilder.select(SQLFunctions.MIN(new Column(BaseColumns._ID))).from(qb.build()).build());
resolver.delete(uri, where.getSQL(), null); resolver.delete(uri, where.getSQL(), null);
} }
} }