Статьи

Введение в залп

Volley — это сетевая библиотека, разработанная Google и представленная во время ввода-вывода Google 2013. Она была разработана из-за отсутствия в Android SDK сетевого класса, способного работать без вмешательства пользователя.

До выпуска Volley канонический Java-класс java.net.HttpURLConnection и Apache org.apache.http.client были единственными инструментами, доступными программистам Android для разработки системы RESTful между клиентом и удаленным бэкэндом.

Оставляя на мгновение тот факт, что эти два класса не освобождаются от ошибок, следует отметить, что все, что выходило за рамки простой HTTP-транзакции, должно было быть написано ex novo . Если вы хотите кэшировать изображения или расставлять приоритеты для запросов, вам нужно было разработать его с нуля .

К счастью, теперь есть залп, созданный и приспособленный для удовлетворения этих потребностей.

На более низких уровнях API (в основном на Gingerbread и Froyo) HttpUrlConnection и HttpClient далеко не идеальны. Есть некоторые известные проблемы и ошибки , которые никогда не были исправлены. Более того, HttpClient устарел в последнем обновлении API (API 22), что означает, что оно больше не будет поддерживаться и может быть удалено в следующем выпуске.

Это достаточные причины для принятия решения о переходе на более надежный способ обработки сетевых запросов.

С момента появления Honeycomb (API 11) было обязательным выполнять сетевые операции в отдельном потоке, отличном от основного потока. Это существенное изменение привело к массовому использованию AsyncTask<Params, Progress, Result> .

С AsyncTask вы сначала определяете некоторые подготовительные действия, такие как определение контекста, в onPreExecute . Затем вы выполняете асинхронные задачи, используя метод doInBackground . Наконец, вы обрабатываете результаты в onPostExecute . Это довольно просто, намного проще, чем реализация службы , и содержит множество примеров и документации.

Основная проблема, однако, заключается в сериализации вызовов. Используя класс AsyncTask , вы не можете решить, какой запрос пойдет первым, а какой ждать. Все происходит FIFO, первым пришел, первым вышел.

Проблемы возникают, например, когда вам нужно загрузить список элементов, к которым прикреплен эскиз. Когда пользователь прокручивает страницу вниз и ожидает новых результатов, вы не можете сказать своей активности сначала загрузить JSON следующей страницы, а затем только изображения предыдущей. Это может стать серьезной проблемой взаимодействия с пользователем в таких приложениях, как Facebook или Twitter, где список новых элементов важнее, чем миниатюра, связанная с ним.

Volley стремится решить эту проблему с помощью мощного API отмены. Вам больше не нужно проверять в onPostExecute ли активность уничтожена при выполнении вызова. Это помогает избежать нежелательных NullPointerException .

Некоторое время назад команда Google+ провела серию тестов производительности для каждого из различных методов, которые можно использовать для отправки сетевых запросов на Android. Волей получил оценку в десять раз лучше, чем другие альтернативы при использовании в приложениях RESTful.

Volley автоматически кеширует запросы, и это действительно спасительно. Давайте на минуту вернемся к примеру, который я привел ранее. У вас есть список элементов — скажем, массив JSON — и у каждого элемента есть описание и миниатюра, связанная с ним. Теперь подумайте о том, что произойдет, если пользователь поворачивает экран: действие уничтожается, список снова загружается, как и изображения. Короче говоря, значительная трата ресурсов и плохой пользовательский опыт.

Залп оказывается чрезвычайно полезным для преодоления этой проблемы. Он запоминает предыдущие звонки и обрабатывает действия по уничтожению и реконструкции. Он кэширует все без необходимости беспокоиться об этом.

Залп идеален для небольших вызовов, таких как объекты JSON, части списков, сведения о выбранном элементе и т. д. Он был разработан для приложений RESTful и в данном конкретном случае дает самое лучшее.

Это не так хорошо, когда используется для потоковых операций и больших загрузок. Вопреки распространенному мнению, имя Волли не из спортивного словаря. Он скорее предназначен для повторяющихся пакетов вызовов, сгруппированных вместе. Как-то интуитивно понятно, почему эта библиотека не пригодится, когда вместо залпа стрел вы хотите запустить пушечное ядро.

Залп работает на трех разных уровнях, каждый из которых работает в своем собственном потоке.

Залп под капотом

В главном потоке, в соответствии с тем, что вы уже делаете в спецификации AsyncTask , вам разрешено только запустить запрос и обработать его ответ. Ни больше ни меньше.

Основным следствием является то, что вы можете фактически игнорировать все, что происходило в методе doInBackground . Volley автоматически управляет HTTP-транзакциями и обнаружением сетевых ошибок, о которых вам нужно было позаботиться ранее.

