СоХабр закрыт.

С 13.05.2019 изменения постов больше не отслеживаются, и новые посты не сохраняются.

| сохранено

H Подключение к mysql при помощи android api, json'a и php в черновиках Из песочницы Tutorial

  1. Введение
  2. Создание проекта в Eclipse (далее ADT)
  3. Создание backend
    1. Шаг 1
    2. Шаг 2
    3. Шаг 3 Заходим в наш PHPMyAdmin (чаще всего это site.ru/phpmyadmin) и создаем новую базу под название andro. В этой базе нам нужно создать таблицу под названием products, которая будет состоять из сущностей: id_product, title, short, price. id_product — номер продукта (значение типа int, длина 128, AUTOINCREMENT); title — название продукта (значение типа VarChar, длина 512); short — описание продукта (значение типа VarChar, длина 2048); price — цена продукта (значение типа int, длина 128). Шаг 4 Открываем файл andro.php для редактирования (для уменьшения вероятности появления ошибки сделайте кодировку файла ANSI). В этом файле нам нужно будет прописать функционал получения данных из нашей БД и вывод их в формате JSON (если вам неизвестно — эта аббревиатура, то для начала прочитайте о нем). Итак, файл должен быть такого содержания: <?php // *** Я для наглядности решил взять константы в пример для данных для подключения*** define('HOST_DBNAME','mysql:host=localhost;dbname=andro'); define('USERNAME','root'); define('PASSWORD','password'); define('JSON_OBJECTS','product'); try { // *** Определяем PDO объект для работы с БД *** $db = new PDO(HOST_DBNAME,USERNAME,PASSWORD); // *** Подготавливаем SQL запрос *** $stmt = $db->prepare("SELECT * FROM `products`"); // *** Выводим результат выполнения запроса *** if($stmt->execute()) { // *** Массив для дальнейшего преобразования в JSON *** $arrToJSON = Array(); // *** Заполняем наш массив для дальнейшего преобразования в JSON *** while($row = $stmt->fetch()) { $arrToJSON[JSON_OBJECTS][] = $row; } // *** Выводим построенный JSON код для дальнейшей работы с ним в приложении *** print json_encode($arrToJSON); // *** Закрываем соединение *** $db = null; // *** Если где-то произошла ошибка выводим её *** } catch (PDOException $e) { print "ERROR WITH PDO >> \n ".$e->getMessage(); } ?> Пока не заходим на andro.php через сайт, так как значений у нас в БД еще нет, поэтому отредактируем ранее созданный файл add_prod.php. Шаг 5 Открываем файл add_prod.php для редактирования (также про кодировку не забываем). Содержимое файла должно быть таким: <form action="" method="get"> Название: <input type="text" name="title" /><br> Описание: <input type="text" name="short" /><br> Цена: <input type="text" name="price" /><br> <input type="hidden" name="add" value="yes" /> <input type="submit" value="Добавить запись" /> </form> <?php // *** Если данные с новым продуктом пришли то выполняем скрипт добавления *** if($_GET["add"] == "yes") { // *** Как всегда расставляем все точки над ё *** define('HOST_DBNAME','mysql:host=localhost;dbname=andro'); define('USERNAME','root'); define('PASSWORD','password'); try { // *** Определяем объект PDO и тем самым подключаемся к нашей БД *** $db = PDO(HOST_DBNAME,USERNAME,PASSWORD); // *** Подготавливаем SQL запрос *** $stmt = $db->prepare('INSERT INTO `products` AS p (p.`title`,p.`short`,p.`price`) VALUES (?,?,?)'); // *** Выполняем запрос и проверяем его выполнение *** if($stmt->execute(Array($_GET['title'],$_GET['short'],$_GET['price']))) { // *** Если запрос прошел *** print 'SUCCESFULL. A new product create!'; } else { // *** Если ничего не получилось и в catch *** print 'ERROR: A product not are create! Please try again'; } // *** Закрываем соединение и чистим $db *** $db = null; // *** Если появилась ошибка *** } catch(PDOException $e) { print "ERROR WITH PDO >> \n ".$e->getMessage; } Шаг 6 Теперь нужно перейти на www.site.ru/add_prod.php и добавить записи (количество на ваше усмотрение). Шаг 7 После добавления зайдите на www.site.ru/andro.php и в результате на странице должно получиться что-то вроде этого . Итак, мы закончили с серверной частью, теперь перейдем к так называемому frontend'у или к части приложения Android. Создание приложения Android Итак, в начале урока мы уже создали форму для дальнейшей разработки, давайте перейдем в ADT. Шаг 1 Перейдем в activity_main.xml и добавим туда это: <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:paddingBottom="@dimen/activity_vertical_margin" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" tools:context=".MainActivity" > <ListView android:id="@+id/listView1" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_alignParentLeft="true" android:layout_alignParentTop="true" android:layout_marginLeft="19dp" android:layout_marginTop="15dp" > </ListView> </RelativeLayout> Далее создадим еще один layout под название list_item.xml (для создания нового layout перейдите в File->New->Android XML File. Resource Type: Layot, Project: AndroMySQL, File: list_item, Root Element: LinearLayout) и добавим в него это: <?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" android:padding="10dp" android:paddingLeft="10dp" android:paddingRight="10dp" > <!-- Поле названия--> <TextView android:id="@+id/name" android:layout_width="fill_parent" android:layout_height="wrap_content" android:paddingBottom="2dip" android:paddingTop="6dip" android:textColor="#43bd00" android:textSize="16sp" android:textStyle="bold" /> <!-- Поле описание --> <TextView android:id="@+id/short" android:layout_width="fill_parent" android:layout_height="wrap_content" android:paddingBottom="2dip" android:textColor="#acacac" /> <!-- Поле цены --> <TextView android:id="@+id/price" android:layout_width="fill_parent" android:layout_height="wrap_content" android:paddingBottom="0dip" android:textColor="#acacac" /> </LinearLayout> Шаг 2 Для работы нам нужен новый класс (class) для получения JSON сформированной строки из нашего andro.php. Переходим в File->New->Class, там вводим: Source folder: AndroMySQL/src, Package: com.example.andromysql (или ваш класс), Name: ServiceHandler, Super Class: java.lang.Object В ServiceHandler.java добавляем текст: package com.example.andromysql; //подключаем наш проект //Подключаем библиотеки нужные для работы нашего нового класса import java.io.IOException; import java.io.UnsupportedEncodingException; import java.util.List; 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.HttpGet; import org.apache.http.client.methods.HttpPost; import org.apache.http.client.utils.URLEncodedUtils; import org.apache.http.impl.client.DefaultHttpClient; import org.apache.http.util.EntityUtils; public class ServiceHandler { static String response = null; public final static int GET = 1; public final static int POST = 2; public ServiceHandler() { } /** * Making service call * @url - ссылка на запрос * @method - http метод для получения значений * */ public String makeServiceCall(String url, int method) { return this.makeServiceCall(url, method, null); } /** * Making service call * @url - url для запроса * @method - http метод для получения значений * @params - http параметры * */ public String makeServiceCall(String url, int method, List<NameValuePair> params) { try { //Клиент HTTP DefaultHttpClient httpClient = new DefaultHttpClient(); HttpEntity httpEntity = null; HttpResponse httpResponse = null; // Проверяем HTTP на метод POST if (method == POST) { HttpPost httpPost = new HttpPost(url); // добавляем параметры if (params != null) { httpPost.setEntity(new UrlEncodedFormEntity(params)); } httpResponse = httpClient.execute(httpPost); } else if (method == GET) { // конкатерируем параметры к URL if (params != null) { String paramString = URLEncodedUtils .format(params, "utf-8"); url += "?" + paramString; } HttpGet httpGet = new HttpGet(url); httpResponse = httpClient.execute(httpGet); } httpEntity = httpResponse.getEntity(); response = EntityUtils.toString(httpEntity); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } catch (ClientProtocolException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } return response; } } Сохраняем наш класс и переходим в MainActivity.java Шаг 3 В MainActivity.java нам нужно добавить такое содержимое: package com.example.andromysql; //Подключаем нужные библиотеки import java.util.ArrayList; import java.util.HashMap; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import android.app.ListActivity; import android.app.ProgressDialog; import android.os.AsyncTask; import android.os.Bundle; import android.util.Log; import android.widget.ListAdapter; import android.widget.ListView; import android.widget.SimpleAdapter; public class MainActivity extends ListActivity { private ProgressDialog pDialog; // URL где хранится наш отформатированный JSON private static String url = "http://www.develsi.com/view.php"; // поменяйте значение на свое // JSON Теги, чтобы в будущем оброщаться в методах private static final String TAG_PRODUCTS = "products"; private static final String TAG_ID = "id_product"; private static final String TAG_NAME = "title"; private static final String TAG_SHORT = "short"; private static final String TAG_PRICE = "price"; // JSONArray - тип данных : массив из JSON строки JSONArray products = null; // Hashmap для ListView ArrayList<HashMap<String, String>> productList; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); productList = new ArrayList<HashMap<String, String>>(); ListView lv = getListView(); // Вызываем асинхронное получение JSON new GetProducts().execute(); } /** * Асинхронный класс для получения JSON строки с HTTP страницы * */ private class GetProducts extends AsyncTask<Void, Void, Void> { @Override protected void onPreExecute() { super.onPreExecute(); // Показываем диалог загрузки на время получения данных pDialog = new ProgressDialog(MainActivity.this); pDialog.setMessage("Загрузка..."); pDialog.setCancelable(false); pDialog.show(); } @Override protected Void doInBackground(Void... arg0) { // Создаем экземпляр класса ServiceHandler ServiceHandler sh = new ServiceHandler(); // Делаем запрос на наш url и получаем ответ в переменную типа String sh String jsonStr = sh.makeServiceCall(url, ServiceHandler.GET); Log.d("Response: ", "> " + jsonStr);// Выводим в консоль результат, то что пришло с сервера //Лучше читать, что именно пришло, так как бывают ошибки из-за лишних символов, пробел и перенос строки - это тоже символ. if (jsonStr != null) { //Проверяем не пустой ли нам пришел ответ try { JSONObject jsonObj = new JSONObject(jsonStr); // Создаем переменную типа JSONObject // Получаем массив продуктов из нашего объекта products = jsonObj.getJSONArray(TAG_PRODUCTS); // Проходим по всем продуктом циклом, для создания ассоциативного массива for (int i = 0; i < products.length(); i++) { JSONObject p = products.getJSONObject(i); //Создаем некоторые переменные для дальнейшей работы String id = p.getString(TAG_ID); // достаем номер продукта String name = p.getString(TAG_NAME); //достаем название продукта String email = p.getString(TAG_SHORT); //достаем описание продукта String address = p.getString(TAG_PRICE); //достаем цену продукта // Временная переменная hashmap для одиночной записи продукта HashMap<String, String> product = new HashMap<String, String>(); // Добовляем значения в наш HashMap в виде key => value product.put(TAG_ID, id); product.put(TAG_NAME, name); product.put(TAG_EMAIL, email); // Добовляем продукты в наш лист продуктов productList.add(product); } } catch (JSONException e) { e.printStackTrace(); } } else { Log.e("ServiceHandler", "Не удалось получить никаких данных из адреса"); } return null; } @Override protected void onPostExecute(Void result) { super.onPostExecute(result); // Закрываем наш диалог загрузки if (pDialog.isShowing()) pDialog.dismiss(); /** * Обновляем запарсенные данные JSON в наш ListView * */ ListAdapter adapter = new SimpleAdapter( MainActivity.this, producttList, R.layout.list_item, new String[] { TAG_NAME, TAG_SHORT }, new int[] { R.id.name, R.id.short}); // Я не стал использовать в примере еще 2 переменные так как мне это не нужно на данный момент, но если вы желаете включить их в показ, то просто добавьте строку TAG_PRICE в new String[] {...} и R.id.price в new int[] {...} setListAdapter(adapter); // Теперь через адаптер добовляем наш получившийся массив в ListView } } } Итог После всех этих трудоемких шагов вы можете запустить ваше приложение на тестовом девайсе, предварительно включив сервер и проверив, выдает ли страница andro.php JSON код. Если вы все выполнили правильно, в итоге у вас должно получится так: 1. Запускаете приложение. 2. Открывается диалог загрузки. 3. Диалог загрузки пропадает и появляется список названий и описаний продуктов. 4. При нажатии кнопки назад происходит выход из приложения. Спасибо всем за внимание.
    4. Заходим в наш PHPMyAdmin (чаще всего это site.ru/phpmyadmin) и создаем новую базу под название andro. В этой базе нам нужно создать таблицу под названием products, которая будет состоять из сущностей: id_product, title, short, price. id_product — номер продукта (значение типа int, длина 128, AUTOINCREMENT); title — название продукта (значение типа VarChar, длина 512); short — описание продукта (значение типа VarChar, длина 2048); price — цена продукта (значение типа int, длина 128). Шаг 4 Открываем файл andro.php для редактирования (для уменьшения вероятности появления ошибки сделайте кодировку файла ANSI). В этом файле нам нужно будет прописать функционал получения данных из нашей БД и вывод их в формате JSON (если вам неизвестно — эта аббревиатура, то для начала прочитайте о нем). Итак, файл должен быть такого содержания: <?php // *** Я для наглядности решил взять константы в пример для данных для подключения*** define('HOST_DBNAME','mysql:host=localhost;dbname=andro'); define('USERNAME','root'); define('PASSWORD','password'); define('JSON_OBJECTS','product'); try { // *** Определяем PDO объект для работы с БД *** $db = new PDO(HOST_DBNAME,USERNAME,PASSWORD); // *** Подготавливаем SQL запрос *** $stmt = $db->prepare("SELECT * FROM `products`"); // *** Выводим результат выполнения запроса *** if($stmt->execute()) { // *** Массив для дальнейшего преобразования в JSON *** $arrToJSON = Array(); // *** Заполняем наш массив для дальнейшего преобразования в JSON *** while($row = $stmt->fetch()) { $arrToJSON[JSON_OBJECTS][] = $row; } // *** Выводим построенный JSON код для дальнейшей работы с ним в приложении *** print json_encode($arrToJSON); // *** Закрываем соединение *** $db = null; // *** Если где-то произошла ошибка выводим её *** } catch (PDOException $e) { print "ERROR WITH PDO >> \n ".$e->getMessage(); } ?> Пока не заходим на andro.php через сайт, так как значений у нас в БД еще нет, поэтому отредактируем ранее созданный файл add_prod.php. Шаг 5 Открываем файл add_prod.php для редактирования (также про кодировку не забываем). Содержимое файла должно быть таким: <form action="" method="get"> Название: <input type="text" name="title" /><br> Описание: <input type="text" name="short" /><br> Цена: <input type="text" name="price" /><br> <input type="hidden" name="add" value="yes" /> <input type="submit" value="Добавить запись" /> </form> <?php // *** Если данные с новым продуктом пришли то выполняем скрипт добавления *** if($_GET["add"] == "yes") { // *** Как всегда расставляем все точки над ё *** define('HOST_DBNAME','mysql:host=localhost;dbname=andro'); define('USERNAME','root'); define('PASSWORD','password'); try { // *** Определяем объект PDO и тем самым подключаемся к нашей БД *** $db = PDO(HOST_DBNAME,USERNAME,PASSWORD); // *** Подготавливаем SQL запрос *** $stmt = $db->prepare('INSERT INTO `products` AS p (p.`title`,p.`short`,p.`price`) VALUES (?,?,?)'); // *** Выполняем запрос и проверяем его выполнение *** if($stmt->execute(Array($_GET['title'],$_GET['short'],$_GET['price']))) { // *** Если запрос прошел *** print 'SUCCESFULL. A new product create!'; } else { // *** Если ничего не получилось и в catch *** print 'ERROR: A product not are create! Please try again'; } // *** Закрываем соединение и чистим $db *** $db = null; // *** Если появилась ошибка *** } catch(PDOException $e) { print "ERROR WITH PDO >> \n ".$e->getMessage; } Шаг 6 Теперь нужно перейти на www.site.ru/add_prod.php и добавить записи (количество на ваше усмотрение). Шаг 7 После добавления зайдите на www.site.ru/andro.php и в результате на странице должно получиться что-то вроде этого . Итак, мы закончили с серверной частью, теперь перейдем к так называемому frontend'у или к части приложения Android. Создание приложения Android Итак, в начале урока мы уже создали форму для дальнейшей разработки, давайте перейдем в ADT. Шаг 1 Перейдем в activity_main.xml и добавим туда это: <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:paddingBottom="@dimen/activity_vertical_margin" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" tools:context=".MainActivity" > <ListView android:id="@+id/listView1" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_alignParentLeft="true" android:layout_alignParentTop="true" android:layout_marginLeft="19dp" android:layout_marginTop="15dp" > </ListView> </RelativeLayout> Далее создадим еще один layout под название list_item.xml (для создания нового layout перейдите в File->New->Android XML File. Resource Type: Layot, Project: AndroMySQL, File: list_item, Root Element: LinearLayout) и добавим в него это: <?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" android:padding="10dp" android:paddingLeft="10dp" android:paddingRight="10dp" > <!-- Поле названия--> <TextView android:id="@+id/name" android:layout_width="fill_parent" android:layout_height="wrap_content" android:paddingBottom="2dip" android:paddingTop="6dip" android:textColor="#43bd00" android:textSize="16sp" android:textStyle="bold" /> <!-- Поле описание --> <TextView android:id="@+id/short" android:layout_width="fill_parent" android:layout_height="wrap_content" android:paddingBottom="2dip" android:textColor="#acacac" /> <!-- Поле цены --> <TextView android:id="@+id/price" android:layout_width="fill_parent" android:layout_height="wrap_content" android:paddingBottom="0dip" android:textColor="#acacac" /> </LinearLayout> Шаг 2 Для работы нам нужен новый класс (class) для получения JSON сформированной строки из нашего andro.php. Переходим в File->New->Class, там вводим: Source folder: AndroMySQL/src, Package: com.example.andromysql (или ваш класс), Name: ServiceHandler, Super Class: java.lang.Object В ServiceHandler.java добавляем текст: package com.example.andromysql; //подключаем наш проект //Подключаем библиотеки нужные для работы нашего нового класса import java.io.IOException; import java.io.UnsupportedEncodingException; import java.util.List; 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.HttpGet; import org.apache.http.client.methods.HttpPost; import org.apache.http.client.utils.URLEncodedUtils; import org.apache.http.impl.client.DefaultHttpClient; import org.apache.http.util.EntityUtils; public class ServiceHandler { static String response = null; public final static int GET = 1; public final static int POST = 2; public ServiceHandler() { } /** * Making service call * @url - ссылка на запрос * @method - http метод для получения значений * */ public String makeServiceCall(String url, int method) { return this.makeServiceCall(url, method, null); } /** * Making service call * @url - url для запроса * @method - http метод для получения значений * @params - http параметры * */ public String makeServiceCall(String url, int method, List<NameValuePair> params) { try { //Клиент HTTP DefaultHttpClient httpClient = new DefaultHttpClient(); HttpEntity httpEntity = null; HttpResponse httpResponse = null; // Проверяем HTTP на метод POST if (method == POST) { HttpPost httpPost = new HttpPost(url); // добавляем параметры if (params != null) { httpPost.setEntity(new UrlEncodedFormEntity(params)); } httpResponse = httpClient.execute(httpPost); } else if (method == GET) { // конкатерируем параметры к URL if (params != null) { String paramString = URLEncodedUtils .format(params, "utf-8"); url += "?" + paramString; } HttpGet httpGet = new HttpGet(url); httpResponse = httpClient.execute(httpGet); } httpEntity = httpResponse.getEntity(); response = EntityUtils.toString(httpEntity); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } catch (ClientProtocolException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } return response; } } Сохраняем наш класс и переходим в MainActivity.java Шаг 3 В MainActivity.java нам нужно добавить такое содержимое: package com.example.andromysql; //Подключаем нужные библиотеки import java.util.ArrayList; import java.util.HashMap; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import android.app.ListActivity; import android.app.ProgressDialog; import android.os.AsyncTask; import android.os.Bundle; import android.util.Log; import android.widget.ListAdapter; import android.widget.ListView; import android.widget.SimpleAdapter; public class MainActivity extends ListActivity { private ProgressDialog pDialog; // URL где хранится наш отформатированный JSON private static String url = "http://www.develsi.com/view.php"; // поменяйте значение на свое // JSON Теги, чтобы в будущем оброщаться в методах private static final String TAG_PRODUCTS = "products"; private static final String TAG_ID = "id_product"; private static final String TAG_NAME = "title"; private static final String TAG_SHORT = "short"; private static final String TAG_PRICE = "price"; // JSONArray - тип данных : массив из JSON строки JSONArray products = null; // Hashmap для ListView ArrayList<HashMap<String, String>> productList; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); productList = new ArrayList<HashMap<String, String>>(); ListView lv = getListView(); // Вызываем асинхронное получение JSON new GetProducts().execute(); } /** * Асинхронный класс для получения JSON строки с HTTP страницы * */ private class GetProducts extends AsyncTask<Void, Void, Void> { @Override protected void onPreExecute() { super.onPreExecute(); // Показываем диалог загрузки на время получения данных pDialog = new ProgressDialog(MainActivity.this); pDialog.setMessage("Загрузка..."); pDialog.setCancelable(false); pDialog.show(); } @Override protected Void doInBackground(Void... arg0) { // Создаем экземпляр класса ServiceHandler ServiceHandler sh = new ServiceHandler(); // Делаем запрос на наш url и получаем ответ в переменную типа String sh String jsonStr = sh.makeServiceCall(url, ServiceHandler.GET); Log.d("Response: ", "> " + jsonStr);// Выводим в консоль результат, то что пришло с сервера //Лучше читать, что именно пришло, так как бывают ошибки из-за лишних символов, пробел и перенос строки - это тоже символ. if (jsonStr != null) { //Проверяем не пустой ли нам пришел ответ try { JSONObject jsonObj = new JSONObject(jsonStr); // Создаем переменную типа JSONObject // Получаем массив продуктов из нашего объекта products = jsonObj.getJSONArray(TAG_PRODUCTS); // Проходим по всем продуктом циклом, для создания ассоциативного массива for (int i = 0; i < products.length(); i++) { JSONObject p = products.getJSONObject(i); //Создаем некоторые переменные для дальнейшей работы String id = p.getString(TAG_ID); // достаем номер продукта String name = p.getString(TAG_NAME); //достаем название продукта String email = p.getString(TAG_SHORT); //достаем описание продукта String address = p.getString(TAG_PRICE); //достаем цену продукта // Временная переменная hashmap для одиночной записи продукта HashMap<String, String> product = new HashMap<String, String>(); // Добовляем значения в наш HashMap в виде key => value product.put(TAG_ID, id); product.put(TAG_NAME, name); product.put(TAG_EMAIL, email); // Добовляем продукты в наш лист продуктов productList.add(product); } } catch (JSONException e) { e.printStackTrace(); } } else { Log.e("ServiceHandler", "Не удалось получить никаких данных из адреса"); } return null; } @Override protected void onPostExecute(Void result) { super.onPostExecute(result); // Закрываем наш диалог загрузки if (pDialog.isShowing()) pDialog.dismiss(); /** * Обновляем запарсенные данные JSON в наш ListView * */ ListAdapter adapter = new SimpleAdapter( MainActivity.this, producttList, R.layout.list_item, new String[] { TAG_NAME, TAG_SHORT }, new int[] { R.id.name, R.id.short}); // Я не стал использовать в примере еще 2 переменные так как мне это не нужно на данный момент, но если вы желаете включить их в показ, то просто добавьте строку TAG_PRICE в new String[] {...} и R.id.price в new int[] {...} setListAdapter(adapter); // Теперь через адаптер добовляем наш получившийся массив в ListView } } } Итог После всех этих трудоемких шагов вы можете запустить ваше приложение на тестовом девайсе, предварительно включив сервер и проверив, выдает ли страница andro.php JSON код. Если вы все выполнили правильно, в итоге у вас должно получится так: 1. Запускаете приложение. 2. Открывается диалог загрузки. 3. Диалог загрузки пропадает и появляется список названий и описаний продуктов. 4. При нажатии кнопки назад происходит выход из приложения. Спасибо всем за внимание.
    5. Шаг 4
    6. Шаг 5
    7. Шаг 6
    8. Шаг 7
  4. Создание приложения Android
    1. Шаг 1
    2. Шаг 2
    3. Шаг 3
  5. Итог

