Так получилось, что при реализации одного из моих проектов понадобился поиск изображений по определенным ключевым словам. Поиск должен был быть реализован исходя из двух простых требований:
- быть бесплатным;
- быть хорошо фильтрованным, чтобы не нарваться на проблемы с Apple (такая же задача стояла и для версии приложения под iOS).
Я долго серфил интернеты в поисках подходящего контент-провайдера, предоставляющего JSON API. В итоге были отобраны 3 основных кандидата: Google Search, Flickr и Bing Image Search. Был еще один вариант подключения к старым бесплатным API от Google, которые уже лет 5 как в статусе deprecated, но все еще работают. Но этот вариант, увы, не подходил.
К сожалению, официальные Google Search API являются, во-первых, платными, во-вторых, в результате выдачи Google по некоторым запросам вылетают неприемлемые изображения. C Bing Image Search ситуация еще более плачевная, хотя доступ к API у них обходится и дешевле.
Чтобы не быть голословным, приведу сравнение выдачи всех трех провайдеров по провокационным запросам с применением максимальной фильтрации выдачи неприемлемого контента, да простят меня автолюбители.



У Google Search, на первый взгляд, все более или менее приемлемо, но если прокрутить страницу чуть ниже, местами встречаются очень печальные картинки. У Bing Image Search все еще гораздо более хардкорно. По понятным причинам скрины этого я приводить здесь не буду. Я проводил еще множество подобных сравнений, в том числе и по пикантным запросам, и во всех них однозначным лидером оказывался Flickr. Поэтому мой выбор пал именно на него. К тому же доступ к их API является бесплатным. Единственным жирным минусом были совсем уж скудные результаты поиска на русском языке. Забегая вперед, скажу, что решено это было на бекэнде – был поднят простенький сервис, переводящий русский текст на английский. Таким образом запрос к Flickr состоял из двух частей: запрос перевода у нашего сервера, отправка полученного результата на Flickr.
Регистрация приложения
Прежде всего нам необходимо получить API key работы с Flickr. Для этого перейдем по ссылке (только для некоммерческого использования)
После заполнения формы вам будут предоставлены Key и Secret:

Интерфейс запросов к Flickr
Для начала определимся, каким образом вообще выглядит запрос к API:
https://api.flickr.com/services/rest/?safe_search=safe&api_key=XXX&sort=relevance&method=flickr.interestingness.getList&per_page=50&media=photos&extras=url_sq,url_t,url_s,url_q,url_m,url_n,url_z,url_c,url_l,url_o&license=1,2,3,4,5,6&format=json
Нам будут интересны два метода: flickr.photos.search и flickr.interestingness.getList.
Метод запроса flickr.interestingness.getList возвращает нам список самых популярных изображений. Изображения не застаиваются и периодически обновляются (по моим наблюдениям раз в пол часа — час).
Метод запроса flickr.photos.search будет использоваться непосредственно для поиска по ключевым словам.
Более подробно про каждый из параметров запроса вы можете почитать у них в официальной документации.
Пример реализации
Для реализации общения с сервисом был реализован следующий класс:
public class Flickr extends Model { public interface FlickrModelResponseHandler { public void onSuccess(ArrayList<HashMap<String, String>> responseArray); public void onFailure(String error); } public Flickr(Context context) { super(context); setUrl("/services/rest/"); } public void fetch (Map<String, String> inData, Map<String, Object> options, final FlickrModelResponseHandler handler) { HashMap<String, String> fetchedParams = new HashMap(inData); fetchedParams.put("api_key", "XXX"); fetchedParams.put("extras", "url_sq,url_t,url_s,url_q,url_m,url_n,url_z,url_c,url_l,url_o"); fetchedParams.put("format", "json"); fetchedParams.put("per_page", "50"); fetchedParams.put("safe_search", "safe"); fetchedParams.put("content_type", "1"); fetchedParams.put("media", "photos"); fetchedParams.put("sort", "relevance"); fetchedParams.put("license", "1,2,3,4,5,6"); super.fetch(fetchedParams, new HashMap<String, Object>() {{ put("host", "https://api.flickr.com"); }}, new ModelResponseHandler() { @Override public void onSuccess(Map<String, Object> responseDict) { ArrayList<HashMap<String, String>> photos = ((HashMap<String, ArrayList<HashMap<String, String>>>)(responseDict.get("photos"))).get("photo"); if (handler != null) handler.onSuccess(photos); } @Override public void onFailure(String error) { if (handler != null) handler.onFailure(error); } }); } @Override protected JSONObject deserialize(String responseString) { Pattern p = Pattern.compile(".*?\\((.*)\\)$"); Matcher m = p.matcher(responseString); String json = null; if (m.matches()) { json = m.group(1); } JSONObject response = null; try { response = new JSONObject(json); } catch (JSONException e) { e.printStackTrace(); } return response; } }
Как видно из кода выше, класс унаследован от класса Model, в котором в свою очередь реализованы примитивные методы общения с сервером (GET, POST, PUT, DELETE запросы). Метод fetch реализует http-метод GET и принимает в качестве первого аргумента параметры запроса, в качестве второго – различные флаги обработки данных и т.д., в качестве третьего – callback интерфейс.
Метод deserialize, как понятно из названия, десериализует полученные данные. Так как Flickr возвращает JSON, обернутый вот в такую бяку: jsonFlickrApi(), то перед десериализацией JSON-объекта регуляркой отбрасываем все лишнее. По остальному, я думаю, тут все понятно.
Теперь реализуем метод, в который непосредственно будет передаваться строка запроса.
private void searchImages(String query) { HashMap<String, String> params = new HashMap<>(); flickr = new Flickr(ImageSearchActivity.this); if (query != null) { params.put("method", "flickr.photos.search"); params.put("text", query); } else { params.put("method", "flickr.interestingness.getList"); } flickr.fetch(params, null, new Flickr.FlickrModelResponseHandler() { @Override public void onSuccess(ArrayList<HashMap<String, String>> responseArray) { adapter.setArray(responseArray); } @Override public void onFailure(String error) { adapter.setArray(new ArrayList<HashMap<String, String>>()); } }); }
Тут, если query равен null, то делаем запрос методом flickr.interestingness.getList, иначе — flickr.photos.search c текстом query.
И, напоследок, результат работы.


