본문 바로가기
안드로이드 자바

[Java][Android] HttpUrlConnection을 이용한 http통신

by teamnova 2021. 2. 27.

이번시간에는 안드로이드 자바로 HttpUrlConnection을 사용해 통신하는 예제를 만들어보겠습니다.

 

안드로이드 프로젝트를 만들다 보면 서버를 사용해야할 때가 오겠죠?

 

이번예제는 클라이언트 입장에서 HttpUrlConnection을 이용해 자신의 서버와 요청하고, 응답을 받아 

정상적으로 통신이 된 결과를 액티비티 텍스트뷰에 띄워보겠습니다.

스틱코드 (stickode.com/mainlogin.html)

빠르고 쉽게 만들기 위해 스틱코드 플러그인을 사용해서 만들어보겠습니다.

 

스틱코드는 자주쓰는 코드를 저장해서 쉽고 빠르게 사용할 수 있고, 

다른사람들의 코드도 즐겨찾기를 통해 쉽게 내코드로 등록하여 사용할 수 있어 사용하는 사람이 늘어나고, 

좋은 코드가 쌓일수록 강력해지는 플러그인 입니다. 

 

프로젝트 생성

 

새로운 프로젝트를 생성 했고, 

 

권한설정

이번시간엔 통신을 이용하기 때문에

AndroidManifest.xml 파일 안에 Internet 권한을 추가해주어야 합니다.

<!-- 인터넷 권한 설정   -->
<uses-permission android:name="android.permission.INTERNET" />
<!-- 인터넷 연결 확인-->
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>

XML 파일 설정

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <EditText
        android:id="@+id/edt_1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="16dp"
        android:layout_marginLeft="16dp"
        android:layout_marginTop="16dp"
        android:ems="5"
        android:inputType="number"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />


    <TextView
        android:id="@+id/textView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="16dp"
        android:layout_marginLeft="16dp"
        android:text=" + "
        android:textSize="20sp"
        app:layout_constraintBottom_toBottomOf="@+id/edt_1"
        app:layout_constraintStart_toEndOf="@+id/edt_1"
        app:layout_constraintTop_toTopOf="@+id/edt_1" />

    <EditText
        android:id="@+id/edt_2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="16dp"
        android:layout_marginLeft="16dp"
        android:ems="5"
        android:inputType="number"
        app:layout_constraintBottom_toBottomOf="@+id/edt_1"
        app:layout_constraintStart_toEndOf="@+id/textView"
        app:layout_constraintTop_toTopOf="@+id/edt_1" />

    <Button
        android:id="@+id/btn_my"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="16dp"
        android:layout_marginLeft="16dp"
        android:layout_marginTop="16dp"
        android:text="서버에서 계산"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/edt_1" />


   
    <TextView
        android:id="@+id/txt_result"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="결과"
        android:textColor="@color/colorPrimary"
        android:textSize="30sp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <ProgressBar
        android:id="@+id/Prod_cpb"
        style="?android:attr/progressBarStyle"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:indeterminateDuration="@android:integer/config_longAnimTime"
        android:visibility="gone"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />


</androidx.constraintlayout.widget.ConstraintLayout>

다음과 같은 화면이 만들어졌네요.

 

자 이제 다음과 같이 버튼을 클릭할때마다 각 서버의 요청을 받아와 

결과를 TextView 에 넣어보겠습니다. 

 

클릭 리스너 등록

// 서버에서 계산 클릭
btn_my.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {

}
});

버튼의 클릭 리스너를 생성하고, 서버와 통신을 진행해 보도록 하겠습니다. 

 

스틱코드를 활용하여 단번에 HttpUrlConnection을 설정해주도록 하겠습니다. 

출처: stickode.com/detail.html?no=1920

다음과같이 자동완성되는 코드를 사용하면 

