Напишем приложение сложение двух чисел, где сложение двух чисел происходит на стороне сервера.
Эта статья из цикла статей Клиент-серверные приложения.
Статья из цикла «Сложение двух чисел». Для меня минимальное освоение любой системы программирования начинается с возможности создания такой программы. Если можно написать приложение, в которой пользователь может ввести два числа, считать их, провести с ними какие-то действия, а потом вывести результат, то, значит, базовое владение имеется. И много задач именно из области программирования, алгоритмики можно будет решать, зная, как в конкретной системе программирования запрограммировать такую программу.
В статье Сложение двух чисел в Android Studio (клиент-серверное приложение) приведен вариант с использованием устаревших классов. В данной статье используются классы, пришедшие на замену.
В статье показан вариант приложения для отправки POST параметров.
Содержание
- Постановка задачи
- Серверная часть
- Создание Android проекта
- XML разметка
- Добавляем разрешения
- Java код
- Разнесение кода по методам
Постановка задачи
В статье показан пример приложения под Android по сложению двух чисел. И там сложение двух чисел происходило в самом приложении, что логично. А в этом приложении мы из полей ввода считаем два числа и отправим их на сервер, который сложит два числа, отправит нам ответ, и мы этот ответ отобразим в текстовом поле.
На сервер поступает HTTP запрос с двумя переменными a и b. Переменные a и b передаются через GET параметры, то есть через адресную строку. Пример такого запроса:
http://[путь с скрипту]?a=2&b=3
Серверная часть
У вас должен быть сервер, доступный из интернета, к которому можно обращаться.
В статье приведен пример серверной части на Java.
В статье приведен пример серверной части на PHP.
В статье буду использовать PHP скрипт, который я расположил по адресу http://demo.harrix.org/demo0011 (если перейти по ссылке без параметров, то должно выдаваться error).
Пример запроса к серверу http://demo.harrix.org/demo0011?a=2&b=3 (а вот тут должно появиться число 5).
Создание Android проекта
Надеюсь, что вы сможете сами создать болванку приложения, так что закинул под сплойер.
XML разметка
Пусть разметка файла activity_main.xml будет такой.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:paddingLeft="16dp" android:paddingRight="16dp" android:orientation="vertical" > <EditText android:id="@+id/editText" android:layout_width="match_parent" android:layout_height="wrap_content" android:ems="10" android:inputType="textPersonName" /> <EditText android:id="@+id/editText2" android:layout_width="match_parent" android:layout_height="wrap_content" android:ems="10" android:inputType="textPersonName" /> <Button android:id="@+id/button" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="Button" /> <TextView android:id="@+id/textView" android:layout_width="match_parent" android:layout_height="wrap_content" /> </LinearLayout> |
Есть два поля ввода чисел, кнопка и поле для вывода суммы чисел. Все элементы имеют свои идентификаторы android:id.
Добавляем разрешения
Приложение у нас будет обращаться в интернет. Поэтому нужно ей дать на это разрешение в AndroidMainfest.xml.
1 |
<uses-permission android:name="android.permission.INTERNET"/> |
Java код
Был такой код файла MainActivity.java.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
package com.example.clientadding2numbers; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); } } |
Вначале объявим переменные кнопки, полей ввода и поля для вывода результата и соединим их с элементами из XML файла. Также назначим слушателя для клика кнопки.
1 2 3 4 5 6 7 8 9 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 |
package com.example.clientadding2numbers; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.view.View; import android.widget.Button; import android.widget.EditText; import android.widget.TextView; public class MainActivity extends AppCompatActivity { private TextView textView; private EditText editText; private EditText editText2; private Button button; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); textView = (TextView)findViewById(R.id.textView); editText = (EditText)findViewById(R.id.editText); editText2 = (EditText)findViewById(R.id.editText2); button = (Button)findViewById(R.id.button); button.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { } }); } } |
Нам потребуется метод, который переводит InputStream в String.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
private String convertInputStreamToString(InputStream in) { BufferedReader reader = null; StringBuffer response = new StringBuffer(); try { reader = new BufferedReader(new InputStreamReader(in)); String line = ""; while ((line = reader.readLine()) != null) { response.append(line); } } catch (IOException e) { e.printStackTrace(); } finally { if (reader != null) { try { reader.close(); } catch (IOException e) { e.printStackTrace(); } } } return response.toString(); } |
Запросы к серверу по времени могут быть длительными. Поэтому обязательно делаем их в другом потоке. Для этого внутри класса MainActivity создадим класс-наследник от AsyncTask.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
class MyAsyncTask extends AsyncTask<String, String, String> { @Override protected void onPreExecute() { super.onPreExecute(); } @Override protected String doInBackground(String... params) { //Тут будет запрос к серверу return null; } @Override protected void onPostExecute(String result) { super.onPostExecute(result); } } |
В классе MyAsyncTask объявим переменные.
1 |
String a, b, answerHTTP; |
А также определим переменную server, которая будет указывать на сервер, к которому мы будем осуществлять запрос. Если вы используйте свой сервер, то поменяйте значение.
1 |
String server = "http://demo.harrix.org/demo0011/"; |
В методе onPreExecute() считаем данные из полей ввода.
1 2 3 4 5 6 |
@Override protected void onPreExecute() { a = editText.getText().toString(); b = editText2.getText().toString(); super.onPreExecute(); } |
В методе onPostExecute() выведем ответ сервера.
1 2 3 4 5 |
@Override protected void onPostExecute(String result) { super.onPostExecute(result); textView.setText(answerHTTP); } |
В методе doInBackground() будем обращаться к серверу.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
@Override protected String doInBackground(String... params) { URL url; HttpURLConnection urlConnection = null; try { url = new URL(server + "?a=" + a + "&b=" + b); urlConnection = (HttpURLConnection) url.openConnection(); int responseCode = urlConnection.getResponseCode(); if(responseCode == HttpURLConnection.HTTP_OK){ answerHTTP = convertInputStreamToString(urlConnection.getInputStream()); } } catch (Exception e) { e.printStackTrace(); } finally { if (urlConnection != null) { urlConnection.disconnect(); } } return null; } |
Теперь остается только создать экземпляр данного класса MyAsyncTask в слушателе клика кнопки и запустить его.
1 2 3 4 5 6 |
button.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { new MyAsyncTask().execute(); } }); |
В итоге у меня получился вот такой файл.
1 2 3 4 5 6 7 8 9 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 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 |
package com.example.clientadding2numbers; import android.os.AsyncTask; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.view.View; import android.widget.Button; import android.widget.EditText; import android.widget.TextView; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.net.HttpURLConnection; import java.net.URL; public class MainActivity extends AppCompatActivity { private TextView textView; private EditText editText; private EditText editText2; private Button button; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); textView = (TextView)findViewById(R.id.textView); editText = (EditText)findViewById(R.id.editText); editText2 = (EditText)findViewById(R.id.editText2); button = (Button)findViewById(R.id.button); button.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { new MyAsyncTask().execute(); } }); } class MyAsyncTask extends AsyncTask<String, String, String> { String a, b, answerHTTP; String server = "http://demo.harrix.org/demo0011/"; @Override protected void onPreExecute() { a = editText.getText().toString(); b = editText2.getText().toString(); super.onPreExecute(); } @Override protected String doInBackground(String... params) { URL url; HttpURLConnection urlConnection = null; try { url = new URL(server + "?a=" + a + "&b=" + b); urlConnection = (HttpURLConnection) url.openConnection(); int responseCode = urlConnection.getResponseCode(); if(responseCode == HttpURLConnection.HTTP_OK){ answerHTTP = convertInputStreamToString(urlConnection.getInputStream()); } } catch (Exception e) { e.printStackTrace(); } finally { if (urlConnection != null) { urlConnection.disconnect(); } } return null; } @Override protected void onPostExecute(String result) { super.onPostExecute(result); textView.setText(answerHTTP); } } private String convertInputStreamToString(InputStream in) { BufferedReader reader = null; StringBuffer response = new StringBuffer(); try { reader = new BufferedReader(new InputStreamReader(in)); String line = ""; while ((line = reader.readLine()) != null) { response.append(line); } } catch (IOException e) { e.printStackTrace(); } finally { if (reader != null) { try { reader.close(); } catch (IOException e) { e.printStackTrace(); } } } return response.toString(); } } |
Можно остановиться на этом, так как у вас есть полноценный работающий код.
Разнесение кода по методам
Сделаем две «оптимизации» вышеприведенного кода.
Во-первых, давайте параметры, которые мы хотим закинуть в GET, будем передавать в виде HashMap
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
private String getDataString(HashMap<String, String> params) throws UnsupportedEncodingException { StringBuilder result = new StringBuilder(); boolean first = true; for (Map.Entry<String, String> entry : params.entrySet()) { if (first) first = false; else result.append("&"); result.append(URLEncoder.encode(entry.getKey(), "UTF-8")); result.append("="); result.append(URLEncoder.encode(entry.getValue(), "UTF-8")); } return result.toString(); } |
Во-вторых, обработку запроса и получение ответа тоже перенесем в отдельный метод.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
public String performGetCall(String requestURL, HashMap<String, String> getDataParams) { String response = ""; URL url; HttpURLConnection urlConnection = null; try { url = new URL(requestURL + "?" + getDataString(getDataParams)); urlConnection = (HttpURLConnection) url.openConnection(); int responseCode = urlConnection.getResponseCode(); if(responseCode == HttpURLConnection.HTTP_OK){ response = convertInputStreamToString(urlConnection.getInputStream()); } } catch (Exception e) { e.printStackTrace(); } finally { if (urlConnection != null) { urlConnection.disconnect(); } } return response; } |
Тогда в методе doInBackground() будет достаточно составить HashMap с нужными параметрами, а потом вызвать performGetCall().
1 2 3 4 5 6 7 8 9 10 |
@Override protected String doInBackground(String... params) { HashMap<String, String> postDataParams = new HashMap<String, String>(); postDataParams.put("a", a); postDataParams.put("b", b); answerHTTP = performGetCall(server, postDataParams); return null; } |
В итоге получим такой код.
1 2 3 4 5 6 7 8 9 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 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 |
package com.example.clientadding2numbers; import android.os.AsyncTask; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.view.View; import android.widget.Button; import android.widget.EditText; import android.widget.TextView; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.UnsupportedEncodingException; import java.net.HttpURLConnection; import java.net.URL; import java.net.URLEncoder; import java.util.HashMap; import java.util.Map; public class MainActivity extends AppCompatActivity { private TextView textView; private EditText editText; private EditText editText2; private Button button; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); textView = (TextView)findViewById(R.id.textView); editText = (EditText)findViewById(R.id.editText); editText2 = (EditText)findViewById(R.id.editText2); button = (Button)findViewById(R.id.button); button.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { new MyAsyncTask().execute(); } }); } class MyAsyncTask extends AsyncTask<String, String, String> { String a, b, answerHTTP; String server = "http://demo.harrix.org/demo0011/"; @Override protected void onPreExecute() { a = editText.getText().toString(); b = editText2.getText().toString(); super.onPreExecute(); } @Override protected String doInBackground(String... params) { HashMap<String, String> postDataParams = new HashMap<String, String>(); postDataParams.put("a", a); postDataParams.put("b", b); answerHTTP = performGetCall(server, postDataParams); return null; } @Override protected void onPostExecute(String result) { super.onPostExecute(result); textView.setText(answerHTTP); } } public String performGetCall(String requestURL, HashMap<String, String> getDataParams) { String response = ""; URL url; HttpURLConnection urlConnection = null; try { url = new URL(requestURL + "?" + getDataString(getDataParams)); urlConnection = (HttpURLConnection) url.openConnection(); int responseCode = urlConnection.getResponseCode(); if(responseCode == HttpURLConnection.HTTP_OK){ response = convertInputStreamToString(urlConnection.getInputStream()); } } catch (Exception e) { e.printStackTrace(); } finally { if (urlConnection != null) { urlConnection.disconnect(); } } return response; } private String getDataString(HashMap<String, String> params) throws UnsupportedEncodingException { StringBuilder result = new StringBuilder(); boolean first = true; for (Map.Entry<String, String> entry : params.entrySet()) { if (first) first = false; else result.append("&"); result.append(URLEncoder.encode(entry.getKey(), "UTF-8")); result.append("="); result.append(URLEncoder.encode(entry.getValue(), "UTF-8")); } return result.toString(); } private String convertInputStreamToString(InputStream in) { BufferedReader reader = null; StringBuffer response = new StringBuffer(); try { reader = new BufferedReader(new InputStreamReader(in)); String line = ""; while ((line = reader.readLine()) != null) { response.append(line); } } catch (IOException e) { e.printStackTrace(); } finally { if (reader != null) { try { reader.close(); } catch (IOException e) { e.printStackTrace(); } } } return response.toString(); } } |
Всё. Можете запускать приложение и проверять работу.