Статьи

OkHttp, HTTP-клиент Android от Square

Большинству приложений необходимы сетевые подключения к внешним службам для доступа и обмена данными. Обычно это происходит через API REST и HTTP-клиент в вашем приложении. OKHttp — это клиентская HTTP-библиотека Android от Square, которая сокращает количество необходимых шагов и позволяет уделять больше времени важным областям вашего приложения.

Почему OkHttp?

Это простая библиотека для использования, загрузка данных быстрее и экономия полосы пропускания. Это устраняет необходимость в тестировании сети, восстановлении после распространенных проблем с подключением и при сбое подключения OkHttp может повторить запрос с другим маршрутом.

OkHttp поддерживает Android 2.3 и выше, что составляет более 99% рынка, основываясь на текущей статистике использования версий.

Использование OkHttp в вашем приложении

OkHttp недавно обновлен до версии 3.0 и в настоящее время имеет версию 3.3.1. Я упоминаю об этом, потому что OkHttp сделал ключевые обновления в этой версии, а методы из предыдущих версий устарели.

Вы можете найти все примеры, которые я расскажу на GitHub .

Добавить зависимости

Откройте build.gradle (Module: app) и добавьте следующую зависимость или проверьте сайт OkHttp на наличие последних обновлений.

dependencies {
    //...
    compile 'com.squareup.okhttp3:okhttp:3.3.1'
}

В AndroidManifest.xml добавьте интернет-разрешение.

 <uses-permission android:name="android.permission.INTERNET"/>

Сеть с OkHttp

Основное внимание в этой статье будет уделено построению следующих сетевых вызовов.

  • ПОЛУЧИТЬ
  • ПОЧТА
  • Загрузка файлов с помощью OkHttp
  • Мониторинг запросов и ход выполнения ответов
  • Обрабатывать куки с OkHttp

Примечание : я стараюсь писать не избыточный код, а СУХОЙ код. Я чувствую, что сделал все возможное, чтобы оптимизировать работу моего кода с помощью OkHttp. Это состоит из структурирования кода на два класса. ApiCallRequestBuilderRequestBody

сетей

Пришло время создать некоторые вызовы API , в частности сетевые запросы GET и POST. Создайте следующий класс Java для обработки каждого из запросов.

 public class ApiCall {

    //GET network request
    public static String GET(OkHttpClient client, HttpUrl url) throws IOException {
        Request request = new Request.Builder()
                .url(url)
                .build();
        Response response = client.newCall(request).execute();
        return response.body().string();
    }

    //POST network request
    public static String POST(OkHttpClient client, HttpUrl url, RequestBody body) throws IOException {
        Request request = new Request.Builder()
                .url(url)
                .post(body)
                .build();
        Response response = client.newCall(request).execute();
        return response.body().string();
    }
}

В обоих методах класса ApiCallOkHttpClient Вы можете конструировать методы по-разному, так что каждый раз создается новый клиент, но это не разумное решение, и я рекомендую вам прочитать этот ответ StackOverflow , чтобы доказать, что это лучшее решение.

В обоих методах параметр URL HttpUrl url Он представлен как экземпляр HttpUrlString

 public static String POST(OkHttpClient client, String url, RequestBody body) throws IOException {
    //...
}

Вы можете применить это в обоих методах, и это будет иметь тот же результат, что и экземпляр HttpUrl Важно отметить, что строка URL может быть неправильно структурирована, что может привести к исключению MalformedURLException .

Последнее, что следует упомянуть, это RequestBodyPOST Он представляет собой набор параметров в парах имя / значение, отправленных в теле запроса POST

Создание запроса URL

Затем вам нужно создать класс Java, который создает URL-адрес сетевого запроса и RequestBodyPOST

 public class RequestBuilder {

    //Login request body
    public static RequestBody LoginBody(String username, String password, String token) {
        return new FormBody.Builder()
                .add("action", "login")
                .add("format", "json")
                .add("username", username)
                .add("password", password)
                .add("logintoken", token)
                .build();
    }

    public static HttpUrl buildURL() {
        return new HttpUrl.Builder()
                .scheme("https") //http
                .host("www.somehostname.com")
                .addPathSegment("pathSegment")//adds "/pathSegment" at the end of hostname
                .addQueryParameter("param1", "value1") //add query parameters to the URL
                .addEncodedQueryParameter("encodedName", "encodedValue")//add encoded query parameters to the URL
                .build();
        /**
         * The return URL:
         *  https://www.somehostname.com/pathSegment?param1=value1&encodedName=encodedValue
         */
    }

}

Java-класс RequestBuilderRequestBodyHttpUrl Примеры состоят из создания RequestBodyHttpUrl

Вернуться к деятельности

До сих пор необходимо подготовить способ работы с сетью из действия и использовать созданные классы. Сначала в классе действия создайте глобальный OkHttpClient

 public class MainActivity extends AppCompatActivity {

    private OkHttpClient client;
    //...
}

Инициализируйте клиента в onCreate()

     @Override
    protected void onCreate(Bundle savedInstanceState) {
        //...
        client = new OkHttpClient();
    //...
}

Вы должны обрабатывать сетевые вызовы в Android асинхронно, в противном случае выдается исключение NetworkOnMainThreadException, когда приложение пытается выполнить сетевую операцию в главном потоке. Чтобы избежать этого исключения, используйте AsyncTask .

Добавьте эти методы в свою активность:

 private void loadContent() {
    new AsyncTask<Void, Void, Void>() {
        @Override
        protected Void doInBackground(Void... params) {
            try {
                response = ApiCall.GET(client, RequestBuilder.buildURL());
                //Parse the response string here
                Log.d("Response", response);
            } catch (IOException e) {
                e.printStackTrace();
            }
            return null;
        }
    }.execute();
}

