Merge "Fix settings slice caching" into pi-dev

This commit is contained in:
Jason Monk
2018-04-26 12:46:20 +00:00
committed by Android (Google) Code Review
2 changed files with 74 additions and 6 deletions

View File

@@ -29,6 +29,7 @@ import android.provider.SettingsSlicesContract;
import android.support.annotation.VisibleForTesting; import android.support.annotation.VisibleForTesting;
import android.support.v4.graphics.drawable.IconCompat; import android.support.v4.graphics.drawable.IconCompat;
import android.text.TextUtils; import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.Log; import android.util.Log;
import android.util.Pair; import android.util.Pair;
@@ -38,6 +39,7 @@ import com.android.settingslib.utils.ThreadUtils;
import java.net.URISyntaxException; import java.net.URISyntaxException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.WeakHashMap; import java.util.WeakHashMap;
@@ -111,12 +113,14 @@ public class SettingsSliceProvider extends SliceProvider {
SlicesDatabaseAccessor mSlicesDatabaseAccessor; SlicesDatabaseAccessor mSlicesDatabaseAccessor;
@VisibleForTesting @VisibleForTesting
Map<Uri, SliceData> mSliceWeakDataCache;
Map<Uri, SliceData> mSliceDataCache; Map<Uri, SliceData> mSliceDataCache;
@Override @Override
public boolean onCreateSliceProvider() { public boolean onCreateSliceProvider() {
mSlicesDatabaseAccessor = new SlicesDatabaseAccessor(getContext()); mSlicesDatabaseAccessor = new SlicesDatabaseAccessor(getContext());
mSliceDataCache = new WeakHashMap<>(); mSliceDataCache = new ArrayMap<>();
mSliceWeakDataCache = new WeakHashMap<>();
return true; return true;
} }
@@ -131,6 +135,17 @@ public class SettingsSliceProvider extends SliceProvider {
} }
} }
@Override
public void onSlicePinned(Uri sliceUri) {
// Start warming the slice, we expect someone will want it soon.
loadSliceInBackground(sliceUri);
}
@Override
public void onSliceUnpinned(Uri sliceUri) {
mSliceDataCache.remove(sliceUri);
}
@Override @Override
public Slice onBindSlice(Uri sliceUri) { public Slice onBindSlice(Uri sliceUri) {
String path = sliceUri.getPath(); String path = sliceUri.getPath();
@@ -141,14 +156,16 @@ public class SettingsSliceProvider extends SliceProvider {
return createWifiSlice(sliceUri); return createWifiSlice(sliceUri);
} }
SliceData cachedSliceData = mSliceDataCache.get(sliceUri); SliceData cachedSliceData = mSliceWeakDataCache.get(sliceUri);
if (cachedSliceData == null) { if (cachedSliceData == null) {
loadSliceInBackground(sliceUri); loadSliceInBackground(sliceUri);
return getSliceStub(sliceUri); return getSliceStub(sliceUri);
} }
// Remove the SliceData from the cache after it has been used to prevent a memory-leak. // Remove the SliceData from the cache after it has been used to prevent a memory-leak.
mSliceDataCache.remove(sliceUri); if (!mSliceDataCache.containsKey(sliceUri)) {
mSliceWeakDataCache.remove(sliceUri);
}
return SliceBuilderUtils.buildSlice(getContext(), cachedSliceData); return SliceBuilderUtils.buildSlice(getContext(), cachedSliceData);
} }
@@ -236,7 +253,12 @@ public class SettingsSliceProvider extends SliceProvider {
long startBuildTime = System.currentTimeMillis(); long startBuildTime = System.currentTimeMillis();
final SliceData sliceData = mSlicesDatabaseAccessor.getSliceDataFromUri(uri); final SliceData sliceData = mSlicesDatabaseAccessor.getSliceDataFromUri(uri);
List<Uri> pinnedSlices = getContext().getSystemService(
SliceManager.class).getPinnedSlices();
if (pinnedSlices.contains(uri)) {
mSliceDataCache.put(uri, sliceData); mSliceDataCache.put(uri, sliceData);
}
mSliceWeakDataCache.put(uri, sliceData);
getContext().getContentResolver().notifyChange(uri, null /* content observer */); getContext().getContentResolver().notifyChange(uri, null /* content observer */);
Log.d(TAG, "Built slice (" + uri + ") in: " + Log.d(TAG, "Built slice (" + uri + ") in: " +

View File

@@ -21,9 +21,12 @@ import static android.content.ContentResolver.SCHEME_CONTENT;
import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy; import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
import android.app.slice.SliceManager;
import android.content.ContentResolver; import android.content.ContentResolver;
import android.content.ContentValues; import android.content.ContentValues;
import android.content.Context; import android.content.Context;
@@ -43,7 +46,9 @@ import org.robolectric.RuntimeEnvironment;
import androidx.slice.Slice; import androidx.slice.Slice;
import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
/** /**
@@ -65,17 +70,22 @@ public class SettingsSliceProviderTest {
private Context mContext; private Context mContext;
private SettingsSliceProvider mProvider; private SettingsSliceProvider mProvider;
private SQLiteDatabase mDb; private SQLiteDatabase mDb;
private SliceManager mManager;
@Before @Before
public void setUp() { public void setUp() {
mContext = spy(RuntimeEnvironment.application); mContext = spy(RuntimeEnvironment.application);
mProvider = spy(new SettingsSliceProvider()); mProvider = spy(new SettingsSliceProvider());
mProvider.mSliceWeakDataCache = new HashMap<>();
mProvider.mSliceDataCache = new HashMap<>(); mProvider.mSliceDataCache = new HashMap<>();
mProvider.mSlicesDatabaseAccessor = new SlicesDatabaseAccessor(mContext); mProvider.mSlicesDatabaseAccessor = new SlicesDatabaseAccessor(mContext);
when(mProvider.getContext()).thenReturn(mContext); when(mProvider.getContext()).thenReturn(mContext);
mDb = SlicesDatabaseHelper.getInstance(mContext).getWritableDatabase(); mDb = SlicesDatabaseHelper.getInstance(mContext).getWritableDatabase();
SlicesDatabaseHelper.getInstance(mContext).setIndexedState(); SlicesDatabaseHelper.getInstance(mContext).setIndexedState();
mManager = mock(SliceManager.class);
when(mContext.getSystemService(SliceManager.class)).thenReturn(mManager);
when(mManager.getPinnedSlices()).thenReturn(Collections.emptyList());
} }
@After @After
@@ -98,6 +108,30 @@ public class SettingsSliceProviderTest {
insertSpecialCase(KEY); insertSpecialCase(KEY);
Uri uri = SliceBuilderUtils.getUri(INTENT_PATH, false); Uri uri = SliceBuilderUtils.getUri(INTENT_PATH, false);
mProvider.loadSlice(uri);
SliceData data = mProvider.mSliceWeakDataCache.get(uri);
assertThat(data.getKey()).isEqualTo(KEY);
assertThat(data.getTitle()).isEqualTo(TITLE);
}
@Test
public void testLoadSlice_doesntCacheWithoutPin() {
insertSpecialCase(KEY);
Uri uri = SliceBuilderUtils.getUri(INTENT_PATH, false);
mProvider.loadSlice(uri);
SliceData data = mProvider.mSliceDataCache.get(uri);
assertThat(data).isNull();
}
@Test
public void testLoadSlice_cachesWithPin() {
insertSpecialCase(KEY);
Uri uri = SliceBuilderUtils.getUri(INTENT_PATH, false);
when(mManager.getPinnedSlices()).thenReturn(Arrays.asList(uri));
mProvider.loadSlice(uri); mProvider.loadSlice(uri);
SliceData data = mProvider.mSliceDataCache.get(uri); SliceData data = mProvider.mSliceDataCache.get(uri);
@@ -108,11 +142,23 @@ public class SettingsSliceProviderTest {
@Test @Test
public void testLoadSlice_cachedEntryRemovedOnBuild() { public void testLoadSlice_cachedEntryRemovedOnBuild() {
SliceData data = getDummyData(); SliceData data = getDummyData();
mProvider.mSliceDataCache.put(data.getUri(), data); mProvider.mSliceWeakDataCache.put(data.getUri(), data);
mProvider.onBindSlice(data.getUri()); mProvider.onBindSlice(data.getUri());
insertSpecialCase(data.getKey()); insertSpecialCase(data.getKey());
SliceData cachedData = mProvider.mSliceDataCache.get(data.getUri()); SliceData cachedData = mProvider.mSliceWeakDataCache.get(data.getUri());
assertThat(cachedData).isNull();
}
@Test
public void testLoadSlice_cachedEntryRemovedOnUnpin() {
SliceData data = getDummyData();
mProvider.mSliceDataCache.put(data.getUri(), data);
mProvider.onSliceUnpinned(data.getUri());
insertSpecialCase(data.getKey());
SliceData cachedData = mProvider.mSliceWeakDataCache.get(data.getUri());
assertThat(cachedData).isNull(); assertThat(cachedData).isNull();
} }