Введение


Всем привет. Сегодня изучал поисковик с таким запросом «как работать с MySQL в ADT Bundle» и, если честно сказать, совершенно ничего не нашел. Решил написать об этом здесь.
Этот урок может помочь любому, от начинающего разработчика под Android, до профи, который начал работать с БД в своем приложении.

Создание проекта в Eclipse (далее ADT)


Итак, поехали. Заходим, создаем новое приложение в нашем ADT под названием AndroMySQL, активити используем: mainActivite.

Создание backend


Нам нужно написать серверную часть. В своем уроке я буду использовать для работы серверной части PHP, MySQL, PHPMyAdmin, Linux Debian.

Шаг 1

Ставим сервер, я использовал хостера VPS. Купил сервер за 300 рублей и на нем работал. Вы можете сделать также, а можете просто установить себе виртуальную машину с Linux, я бы порекомендовал для этого не очень опытным «кодерам» Ubunt'u. Как настроить сервер можно легко найти на хабре, есть сотни статей.

Шаг 2

Теперь, когда наш сайт (далее site.ru) доступен, мы можем приступить к делу и взяться за написание серверной части нашего проекта.
Заходим в корень site.ru (через FTP/SFTP или проводник) и создаем там файл andro.php. В итоге созданный файл должен быть виден по такой ссылке: www.site.ru/andro.php. Далее создаем файл add_prod.php (аналогично первому файлу add_prod.php должен быть доступен по ссылке www.site.ru/add_prod.php).

