Статьи

Android SDK: удаленные вызовы API

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

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

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


Для этого урока я буду использовать стандартный стек LAMP. Внешняя база данных будет MySQL, и я буду использовать PHP для создания API. Файл PHP будет записывать и читать в базу данных MySQL на основе запросов, полученных от приложения. Он также отправит результаты запросов обратно в приложение. Запросы и ответы будут в формате JSON, поскольку обе стороны понимают этот формат. Цель состоит в том, чтобы ввести имя человека из приложения Android и получить возраст человека из внешней базы данных. Я знаю, что это просто, но это широкая область, и это хороший способ проиллюстрировать процесс.

Примечание: я использую стек LAMP, потому что он бесплатный и доступный. В коде загрузки я дам дальнейшие инструкции по настройке для тех, кто в этом нуждается, но я не буду вдаваться в подробности об этой стороне. Я покажу код и объясню его функции, но было бы очень долго читать о PHP, SQL, JSON и так далее.
Также я не затрагиваю вопросы безопасности в этом уроке, я, скорее всего, расскажу об этом отдельно в дальнейшем.

Подключитесь к своему экземпляру MySQL, как хотите, и выполните следующее.

1
2
create database android_remoteDB
use android_remoteDB
1
2
3
4
5
create table users(
   _id int(10) primary key auto_increment,
   user_name varchar(25) not null unique,
   user_age int(2) not null
);

1
2
3
4
5
6
insert into users (user_name,user_age) values («green ranger»,27);
insert into users (user_name,user_age) values («red ranger»,24);
insert into users (user_name,user_age) values («blue ranger»,23);
insert into users (user_name,user_age) values («black ranger»,29);
insert into users (user_name,user_age) values («yellow ranger»,22);
insert into users(user_name,user_age) values («pink ranger»,21);

В этом руководстве я буду использовать один файл PHP с именем android_api.php . Это должно быть размещено где-нибудь, где к нему можно получить доступ через HTTP-запрос. Он может находиться в каталоге www на локальной установке LAMP или WAMP или на размещенном сервере, к которому вы можете получить доступ.

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

