Статьи

Android Full App, часть 2: использование HTTP API

Это вторая часть серии «Полное руководство по Android» . Полное приложение предназначено, чтобы обеспечить легкий способ выполнять поиск фильмов / актеров через Интернет. В первой части серии ( «Основной интерфейс действий» ) мы создали проект Eclipse и настроили базовый интерфейс для основной деятельности приложения. В этой части мы рассмотрим, как использовать внешний HTTP API и как интегрировать возможности поиска API в наше приложение.

Для просмотра фильмов и актеров мы будем использовать TMDb API . С официального сайта:
«API TMDb является мощным ресурсом для любых разработчиков, которые хотят интегрировать данные фильмов и актеров вместе с постерами или фанатами фильмов. Все методы API доступны в XML, YAML и JSON ».

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

Список доступных методов API можно найти на странице документации TMDb API, и наиболее важными из них являются следующие:

  • Movie.search : обеспечивает самый простой и быстрый способ поиска фильма.
  • Person.search : используется для поиска актера, актрисы или продюсера.

Для поиска фильмов приведен пример URL:

http://api.themoviedb.org/2.1/Movie.search/en/xml/APIKEY/Transformers

Для поиска людей приведен пример URL:

http://api.themoviedb.org/2.1/Person.search/en/xml/APIKEY/Brad+Pitt

(где APIKEY должен быть заменен действительным ключом API)

Как видите, API довольно прост и понятен в использовании. Это включает в себя только выполнение запросов HTTP GET по определенному URL-адресу, а затем получение ответов в предварительно заданном формате.

Далее мы рассмотрим, как использовать сетевые возможности Android для использования API и представления предоставленных данных. Обратите внимание, что мы будем использовать формат XML для ответов, но это будет продемонстрировано в следующем руководстве.

Для манипулирования HTTP-запросами / ответами в среде Android можно использовать стандартные классы из пакета java.net . Таким образом, такие классы, как URL , URLConnection , HttpURLConnection и т. Д., Могут использоваться известным способом. Однако вы можете избежать работы с деталями низкого уровня, используя клиентские библиотеки Apache HTTP. Эта библиотека основана на хорошо известной платформе HTTP-клиента Apache Commons .

Давайте начнем с кода. Мы создадим класс с именем «HttpRetriever», который будет отвечать за выполнение всех HTTP-запросов и будет возвращать ответы как в текстовом формате, так и в виде потока (для работы с изображениями). Код для этого класса следующий:

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
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
package com.javacodegeeks.android.apps.moviesearchapp.services;
 
import java.io.IOException;
import java.io.InputStream;
 
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.util.EntityUtils;
 
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.util.Log;
 
import com.javacodegeeks.android.apps.moviesearchapp.io.FlushedInputStream;
import com.javacodegeeks.android.apps.moviesearchapp.util.Utils;
 
public class HttpRetriever {
    
   private DefaultHttpClient client = new DefaultHttpClient();  
    
   public String retrieve(String url) {
       
        HttpGet getRequest = new HttpGet(url);
         
      try {
          
         HttpResponse getResponse = client.execute(getRequest);
         final int statusCode = getResponse.getStatusLine().getStatusCode();
          
         if (statusCode != HttpStatus.SC_OK) {
            Log.w(getClass().getSimpleName(), "Error " + statusCode + " for URL " + url);
            return null;
         }
          
         HttpEntity getResponseEntity = getResponse.getEntity();
          
         if (getResponseEntity != null) {
            return EntityUtils.toString(getResponseEntity);
         }
          
      }
      catch (IOException e) {
         getRequest.abort();
         Log.w(getClass().getSimpleName(), "Error for URL " + url, e);
      }
       
      return null;
       
   }
    
   public InputStream retrieveStream(String url) {
       
      HttpGet getRequest = new HttpGet(url);
         
      try {
          
         HttpResponse getResponse = client.execute(getRequest);
         final int statusCode = getResponse.getStatusLine().getStatusCode();
          
         if (statusCode != HttpStatus.SC_OK) {
            Log.w(getClass().getSimpleName(), "Error " + statusCode + " for URL " + url);
            return null;
         }
 
         HttpEntity getResponseEntity = getResponse.getEntity();
         return getResponseEntity.getContent();
          
      }
      catch (IOException e) {
         getRequest.abort();
         Log.w(getClass().getSimpleName(), "Error for URL " + url, e);
      }
       
      return null;
       
   }
    