// 내서버 클릭
btn_my.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        // HttpUrlConnection
        Thread th = new Thread(new Runnable() {
            @Override
            public void run() {
                try {

                    String page = "url";
                    // URL 객체 생성
                    URL url = new URL(page);
                    // 연결 객체 생성
                    HttpURLConnection conn = (HttpURLConnection)url.openConnection();

                    // Post 파라미터
                    String params = "param=1"
                    + "&param2=2" + "sale";
                    // 결과값 저장 문자열
                    final StringBuilder sb = new StringBuilder();
                    // 연결되면
                    if(conn != null) {
                        Log.i("tag", "conn 연결");
                        // 응답 타임아웃 설정
                        conn.setRequestProperty("Accept", "application/json");
                        conn.setConnectTimeout(10000);
                        // POST 요청방식
                        conn.setRequestMethod("POST");
                        // 포스트 파라미터 전달
                        conn.getOutputStream().write(params.getBytes("utf-8"));
                        // url에 접속 성공하면 (200)
                        if(conn.getResponseCode() == HttpURLConnection.HTTP_OK) {
                            // 결과 값 읽어오는 부분
                            BufferedReader br = new BufferedReader(new InputStreamReader(
                              conn.getInputStream(), "utf-8"
                            ));
                            String line;
                            while ((line = br.readLine()) != null) {
                              sb.append(line);
                            }
                            // 버퍼리더 종료
                            br.close();
                            Log.i("tag", "결과 문자열 :" +sb.toString());
                            // 응답 Json 타입일 경우
                            //JSONArray jsonResponse = new JSONArray(sb.toString());
                            //Log.i("tag", "확인 jsonArray : " + jsonResponse);

                        }
                        // 연결 끊기
                        conn.disconnect();
                    }

                    //백그라운드 스레드에서는 메인화면을 변경 할 수 없음
                    // runOnUiThread(메인 스레드영역)
                    runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                              Toast.makeText(getApplicationContext(), "응답" + sb.toString(), Toast.LENGTH_SHORT).show();
                        }
                    });

                }catch (Exception e) {
                    Log.i("tag", "error :" + e);
                }
            }
        });
        th.start();

    }
});

다음과 같이 완성되는 것을 확인할 수 있습니다.

 

이제 상단의 EditText를 파라미터로 넘길 수 있게 수정해보겠습니다.

인터넷이 연결 되었을 때만 실행 될수 있게 예외처리를 추가해보겠습니다.

통신은 자주 쓰기 때문에 인터넷 체크도 스틱코드에 등록해두면 손쉽게 코드작성을 할 수 있습니다.

 

먼저 인터넷을 체크할 수 있는 클래스 파일을 만들어주겠습니다.

이렇게 생긴 클래스 파일에서 스틱코드를 사용해 클래스 파일을 셋팅해보겠습니다.
출처 : stickode.com/detail.html?no=1923

package com.example.httpurlconnectionexample;
import android.content.Context;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;

public class NetworkStatus {
    public static final int TYPE_WIFI = 1;
    public static final int TYPE_MOBILE = 2;
    public static final int TYPE_NOT_CONNECTED = 3;

    public static int getConnectivityStatus(Context context){ //해당 context의 서비스를 사용하기위해서 context객체를 받는다.
        ConnectivityManager manager = (ConnectivityManager) context.getSystemService(context.CONNECTIVITY_SERVICE);

        NetworkInfo networkInfo = manager.getActiveNetworkInfo();
        if(networkInfo != null){
            int type = networkInfo.getType();
            if(type == ConnectivityManager.TYPE_MOBILE){//쓰리지나 LTE로 연결된것(모바일을 뜻한다.)
                return TYPE_MOBILE;
            }else if(type == ConnectivityManager.TYPE_WIFI){//와이파이 연결된것
                return TYPE_WIFI;
            }
        }
        return TYPE_NOT_CONNECTED;  //연결이 되지않은 상태
    }
}

그럼 다음과 같이 코드가 단번에 완성됩니다.

 

출처 : stickode.com/detail.html?no=1923

이제 인터넷 연결을 체크할 곳에 코드를 추가해보겠습니다.

마찬가지로 스틱코드로 셋팅을 해보겠습니다.

int status = NetworkStatus.getConnectivityStatus(getApplicationContext());
if(status == NetworkStatus.TYPE_MOBILE || status == NetworkStatus.TYPE_WIFI) {

}else {
Toast.makeText(getApplicationContext(), "인터넷 연결을 확인해주세요.", Toast.LENGTH_SHORT).show();
}

이렇게 만들어진 예외처리 안에 통신을 넣으면  

인터넷 연결 예외처리가 완료되었습니다.

 

자그럼 이번에는 통신이 시작하면 프로그레스바를 보이게 처리하고, 

통신이 끝나면 프로그레스바를 없애는 코드까지 추가해서 셋팅을 완성 해보겠습니다.

이렇게 추가해서 통신할 셋팅이 완성되었습니다.

 

아래 코드는 mainActivity 전문입니다.

package com.example.httpurlconnectionexample;

import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;

public class MainActivity extends AppCompatActivity {

    // 변수 초기설정
    // 버튼
    Button btn_my;
    // 텍스트뷰
    TextView txt_result;
    // 에딧텍스트
    EditText edt_1;
    EditText edt_2;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // UI 요소 연결
        // 버튼
        btn_my = findViewById(R.id.btn_my);
        // 텍스트뷰
        txt_result = findViewById(R.id.txt_result);
        // 에딧텍스트
        edt_1 = findViewById(R.id.edt_1);
        edt_2 = findViewById(R.id.edt_2);

