안드로이드 앱에서 입력한 키워드를 웹서버에서 실행되는 PHP를 이용하여 MySQL 데이터베이스 서버에 질의하는 예제입니다.
질의 결과는 JSON 형식으로 가져와서 화면에 보여줍니다.
현재 예제에선 키워드를 2개 사용하고 있습니다. 3개 이상 필요한 경우 노란색 코드를 참고하여 추가하세요.
activity_main.xml, MainActivity.java, query.php 파일을 수정하면 됩니다.
1. 웹기반 테스트
2. Android 앱에서 테스트
3. 코드 설명
4. 관련 포스팅
|
최초작성 2017. 8. 16
최종작성 2019. 11. 17 androidx 사용하도록 변경
다음 포스팅에 연결된 포스팅입니다.
Amazon AWS를 사용하여 구현한 예제입니다.
AWS Rest API를 Android와 연동해보기( Lambda + API Gateway + DynamoDB )
https://webnautes.tistory.com/1590
1. 웹기반 테스트
Android 앱에서 진행하기 전에 웹기반으로 먼저 테스트를 진행합니다.
PC에 설치된 웹서버 및 데이터베이스 서버 접속하는데 문제 없는지, PHP 코드에서 테이블 접근시 문제 없는지 등을 확인해보기 위해서입니다.
1. 앞에서 진행했던 포스팅의 결과로 현재 db 데이터베이스의 Person 테이블에 다음 데이터가 저장되어 있는 상태입니다.
Android PHP MySQL 예제 - 데이터베이스에 데이터 입력하기
http://webnautes.tistory.com/828
웹브라우저에서 http://localhost/insert.php 에 접속하여 추가로 데이터를 더 집어 넣었습니다.
2. 다음 코드를 query.php 파일의 내용으로 복사해줍니다.
윈도우라면 C:\wamp64\www\ 경로에 query.php 파일을 생성합니다.
우분투라면 /var/www/html/ 경로에 query.php 파일을 생성합니다.
dbcon.php 파일은 이전 포스팅에 있는 것을 그대로 사용합니다.
<?php error_reporting(E_ALL); ini_set('display_errors',1);
include('dbcon.php');
//POST 값을 읽어온다. $country=isset($_POST['country']) ? $_POST['country'] : ''; $name = isset($_POST['name']) ? $_POST['name'] : ''; $android = strpos($_SERVER['HTTP_USER_AGENT'], "Android");
if ($country != "" ){
$sql="select * from person where country='$country' and name='$name'"; $stmt = $con->prepare($sql); $stmt->execute(); if ($stmt->rowCount() == 0){
echo "'"; echo $country,", ",$name; echo "'은 찾을 수 없습니다."; } else{
$data = array();
while($row=$stmt->fetch(PDO::FETCH_ASSOC)){
extract($row);
array_push($data, array('id'=>$row["id"], 'name'=>$row["name"], 'country'=>$row["country"] )); }
if (!$android) { echo "<pre>"; print_r($data); echo '</pre>'; }else { header('Content-Type: application/json; charset=utf8'); $json = json_encode(array("webnautes"=>$data), JSON_PRETTY_PRINT+JSON_UNESCAPED_UNICODE); echo $json; } } } else { echo "검색할 나라를 입력하세요 "; }
?>
<?php
$android = strpos($_SERVER['HTTP_USER_AGENT'], "Android");
if (!$android){ ?>
<html> <body> <form action="<?php $_PHP_SELF ?>" method="POST"> 나라: <input type = "text" name = "country" /> 이름: <input type = "text" name = "name" /> <input type = "submit" /> </form> </body> </html> <?php }
?> |
3. PC의 웹브라우저에서 http://localhost/query.php 주소로 접속하면 다음과 같은 화면이 보이게 됩니다.
이미 데이터베이스의 테이블에 저장되어 있는 나라인 조선, 홍길동을 입력하고 제출을 클릭하면 JSON 포맷으로 결과를 보여줍니다.
없는 데이터를 검색한 경우입니다.
부분 문자열 검색은 구현안되어 있어서 일부만 입력하면 없다고 결과가 나옵니다.
4. 안드로이드 폰의 웹브라우저에서도 확인해봅니다.
주의할 점은 PC와 안드로이드가 같은 공유기에서 아이피를 할당받았거나 PC가 고정 아이피여야 테스트가 가능합니다.
본 포스팅에서는 안드로이드 폰의 핫스팟을 노트북에서 사용하도록 해서 테스트를 진행했습니다.
ifconfig 명령을 사용하여 노트북의 IP를 확인합니다. 여기에서는 192.168.43.52입니다.
크롬의 경우에는 데스크톱 버전으로 보기를 체크해줘야 텍스트 박스가 제대로 보입니다.
검색할 키워드를 입력하고 제출을 터치하면
PC와 동일한 결과를 얻을 수 있습니다.
문제가 있는 경우 PC와 스마트폰이 같은 네트워크(같은 공유기)를 사용하고 있는지 체크해보세요.
2. Android 앱에서 테스트
1. 이제 웹서버의 PHP 파일에 접근하는 안드로이드 앱을 작성합니다.
안드로이드 스튜디오에서 새로운 Empty 프로젝트를 생성합니다.
2. AndroidManifest.xml 매니페스트 파일의 manifest 태그 하위 항목으로 인터넷 접근 허용 퍼미션을 추가합니다.
android 9.0 이상에서 동작하게 하려면 usesCleartextTraffic 옵션도 추가해야 합니다. 해주지 않으면 “cleartext http traffic to not permitted” 라는 에러가 발생합니다.
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.tistory.webnautes.phptest">
<uses-permission android:name="android.permission.INTERNET" />
<application android:usesCleartextTraffic="true" android:allowBackup="true" |
3. activity_main.xml 레이아웃 파일을 다음 내용으로 변경합니다.
<?xml version="1.0" encoding="utf-8"?> <LinearLayout 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:orientation="vertical" tools:context=".MainActivity">
<TextView android:id="@+id/textView_main_label" android:layout_width="match_parent" android:layout_height="40dp" android:text="검색할 나라와 이름을 입력해주세요 . " />
<LinearLayout android:layout_width="match_parent" android:layout_height="50dp" android:orientation="horizontal">
<EditText android:id="@+id/editText_main_searchKeyword1" android:layout_width="0dp" android:layout_weight="0.35" android:hint="나라" android:layout_height="match_parent" />
<EditText android:id="@+id/editText_main_searchKeyword2" android:layout_width="0dp" android:layout_weight="0.35" android:hint="이름" android:layout_height="match_parent" />
<Button android:id="@+id/button_main_search" android:layout_width="0dp" android:layout_weight="0.3" android:layout_height="match_parent" android:text="Search" /> </LinearLayout>
<ListView android:layout_width="match_parent" android:layout_height="0dp" android:layout_weight="0.4" android:id="@+id/listView_main_list"/>
<TextView android:id="@+id/textView_main_result" android:layout_width="match_parent" android:layout_height="0dp" android:layout_weight="0.4" /> </LinearLayout> |
4. item_list.xml 레이아웃 파일을 추가로 생성하여 아래 내용으로 바꿉니다.
ListView의 한 줄을 여러 개의 열로 표현하기 위해 필요한 레이아웃입니다.
세 개의 TextVew에 각각의 크기를 지정해주었습니다.
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal" android:layout_margin="15dp" android:padding="10dp">
<TextView android:id="@+id/textView_list_id" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:layout_margin="5dp" android:padding="5dp" />
<TextView android:id="@+id/textView_list_name" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="4" android:layout_margin="5dp" android:padding="5dp" />
<TextView android:id="@+id/textView_list_address" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="5" android:layout_margin="5dp" android:padding="5dp" />
</LinearLayout> |
5. MainActivity.java 자바 파일을 아래 내용으로 바꿉니다.
코드에서 다음 라인은 접속할 서버 IP로 변경해야 합니다.
String serverURL = "http://서버IP/query.php"; |
import android.app.ProgressDialog; import android.os.AsyncTask; import android.os.Bundle; import androidx.appcompat.app.AppCompatActivity; import android.util.Log; import android.view.View; import android.widget.Button; import android.widget.EditText; import android.widget.ListAdapter; import android.widget.ListView; import android.widget.SimpleAdapter; import android.widget.TextView;
import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject;
import java.io.BufferedReader;
import java.io.InputStream; import java.io.InputStreamReader;
import java.io.OutputStream; import java.net.HttpURLConnection; import java.net.URL; import java.util.ArrayList; import java.util.HashMap;
public class MainActivity extends AppCompatActivity {
private static String TAG = "phpquerytest";
private static final String TAG_JSON="webnautes"; private static final String TAG_ID = "id"; private static final String TAG_NAME = "name"; private static final String TAG_ADDRESS ="country";
private TextView mTextViewResult; ArrayList<HashMap<String, String>> mArrayList; ListView mListViewList; EditText mEditTextSearchKeyword1, mEditTextSearchKeyword2; String mJsonString;
@Override protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); setContentView(R.layout.activity_main);
mTextViewResult = (TextView)findViewById(R.id.textView_main_result); mListViewList = (ListView) findViewById(R.id.listView_main_list); mEditTextSearchKeyword1 = (EditText) findViewById(R.id.editText_main_searchKeyword1); mEditTextSearchKeyword2 = (EditText) findViewById(R.id.editText_main_searchKeyword2);
Button button_search = (Button) findViewById(R.id.button_main_search); button_search.setOnClickListener(new View.OnClickListener() { public void onClick(View v) {
mArrayList.clear();
GetData task = new GetData(); task.execute( mEditTextSearchKeyword1.getText().toString(), mEditTextSearchKeyword2.getText().toString()); } });
mArrayList = new ArrayList<>();
}
private class GetData extends AsyncTask<String, Void, String>{
ProgressDialog progressDialog; String errorString = null;
@Override protected void onPreExecute() { super.onPreExecute();
progressDialog = ProgressDialog.show(MainActivity.this, "Please Wait", null, true, true); }
@Override protected void onPostExecute(String result) { super.onPostExecute(result);
progressDialog.dismiss(); mTextViewResult.setText(result); Log.d(TAG, "response - " + result);
if (result == null){
mTextViewResult.setText(errorString); } else {
mJsonString = result; showResult(); } }
@Override protected String doInBackground(String... params) {
String searchKeyword1 = params[0]; String searchKeyword2 = params[1];
String serverURL = "http://서버IP/query.php"; String postParameters = "country=" + searchKeyword1 + "&name=" + searchKeyword2;
try {
URL url = new URL(serverURL); HttpURLConnection httpURLConnection = (HttpURLConnection) url.openConnection();
httpURLConnection.setReadTimeout(5000); httpURLConnection.setConnectTimeout(5000); httpURLConnection.setRequestMethod("POST"); httpURLConnection.setDoInput(true); httpURLConnection.connect();
OutputStream outputStream = httpURLConnection.getOutputStream(); outputStream.write(postParameters.getBytes("UTF-8")); outputStream.flush(); outputStream.close();
int responseStatusCode = httpURLConnection.getResponseCode(); Log.d(TAG, "response code - " + responseStatusCode);
InputStream inputStream; if(responseStatusCode == HttpURLConnection.HTTP_OK) { inputStream = httpURLConnection.getInputStream(); } else{ inputStream = httpURLConnection.getErrorStream(); }
InputStreamReader inputStreamReader = new InputStreamReader(inputStream, "UTF-8"); BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
StringBuilder sb = new StringBuilder(); String line;
while((line = bufferedReader.readLine()) != null){ sb.append(line); }
bufferedReader.close();
return sb.toString().trim();
} catch (Exception e) {
Log.d(TAG, "InsertData: Error ", e); errorString = e.toString();
return null; }
} }
private void showResult(){ try { JSONObject jsonObject = new JSONObject(mJsonString); JSONArray jsonArray = jsonObject.getJSONArray(TAG_JSON);
for(int i=0;i<jsonArray.length();i++){
JSONObject item = jsonArray.getJSONObject(i);
String id = item.getString(TAG_ID); String name = item.getString(TAG_NAME); String address = item.getString(TAG_ADDRESS);
HashMap<String,String> hashMap = new HashMap<>();
hashMap.put(TAG_ID, id); hashMap.put(TAG_NAME, name); hashMap.put(TAG_ADDRESS, address);
mArrayList.add(hashMap); }
ListAdapter adapter = new SimpleAdapter( MainActivity.this, mArrayList, R.layout.item_list, new String[]{TAG_ID,TAG_NAME, TAG_ADDRESS}, new int[]{R.id.textView_list_id, R.id.textView_list_name, R.id.textView_list_address} );
mListViewList.setAdapter(adapter);
} catch (JSONException e) {
Log.d(TAG, "showResult : ", e); }
}
} |
6. 실행결과 입니다. 검색할 나라와 이름을 입력하고 SEARCH 버튼을 클릭하면 PHP 코드를 이용하여 데이터베이스 서버에 질의하고 결과를 가져옵니다.
중앙의 리스트뷰에는 검색 결과를 파싱하여 보여주며, 하단의 텍스트뷰에서는 JSON 데이터를 보여줍니다.
검색 결과가 없는 경우에는 PHP에서 보내준 에러메시지를 보여줍니다.
일정 시간 내에 서버로 접속이 안되는 경우 시간 초과 예외로 처리하고 있습니다.
java.net.SocketTimeoutException: failed to connect 메시지를 보여줍니다.
3. 코드 설명
검색어가 하나인 경우를 예로 들어 설명하고 있습니다.
(자바 코드) 버튼을 클릭하면 검색창에 있는 내용을 아규먼트로하여 GetData AsyncTask를 실행합니다.
button_search.setOnClickListener(new View.OnClickListener() { public void onClick(View v) {
mArrayList.clear();
GetData task = new GetData(); task.execute( mEditTextSearchKeyword.getText().toString()); } }); |
(자바 코드) 검색한 나라이름(searchKeyword)을 POST 방식 HTTP 통신의 아규먼트로 하여 서버에 있는 PHP 파일을 실행합니다.
String serverURL = "http://서버IP/query.php"; String postParameters = "country=" + searchKeyword; . . . . . . . . . . . . . outputStream.write(postParameters.getBytes("UTF-8")); |
(PHP 코드) POST 방식으로 전달받은 값을 사용하여 SELECT문을 실행합니다.
if ($country != "" ){
$sql="select * from person where country='$country'"; $stmt = $con->prepare($sql); $stmt->execute(); |
(PHP 코드) 질의 결과 데이터가 없으면 에러를 출력하고 데이터를 얻으면 배열을 생성합니다.
if ($stmt->rowCount() == 0){
echo "'"; echo $country; echo "'은 찾을 수 없습니다."; } else{
$data = array();
while($row=$stmt->fetch(PDO::FETCH_ASSOC)){
extract($row);
array_push($data, array('id'=>$row["id"], 'name'=>$row["name"], 'country'=>$row["country"] )); } |
(PHP 코드) 안드로이드에 전달하기 위해 JSON 포맷으로 변경 후 에코합니다.
if (!$android) { echo "<pre>"; print_r($data); echo '</pre>'; }else { header('Content-Type: application/json; charset=utf8'); $json = json_encode(array("webnautes"=>$data), JSON_PRETTY_PRINT+JSON_UNESCAPED_UNICODE); echo $json; } |
(자바코드) StringBuilder를 사용하여 PHP가 에코한 문자열을 저장하고 스트링으로 변환하여 리턴합니다.
InputStreamReader inputStreamReader = new InputStreamReader(inputStream, "UTF-8"); BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
StringBuilder sb = new StringBuilder(); String line;
while((line = bufferedReader.readLine()) != null){ sb.append(line); }
bufferedReader.close();
return sb.toString().trim(); |
(자바 코드) 에러가 있는 경우 에러메시지를 보여주고 아니면 JSON을 파싱하여 화면에 보여주는 showResult 메소드를 호출합니다.
@Override protected void onPostExecute(String result) { super.onPostExecute(result);
progressDialog.dismiss(); mTextViewResult.setText(result); Log.d(TAG, "response - " + result);
if (result == null){
mTextViewResult.setText(errorString); } else {
mJsonString = result; showResult(); } } |
(자바 코드) 안드로이드 앱에서 받은 JSON 포맷의 데이터입니다.
간단한 규칙이 있는데 중괄호 {} 는 JSONObject 대괄호 [] JSONArray입니다.
{ "webnautes": [ { "id": "1", "name": "홍길동", "country": "조선" }, { "id": "2", "name": "잔다르크", "country": "프랑스" } ] } |
(자바 코드) 첫번째 괄호는 중괄호 {} 이므로 JSONObject입니다.
JSONObject jsonObject = new JSONObject(mJsonString); |
(자바 코드) jsonObject에서 TAG_JSON 키를 갖는 JSONArray를 가져옵니다.
JSONArray jsonArray = jsonObject.getJSONArray(TAG_JSON); |
(자바 코드) 이제 다음같은 구조를 jsonArray가 갖고 있게 됩니다.
[ { "id": "1", "name": "홍길동", "country": "조선" }, { "id": "2", "name": "잔다르크", "country": "프랑스" } ] |
(자바 코드) JsonArray에는 JSONObject가 데이터 갯수만큼 포함되어 있습니다.
인덱스를 사용하여 JsonArray에서 JSONObject를 하나씩 가져옵니다.
for(int i=0;i<jsonArray.length();i++){ JSONObject item = jsonArray.getJSONObject(i); |
(자바 코드) JSONObject에서 키 id, name, country의 값을 가져옵니다.
String id = item.getString(TAG_ID); String name = item.getString(TAG_NAME); String country = item.getString(TAG_COUNTRY); |
(자바 코드) 데이터를 새로 생성한 PersonalData 클래스의 멤버변수에 입력하고 ArrayList에 추가합니다.
PersonalData personalData = new PersonalData();
personalData.setMember_id(id); personalData.setMember_name(name); personalData.setMember_country(country);
mArrayList.add(personalData); |
(자바코드) SimpleAdapter를 사용하여 화면에 파싱된 데이터를 보여줍니다.
ListAdapter adapter = new SimpleAdapter( MainActivity.this, mArrayList, R.layout.item_list, new String[]{TAG_ID,TAG_NAME, TAG_COUNTRY}, new int[]{R.id.textView_list_id, R.id.textView_list_name, R.id.textView_list_address} );
mListViewList.setAdapter(adapter); |
4. 관련 포스팅