77일차(3)/Android App(39) : Http POST방식 요청하기(2)
- 새 모듈 생성
step17httprequest2 - Empty Activity
Util 클래스 생성
package com.example.step17httprequest2;
import android.app.Activity;
import android.content.Context;
import android.os.AsyncTask;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewParent;
import android.view.inputmethod.InputMethodManager;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
public class Util {
//키보드 숨기는 메소드
public static void hideKeyboard(Activity activity){
InputMethodManager iManager=(InputMethodManager)
activity.getSystemService(Context.INPUT_METHOD_SERVICE);
if(activity.getCurrentFocus()==null)return;
iManager.hideSoftInputFromWindow(activity.getCurrentFocus().getWindowToken(), 0);
}
//메소드의 인자로 전달되는 View 객체의 포커스 해제하는 메소드
public static void releaseFocus(View view) {
ViewParent parent = view.getParent();
ViewGroup group = null;
View child = null;
while (parent != null) {
if (parent instanceof ViewGroup) {
group = (ViewGroup) parent;
for (int i = 0; i < group.getChildCount(); i++) {
child = group.getChildAt(i);
if(child != view && child.isFocusable())
child.requestFocus();
}
}
parent = parent.getParent();
}
}
public static interface RequestListener{
public void onSuccess(int requestId, Map<String, Object> result);
public void onFail(int requestId, Map<String, Object> result);
}
/*
1. 사용할때 RequestListener 인터페이스 Type 을 전달한다.
2. 결과는 RequestListener 객체에 전달된다.
3. Map<String,Object> 에서 응답 코드는
"code" 라는 키값으로 200, 404, 500, -1 중에 하나가 리턴되고
-1 이 리턴되면 Exception 발생으로 실패이다. onFail() 호출
*/
public static void sendGetRequest(int requestId,
String requestUrl,
Map<String,String> params,
RequestListener listener){
RequestTask task=new RequestTask();
task.setRequestId(requestId);
task.setRequestUrl(requestUrl);
task.setListener(listener);
task.execute(params);
}
private static class RequestTask extends AsyncTask<Map<String,String>, Void, Map<String,Object>> {
private int requestId;
private String requestUrl;
private RequestListener listener;
public void setListener(RequestListener listener) {
this.listener = listener;
}
public void setRequestId(int requestId) {
this.requestId = requestId;
}
public void setRequestUrl(String requestUrl) {
this.requestUrl = requestUrl;
}
@Override
protected Map<String, Object> doInBackground(Map<String, String>... params) {
String requestUrl=this.requestUrl;
Map<String, String> param=params[0];
if(param!=null){//요청 파리미터가 존재 한다면
//서버에 전송할 데이터를 문자열로 구성하기
StringBuffer buffer=new StringBuffer();
Set<String> keySet=param.keySet();
Iterator<String> it=keySet.iterator();
boolean isFirst=true;
//반복문 돌면서 map 에 담긴 모든 요소를 전송할수 있도록 구성한다.
while(it.hasNext()){
String key=it.next();
String arg=null;
//파라미터가 한글일 경우 깨지지 않도록 하기 위해.
String encodedValue=null;
try {
encodedValue= URLEncoder.encode(param.get(key), "utf-8");
} catch (UnsupportedEncodingException e) {}
if(isFirst){
arg="?"+key+"="+encodedValue;
isFirst=false;
}else{
arg="&"+key+"="+encodedValue;
}
buffer.append(arg);
}
String data=buffer.toString();
requestUrl +=data;
}
//서버가 http 요청에 대해서 응답하는 문자열을 누적할 객체
StringBuilder builder=new StringBuilder();
HttpURLConnection conn=null;
InputStreamReader isr=null;
BufferedReader br=null;
//결과값을 담을 Map Type 객체
Map<String,Object> map=new HashMap<String,Object>();
try{
//URL 객체 생성
URL url=new URL(requestUrl);
//HttpURLConnection 객체의 참조값 얻어오기
conn=(HttpURLConnection)url.openConnection();
if(conn!=null){//연결이 되었다면
conn.setConnectTimeout(20000); //응답을 기다리는 최대 대기 시간
conn.setRequestMethod("GET");//Default 설정
conn.setUseCaches(false);//케쉬 사용 여부
//응답 코드를 읽어온다.
int responseCode=conn.getResponseCode();
//Map 객체에 응답 코드를 담는다.
map.put("code", responseCode);
if(responseCode==200){//정상 응답이라면...
//서버가 출력하는 문자열을 읽어오기 위한 객체
isr=new InputStreamReader(conn.getInputStream());
br=new BufferedReader(isr);
//반복문 돌면서 읽어오기
while(true){
//한줄씩 읽어들인다.
String line=br.readLine();
//더이상 읽어올 문자열이 없으면 반복문 탈출
if(line==null)break;
//읽어온 문자열 누적 시키기
builder.append(line);
}
//출력받은 문자열 전체 얻어내기
String str=builder.toString();
//아래 코드는 수행 불가
//console.setText(str);
//Map 객체에 결과 문자열을 담는다.
map.put("data", str);
}
}
}catch(Exception e){//예외가 발생하면
//에러 정보를 담는다.
map.put("code",-1);
map.put("data", e.getMessage());
}finally {
try{
if(isr!=null)isr.close();
if(br!=null)br.close();
if(conn!=null)conn.disconnect();
}catch(Exception e){}
}
//결과를 담고 있는 Map 객체를 리턴해준다.
return map;
}
@Override
protected void onPostExecute(Map<String, Object> map) {
super.onPostExecute(map);
int code=(int)map.get("code");
if(code!=-1){//성공이라면
listener.onSuccess(requestId, map);
}else{//실패 (예외발생)
listener.onFail(requestId, map);
}
}
}
//POST 방식 REQUEST
public static void sendPostRequest(int requestId, String requestUrl, Map<String, String> params, RequestListener listener){
PostRequestTask task=new PostRequestTask();
task.setRequestId(requestId);
task.setRequestUrl(requestUrl);
task.setListener(listener);
task.execute(params);
}
public static class PostRequestTask extends AsyncTask<Map<String, String>, Void, Map<String, Object>>{
private int requestId;
private String requestUrl;
private RequestListener listener;
public void setListener(RequestListener listener) {
this.listener = listener;
}
public void setRequestId(int requestId) {
this.requestId = requestId;
}
public void setRequestUrl(String requestUrl) {
this.requestUrl = requestUrl;
}
@Override
protected Map<String, Object> doInBackground(Map<String, String>... params) {
Map<String, String> param=params[0];
String queryString="";
if(param!=null){//요청 파리미터가 존재 한다면
//서버에 전송할 데이터를 문자열로 구성하기
StringBuffer buffer=new StringBuffer();
Set<String> keySet=param.keySet();
Iterator<String> it=keySet.iterator();
boolean isFirst=true;
//반복문 돌면서 map 에 담긴 모든 요소를 전송할수 있도록 구성한다.
while(it.hasNext()){
String key=it.next();
String arg=null;
if(isFirst){
arg=key+"="+param.get(key);
isFirst=false;
}else{
arg="&"+key+"="+param.get(key);
}
buffer.append(arg);
}
queryString=buffer.toString();
}
//서버가 http 요청에 대해서 응답하는 문자열을 누적할 객체
StringBuilder builder=new StringBuilder();
HttpURLConnection conn=null;
InputStreamReader isr=null;
BufferedReader br=null;
PrintWriter pw=null;
//결과값을 담을 Map Type 객체
Map<String,Object> map=new HashMap<String,Object>();
try{
//URL 객체 생성
URL url=new URL(requestUrl);
//HttpURLConnection 객체의 참조값 얻어오기
conn=(HttpURLConnection)url.openConnection();
if(conn!=null){//연결이 되었다면
conn.setConnectTimeout(20000); //응답을 기다리는 최대 대기 시간
conn.setDoOutput(true);
conn.setRequestMethod("POST");
conn.setUseCaches(false);//케쉬 사용 여부
//전송하는 데이터에 맞게 값 설정하기
conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded"); //폼전송과 동일
//출력할 스트림 객체 얻어오기
OutputStreamWriter osw=
new OutputStreamWriter(conn.getOutputStream());
//문자열을 바로 출력하기 위해 osw 객체를 PrintWriter 객체로 감싼다
pw=new PrintWriter(osw);
//서버로 출력하기
pw.write(queryString);
pw.flush();
//응답 코드를 읽어온다.
int responseCode=conn.getResponseCode();
//Map 객체에 응답 코드를 담는다.
map.put("code", responseCode);
if(responseCode==200){//정상 응답이라면...
//서버가 출력하는 문자열을 읽어오기 위한 객체
isr=new InputStreamReader(conn.getInputStream());
br=new BufferedReader(isr);
//반복문 돌면서 읽어오기
while(true){
//한줄씩 읽어들인다.
String line=br.readLine();
//더이상 읽어올 문자열이 없으면 반복문 탈출
if(line==null)break;
//읽어온 문자열 누적 시키기
builder.append(line);
}
//출력받은 문자열 전체 얻어내기
String str=builder.toString();
//아래 코드는 수행 불가
//console.setText(str);
//Map 객체에 결과 문자열을 담는다.
map.put("data", str);
}
}
}catch(Exception e){//예외가 발생하면
//에러 정보를 담는다.
map.put("code",-1);
map.put("data", e.getMessage());
}finally {
try{
if(pw!=null)pw.close();
if(isr!=null)isr.close();
if(br!=null)br.close();
if(conn!=null)conn.disconnect();
}catch(Exception e){}
}
//결과를 담고 있는 Map 객체를 리턴해준다.
return map;
}
@Override
protected void onPostExecute(Map<String, Object> map) {
super.onPostExecute(map);
int code=(int)map.get("code");
if(code!=-1){//성공이라면
listener.onSuccess(requestId, map);
}else{//실패 (예외발생)
listener.onFail(requestId, map);
}
}
}
}
- get, post방식 요청을 알아서 해주는 sendGetRequest 메소드
- 이런 작업을 Util 안에서 대신해준다.
- 맵에 있는 내용을 읽어와서 get방식 파라미터로 가져와준다. 인코딩까지 해서!
- 저 aaa=x 라고 뒤에 붙어있는 내용을 data 키 값에 저장해준다.
- Util.sendGetRequest( ) 으로 () 안에 인자 4개의 값만 전달하면 된다.
이런 유틸리티를 사용하면 편리하게 요청할 수있다.
- 아래에서 보면 요청 파라미터를 구성하고, UrlConnection도 작업도 하고
요청Url에 대신 요청해주고 StringBuilder에 누적시켜서 결과값을 "data"라는 키값으로 Map에 담는다.
- 에러 발생시 map에 예외 결과를 담아주고 결과 리턴
- 결과는 onPostExecute 메소드에서 출력한다.
- 이 리스너를 구현해주어야 한다.
- 결과값을 이 onSuccess / onFail 메소드에서 받으면 된다.
1. 사용할때 RequestListener 인터페이스 Type 을 전달한다.
2. 결과는 RequestListener 객체에 전달된다.
3. Map<String,Object> 에서 응답 코드는 "code" 라는 키값으로 200, 404, 500, -1 중에 하나가 리턴되고
-1 이 리턴되면 Exception 발생으로 실패한 것이다. onFail() 이 호출됨
MainActivity
package com.example.step17httprequest2;
import android.os.Bundle;
import android.widget.Button;
import android.widget.EditText;
import androidx.appcompat.app.AppCompatActivity;
import java.util.Map;
public class MainActivity extends AppCompatActivity implements Util.RequestListener {
EditText editText;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
editText=findViewById(R.id.editText);
EditText inputUrl=findViewById(R.id.inputUrl);
Button requestBtn=findViewById(R.id.requestBtn);
//요청 버튼을 클릭했을때 동작할 준비
requestBtn.setOnClickListener(v -> {
//입력한 요청 url을 읽어온다.
String url=inputUrl.getText().toString();
//Util 클래스의 static 메소드를 활용해서 GET방식 요청하고 결과 받아오기
Util.sendGetRequest(999, url, null, this);
});
}
@Override
public void onSuccess(int requestId, Map<String, Object> result) {
if(requestId == 999){
//Map에 data라는 키값으로 담긴 String type을 읽어온다.
String data=(String)result.get("data");
editText.setText(data);
}
}
@Override
public void onFail(int requestId, Map<String, Object> result) {
//에러 메시지를 읽어와서 EditText에 출력하기
String data=(String) result.get("data");
editText.setText(data);
}
}
- 액티비티에 리스너를 구현하고, 메소드 오버라이드!
- 요청 id, url, 파라미터, 리스너 전달 (리스너는 구현해서 mainActivity이다)
- 이렇게 분리된 Util을 사용하면 코딩 양이 크게 줄어든다.
- 결과가 들어오면 onSuccess 메소드로 실행 순서가 들어온다.
- Manifest에 인터넷 허용permission 설정
- 레이아웃은 이전 예제와 동일하게
- 요청은 여러번 보낼 수 있다.
- 요청을 보내면 onSuccess에 id값이 들어온다.
- 이 아이디 값으로 요청을 구분할 수 있다.
- 결과를 받으려면 리스너를 등록해야 한다!
등록한것이 액티비티일 수도 있고 fragment일 수도 있고... 그 this로 결과가 들어온다.
- 이렇게 Util을 사용해서 get방식 요청하기
- 성공시에는 흐름이 onSucess로 들어가고, 실패했을 때는 fail 호출되도록!
- 실패해서 exception 발생시 -1과 "code" 메세지(예외 정보)를 담는다.
- onPostExecute의 리스너에서 onSuccess / onFail을 호출한다.
- 에러를 발생시키기 위해 인터넷을 사용할 수 없게 permission을 빼고 run 해보았다.
- 그냥 요청을 누르면 응답되지 않는다고 나오고,
어떤 값을 넣으면 permissio이 없다고 나온다.
- 이 경우 에러 메시지는 읽어서 EditText에 담아준다.
'국비교육(22-23)' 카테고리의 다른 글
78일차(1)/Android App(41) : Http POST방식 요청하기(4) - JSONArray 활용 (1) | 2023.01.30 |
---|---|
77일차(4)/Android App(40) : Http POST방식 요청하기(3) (0) | 2023.01.30 |
77일차(2)/Android App(38) : Http POST방식 요청하기(1) (0) | 2023.01.27 |
77일차(1)/Android App(37) : AsyncTask(2) (0) | 2023.01.27 |
76일차(4)/Android App(36) : AsyncTask(1) (0) | 2023.01.27 |