Примечание. На практике вы можете хорошо разбить вещи на несколько файлов PHP, поэтому у вас может быть один файл для параметров конфигурации, другой для обработки запросов к базе данных и т. Д. Чтобы все было просто и быстро, я буду придерживаться одного файла.
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
<?php
 
 
// check that the tag is present
if (isset($_POST[‘tag’]) && $_POST[‘tag’] != ») {
     
    $tag = $_POST[‘tag’];
 
    // Create a connection the the database and table
    $error = ‘Could not connect to the database’;
    //Depending on your setup you might need to change the below for your own credentials
    mysql_connect(‘localhost’,’root’,») or die ($error);
    mysql_select_db(‘remote_db’) or die ($error);
 
    
 
    // check if tag = ‘getAge’
    if ($tag == ‘getAge’) {
         
        //Get name
        $name = mysql_real_escape_string($_POST[‘name’]);
         
        //Select records from the database
        $find = mysql_query(«SELECT * FROM users WHERE user_name=’$name'»);
         
        //check that records exist
        if (mysql_num_rows($find)>0) {
         
          while ($find_row = mysql_fetch_assoc($find))
        {
            //Get user age
            $age = $find_row[‘user_age’];
        }
            //Return the response as a success, including age.
            $response[«success»] = 1;
            $response[«user»][«name»] = $name;
            $response[«user»][«age»] = $age;
            echo json_encode($response);
            }
         else {
            //Return error
            $response[«success»] = 0;
            $response[«error»] = 1;
            $response[«error_msg»] = «User could not be found»;
             echo json_encode($response);
         }
    } else {
        echo «No action for the tag»;
    }
} else {
    echo «Unknown error»;
}
?>

В среде IDE создайте новый проект Android, выбрав « Файл» → «Создать» → «Проект приложения Android» . Далее назовите проект. Я пошел с RemoteDB . Начиная с этого момента, вы можете нажимать кнопку «Далее» на экранах, которые вам представлены.



Чтобы создать новый класс, перейдите в новый проект и откройте папку src . Щелкните правой кнопкой мыши на пакете, разверните новый и выберите класс . Обязательно назовите его JSONParser .


Сам класс является довольно общим и широко используемым. Он отправляет запрос на указанный URL-адрес и преобразует ответ в формат JSON.

Затем откройте класс и вставьте следующее.

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
package com.example.remotedb;
 
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.DefaultHttpClient;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.util.List;
import android.util.Log;
 
public class JSONParser {
 
    static InputStream is = null;
    static JSONObject json = null;
    static String outPut = «»;
 
    // constructor
    public JSONParser() {
 
    }
 
    public JSONObject getJSONFromUrl(String url, List<NameValuePair> params) {
 
        // Making the HTTP request
        try {
             
            DefaultHttpClient httpClient = new DefaultHttpClient();
            HttpPost httpPost = new HttpPost(url);
            httpPost.setEntity(new UrlEncodedFormEntity(params));
 
            HttpResponse httpResponse = httpClient.execute(httpPost);
            HttpEntity httpEntity = httpResponse.getEntity();
            is = httpEntity.getContent();
 
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        } catch (ClientProtocolException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
 
        try {
            BufferedReader in = new BufferedReader(new InputStreamReader(
                    is, «iso-8859-1»), 8);
            StringBuilder sb = new StringBuilder();
            String line = null;
            while ((line = in.readLine()) != null) {
                sb.append(line + «\n»);
            }
            is.close();
            outPut = sb.toString();
            Log.e(«JSON», outPut);
        } catch (Exception e) {
            Log.e(«Buffer Error», «Error converting result » + e.toString());
        }
 
        try {
            json = new JSONObject(outPut);
        } catch (JSONException e) {
            Log.e(«JSON Parser», «Error parsing data » + e.toString());
        }
 
        // return JSON String
        return json;
 
    }
}

Откройте файл activity_main.xml в Res → Layout …

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
<?xml version=»1.0″ encoding=»utf-8″?>
<LinearLayout xmlns:android=»http://schemas.android.com/apk/res/android»
        android:layout_width=»fill_parent»
        android:layout_height=»wrap_content»
        android:orientation=»vertical»
         >
        
        <!— User Name Label —>
        <TextView
            android:layout_width=»fill_parent»
            android:layout_height=»wrap_content»
            android:text=»User Name:»
            android:padding=»10dip»
            android:textColor=»#33B5E5″ />
         
        <!— User Name Text Field —>
        <EditText
            android:id=»@+id/Enter»
            android:hint=»Please Enter a Name»
            android:layout_width=»fill_parent»
            android:layout_height=»wrap_content» />
         
         <!— Text View to display the returned age or a error message —>
        <TextView android:id=»@+id/Results»
                    android:layout_width=»fill_parent»
                    android:layout_height=»wrap_content»
                    android:text=»User Age:»
                    android:textColor=»#99CC00″
                    android:padding=»10dip»
                    android:textStyle=»bold»/>
  
        <!— Button to fire the getAge Method—>
        <Button
            android:id=»@+id/GetAge»
            android:layout_width=»fill_parent»
            android:layout_height=»wrap_content»
            android:layout_marginTop=»10dip»
            android:text=»Get Age» />
  
         
    </LinearLayout>

Примечание. Как правило, рекомендуется размещать текст в файле strings.xml .

Теперь о основной деятельности, которая находится в Src → Packagename → MainActicity.java …

Опять же, это довольно просто. У нас есть метод getAge , который вызывается при нажатии кнопки. Он передает параметры, такие как URL и имя, в JSONParser и создает объект JSON из ответа. Затем он читается, и в текстовом окне результатов отображается либо возраст, либо сообщение об ошибке, в зависимости от того, был ли запрос успешным или нет.

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
package com.example.remotedb;
 
import java.util.ArrayList;
import java.util.List;
import org.apache.http.NameValuePair;
import org.apache.http.message.BasicNameValuePair;
import org.json.JSONException;
import org.json.JSONObject;
import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
 
public class MainActivity extends Activity {
 
    private JSONParser jsonParser;
    //10.0.2.2 is the address used by the Android emulators to refer to the host address
    // change this to the IP of another host if required
    private static String ageURL = «http://10.0.2.2/android_api.php»;
    private static String getAge = «getAge»;
    private static String jsonResult = «success»;
    String uname;
    String age_res;
    TextView Results;
     
     
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
         
        //Invoke the Json Parser
        jsonParser = new JSONParser();
 
     
     final EditText Enter = (EditText) findViewById(R.id.Enter);
     Results = (TextView) findViewById(R.id.Results);
     Button GetAge = (Button) findViewById(R.id.GetAge);
     
    //Get Age Button
    GetAge.setOnClickListener(new View.OnClickListener() {
        public void onClick(View view) {
             
            //Get the contents of the edit text
            uname = Enter.getText().toString();
             
            //Pass the name to the JSON method and create a JSON object from the return
            JSONObject json = getAge(uname);
             
            // check the success of the JSON call
            try {
                if (json.getString(jsonResult) != null) {
                    Results.setText(«»);
                    String res = json.getString(jsonResult);
                    if(Integer.parseInt(res) == 1){
                        //If it’s a success create a new JSON object for the user element
                        JSONObject json_user = json.getJSONObject(«user»);
                        //Set the results text to the age from the above JSON object
                        Results.setText(«User Age: » + json_user.getString(«age»));
                         
                    }else{
                        //If the user could not be found
                        Results.setText(«User could not be found»);
                    }
                }
            } catch (JSONException e) {
                e.printStackTrace();
            }
             
             
        }
         
         
    });
     
    }
     
    //The below passes the tag and the user name over to the JSON parser class
    public JSONObject getAge(String name){
        List<NameValuePair> params = new ArrayList<NameValuePair>();
        params.add(new BasicNameValuePair(«tag», getAge));
        params.add(new BasicNameValuePair(«name», name));
        JSONObject json = jsonParser.getJSONFromUrl(ageURL, params);
        return json;
    }
 
}

Одним из требований приложения является разрешение android.permission.INTERNET . Это необходимо добавить в файл AndroidManifest.xml .


Линия это:

1
uses-permission android:name=»android.permission.INTERNET»

После добавления манифест должен выглядеть примерно так, как показано ниже.

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
<?xml version=»1.0″ encoding=»utf-8″?>
<manifest xmlns:android=»http://schemas.android.com/apk/res/android»
    package=»com.example.remotedb»
    android:versionCode=»1″
    android:versionName=»1.0″ >
 
    <uses-sdk
        android:minSdkVersion=»8″
        android:targetSdkVersion=»17″ />
    <uses-permission android:name=»android.permission.INTERNET» />
 
    <application
        android:allowBackup=»true»
        android:icon=»@drawable/ic_launcher»
        android:label=»@string/app_name»
        android:theme=»@style/AppTheme» >
        <activity
            android:name=»com.example.remotedb.MainActivity»
            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>
 
</manifest>

Вы должны быть хороши, чтобы пойти на этом этапе. Попробуйте запустить приложение.


Да, конечный результат крайне не впечатляет. Мы заставили номер появиться на экране. Хотя на самом деле мы сделали гораздо больше. Мы взяли информацию с одной платформы и отправили на совершенно чужую платформу. Зарубежная платформа смогла выполнить задачу на основе этой информации и извлечь информацию из базы данных, к которой исходная платформа не знала или не имела доступа к ней. Вторая платформа смогла отправить информацию обратно в оригинал, который затем смог отобразить информацию для пользователя.

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

Простые текстовые объекты, такие как JSON, очень похожи на азбуку Морзе. Они превосходят другие языки и могут быть поняты повсеместно. Это делает их чрезвычайно полезными и универсальными. Это простые вещи, которые часто решают сложные проблемы.