   public Bitmap retrieveBitmap(String url) throws Exception {
       
      InputStream inputStream = null;
      try {
         inputStream = this.retrieveStream(url);
         final Bitmap bitmap = BitmapFactory.decodeStream(new FlushedInputStream(inputStream));
         return bitmap;
      }
      finally {
         Utils.closeStreamQuietly(inputStream);
      }
       
   }
 
}

Для фактического выполнения HTTP-запросов мы используем экземпляр класса DefaultHttpClient , который, как следует из его названия, является реализацией HTTP-клиента по умолчанию, то есть реализацией интерфейса HttpClient по умолчанию . Мы также используем класс HttpGet (для представления запроса GET) и предоставляем целевой URL для аргумента конструктора. HTTP-клиент выполняет запрос и предоставляет объект HttpResponse, который содержит фактический ответ сервера вместе с любой другой информацией. Например, мы можем получить код состояния ответа и сравнить его с кодом для успешных запросов HTTP ( HttpStatus.SC_OK ). Для успешных запросов мы берем ссылку на вложенный объект HttpEntity, и из этого у нас есть доступ к фактическим данным ответа. Для текстовых ответов мы конвертируем сущность в строку, используя статический метод toString класса EntityUtils . Если мы хотим извлечь данные в виде потока байтов (например, для обработки двоичных загрузок), мы используем метод getContent класса HttpEntity , который создает новый объект InputStream объекта.

Обратите внимание, что существует также третий метод для непосредственного возврата растровых объектов. Это будет полезно в последующих частях учебника, где мы будем загружать изображения из Интернета. В этом методе мы выполняем запрос GET и извлекаем InputStream как обычно. Затем мы используем метод decodeStream класса BitmapFactory для создания нового объекта Bitmap. Обратите внимание, что мы не предоставляем непосредственно загруженный InputStream , но сначала обертываем его классом FlushedInputStream. Как упоминалось в официальном блоге разработчиков Android , в предыдущих версиях метода decodeStream была ошибка, которая может вызывать проблемы при загрузке изображения через медленное соединение. Пользовательский класс FlushedInputStream, который расширяет FilterInputStream , используется вместо этого для решения проблемы. Код для этого класса следующий:

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
package com.javacodegeeks.android.apps.moviesearchapp.io;
 
import java.io.FilterInputStream;
import java.io.IOException;
import java.io.InputStream;
 
public class FlushedInputStream extends FilterInputStream {
  
    public FlushedInputStream(InputStream inputStream) {
        super(inputStream);
    }
 
    @Override
    public long skip(long n) throws IOException {
        long totalBytesSkipped = 0L;
        while (totalBytesSkipped < n) {
            long bytesSkipped = in.skip(n - totalBytesSkipped);
            if (bytesSkipped == 0L) {
                  int b = read();
                  if (b < 0) {
                      break// we reached EOF
                  } else {
                      bytesSkipped = 1; // we read one byte
                  }
           }
            totalBytesSkipped += bytesSkipped;
        }
        return totalBytesSkipped;
    }
     
}

Это гарантирует, что skip () фактически пропускает предоставленное количество байтов, если мы не достигли конца файла. Наконец, мы используем метод closeStreamQuietly пользовательского класса Utils для обработки исключений, которые могут возникнуть при закрытии InputStream . Код выглядит следующим образом:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
package com.javacodegeeks.android.apps.moviesearchapp.util;
 
import java.io.IOException;
import java.io.InputStream;
 
public class Utils {
  
 public static void closeStreamQuietly(InputStream inputStream) {
   try {
   if (inputStream != null) {
        inputStream.close(); 
    }
  } catch (IOException e) {
   // ignore exception
  }
 }
 
}

Наконец, чтобы иметь возможность выполнять HTTP-запросы, должно быть предоставлено соответствующее разрешение. Таким образом, добавьте android.permission.INTERNET в файл AndroidManifest.xml проекта, который теперь выглядит следующим образом:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
<?xml version="1.0" encoding="utf-8"?>
      package="com.javacodegeeks.android.apps.moviesearchapp"
      android:versionCode="1"
      android:versionName="1.0">
    <application android:icon="@drawable/icon" android:label="@string/app_name">
        <activity android:name=".MovieSearchAppActivity"
                  android:label="@string/app_name">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
 
    </application>
    <uses-sdk android:minSdkVersion="3" />
 <uses-permission android:name="android.permission.INTERNET"></uses-permission>
</manifest>

Итак, мы подготовили инфраструктуру для выполнения HTTP-запросов GET. В следующих уроках мы будем использовать это для извлечения данных и изображений XML для нужд нашего приложения. Вы можете скачать здесь созданный проект Eclipse.

Статьи по Теме :