Журнал LinuxFormat - перейти на главную

LXF160:Android-про­грам­ми­ро­ва­ние

Материал из Linuxformat
Версия от 09:06, 29 сентября 2018; Olkol (обсуждение | вклад)

(разн.) ← Предыдущая | Текущая версия (разн.) | Следующая → (разн.)
Перейти к: навигация, поиск

[[Категория:Android]

» Про­грам­ми­ро­ва­ние С базами данных Android умеет работать и в облаках

Android:Ба­зы дан­ных в об­ла­ке

Джуль­ет­та Кемп по­ка­зы­ва­ет, как под­клю­чить­ся к об­ла­ку и за­ста­вить свое при­ло­жение взаи­мо­дей­ст­во­вать с сер­ве­ром.



В про­шлом ме­ся­це мы ра­бо­та­ли с локаль­ной ба­зой дан­ных Android. Ну, а ес­ли нуж­но свя­зать­ся с уда­лен­ной ба­зой дан­ных? Есть несколь­ко спо­со­бов это сде­лать, но на на­шем уро­ке мы рас­смот­рим ва­ри­ант, при ко­то­ром для об­щения с ба­зой дан­ных ис­поль­зу­ют­ся HTTP-за­про­сы – это, по­жа­луй, са­мый про­стой спо­соб. Этот ва­ри­ант так­же яв­ля­ет­ся рас­ши­ряе­мым: мы по­лу­ча­ем ин­фор­ма­цию из ба­зы дан­ных, но с по­мо­щью тех же ме­то­дов и API мож­но по­лу­чить все, что го­тов пре­доста­вить web-сер­вер. Мы бу­дем об­ра­ба­ты­вать дан­ные с по­мо­щью JSON и по­смот­рим, как де­лать это в фоне, что­бы основ­ное при­ло­жение не за­ви­са­ло при ожи­дании под­клю­чения от сер­ве­ра.

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

Ба­зо­вый за­прос HTTP

Пре­ж­де чем на­чать пи­сать код HTTP-за­про­са, при­ло­же­нию нуж­но пре­дос­та­вить пра­ва дос­ту­па к се­ти. До­бавь­те в ма­ни­фест стро­ку

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

Для вы­полнения HTTP-за­про­са су­ще­ст­ву­ет два ва­ри­ан­та API: HTTPClient и HttpURLConnection. На­чи­ная с Android 2.3, Google со­ве­ту­ет ис­поль­зо­вать HttpURLConnection, но на прак­ти­ке мно­гие еще пред­по­чи­та­ют HTTPClient, и с ним лег­че по­лу­чить со­ве­ты или по­мощь. Что­бы про­де­мон­ст­ри­ро­вать оба клас­са в дей­ст­вии, я восполь­зу­юсь HTTPClient для на­пи­сания про­сто­го ме­то­да по­лу­чения дан­ных с сер­ве­ра, а HttpURLConnection — для про­сто­го ме­то­да от­прав­ки дан­ных на сер­вер.

Раз­бор JSON

Для от­прав­ки дан­ных ту­да и об­рат­но вам по­на­до­бит­ся фор­мат хранения дан­ных, ко­то­рый смо­гут по­нять и те­ле­фон, и web-сер­вер. Хо­ро­ший ва­ри­ант, ко­то­рым мы здесь и восполь­зу­ем­ся – JSON, со­кра­щение от “JavaScript Object Notation [Фор­мат опи­сания объ­ек­тов JavaScript]”. Это лег­кий фор­мат об­ме­на дан­ны­ми, ко­то­рый по­ня­тен и для чтения/за­пи­си че­ло­ве­ку, и для раз­бо­ра/генера­ции ком­пь­ю­те­ру. Су­ще­ст­ву­ет мно­же­ст­во дру­гих фор­ма­тов об­ме­на дан­ны­ми, но JSON – хо­ро­ший ба­зо­вый фор­мат для стро­ко­вых дан­ных, ко­то­рые и хранятся в на­шей ба­зе.

Один JSONObject бу­дет на­по­ми­нать опи­сание од­но­го из за­даний из таб­ли­цы за­даний при­ло­жения ToDo:

{“_id”:1,”task”:”test task”, “duedate”:”2011-12-24”, “createdate”:1324660453756, “category_link”:2}

П­режде чем вступать во взаи­мо­дей­ст­ви­е с на­стоя­щим сер­ве­ром MySQL, рассмотрим про­стой при­ме­р на PHP, ко­то­рый воспро­из­во­дит эту стро­ку вруч­ную:

<?php

$output=array(

“_id” => 1,

“category” => ‘work’

); print(json_encode($output));

?>

Со­храните этот код на сер­ве­ре в фай­ле test.php, и те­перь он го­тов от­ве­тить на за­прос ва­ше­го те­ле­фо­на. За­тем соз­дай­те но­вый класс Android в сво­ем про­ек­те RemoteTodoSync.java:

public class RemoteTodoSync extends Activity {

public static final String WEBADDRESS = “http://yourserver/test.php”;

private static final String TAG = “RemoteTodoSync”;

public void onCreate(Bundle savedInstanceState)

{

super.onCreate(savedInstanceState);

retrieveData();

Intent i = new Intent();

setResult(RESULT_OK, i);

finish();

}

}

До­бавь­те его в AndroidManifest.xml, за­тем до­бавь­те та­кие стро­ки в ToDo.java для вы­зо­ва Дей­ст­вия и об­ра­бот­ки ре­зуль­та­та:

public void onCreate(Bundle savedInstanceState) {

...

startActivityForResult(new Intent(this.getBaseContext(),

RemoteTodoSync.class), SERVER_UPDATE_ID);

...

}

@Override protected void onActivityResult(int requestCode, int resultCode, Intent i) {

super.onActivityResult(requestCode, resultCode, i);

switch (requestCode) {

case SERVER_UPDATE_ID:

populateList();

default:

// do nothing; no other result expected

}

}

Ме­тод retrieveData() клас­са RemoteTodoSync по­лу­чит на­шу тес­то­вую стро­ку (пред­став­ляю­щую объ­ект JSONObject) из фай­ла test.php:

private void retrieveData() {

InputStream is = null;

try {

HttpClient httpClient = new DefaultHttpClient();

HttpPost httpPost = new HttpPost(WEBADDRESS);

HttpResponse response = httpClient.execute(httpPost);

HttpEntity entity = response.getEntity();

is = entity.getContent();

} catch (IOException e) {

Log.e(TAG, “IOException “ + e.toString());

e.printStackTrace();

}

try {

BufferedReader reader = new BufferedReader

(new InputStreamReader(is, “iso-8859-1”), 8);

String line = null;

while ((line = reader.readLine()) != null) {

JSONObject jo = new JSONObject(line);

Log.v(TAG, “id is: “ + jo.getInt(TodoDatabaseProvider.ID));

Log.v(TAG, “task is: “ + jo.getString(TodoDatabaseProvider.TASK));

}

is.close();

} catch (Exception e) {

Log.e(TAG, “Exception: “ + e.toString());

e.printStackTrace();

}

}

В пер­вом бло­ке try соз­да­ет­ся HttpClient, от­прав­ля­ет­ся за­прос на URL, за­дан­ный в WEBADDRESS, и ре­зуль­тат это­го за­про­са со­хра­ня­ет­ся в InputStream. Во вто­ром бло­ке про­из­во­дит­ся раз­бор это­го ре­зуль­та­та; сей­час ре­зуль­тат про­сто вы­во­дит­ся в лог-файл без до­полнитель­ных дей­ст­вий.

По­смот­рев на код, вы уви­ди­те, что на са­мом де­ле счи­ты­ва­ет­ся стро­ка (String), ко­то­рая за­тем вновь пре­вра­ща­ет­ся в JSONObject. Так ее про­ще ра­зо­брать, по­сколь­ку мож­но восполь­зо­вать­ся ме­то­да­ми раз­бо­ра JSONObject, а не занимать­ся этим вруч­ную. Как ил­лю­ст­ри­ру­ют при­ве­ден­ные здесь при­ме­ры по­лу­чения це­лых чи­сел и строк, ме­то­да­ми getX() из объ­ек­та JSONObject мож­но по­лу­чить зна­чения лю­бых ти­пов.

InputStreamReader пре­об­ра­зу­ет бай­ты вход­но­го по­то­ка (is) в сим­во­лы с по­мо­щью пре­об­ра­зо­ва­те­ля сим­во­лов. BufferedReader бу­фе­ри­зу­ет ре­зуль­тат в груп­пах по во­семь сим­во­лов. С по­мо­щью это­го при­ме­ра мож­но по­смот­реть, что про­ис­хо­дит (и убе­дить­ся, что все клас­сы инициа­ли­зи­ро­ва­ны пра­виль­но). Од­на­ко дан­ные, по­лу­чен­ные от на­стоя­ще­го сер­ве­ра MySQL, бу­дут пред­став­ле­ны в ви­де JSONArray, ко­то­рый тре­бу­ет несколь­ко боль­ше­го раз­бо­ра и вы­гля­дит так (квад­рат­ные скоб­ки ог­раничи­ва­ют мас­сив):

[{“_id”:1,”task”:”test task”, “duedate”:”2011-12-24”, “createdate”:1324660453756, “category_link”:2}

{“_id”:2,”task”:”new work task”, “duedate”:”2011-12-31”, “createdate”:1324661242417, “category_link”:1}]

Вот про­стой при­мер PHP-ко­да для за­пуска на web-сер­ве­ре (об­ра­ти­те внимание, здесь под­ра­зу­ме­ва­ет­ся, что ба­за MySQL уже соз­да­на и за­полнена дан­ны­ми, ина­че ника­ких дан­ных из за­про­са вы не по­лу­чи­те):

<?php mysql_connect(“host”, “user”, “password”); mysql_select_db(“todo”);

$output;

$q = mysql_query(“select * from tasks”); while($e = mysql_fetch_assoc($q)) {

$output[] = $e;

} print(json_encode($output)); mysql_close();

?>

Но­вый улуч­шен­ный ва­ри­ант раз­бо­ра JSON вы­гля­дит так (пер­вый блок try HttpClient ос­тал­ся без из­ме­не­ний):

try {

BufferedReader reader = new BufferedReader

(new InputStreamReader(is, “iso-8859-1”), 8);

String line = null;

while ((line = reader.readLine()) != null) {

JSONArray resultArray = new JSONArray(line);

for (int i = 0; i < resultArray.length(); i++) {

JSONObject jo = (JSONObject) resultArray.get(i);

Log.v(TAG, “id is: “ + jo.getInt(TodoDatabaseProvider.ID);

Log.v(TAG, “task is: “ + jo.getString(TodoDatabaseProvider.TASK));

}

}

is.close();

}

Сер­вер вернет од­ну стро­ку для все­го мас­си­ва (хо­тя ес­ли он вернет несколь­ко строк, наш код сумеет справиться с их об­ра­бо­ткой), ко­то­рую мы раз­би­ра­ем сна­ча­ла в от­дель­ные объ­ек­ты JSONObject, а за­тем в необ­хо­ди­мые ком­понен­ты. Опять же, воз­вра­ща­ет­ся стро­ка, ко­то­рая пре­об­ра­зу­ет­ся в JSONArray – это уп­ро­ща­ет раз­бор.

Ра­бо­та с по­лу­чен­ны­ми дан­ны­ми

Конеч­но, в том что­бы про­сто вы­вес­ти дан­ные в лог-файл, про­ку ма­ло. Нам нуж­на ка­кая-то фор­ма син­хрониза­ции с ба­зой дан­ных Android. На этом уро­ке я не бу­ду го­во­рить о син­хрониза­ции (это до­воль­но слож­ная те­ма – ло­ги­ка ее ра­бо­ты и про­бле­мы, ко­то­рые нуж­но об­су­дить, за­ня­ли бы боль­шую часть ста­тьи). Вме­сто это­го мы вы­полним про­стую про­вер­ку на дуб­ли­ка­ты и за­тем ско­пи­ру­ем дан­ные с сер­ве­ра на уст­рой­ст­во Android. В сле­дую­щем раз­де­ле мы зай­мем­ся от­прав­кой дан­ных с уст­рой­ст­ва Android на сер­вер, но по тем же при­чи­нам эко­но­мии мес­та и внимания мы не бу­дем ка­сать­ся сер­вер­но­го ко­да, необ­хо­ди­мо­го для их сбро­са в MySQL на сто­роне сер­ве­ра.

При из­менении ко­да локаль­ной ба­зы дан­ных для ра­бо­ты с сер­ве­ром ме­тод retrieveData() во мно­гом останет­ся прежним. Од­на­ко JSONArray todoDataFromServer станет пе­ре­мен­ной клас­са, и мы не бу­дем раз­би­рать ее в ком­понен­ты JSONObject. Вме­сто это­го мы до­ба­вим вы­зов addTodosFromServer() в onCreate() по­сле вы­зо­ва retrieveData(). Это боль­шой фраг­мент ко­да, и мы по­мес­ти­ли его на DVD в Лис­тин­ге 1.

addTodosFromServer() про­сто пе­ре­би­ра­ет объ­ек­ты JSONObject, воз­вра­щен­ные сер­ве­ром в мас­си­ве JSONArray, про­ве­ря­ет, есть ли объ­ект в локаль­ной ба­зе дан­ных, ме­то­дом checkIfTaskExists() и до­бав­ля­ет его в ба­зу дан­ных, ес­ли его там нет.

checkIfTaskExists() по­лу­ча­ет стро­ку «за­дания» из JSONObject и вы­пол­ня­ет SQL-за­прос, ко­то­рый ищет это зна­чение в локаль­ной ба­зе дан­ных. Ес­ли за­прос не воз­вра­ща­ет ре­зуль­та­тов, за­дания не су­ще­ст­ву­ет локаль­но, и его нуж­но до­ба­вить.

На­конец, ме­тод insertTask() до­бав­ля­ет но­вую стро­ку в ба­зу дан­ных, по­лу­ча­ет но­вые зна­чений из JSONObject и за­тем об­нов­ля­ет но­вый URI эти­ми зна­чения­ми. Ис­поль­зуе­мые здесь ме­то­ды insert() и update() рас­смат­ри­ва­лись в пре­ды­ду­щей ста­тье (ес­ли вы ее про­пусти­ли, их код мож­но най­ти на DVD).

Персональные инструменты
купить
подписаться
Яндекс.Метрика