Когда вы добавляете запрос в очередь, под капотом происходит несколько вещей. Сначала Волей проверяет, можно ли обслужить запрос из кеша. Если это возможно, кэшированный ответ читается, анализируется и доставляется. В противном случае он передается в сетевой поток.

В сетевом потоке постоянно работает циклический перебор с серией потоков. Первый доступный сетевой поток отменяет запрос, делает HTTP-запрос, анализирует ответ и записывает его в кэш. Чтобы закончить, он отправляет проанализированный ответ обратно в основной поток, где ваши слушатели ожидают обработки результата.

Залп не очень удобен в настройке. Похоже, что нет официального репозитория Maven, и это весьма сбивает с толку. Вы должны полагаться на официальный исходный код. Вы можете импортировать залп одним из нескольких способов.

Перво-наперво, загрузите источник Volley из своего хранилища . Если вы уверены в этом, этот Git   Команда может сделать всю работу за вас:

1
git clone https://android.googlesource.com/platform/frameworks/volley

Еще несколько недель назад вы могли все обернуть, используя командную строку ant ( android update project -p . затем ant jar ) и импортировав свою библиотеку JAR в проект Android Studio с помощью простых compile files('libs/volley.jar')

Однако недавно Google обновил Volley до стиля сборки Android Studio, усложнив создание автономного JAR-файла . Вы все еще можете сделать это, но только с более старыми версиями библиотеки. Я лично не рекомендую вам использовать эту опцию, хотя она может показаться самой быстрой.

Вы должны настроить Volley классическим способом , то есть импортировать источник в виде модуля . В Android Studio, когда ваш проект открыт, выберите « Файл»> «Новый модуль» и выберите « Импортировать существующий проект» . Выберите каталог, в который вы только что загрузили исходный код, и подтвердите. Папка с именем Volley будет отображаться в структуре вашего проекта. Android Studio автоматически обновляет ваш файл settings.gradle, чтобы включить модуль Volley, поэтому вам просто нужно добавить его в compile project(':volley') зависимостей compile project(':volley') и все готово.

Есть третий путь. Вы можете добавить в раздел зависимостей файла build.gradle следующую строку:

1
compile ‘com.mcxiaoke.volley:library-aar:1.0.15’

Это зеркальная копия официального репозитория Google, регулярно синхронизируемая и обновляемая. Это, наверное, самый простой и быстрый способ начать. Однако следует помнить, что это неофициальный репозиторий Maven, без гарантий и не поддерживается Google.

На мой взгляд, все же лучше потратить еще несколько минут на импорт официального исходного кода. Таким образом, вы можете легко перейти к исходным определениям и реализациям, чтобы в случае сомнений вы всегда могли положиться на официальный источник информации о волейболе — и даже изменить его, если вам нужно.

Volley в основном работает только с двумя классами, RequestQueue и Request . Сначала вы создаете RequestQueue , который управляет рабочими потоками и доставляет проанализированные результаты обратно в основной поток. Затем вы передаете ему один или несколько объектов Request .

Конструктор Request всегда принимает в качестве параметров тип метода (GET, POST и т. Д.), URL-адрес ресурса и прослушиватели событий. Затем, в зависимости от типа запроса, он может запросить еще несколько переменных.

В следующем примере я создаю RequestQueue   объект, вызывая один из удобных методов Volley, Volley.newRequestQueue . Это устанавливает RequestQueue   объект, используя значения по умолчанию, определенные Volley.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
String url = «http://httpbin.org/html»;
 
// Request a string response
StringRequest stringRequest = new StringRequest(Request.Method.GET, url,
            new Response.Listener<String>() {
    @Override
    public void onResponse(String response) {
 
        // Result handling
        System.out.println(response.substring(0,100));
 
    }
}, new Response.ErrorListener() {
    @Override
    public void onErrorResponse(VolleyError error) {
         
        // Error handling
        System.out.println(«Something went wrong!»);
        error.printStackTrace();
 
    }
});
 
// Add the request to the queue
Volley.newRequestQueue(this).add(stringRequest);

Как видите, это невероятно просто. Вы создаете запрос и добавляете его в очередь запросов. И вы сделали.

Обратите внимание, что синтаксис слушателя похож на AsyncTask.onPostExecute , он просто становится onResponse . Это не совпадение. Разработчики, которые работали над Volley, целенаправленно делали API библиотеки настолько похожим на методы AsyncTask . Это значительно облегчает переход от использования AsyncTask к Volley.

Если вам нужно запустить несколько запросов в нескольких действиях, вам следует избегать использования вышеуказанного подхода, Volley.newRequestQueue.add . Это много   лучше создать экземпляр одной общей очереди запросов и использовать ее в своем проекте:

1
MySingletonClass.getInstance().getRequestQueue().add(myRequest);

Мы специально увидим, как разработать что-то подобное в следующем уроке этой серии.