        // 서버에서 계산 클릭
        btn_my.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                final String edit_1 = edt_1.getText().toString().trim();
                final String edit_2 = edt_2.getText().toString().trim();

                int status = NetworkStatus.getConnectivityStatus(getApplicationContext());
                if(status == NetworkStatus.TYPE_MOBILE || status == NetworkStatus.TYPE_WIFI) {

                    // 프로그래스바 보이게 처리
                    findViewById(R.id.cpb).setVisibility(View.VISIBLE);

                    // HttpUrlConnection
                    final Thread th = new Thread(new Runnable() {
                        @Override
                        public void run() {
                            try {
                                String page = "https://sunslog.cf/test.php";
                                // URL 객체 생성
                                URL url = new URL(page);
                                // 연결 객체 생성
                                HttpURLConnection conn = (HttpURLConnection)url.openConnection();

                                // Post 파라미터
                                String params = "param=" + edit_1
                                        + "&param2=" + edit_2;
                                // 결과값 저장 문자열
                                final StringBuilder sb = new StringBuilder();
                                // 연결되면
                                if(conn != null) {
                                    Log.i("tag", "conn 연결");
                                    // 응답 타임아웃 설정
                                    conn.setRequestProperty("Accept", "application/json");
                                    conn.setConnectTimeout(10000);
                                    // POST 요청방식
                                    conn.setRequestMethod("POST");
                                    // 포스트 파라미터 전달
                                    conn.getOutputStream().write(params.getBytes("utf-8"));
                                    // url에 접속 성공하면 (200)
                                    if(conn.getResponseCode() == HttpURLConnection.HTTP_OK) {
                                        // 결과 값 읽어오는 부분
                                        BufferedReader br = new BufferedReader(new InputStreamReader(
                                                conn.getInputStream(), "utf-8"
                                        ));
                                        String line;
                                        while ((line = br.readLine()) != null) {
                                            sb.append(line);
                                        }
                                        // 버퍼리더 종료
                                        br.close();
                                        Log.i("tag", "결과 문자열 :" +sb.toString());
                                        // 응답 Json 타입일 경우
                                        //JSONArray jsonResponse = new JSONArray(sb.toString());
                                        //Log.i("tag", "확인 jsonArray : " + jsonResponse);

                                    }else {

                                        // runOnUiThread 기본
                                        runOnUiThread(new Runnable() {
                                            @Override
                                            public void run() {
                                                // 프로그래스바 안보이게 처리
                                                findViewById(R.id.cpb).setVisibility(View.GONE);
                                                Toast.makeText(getApplicationContext(), "네트워크 문제 발생", Toast.LENGTH_SHORT).show();
                                            }
                                        });
                                    }
                                    // 연결 끊기
                                    conn.disconnect();
                                }

                                //백그라운드 스레드에서는 메인화면을 변경 할 수 없음
                                // runOnUiThread(메인 스레드영역)
                                runOnUiThread(new Runnable() {
                                    @Override
                                    public void run() {
                                        txt_result.setText(sb.toString());
                                        // 프로그래스바 안보이게 처리
                                        findViewById(R.id.cpb).setVisibility(View.GONE);
                                    }
                                });

                            }catch (Exception e) {
                                Log.i("tag", "error :" + e);
                            }
                        }
                    });
                    th.start();

                }else {
                  Toast.makeText(getApplicationContext(), "인터넷 연결을 확인해주세요.", Toast.LENGTH_SHORT).show();
                }

            }
        });

    }
}

 

이제 테스트를 진행해보겠습니다.

 

 

저는 개인적으로 사용하는 서버로 테스트를 진행하였습니다. 

로컬에 서버를 구축하시거나, aws를 빌려 서버를 구축해서 테스트해보시기 바랍니다.

 

아파치 서버에서 PHP 언어를 사용하여 요청값을 더해서 응답결과로 보내보았습니다. 

<?php   
    $param = $_POST[param];
    $param2 = $_POST[param2];

    if($param == "") { $param = 0; }
    if($param2 == "") { $param2 = 0; }
    
    $result = '결과 값은 "'.(intval($param) + intval($param2)).'" 입니다.';
    echo $result;
?>

 

테스트 결과입니다.

 

 

 

이렇게 스틱코드를 사용해 HttpUrlConnection을 사용해보았습니다.

 

 

이 예제에서 사용된 스틱코드

출처 : stickode.com/detail.html?no=1920 - 안드로이드 HttpUrlConnection  

출처 : stickode.com/detail.html?no=1923 - 인터넷 연결확인, 네트워크 상태 체크