За последние несколько лет мобильное пространство резко возросло, что привело к осознанию того, что сервис-ориентированная архитектура является отличным способом реализации бизнес-логики для потребления как веб-приложениями, так и мобильными приложениями. В то время как мобильные устройства эволюционировали кардинально, их возросшая доступность для потребителей усилила нагрузку на сети и поставщиков услуг, которые все еще пытаются удовлетворить спрос. Чтобы снизить затраты на оборудование, разработчики теперь должны урезать байты пропускной способности так же, как они делали это с памятью в первые дни вычислений.
Эта статья из двух частей проведет вас через процесс создания эффективного веб-сервиса REST на основе PHP, который будет использоваться приложением на базе Android. Некоторые из представленных здесь концепций также применимы к другим мобильным платформам, таким как iOS. Я предполагаю, что вы уже знакомы с основами разработки на PHP и Android, и что у вас есть подходящие среды разработки, настроенные для обоих. Я сосредоточусь в основном на демонстрации того, как обрабатывать сериализацию и сжатие данных в обеих средах.
Общий запрос
Вот конкретные части типичной операции HTTP, которые нас интересуют:
- Клиент (например, приложение Android) отправляет HTTP-запрос в службу REST (например, на сервер) и использует заголовки запроса, чтобы указать, какие форматы сериализации и сжатия данных он поддерживает.
- На основе заголовков запроса сервер определяет, какие форматы сериализации и сжатия данных он имеет с клиентом, выбирает один из них, применяет их к запрашиваемым данным и отправляет ответ, содержащий заголовки с указанием их выбора и данных, обратно в клиент.
- Основываясь на заголовках ответа, клиент применяет соответствующие процедуры распаковки и десериализации к данным, чтобы восстановить их в исходное состояние, а затем может использовать их по назначению.
Давайте начнем с самого начала и пройдем каждый шаг.
Запрос данных
Для отправки HTTP-запросов вашему Android-приложению требуется разрешение на доступ к Интернету. Вы должны объявить это в файле AndroidManifest.xml
вашего проекта следующим образом:
<?xml version="1.0" encoding="utf-8"?> <manifest ...> <uses-permission android:name="android.permission.INTERNET" /> </manifest>
После этого вам нужен класс, который фактически обрабатывает выполнение HTTP-запросов и обработку ответов. Для этого я буду использовать собственный класс AndroidHttpClient
который поставляется с конфигурацией по умолчанию, подходящей для большинства целей, включая использование потокового диспетчера соединений.
Класс AndroidHttpClient
доступен только в Android 2.2 (уровень API 8) и выше. Чтобы поддерживать более старые версии , посмотрите на класс DefaultHttpClient
из библиотеки Apache Harmony, который Android включил в свою первую версию.
import android.net.http.AndroidHttpClient; import java.io.IOException; import org.apache.http.HttpResponse; import org.apache.http.client.methods.HttpGet; public class DataModel { protected AndroidHttpClient httpClient; protected HttpGet httpRequest; public DataModel() { this.httpClient = AndroidHttpClient.newInstance( "Android " + android.os.Build.VERSION.RELEASE // Your app name would also be an acceptable value here ); } public DataValueObject getData() throws IOException { HttpGet httpRequest = new HttpGet( "http://10.0.2.2/php-android/"); httpRequest.addHeader("Accept", "application/json;q=1,application/x-msgpack;q=0.9"); httpRequest.addHeader("Accept-Encoding", "bzip2,gzip,deflate"); HttpResponse httpResponse = this.httpClient.execute(httpRequest); // ... } }
Когда этот класс создается, он внутренне создает экземпляр AndroidHttpClient
. Когда метод getData()
вызывается для запроса данных, он создает экземпляр HttpGet
, заполняет его данными запроса и использует экземпляр AndroidHttpClient
для его отправки. В данные запроса включены следующие данные:
- URL запрашиваемого ресурса (т. Е. Конечная точка REST). Если вы размещаете его на том же компьютере, где размещена среда разработки Android, вы можете получить к нему доступ с эмулятора, используя IP-адрес 10.0.2.2 (как в приведенном выше примере) и с физического устройства, подключенного через USB, с IP-адресом 10.0. 1.2.
- Форматы сериализации данных, поддерживаемые клиентом, в данном случае JSON и MessagePack , и их соответствующие коэффициенты качества, числа с плавающей запятой от 0 до 1 с более высокими числами, указывающими более высокое предпочтение для этого конкретного формата. Более подробную информацию об этом можно найти в RFC 2616, раздел 12.
- Форматы кодирования данных, поддерживаемые клиентом, в данном случае bzip2 , gzip и deflate . Более подробную информацию об этом можно найти в RFC 2616, раздел 3.5 .
Экземпляр AndroidHttpClient
может столкнуться с проблемами, такими как недоступность сервера. Его метод execute()
может IOException
чтобы указать это, который код, вызывающий getData()
может перехватить и обработать соответствующим образом.
Мы вернемся к остальной части метода getData()
в следующем разделе, когда рассмотрим обработку ответа, но сейчас нам нужно реализовать использование этого класса в деятельности Android .
Реализация фоновой задачи
Запрос и обработка данных в контексте мобильного приложения — это асинхронная операция. То есть мы хотим, чтобы приложение отправило запрос на данные, а затем предприняло некоторые действия после получения ответа, например, заполнив данные пользовательским интерфейсом.
По умолчанию Android содержит каждое отдельное приложение в своем собственном процессе и потоке , последний из которых часто называют потоком пользовательского интерфейса, поскольку на нем выполняются операции пользовательского интерфейса. Таким образом, интенсивные операции, такие как получение и обработка данных из веб-службы, должны выполняться в отдельном потоке, чтобы не блокировать операции пользовательского интерфейса, что обычно приводит к раздражению пользователей. Для этого нам нужно написать подкласс AsyncTask
для инкапсуляции процесса.
import java.io.IOException; import android.app.Activity; import android.app.AlertDialog; import android.app.ProgressDialog; import android.content.DialogInterface; import android.os.AsyncTask; public class GetDataTask extends AsyncTask<Void, Void, DataValueObject> { ProgressDialog progressDialog; Activity activity; String error; public GetDataTask(Activity activity) { super(); this.activity = activity; } protected String getString(int id) { return this.activity.getResources().getString(id); } @Override protected void onPreExecute() { progressDialog = ProgressDialog.show( this.activity, "", getString(R.string.loading), true, false ); } @Override protected DataValueObject doInBackground(Void... params) { DataModel dataModel = new DataModel(); DataValueObject dvo = null; try { dvo = dataModel.getData(); } catch (IOException e) { this.error = getString(R.string.error); // or e.getMessage() when debugging } return dvo; } @Override protected void onPostExecute(DataValueObject dvo) { if (dvo != null) { // Do something useful with dvo here // Dismiss the progress dialog when done progressDialog.dismiss(); } else { // Dismiss the progress dialog progressDialog.dismiss(); // Display a simple error dialog to the user new AlertDialog.Builder(this.activity) .setMessage(this.error) .setNeutralButton( getString(R.string.ok), new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int id) { dialog.cancel(); } } ) .create() .show(); } } }
В рамках расширения AsyncTask
этот класс указывает, что результатом операции будет экземпляр класса DataValueObject
который возвращает метод DataValueObject
getData()
класса DataModel
. Конструктор класса принимает в качестве единственного параметра экземпляр действия, который его вызывает. Это используется позже для доступа к локализованным строкам и использования действия в других задачах, зависящих от контекста.
onPreExecute()
и onPostExecute()
будут различаться в зависимости от того, что вы хотите сделать до и после запуска фоновой операции. Мои примеры, приведенные выше, показывают простой пример использования диалогового окна прогресса и, возможно, диалогового окна с предупреждением при возникновении ошибки.
doInBackground()
содержит логику, которая должна выполняться в фоновом потоке, в этом случае вызывая getData()
. Он возвращает экземпляр DataValueObject
или null, если возникает ошибка. Это значение затем передается в onPostExecute()
когда оно вызывается, чтобы вы могли сделать с ним что-то полезное.
Выполнение фоновой задачи
Теперь, когда мы правильно инкапсулировали фоновую задачу, которую мы хотим запустить для извлечения данных, нам нужно фактически вызвать ее в действии. Частично это потребует проверки доступности сетевого подключения, чтобы запрос данных действительно мог попасть на сервер. Для этого необходимо добавить дополнительное разрешение в ваш файл AndroidManifest.xml
:
<?xml version="1.0" encoding="utf-8"?> <manifest ...> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> </manifest>
Класс действия, который запускает фоновую задачу при создании действия, может выглядеть следующим образом:
import android.app.Activity; import android.content.Context; import android.net.ConnectivityManager; import android.net.NetworkInfo; import android.os.Bundle; public class MyActivity extends Activity { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); if (networkIsAvailable()) { new GetDataTask(this).execute(); } else { // Display an error to the user about network unavailability } } public boolean networkIsAvailable() { ConnectivityManager cm = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE); NetworkInfo networkInfo = cm.getActiveNetworkInfo(); return networkInfo != null && networkInfo.isConnected(); } }
Метод networkIsAvailable()
просто запрашивает соответствующую службу, чтобы определить, доступно ли сетевое соединение. Если ваше приложение имеет базовый класс активности, это может быть полезным методом для включения в него. onCreate()
использует networkIsAvailable()
чтобы либо выполнить фоновую задачу, либо отобразить соответствующее сообщение об ошибке для пользователя. Выполнение задачи — это просто создание его экземпляра со ссылкой на действие и вызов метода execute()
.
В следующий раз
В первой части этой статьи основное внимание уделялось настройке приложения Android для выполнения запроса. Во второй части этой статьи мы познакомимся с обработкой сериализации и сжатия данных в обеих средах. Будьте на связи!
Изображение через Fotolia