Volley пригодится для реализации трех очень распространенных типов запросов:

  • StringRequest
  • ImageRequest
  • JsonRequest

Каждый из этих классов расширяет класс Result который мы использовали ранее. Мы уже смотрели на StringRequest в предыдущем примере. Давайте посмотрим, как работает JsonRequest .

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
String url = «http://httpbin.org/get?site=code&network=tutsplus»;
 
JsonObjectRequest jsonRequest = new JsonObjectRequest
        (Request.Method.GET, url, null, new Response.Listener<JSONObject>() {
            @Override
            public void onResponse(JSONObject response) {
                // the response is already constructed as a JSONObject!
                try {
                    response = response.getJSONObject(«args»);
                    String site = response.getString(«site»),
                            network = response.getString(«network»);
                    System.out.println(«Site: «+site+»\nNetwork: «+network);
                } catch (JSONException e) {
                    e.printStackTrace();
                }
            }
        }, new Response.ErrorListener() {
 
            @Override
            public void onErrorResponse(VolleyError error) {
                error.printStackTrace();
            }
        });
 
Volley.newRequestQueue(this).add(jsonRequest);

Прекрасный. Не так ли? Как видите, тип результата уже установлен в JSONObject . Вы также можете запросить JSONArray , используя JsonArrayRequest вместо JsonObjectRequest .

Как и прежде, первый параметр конструктора — это используемый метод HTTP. Затем вы предоставляете URL для получения JSON. Третья переменная в приведенном выше примере равна null . Это нормально, так как это означает, что никакие параметры не будут опубликованы вместе с запросом. Наконец, у вас есть прослушиватель для получения ответа JSON и прослушиватель ошибок. Вы можете передать значение null если хотите игнорировать ошибки.

Получение изображений требует немного больше работы. Существует три возможных способа запроса изображения. ImageRequest
является стандартным. Он отображает запрошенное вами изображение в общем ImageView , извлекая его по предоставленному URL. Все операции декодирования и изменения размера, которые вы, возможно, захотите, чтобы Volley выполнял, выполняются в рабочем потоке. Второй вариант — это класс ImageLoader , который можно рассматривать как оркестратор большого числа ImageRequests , например, для заполнения ListView изображениями. Третий вариант — NetworkImageView , который является своего рода заменой XML для элемента макета ImageView .

Давайте посмотрим на пример.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
String url = «http://i.imgur.com/Nwk25LA.jpg»;
mImageView = (ImageView) findViewById(R.id.image);
 
