This results in a ~1% increase in database size, which is worth it for reducing complexity and making the tiles and resources tables more similar in structure.
* Under the hood, SQLite creates surrogate keys (ROWID) anyway. We may as well take advantage of this and use the surrogates for foreign keys as well, since they are simpler and more efficient than compound foreign keys.
* Create indexes for efficient eviction queries
When inserting an cached resource, or removing a region, remove least-recently used resources and tiles, not used by offline regions, until the used database size, as calculated by multiplying the number of in-use pages by the page size, is less than the maximum cache size minus 5 times the page size.
In addition, OfflineDatabase may be configured to ignore cache puts of individual resources larger than a certain size.
This policy is similar but not identical to the former SQLiteCache policy:
* It accounts for offline, by exempting resources required by offline regions from eviction.
* It must delete from two tables (resources and tiles), rather than one. Currently the strategy is naive: evict 50 rows at a time from each table.
* It makes maximumCacheSize and maximumCacheEntrySize completely independent. The SQLiteCache implementation evicted when `usedSize > maximumCacheSize - 2 * maximumCacheEntrySize`. This evicts when `usedSize > maximumCacheSize - 5 * pageSize`.
* It uses a non-unlimited default value for maximumCacheSize: 50 MB. We should have always had a limit in place; "a cache without an eviction policy is a resource leak".
The addLocationListener method didn't check if the LocationListener was
already in the list.
From MapView, if we call setMyLocationEnabled(true), it adds a
LocationListener and onResume adds the same LocationListener again, but
is just removed by onPause.
So LocationListener is added twice but removed just once, so
LocationServices singleton keeps an instance of the the LocationListener
that is never released.
There is no such thing as a cancelled response, only cancelled requests. A request that is cancelled does not have its callback called with a Response.