Шаг 3
Заходим в наш PHPMyAdmin (чаще всего это site.ru/phpmyadmin) и создаем новую базу под название andro. В этой базе нам нужно создать таблицу под названием products, которая будет состоять из сущностей: id_product, title, short, price.

id_product — номер продукта (значение типа int, длина 128, AUTOINCREMENT);
title — название продукта (значение типа VarChar, длина 512);
short — описание продукта (значение типа VarChar, длина 2048);
price — цена продукта (значение типа int, длина 128).

Шаг 4

Открываем файл andro.php для редактирования (для уменьшения вероятности появления ошибки сделайте кодировку файла ANSI). В этом файле нам нужно будет прописать функционал получения данных из нашей БД и вывод их в формате JSON (если вам неизвестно — эта аббревиатура, то для начала прочитайте о нем). Итак, файл должен быть такого содержания:

<?php
// *** Я для наглядности решил взять константы в пример для данных для подключения***
define('HOST_DBNAME','mysql:host=localhost;dbname=andro');
define('USERNAME','root');
define('PASSWORD','password');
define('JSON_OBJECTS','product');

try {
// *** Определяем PDO объект для работы с БД ***
$db = new PDO(HOST_DBNAME,USERNAME,PASSWORD);

// *** Подготавливаем SQL запрос ***
$stmt = $db->prepare("SELECT * FROM `products`");

// *** Выводим результат выполнения запроса ***
if($stmt->execute()) {

// *** Массив для дальнейшего преобразования в JSON ***
$arrToJSON = Array();

// *** Заполняем наш массив для дальнейшего преобразования в JSON ***
while($row = $stmt->fetch()) {
   $arrToJSON[JSON_OBJECTS][] = $row;
}

// *** Выводим построенный JSON код для дальнейшей работы с ним в приложении ***
print json_encode($arrToJSON);

// *** Закрываем соединение ***
$db = null;

// *** Если где-то произошла ошибка выводим её ***
} catch (PDOException $e) {
   print "ERROR WITH PDO >> \n ".$e->getMessage();
}
?>

Пока не заходим на andro.php через сайт, так как значений у нас в БД еще нет, поэтому отредактируем ранее созданный файл add_prod.php.

Шаг 5

Открываем файл add_prod.php для редактирования (также про кодировку не забываем). Содержимое файла должно быть таким:

<form action="" method="get">
Название: <input type="text" name="title" /><br>
Описание: <input type="text" name="short" /><br>
Цена: <input type="text" name="price" /><br>
<input type="hidden" name="add" value="yes" />
<input type="submit" value="Добавить запись" />
</form>
<?php

// *** Если данные с новым продуктом пришли то выполняем скрипт добавления ***
if($_GET["add"] == "yes") {

// *** Как всегда расставляем все точки над ё *** 
define('HOST_DBNAME','mysql:host=localhost;dbname=andro');
define('USERNAME','root');
define('PASSWORD','password');

try {
// *** Определяем объект PDO и тем самым подключаемся к нашей БД ***
$db = PDO(HOST_DBNAME,USERNAME,PASSWORD);

// *** Подготавливаем SQL запрос ***
$stmt = $db->prepare('INSERT INTO `products` AS p (p.`title`,p.`short`,p.`price`) VALUES (?,?,?)');

// *** Выполняем запрос и проверяем его выполнение ***
if($stmt->execute(Array($_GET['title'],$_GET['short'],$_GET['price']))) {
   // *** Если запрос прошел ***
   print 'SUCCESFULL. A new product create!';
} else {
   // *** Если ничего не получилось и в catch  ***
   print 'ERROR: A product not are create! Please try again';
}

// *** Закрываем соединение и чистим $db ***
$db = null;

// *** Если появилась ошибка ***
} catch(PDOException $e) {
      print "ERROR WITH PDO >> \n ".$e->getMessage;
}


Шаг 6

Теперь нужно перейти на www.site.ru/add_prod.php и добавить записи (количество на ваше усмотрение).

Шаг 7

После добавления зайдите на www.site.ru/andro.php и в результате на странице должно получиться что-то вроде этого.

Итак, мы закончили с серверной частью, теперь перейдем к так называемому frontend'у или к части приложения Android.

Создание приложения Android


Итак, в начале урока мы уже создали форму для дальнейшей разработки, давайте перейдем в ADT.

Шаг 1

Перейдем в activity_main.xml и добавим туда это:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context=".MainActivity" >

    <ListView
        android:id="@+id/listView1"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentLeft="true"
        android:layout_alignParentTop="true"
        android:layout_marginLeft="19dp"
        android:layout_marginTop="15dp" >
    </ListView>

</RelativeLayout>


Далее создадим еще один layout под название list_item.xml (для создания нового layout перейдите в File->New->Android XML File. Resource Type: Layot, Project: AndroMySQL, File: list_item, Root Element: LinearLayout) и добавим в него это:

<?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"
    android:padding="10dp"
    android:paddingLeft="10dp"
    android:paddingRight="10dp" >
 
    <!-- Поле названия-->
 
    <TextView
        android:id="@+id/name"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:paddingBottom="2dip"
        android:paddingTop="6dip"
        android:textColor="#43bd00"
        android:textSize="16sp"
        android:textStyle="bold" />
 
    <!-- Поле описание -->
    <TextView
        android:id="@+id/short"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:paddingBottom="2dip"
        android:textColor="#acacac" />
 
   <!-- Поле цены -->
   <TextView
        android:id="@+id/price"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:paddingBottom="0dip"
        android:textColor="#acacac" />

 
</LinearLayout>


Шаг 2

Для работы нам нужен новый класс (class) для получения JSON сформированной строки из нашего andro.php. Переходим в File->New->Class, там вводим:
Source folder: AndroMySQL/src,
Package: com.example.andromysql (или ваш класс),
Name: ServiceHandler,
Super Class: java.lang.Object

В ServiceHandler.java добавляем текст:

package com.example.andromysql; //подключаем наш проект
 
//Подключаем библиотеки нужные для работы нашего нового класса
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.List;
 
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.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.utils.URLEncodedUtils;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.util.EntityUtils;
 
public class ServiceHandler {
 
    static String response = null;
    public final static int GET = 1;
    public final static int POST = 2;
 
    public ServiceHandler() {
 
    }
 
    /**
     * Making service call
     * @url - ссылка на запрос
     * @method - http метод для получения значений
     * */
    public String makeServiceCall(String url, int method) {
        return this.makeServiceCall(url, method, null);
    }
 
    /**
     * Making service call
     * @url - url для запроса
     * @method - http метод для получения значений
     * @params - http параметры
     * */
    public String makeServiceCall(String url, int method,
            List<NameValuePair> params) {
        try {
            //Клиент HTTP
            DefaultHttpClient httpClient = new DefaultHttpClient();
            HttpEntity httpEntity = null;
            HttpResponse httpResponse = null;
             
            // Проверяем HTTP на метод POST
            if (method == POST) {
                HttpPost httpPost = new HttpPost(url);
                // добавляем параметры
                if (params != null) {
                    httpPost.setEntity(new UrlEncodedFormEntity(params));
                }
 
                httpResponse = httpClient.execute(httpPost);
 
            } else if (method == GET) {
                // конкатерируем параметры к URL
                if (params != null) {
                    String paramString = URLEncodedUtils
                            .format(params, "utf-8");
                    url += "?" + paramString;
                }
                HttpGet httpGet = new HttpGet(url);
 
                httpResponse = httpClient.execute(httpGet);
 
            }
            httpEntity = httpResponse.getEntity();
            response = EntityUtils.toString(httpEntity);
 
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        } catch (ClientProtocolException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
         
        return response;
 
    }
}

Сохраняем наш класс и переходим в MainActivity.java

Шаг 3

В MainActivity.java нам нужно добавить такое содержимое:

package com.example.andromysql;

//Подключаем нужные библиотеки
import java.util.ArrayList;
import java.util.HashMap;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import android.app.ListActivity;
import android.app.ProgressDialog;
import android.os.AsyncTask;
import android.os.Bundle;
import android.util.Log;
import android.widget.ListAdapter;
import android.widget.ListView;
import android.widget.SimpleAdapter;

public class MainActivity extends ListActivity {
	 
    private ProgressDialog pDialog;
 
    // URL где хранится наш отформатированный JSON
    private static String url = "http://www.develsi.com/view.php"; // поменяйте значение на свое
 
    // JSON Теги, чтобы в будущем оброщаться в методах
    private static final String TAG_PRODUCTS = "products";
    private static final String TAG_ID = "id_product";
    private static final String TAG_NAME = "title";
    private static final String TAG_SHORT = "short";
    private static final String TAG_PRICE = "price";
 
    // JSONArray - тип данных : массив из JSON строки
    JSONArray products = null;
 
    // Hashmap для ListView
    ArrayList<HashMap<String, String>> productList;
 
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
 
        productList = new ArrayList<HashMap<String, String>>();
 
        ListView lv = getListView();
        
     // Вызываем асинхронное получение JSON
        new GetProducts().execute();
    }
    
    
    /**
     * Асинхронный класс для получения JSON строки с HTTP страницы
     * */
    private class GetProducts extends AsyncTask<Void, Void, Void> {
 
        @Override
        protected void onPreExecute() {
            super.onPreExecute();
            // Показываем диалог загрузки на время получения данных
            pDialog = new ProgressDialog(MainActivity.this);
            pDialog.setMessage("Загрузка...");
            pDialog.setCancelable(false);
            pDialog.show();
 
        }
 
        @Override
        protected Void doInBackground(Void... arg0) {
            // Создаем экземпляр класса ServiceHandler
            ServiceHandler sh = new ServiceHandler();
 
            // Делаем запрос на наш url и получаем ответ в переменную типа String sh
            String jsonStr = sh.makeServiceCall(url, ServiceHandler.GET);
 
            Log.d("Response: ", "> " + jsonStr);// Выводим в консоль результат, то что пришло с сервера
 //Лучше читать, что именно пришло, так как бывают ошибки из-за лишних символов, пробел и перенос строки - это тоже символ.

            if (jsonStr != null) { //Проверяем не пустой ли нам пришел ответ
                try {
                    JSONObject jsonObj = new JSONObject(jsonStr); // Создаем переменную типа JSONObject
                     
                    // Получаем массив продуктов из нашего объекта
                    products = jsonObj.getJSONArray(TAG_PRODUCTS);
 
                    // Проходим по всем продуктом циклом, для создания ассоциативного массива
                    for (int i = 0; i < products.length(); i++) {
                        JSONObject p = products.getJSONObject(i);
                         //Создаем некоторые переменные для дальнейшей работы
                        String id = p.getString(TAG_ID); // достаем номер продукта
                        String name = p.getString(TAG_NAME); //достаем название продукта
                        String email = p.getString(TAG_SHORT); //достаем описание продукта
                        String address = p.getString(TAG_PRICE); //достаем цену продукта
 
                        // Временная переменная hashmap для одиночной записи продукта
                        HashMap<String, String> product = new HashMap<String, String>();
 
                        // Добовляем значения в наш HashMap в виде key => value
                        product.put(TAG_ID, id); 
                        product.put(TAG_NAME, name);
                        product.put(TAG_EMAIL, email);
 
                        // Добовляем продукты в наш лист продуктов
                        productList.add(product);
                    }
                } catch (JSONException e) {
                    e.printStackTrace();
                }
            } else {
                Log.e("ServiceHandler", "Не удалось получить никаких данных из адреса");
            }
 
            return null;
        }
 
        @Override
        protected void onPostExecute(Void result) {
            super.onPostExecute(result);
            // Закрываем наш диалог загрузки
            if (pDialog.isShowing())
                pDialog.dismiss();
            /**
             * Обновляем запарсенные данные JSON в наш ListView
             * */
            ListAdapter adapter = new SimpleAdapter(
                    MainActivity.this, producttList,
                    R.layout.list_item, new String[] { TAG_NAME, TAG_SHORT }, new int[] { R.id.name,
                            R.id.short});
 // Я не стал использовать в примере еще 2 переменные так как мне это не нужно на данный момент, но если вы желаете включить их в показ, то просто добавьте строку TAG_PRICE в new String[] {...} и R.id.price в new int[] {...}

            setListAdapter(adapter); // Теперь через адаптер добовляем наш получившийся массив в ListView
        }
 
    }
    
    
}



Итог


После всех этих трудоемких шагов вы можете запустить ваше приложение на тестовом девайсе, предварительно включив сервер и проверив, выдает ли страница andro.php JSON код. Если вы все выполнили правильно, в итоге у вас должно получится так:
1. Запускаете приложение.
2. Открывается диалог загрузки.
3. Диалог загрузки пропадает и появляется список названий и описаний продуктов.
4. При нажатии кнопки назад происходит выход из приложения.

Спасибо всем за внимание.
–21
1848

комментарии (30)

+8
webportal ,  
Не могу ничего сказать про код приложения. Но так называемый бэкенд… Это нечто…
–6
+1 –7
ctaciv ,  
Ни чего за то работает и ребята кто пожелает научится с самых основ сможет нормально прочитать основные функции для работы с серверной частью
+1
SerafimArts ,   * (был изменён)
Неа, не работает. В PHP уже давно нет таких функций, как mysql_*
0
DevMan ,  
Справедливости ради: функции mysql_* в PHP до сих пор есть. А жаль.
0
SerafimArts ,  
<?php
mysql_connect('some', 'any', 'any2');


G:\sdk\php-5.6\php.exe G:\test.php PHP Fatal error: Call to undefined function mysql_connect() in G:\test.php on line 2
0
OnYourLips ,   * (был изменён)
Это вы экстеншн просто не поставили.
Работать будет, но будет выдавать предупреждение E_DEPRECATED, что делает уже давно.

P.S. Одновременно ответили, удалить не могу.
0
SerafimArts ,  
Ой, наврал. У меня в php.ini отключено это расширение было =)
В любом случае вот это не лучше:
Deprecated: mysql_connect(): The mysql extension is deprecated and will be removed in the future: use mysqli or PDO instead in G:\test.php on line 2
0
DevMan ,  
Вы или собрали PHP без модуля или не активировали его в настройках.
$ php -v
PHP 5.6.2 (cli) (built: Oct 19 2014 05:49:36) 
Copyright (c) 1997-2014 The PHP Group
Zend Engine v2.6.0, Copyright (c) 1998-2014 Zend Technologies

$ php -m | grep mysql
mysql
mysqli
mysqlnd
pdo_mysql


Ну и
Warning

This extension is deprecated as of PHP 5.5.0, and will be removed in the future. Instead, the MySQLi or PDO_MySQL extension should be used.
0
Suvitruf ,  
Там всё тоже печально. Не настолько как бэкенд, но тоже много антипатернов.
+6
+7 –1
OnYourLips ,  
Код на PHP бьет все рекорды: это самый худший код, который я видел за последние три года.
–1
+2 –3
ctaciv ,  
Что именно тебе там не понравилось?
+3
DevMan ,  
Всё.
–2
ctaciv ,  
Можешь по конкрентнее, как я могу учится на своих ошибках если ты просто говоришь все!
0
DevMan ,   * (был изменён)
Расширение mysql уже давно в деприкейте плюс никакой фильтрации данных. Ну и за комментами кода не видно (коменты — хорошо, размещены неудачно).
0
ctaciv ,  
Спасибо за точный ответ. Учту.
+3
webportal ,   * (был изменён)
Хотите конкретики?

1. Как уже сказали выше mysql_* депрекейтед.
2. Ошибка эта функция работает с ресурсом а не запросом, это во первых. Во вторых тут по хорошему нужно проверить переменную $result на false
if(mysql_num_rows($query) >= 1)

3. Не нужные куски кода
$iter = 0;
++$iter;

4. Заменить
$arr['products'][$iter] = $data;

на
$arr['products'][] = $data;

5. Обезопасить запрос
$query = sprintf("INSERT INTO products (title,short,price) VALUES ('%s','%s','%s')",
    mysql_real_escape_string($_GET['title']),mysql_real_escape_string($_GET['short']),mysql_real_escape_string($_GET['price']));

6. Не хорошо использовать не объявленные константы как ключи массива $_GET[title]

Это что касается грубых ошибок. Можно ещё много чего про этот код рассказать… Кстати вы ниже сказали что давно пишете на php не подскажете сколько именно?
–4
ctaciv ,  
8 лет
+3
webportal ,  
С 11 лет?
+1
ewolf ,  
Хм, ctaciv, скажите, Вы давно пишите программы на php?
–3
ctaciv ,  
Более чем. Просто мне в этом уроке не важно было PHP. Здесь основа в Java+MySQL, или какая-либо другая бд, но весь смысл в общем в том как принять JSON в Java и вывести в форме списком.
+2
trilodi ,  
Это не формат работы с БД, а лишь получение данных от сервера по запросу, аля api
0
ctaciv ,  
Ну это вы уже сочиняете. С помощью Java кода который написан в статье, вы спокойно можете работать с любой бд в ADT. Да это API, но классы и весь код мой. Поэтому думаю много кому бы пригодилось.
0
trilodi ,  
Видимо вы плохо представляете себе что значит работать с БД. для работы с БД в JAVA (а не в adt) есть специальные драйвера, которые напрямую взаимодействуют с базой. А подход описаный вами называется что то вроде запрос и обработка данных с сервера в Android приложении по средством JSON, а уж какую БД вы используете на сервере Android приложению в этом случае глубоко пофиг
0
trilodi ,   * (был изменён)
Да и API не значит что оно чужое, апи каждый под свои нужды может написать
0
Tishka17 ,  
раз пошла такая пьянка а что вы предложите для работы с транзакциями таким способом? Расшифрую что я имею виду. Хочу делать примерно так:
1. beginTransaction
2. insert
3. код не связанный с бд
4. ещё insert
5. rollback или commit транзакции
+4
dekameron ,  
Благодаря «Блогам о РНР» от людей, начавших изучение языка по таким же блогам неделю назад, количество PHP-программисткодеров и так растет в геометрической прогрессии.
Думаю, подобная статья на хабре сделает ее экспотенциальной
+5
SerafimArts ,  
Объявлена неделя PHP-кодеров.
Количество PHP кода увеличено вдвое.
+1
+2 –1
Blumfontein ,  
*говнокода
0
Tishka17 ,  
А почему бы не заюзать в андроиде gson для парсинга?

А ещё из документации по DefaultHttpClient:
For Android 2.3 (Gingerbread) and later, HttpURLConnection is the best choice.

//А в коде у вас то productList, то contactList
0
ctaciv ,  
Вообще писал для контактов, но урок решил немного переделать и поэтому переписывал, видимо не заметил где-то. Ну вот пусть новички не копируют все подряд, а чуть чуть задумаются и почитают