/** * Returns an unmodifiable Map of url->Metadata (i.e. A second HashMap) for a list of urls. Must * not be called from UI or Gecko threads. */ public static Map<String, Map<String, Object>> getForUrls( final ContentResolver cr, final List<String> urls, final List<String> columns) { ThreadUtils.assertNotOnUiThread(); ThreadUtils.assertNotOnGeckoThread(); final Map<String, Map<String, Object>> data = new HashMap<String, Map<String, Object>>(); // Nothing to query for if (urls.isEmpty() || columns.isEmpty()) { Log.e(LOGTAG, "Queried metadata for nothing"); return data; } // Search the cache for any of these urls List<String> urlsToQuery = new ArrayList<String>(); for (String url : urls) { final Map<String, Object> hit = cache.get(url); if (hit != null) { // Cache hit! data.put(url, hit); } else { urlsToQuery.add(url); } } Telemetry.HistogramAdd("FENNEC_TILES_CACHE_HIT", data.size()); // If everything was in the cache, we're done! if (urlsToQuery.size() == 0) { return Collections.unmodifiableMap(data); } final String selection = DBUtils.computeSQLInClause(urlsToQuery.size(), URLMetadataTable.URL_COLUMN); // We need the url to build our final HashMap, so we force it to be included in the query. if (!columns.contains(URLMetadataTable.URL_COLUMN)) { columns.add(URLMetadataTable.URL_COLUMN); } final Cursor cursor = cr.query( URLMetadataTable.CONTENT_URI, columns.toArray(new String[columns.size()]), // columns, selection, // selection urlsToQuery.toArray(new String[urlsToQuery.size()]), // selectionargs null); try { if (!cursor.moveToFirst()) { return Collections.unmodifiableMap(data); } do { final Map<String, Object> metadata = fromCursor(cursor); final String url = cursor.getString(cursor.getColumnIndexOrThrow(URLMetadataTable.URL_COLUMN)); data.put(url, metadata); cache.put(url, metadata); } while (cursor.moveToNext()); } finally { cursor.close(); } return Collections.unmodifiableMap(data); }