ImageRequest imgRequest = new ImageRequest(url,
        new Response.Listener<Bitmap>() {
    @Override
    public void onResponse(Bitmap response) {
        mImageView.setImageBitmap(response);
    }
}, 0, 0, ImageView.ScaleType.FIT_XY, Bitmap.Config.ARGB_8888, new Response.ErrorListener() {
    @Override
    public void onErrorResponse(VolleyError error) {
        mImageView.setBackgroundColor(Color.parseColor(«#ff0000»));
        error.printStackTrace();
    }
});
 
Volley.newRequestQueue(this).add(imgRequest);

Первый параметр — это URL-адрес картинки, а второй — слушатель результата. Третьим и четвертым параметрами являются целые числа, maxWidth и maxHeight . Вы можете установить их в 0 чтобы игнорировать эти параметры. После этого ImageRequest запрашивает ScaleType используемый для расчета необходимого размера изображения, и формат для декодирования растрового изображения. Я предлагаю всегда использовать Bitmap.Config.ARGB_8888 . Наконец, мы передаем прослушиватель ошибок.

Обратите внимание, что Volley автоматически устанавливает приоритет этого запроса на LOW .

1
2
3
4
5
6
// Snippet taken from ImageRequest.java,
// in the Volley source code
@Override
public Priority getPriority() {
    return Priority.LOW;
}

Переключиться с GET-запроса на POST-запрос очень просто. Вам нужно изменить Request.Method в конструкторе запроса и переопределить метод getParams , возвращая правильный Map<String, String> содержащий параметры запроса.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
String url = «http://httpbin.org/post»;
 
StringRequest postRequest = new StringRequest(Request.Method.POST, url,
        new Response.Listener<String>() {
            @Override
            public void onResponse(String response) {
                try {
                    JSONObject jsonResponse = new JSONObject(response).getJSONObject(«form»);
                    String site = jsonResponse.getString(«site»),
                            network = jsonResponse.getString(«network»);
                    System.out.println(«Site: «+site+»\nNetwork: «+network);
                } catch (JSONException e) {
                    e.printStackTrace();
                }
            }
        },
        new Response.ErrorListener() {
            @Override
            public void onErrorResponse(VolleyError error) {
                error.printStackTrace();
            }
        }
) {
    @Override
    protected Map<String, String> getParams()
    {
        Map<String, String> params = new HashMap<>();
        // the POST parameters:
        params.put(«site», «code»);
        params.put(«network», «tutsplus»);
        return params;
    }
};
Volley.newRequestQueue(this).add(postRequest);

Если вы хотите отменить все ваши запросы, добавьте следующий фрагмент кода в метод onStop :

01
02
03
04
05
06
07
08
09
10
11
@Override
protected void onStop() {
    super.onStop();
    mRequestQueue.cancelAll(new RequestQueue.RequestFilter() {
        @Override
        public boolean apply(Request<?> request) {
            // do I have to cancel this?
            return true;
        }
    });
}

Таким образом, вам не нужно беспокоиться о возможности того, что пользователь уже уничтожил активность при onResponse . В этом случае будет выброшено NullPointerException .

Однако запросы POST и PUT должны продолжаться даже после изменения пользователем действий. Мы можем сделать это с помощью тегов . При создании запроса GET добавьте к нему тег.

1
2
3
// after declaring your request
request.setTag(«GET»);
mRequestQueue.add(request);

Чтобы отменить каждый ожидающий запрос GET, мы просто добавляем следующую строку кода:

1
mRequestQueue.cancelAll(«GET»);

Таким образом, вы только отменяете запросы GET, оставляя другие запросы без изменений. Обратите внимание, что теперь вам нужно вручную обработать случай, когда действие преждевременно уничтожено.

Volley не предоставляет ни способа установки файлов cookie запроса, ни его приоритета. Это, вероятно, произойдет в будущем, поскольку это серьезное упущение. Однако на данный момент вы должны расширить класс Request .

Для управления файлами cookie вы можете поиграть с заголовками запроса, переопределив метод getHeaders :

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
public class CustomRequest extends JsonObjectRequest {
 
    // Since we’re extending a Request class
    // we just use its constructor
    public CustomRequest(int method, String url, JSONObject jsonRequest,
                         Response.Listener<JSONObject> listener, Response.ErrorListener errorListener) {
        super(method, url, jsonRequest, listener, errorListener);
    }
 
    private Map<String, String> headers = new HashMap<>();
 
    /**
     * Custom class!
     */
    public void setCookies(List<String> cookies) {
        StringBuilder sb = new StringBuilder();
        for (String cookie : cookies) {
            sb.append(cookie).append(«; «);
        }
        headers.put(«Cookie», sb.toString());
    }
 
    @Override
    public Map<String, String> getHeaders() throws AuthFailureError {
        return headers;
    }
     
}

С помощью этой реализации вы можете напрямую предоставить список файлов cookie для запроса, используя setCookies .

01
02
03
04
05
06
07
08
09
10
11
12
// Firstly, you create the list of the cookies,
// conformed to the HTTP conventions
// ie key=value
List<String> cookies = new ArrayList<>();
cookies.add(«site=code»);
cookies.add(«network=tutsplus»);
 
// then you invoke your custom method
customRequest.setCookies(cookies);
 
// and finally add the request to the queue
Volley.newRequestQueue(this).add(customRequest);

Для приоритета вам также необходимо расширить класс Request , переопределяя метод getPriority . Вот как может выглядеть реализация:

01
02
03
04
05
06
07
08
09
10
11
12
Priority mPriority;
 
public void setPriority(Priority priority) {
    mPriority = priority;
}
 
@Override
public Priority getPriority() {
    // If you didn’t use the setPriority method,
    // the priority is automatically set to NORMAL
    return mPriority != null ?
}

Затем в главном потоке вызовите эту строку кода, чтобы установить приоритет запроса:

1
customRequest.setPriority(Priority.HIGH);

Вы можете выбрать один из четырех возможных приоритетов, как показано ниже:

1
2
3
4
Priority.LOW // images, thumbnails, …
Priority.NORMAL // residual
Priority.HIGH // descriptions, lists, …
Priority.IMMEDIATE // login, logout, …

В этой статье мы рассмотрели, как работает сетевая библиотека Volley. Мы впервые увидели, почему и когда лучше использовать Volley вместо другого решения, уже включенного в Android SDK. Затем мы углубимся в детали библиотеки, рассматривая ее рабочий процесс и поддерживаемые типы запросов. Наконец, мы запачкали руки, создав простые запросы и внедрив пользовательские запросы для обработки файлов cookie и определения приоритетов.

В следующей части этой серии о Volley мы создадим простое приложение, которое использует Volley. Я покажу вам, как создать приложение погоды для Марса, используя данные о погоде, которые собираются на Марсе с помощью марсохода Curiosity.

Если вы много занимаетесь разработкой для Android, почему бы не ускорить рабочий процесс с помощью одного из тысяч полезных шаблонов приложений для Android, доступных на Envato Market? Или обратитесь к одному из разработчиков Android в Envato Studio, чтобы помочь вам с вашим проектом — они могут даже разработать приложение для вас с нуля!