private void attemptLogin(String url) {
    new AsyncTask<String, Void, Void>() {
        @Override
        protected Void doInBackground(String... params) {

            try {
                response = ApiCall.POST(
                        client,
                        params[0],
                        RequestBuilder.LoginBody("username", "password","token"));

                Log.d("Response", response);
            } catch (IOException e) {
                e.printStackTrace();
            }
            return null;
        }
    }.execute(url);
}

Две функции, показанные здесь, представляют оба примера в классе RequestBuilder Они только для демонстрационных целей, и вы могли бы конструировать их по-другому.

Оставляя в стороне реализацию AsyncTask Весь код, реализованный в обоих предыдущих классах, структурирован в одну строку, которая возвращает тело ответа сетевого запроса, а избыточный код отсутствует. Довольно круто!

 ApiCall.GET(client, RequestBuilder.buildURL());

После получения ответа может потребоваться некоторый анализ, поскольку API обычно возвращают потоки JSONXML

Загрузка файлов

В этом разделе я расскажу о загрузке файлов с помощью OkHttp Один из распространенных типов загрузки — multipart/form-data В следующем примере загрузки изображения показано, как OkHttp может справиться с этим, но он действителен для других типов файлов.

 //Upload request body
public static MultipartBody uploadRequestBody(String title, String imageFormat, String token, File file) {

    MediaType MEDIA_TYPE = MediaType.parse("image/" + imageFormat); // e.g. "image/png"
    return new MultipartBody.Builder()
            .setType(MultipartBody.FORM)
            .addFormDataPart("action", "upload")
            .addFormDataPart("format", "json")
            .addFormDataPart("filename", title + "." + imageFormat) //e.g. title.png --> imageFormat = png
            .addFormDataPart("file", "...", RequestBody.create(MEDIA_TYPE, file))
            .addFormDataPart("token", token)
            .build();
}

Эта функция возвращает объект MultipartBodyRequestBodyRequestBuilder

Теперь, когда тело запроса на загрузку завершено, как выполнить вызов на загрузку? Загрузки — это вызовы POST

  File file = new File(""); //provide a valid file
 ApiCall.POST(client, url, RequestBuilder.uploadRequestBody("title", "png", "someUploadToken", file));

Не забудьте назвать это асинхронно. Этот вызов возвращает подтверждение действия загрузки, успешно или нет.

Что дальше

Это все для основ OkHttp, но далее я упомяну некоторые советы и инструменты, которые могут быть полезны с OkHttp.

Мониторинг хода выполнения запроса

Часто при загрузке мультимедиа полезна возможность отслеживать прогресс. Некоторое время назад я нашел это решение Gist, которое является решением этой проблемы. Чтобы отслеживать ход выполнения запроса с этим решением, вы создаете экземпляр RequestBody

 //Monitored request
File file = new File("");
MultipartBody body = RequestBuilder.uploadRequestBody("title", "png", "someUploadToken", file);

CountingRequestBody monitoredRequest = new CountingRequestBody(body, new CountingRequestBody.Listener() {
    @Override
    public void onRequestProgress(long bytesWritten, long contentLength) {
        //Update a progress bar with the following percentage
        float percentage = 100f * bytesWritten / contentLength;
        if (percentage >= 0) {
            //TODO: Progress bar
        } else {
            //Something went wrong
        }
    }
});

Я изменил код для использования его с OkHttp 3. Вот ссылка на класс в GitHub с соответствующими изменениями.

Обрабатывать Cookies с OkHttp

Вы можете обрабатывать куки в OkHttp с CookieJar Следующий пример является примером того, как обрабатывать куки в OkHttp.

 public class CookieStore implements CookieJar {

    private final Set<Cookie> cookieStore = new HashSet<>();

    @Override
    public void saveFromResponse(HttpUrl url, List<Cookie> cookies) {
        /**
         *Saves cookies from HTTP response
         * If the response includes a trailer this method is called second time
         */
        //Save cookies to the store
        cookieStore.addAll(cookies);
    }

    @Override
    public List<Cookie> loadForRequest(HttpUrl url) {
        /**
         * Load cookies from the jar for an HTTP request.
         * This method returns cookies that have not yet expired
         */
        List<Cookie> validCookies = new ArrayList<>();
        for (Cookie cookie : cookieStore) {
            LogCookie(cookie);
            if (cookie.expiresAt() < System.currentTimeMillis()) {
                // invalid cookies
            } else {
                validCookies.add(cookie);
            }
        }
        return validCookies;
    }

    //Print the values of cookies - Useful for testing
    private void LogCookie(Cookie cookie) {
        System.out.println("String: " + cookie.toString());
        System.out.println("Expires: " + cookie.expiresAt());
        System.out.println("Hash: " + cookie.hashCode());
        System.out.println("Path: " + cookie.path());
        System.out.println("Domain: " + cookie.domain());
        System.out.println("Name: " + cookie.name());
        System.out.println("Value: " + cookie.value());
    }
}

Для обработки файлов cookie на основе указанных политик в классе CookieStoreOkHttpClient

 OkHttpClient client = new OkHttpClient.Builder()
    .cookieJar(new CookieStore())
    .build();

Теперь вы обновили OkHttpClientCookieStore

Этого примера достаточно для сеансовых файлов cookie, но для тех, кто хочет сохранить файлы cookie при последующих запусках приложения, этой политики недостаточно. Существует решение этой проблемы с помощью реализации PersistentCookieJar, основанной на SharedPreferences Чтобы использовать этот пакет с OkHttpClientинструкциям по использованию .

Все ок?

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