Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Разработка мобильных приложений в среде Android Studio. Учебное пособие

.pdf
Скачиваний:
0
Добавлен:
07.09.2026
Размер:
1 Мб
Скачать
☆
5.1. Загрузка данных с сайта
71
e.printStackTrace(); }
Чтобы загрузить данные, находящиеся по переданной ссылке, необ­ходимо использовать класс HttpURLConnection [33]. Данный класс предна­значен для установки связи с ресурсом, поддержания соединения, а также загрузки данных. Начало соединения обеспечивается методом openConnec­tion(), который открывает сессию (также помещается в блок обработки ошибок):
HttpURLConnection conn = null;
try {
conn = (HttpURLConnection) url.openConnection();
} catch (IOException e) {
e.printStackTrace();
}
Далее необходимо проверить, есть ли доступ к запрашиваемому ре­сурсу. Для этого используется код ответа на запрос (200 = запрос прошел удачно):
try {
if (conn.getResponseCode() == HttpURLConnection.HTTP_OK)
{
}
} catch (IOException e) {
e.printStackTrace();
}
После этого можно непосредственно получать данные с использова­нием InputStreamReader – потока для приема [34]. Его необходимо инициа­лизировать потоком класса HttpURLConnection, который можно получить путем использования метода getInputStream. Общая задача заключается в том, чтобы прочитать текстовые данные, поэтому для получения данных из
5. Работа с сетью
72
потока InputStreamReader используется метод класса BufferedReader read­Line(), который вернет всю прочитанную строку. Класс BufferedReader поз-
воляет накапливать полученные данные и отдавать их порциями по запро­су, в данном случае построчно [35]. Данный класс будет иметь размер бу­фера, указанного при создании, в примере 8128. Таким образом, чтение данных по запросу на сайт будет реализовано в следующем виде:
StringBuilder response = new StringBuilder(); BufferedReader input = new BufferedReader(new InputStreamReader (conn.getInputStream()), 8192); String line = null;
while ((line = input.readLine()) != null) { response.append(line); }
input.close();
В примере используется класс StringBuilder, позволяющий выполнять операции со строками, в данном случае, добавление строк друг к другу (ме­тод append) [36]. Его использование необязательно.
5.2. JSON-файлы
В настоящее время для передачи какой-либо информации по сети Интернет используется формат JSON, предназначенный для кодирования объектов JavaScript в виде строк. Структура JSON может включать в себя:
1. Объекты (непосредственно данные, например строка или число)
следующего формата:
{ name : value , .... }
2. Массивы – обозначаются в квадратных скобках:
[ object , object .... ]
3. Значения – перечисление строк или чисел через запятую.
5.2. JSON-файлы
73
5.2.1. Чтение JSON-файлов
В Android имеется встроенное средство для чтения JSON [37] – набор
классов для каждой части структуры:
1) для объектов – JSONObject;
2) массивы – JSONArray;
3) значения – JSONStringer;
4) ошибки – JSONExeption.
У каждого из представленных выше классов есть методы для получе­ния внутренней структуры – массива, объекта или значения: getBoolean, getDouble, getJSONObject и т.д. Стоит отметить, что эти классы не содер­жат функциональных возможностей для автоматической конвертации JSON в объект класса. Задача последовательного применения методов для правильного «парсинга» структуры, как и преобразование полученных зна­чений к объекту класса, полностью лежит на разработчике.
Рассмотрим процесс разбора структуры файла на примере. Предпо­ложим, чтоимеется следующий JSON:
{
"fruits":[
"Apple",
"Avocado",
"Banana",
"Cherry"
]
}
В данной структуре только один объект – весь JSON. Он содержит еще один объект – массив значений. Каждый элемент представленного массива – строка, которая уже не является объектом JSON-структуры, а просто значением.
Для того чтобы получить из этой структуры значения (в данном слу­чае названия фруктов), необходимо выполнить следующие действия:
1. Скачанный json с сайта представляет из себя строку, поэтому пер­вый шаг – это преобразование строки в формат JSONObject. Для этого необходимо создать экземпляр класса JSONObject с конструктором, при­нимающим на вход строку:
5. Работа с сетью
74
String json = "{\n" + "\"fruits\":[\n" + "\t\"Apple\",\n" + "\t\"Avocado\",\n" + "\t\"Banana\",\n" + "\t\"Cherry\"\n" + "\t]\n" + "}\n";
JSONObject object = new JSONObject(json);
2. Далее из созданного объекта необходимо получить следующий элемент – массив, создав экземпляр класса JSONArray, который инициали­зируется с помощью содержащегося в object массива. Данный массив мож­но получить с использованием функции getJSONArray(). В одном объекте может содержаться несколько массивов, других объектов и просто значе­ний, каждое из которых должно имеет свое название. В примере название массива – fruits, значение которого из объекта получаем следующим обра­зом:
JSONArray fruitsArray = object.getJSONArray("fruits");
Например, для получения числа из объекта необходимо воспользо-
ваться следующей функцией:
int myInt = object.getInt("myIntName");
Аналогично имеется возможность получить почти все типы данных.
3. Получение элемента массива осуществляется с использованием метода getString() (getInt, getLong и др.). Входным значением для данных функций является индекс массива:
String fruitName = fruitsArray.getString(4);
5.2.2. Использование GSON
При достаточно простой JSON-структуре рассмотренный метод «пар-
синга» является приемлемым. Но в случае, если структура содержит много
5.2. JSON-файлы
75
объектов разных типов данных, то рекомендуется использовать библиотеку Gson [38]. Она позволяет преобразовать JSON-структуру в соответствую­щий ей java-класс и наоборот. Данная библиотека не входит в стандартный набор средств среды разработки, поэтому в начале ее нужно подключить в
gradle, в части dependencies:
implementation 'com.google.code.gson:gson:2.8.5'
После чего необходимо «пересобрать» проект для закачивания необ-
ходимых файлов.
Далее создадим класс, представляющий JSON-структуру, в java-коде. Это можно сделать либо вручную, либо используя специальные онлайн­ресурсы (например, сайт http://www.jsonschema2pojo.org)[39]. Рассмотрен­ная ранее структура массива фруктов имеет достаточно простой формат, поэтому создадим класс вручную с произвольным названием, например
Fruits:
public class Fruits {
}
Рассматриваемая структура содержит массив строк, поэтому его необходимо включить в класс. В данном случае вместо типа данных масси­ва рекомендуется использовать List<String>, начальное значение которого
null:
public class Fruits {
private List<String> fruits = null;
}
Далее необходимо указать, что созданный список соответствует мас­сиву с названием fruits из рассматриваемого JSON. В этом случае исполь­зуется аннотация SerializedName(“name”), которая отвечает за хранение имени объекта при его сериализации и десериализации:
public class Fruits {
@SerializedName("fruits")
private List<String> fruits = null;
}
5. Работа с сетью
76
Аннотации представляют из себя дескрипторы, включаемые в текст программы, и используются для хранения метаданных программного кода, необходимых на разных этапах жизненного цикла программы. Информа­ция, хранимая в аннотациях, может использоваться соответствующими об­работчиками для создания необходимых вспомогательных файлов или для маркировки классов, полей и т.д.
Рассматриваемая библиотека преобразует JSON в java-класс с ис­пользованием сериализации, поэтому необходимо указать списку еще одну аннотацию – @Expose, определяющую можно ли данное поле получить из
JSON-структуры, т.е. можно ли его сериализовать:
public class Fruits {
@SerializedName("fruits")
@Expose
private List<String> fruits = null;
}
Создание класса, отображающего структуру JSON, закончено.
Теперь можно использовать библиотеку GSON. Для начала необхо­димо создать экземпляр класса Gson:
Gson gson = new Gson();
Данный класс содержит метод fromJson(), принимающий два пара­метра. Один из которых может быть одним из следующих типов:
1. String.
2. Reader.
3. JsonReader.
4. JsonElement.
Каждый из представленных выше типов содержит JSON-структуру в том или ином виде. В данном учебном пособии будет использоваться ее строковое представление. Таким образом, преобразование JSON в класс
Fruits будет представлено в следующем виде:
Gson gson = new Gson();
Fruits fruitItems = gson.fromJson(json, Fruits.class);
5.3. Геолокация
77
где в классе Fruits в списке fruits содержится массив фруктов из JSON- структуры.
И не забываем, что вся работа с сетью выполняется в дополнительном
потоке, реализация которого представлена в разд. 4.
5.3. Геолокация
Часто в приложении необходимо определять местонахождение поль­зователя (геолокацию). Рассмотрим действия, которые необходимо выпол­нить для реализации геолокации.
Первое – определить в приложении разрешения на использование гео­локации и сети Интернета, добавив в манифест следующие строки:
<uses-permission an­droid:name="android.permission.ACCESS_COARSE_LOCATION"/>
<uses-permission android:name="android.permission.INTERNET"/>
Второе – подключить библиотеки сервисов GooglePlay, содержащие требуемый инструментарий для работы с геоданными [40], дописав в gradle следующую строку:
implementation 'com.google.android.gms:play-services-location:16.0.0'
Далее вся работа будет происходить в классе Activity. Сначала прове­ряем, дал ли пользователь разрешения на использование его местоположе­ния [41]. Данная проверка должна происходить в начале работы Activity, таким образом, помещаем ее в функцию onCreate(). Если текущая версия > 23, то запрос выполняет подтверждение разрешения:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M &&
ActivityCompat.checkSelfPermission(this, Mani­fest.permission.ACCESS_COARSE_LOCATION)
Если в манифесте нет разрешения на использование геолокации, то необ­ходимо добавить его в манифест:
5. Работа с сетью
78
!= PackageManager.PERMISSION_GRANTED) { requestPermissions(new String[]{Manifest.permission.ACCESS_COARSE_LOCATION}, 1000); } else { LocationManager locationManager = (LocationManager) this .getSystemService(Context.LOCATION_SERVICE); Location location = locationManag­er.getLastKnownLocation(LocationManager.NETWORK_PROVIDER); try { String country = getLocation(location.getLatitude(), loca­tion.getLongitude()); Log.d("My_country",country); } catch (Exception e) { e.printStackTrace(); } }
Функция ActivityCompat.checkSelfPermission() принимает два параметра – текущее Activity и проверяемое разрешение Manifest.permission.ACCESS_COARSE_LOCATION. Выходное значение типа int: 0 – разрешение дано. Вместо числового значения можно использовать константную системную переменную PackageManager.PERMISSION_GRANTED. Если разрешение дано, проис­ходит вызов функции getLocation(), содержимое и описание которой при­ведено ниже. Если же нет, то необходимо проверить, запрашивалось ли раньше это разрешение, и отклонил ли его пользователь функцией ActivityCompat.shouldShowRequestPermissionRationale(). Если не отклонил, показываем окно запроса разрешения функцией ActivityCompat.requestPermissions() (рис. 24), которая на вход принимает текущее Activity и перечень необходимых для запроса разрешений. По­следний параметр задается, чтобы знать, на какие разрешения происходит запрос (может быть любое число по решению разработчика).
Для того чтобы после нажатия одной из кнопок получить результат выполнения операции, необходимо определить функцию
onRequestPermissionsResult().
5.3. Геолокация
79
Рис. 24. Запрос разрешения на геолокацию
@Override public void onRequestPermissionsResult(int requestCode, String[] permis­sions, int[] grantResults) { super.onRequestPermissionsResult(requestCode, permissions, grantRe­sults);
switch (requestCode) { case 1000: { if (grantResults.length > 0 && grantResults[0] == PackageManag­er.PERMISSION_GRANTED) { LocationManager locationManager = (LocationManager) this .getSystemService(Context.LOCATION_SERVICE);
5. Работа с сетью
80
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
Location location = locationManager
.getLastKnownLocation(LocationManager.NETWORK_PROVIDER); String country = getLocation(location.getLatitude(), loca­tion.getLongitude()); Log.d("My_country",country); return; }
} else { Toast.makeText(this, "Permission not granted", Toast.LENGTH_SHORT).show(); } break; } } }
В массиве grantResults хранится информация о разрешенных действи­ях. Можно просто проверить этот массив на пустоту, и если он не пуст, то разрешения были даны. Можно также проверить параметр requestCode (третий параметр в списке параметров функции ActivityCompat.requestPermissions()), который указывает, на какие разреше­ния был запрос.
При помощи разрешения Manifest.permission.ACCESS_FINE_LOCATION) приложение может получить доступ к точному местоположению. Manifest.permission.ACCESS_COARSE_LOCATION – приблизительное местоположение.
Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]