서버통신_Retrofit2_GET_POST_이미지 업로드

소정·2023년 3월 15일
0

Android_with_Java

목록 보기
30/34

[1] Retrofit2라이브러리 ★★★★★

서버와 통신하기 위한 코드를 써주는 친구

1. 라이브러리 추가 - retrofit2 / gson / converter-gson

  • retrofit2(squareup)
  • gson (google)
  • converter-gson (squareup)

2. gradle 에서 viewBinding도 추가

3. 9가지 실습

0) 네트워크 사용하기 위해 인터넷 퍼미션 받기

1) GET 방식으로 서버에 있는 json파일을 읽어오기

  • filezila에 서버에서 읽어올 json 파일 등록
  1. retrofit 객체 생성 (빌더 이용)
    1-1) 베이스 url 설정
    1-2) retrofit과 gson을 연결해주는 Converter 공장 더하기
    cf) simpleXmlConverter : xml용 컨버터
    1-3) Retrofit 빌더를 통해 생성
  1. 원하는 GET,POST 동작을 하는 인터페이스(작업 요구서) 설계
  • 리턴 Call로 오고 제네릭으로 결과를 어떻게 받을지도 정할 수 있다

Call : 네크워크 작업을 위한 코드를 가진 것

package com.bsj0420.ex85retrofit;

import retrofit2.Call;
import retrofit2.http.GET;

//인터페이스는 클래스와 비슷하지만 멤버로 추상메소드(이름만 있는 메소드)만 만들 수 있음
public interface RetrofitService {
    
    //원하는 작업을 위한 코드를 쓰는게 아니라 요구사항 명세

    //1.단순하게 GET방식으로 json파일을 읽어오기
    @GET("Retrofit/board.json")
    Call<Item> getBoardJson(); 

}
  1. 위 2단계에서 설계한 RetrofitService 인터페이스 객체 생성

  2. 위애서 만든 서비스 객체의 추상메소드를 호출하여 실제 서버작업을 수행하는 Call 이라는 객체 리턴받기

  3. 위 4단계에서 리턴된 Call 객체에게 네트워크 작업을 수행하도록 요청 - 비동기방식으로(별도 스레드)


main.java 총 코드

package com.bsj0420.ex85retrofit;

import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;

import com.bsj0420.ex85retrofit.databinding.ActivityMainBinding;
import com.google.gson.Gson;

import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;

public class MainActivity extends AppCompatActivity {

    //Http 통신을 위한 라이브러리 : OkHttp, Retrofit2, Volley
    //이 세개 중에서 가장 보편적으로 많이 사용하는 Retrolfit2 실습

    ActivityMainBinding binding;

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

        binding = ActivityMainBinding.inflate(getLayoutInflater());
        setContentView(binding.getRoot());

        binding.btnGet.setOnClickListener(v-> clickBtnGet());

    }

    private void clickBtnGet() {
        //단순하게 GET 방식으로 서버에 있는 json파일을 읽어오기
        
        //retrofit을 이용하여 서버에서 json 파일을 읽어와서 Item 객체로 곧바로 파싱

        //1. retrofit 객체 생성 (빌더 이용)
        Retrofit.Builder builder = new Retrofit.Builder();
        //1-1) 베이스 url 설정
        builder.baseUrl("http://mrhisj23.dothome.co.kr/");
        //1-2) retrofit과 gson을 연결해주는 Converter 공장 더하기
        // cf) simpleXmlConverter : xml용 컨버터
        builder.addConverterFactory(GsonConverterFactory.create());
        //1-3) Retrofit 빌더를 통해 생성
        Retrofit retrofit = builder.build();

        //2.원하는 GET,POST 동작을 하는 인터페이스(작업 요구서) 설계
        //RetrofitService.java 인터페이스 설계 및 추상메소드 : getBoardJson()
        
        //3. 위 2단계에서 설계한 RetrofitService 인터페이스 객체 생성
        RetrofitService retrofitService = retrofit.create(RetrofitService.class); //이때 url 만들고 스레드 작업 알아서 함
        // RetrofitService.class : 설계도

        //4. 위애서 만든 서비스 객체의 추상메소드를 호출하여 실제 서버작업을 수행하는 Call 이라는 객체 리턴받기
        Call<Item> call = retrofitService.getBoardJson(); 
        
        //5. 위 4단계에서 리턴된 Call 객체에게 네트워크 작업을 수행하도록 요청 - 비동기방식으로(별도 스레드)
        call.enqueue(new Callback<Item>() { //enqueue : 작업대에 올라가는 것 ()안에 결과 오면 자동으로 발동하는 객체 적기
            @Override
            public void onResponse(Call<Item> call, Response<Item> response) {
                //응답 받았을 때
                //파라미터로 전달된 응답객체로 부터 GSON라이브러리에 의해 
                //자동으로 Item객체로 파싱되어 있는 데이터 값 얻어오기
                Item item = response.body(); //실제 데이터 값 들어 있는 body()
                binding.tv.setText(item.name +" : "+item.msg);
            }

            @Override
            public void onFailure(Call<Item> call, Throwable t) {
                //실패 했을 때
                binding.tv.setText("failune : "+ t.getMessage());
            }
        });

    }
}

2) @Path : 경로의 이름을 위 1번처럼 고정하지 않고 사용자에게 파라미터로 전달을 받아서 지정 방법

연결 5단계
1. retrofit 객체 생성

  1. Service 인터페이스 설계 [원하는 GET/POST 동작을 하는 추상메소드 설계 - 작업요구서]
//[2] 경로의 이름을 위 1번처럼 고정하지 않고 사용자에게 파라미터로 전달을 받아서 지정 방법 [@Path]
    @GET("{aaa}/{bbb}")
    Call<Item> getBoardJsonByPath(@Path("aaa") String path, @Path("bbb") String fileName);
  1. Service 인터페이스 객체로 생성

  2. 추상메소드 호출하여 네트워크 작업 수행 Call 객체 받기

  3. 네크워크 작업 시작


main.java 총 코드

package com.bsj0420.ex85retrofit;

import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;

import com.bsj0420.ex85retrofit.databinding.ActivityMainBinding;
import com.google.gson.Gson;

import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;

public class MainActivity extends AppCompatActivity {

    //Http 통신을 위한 라이브러리 : OkHttp, Retrofit2, Volley
    //이 세개 중에서 가장 보편적으로 많이 사용하는 Retrolfit2 실습

    ActivityMainBinding binding;

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

        binding = ActivityMainBinding.inflate(getLayoutInflater());
        setContentView(binding.getRoot());

        binding.btnPath.setOnClickListener(v-> clickBtnPath());

    }

    private void clickBtnPath() {

        //경로의 이름을 고정하지 않고 파라미터로 전달하여 지정해보기

        //1. retrofit 객체 생성
        Retrofit.Builder builder = new Retrofit.Builder();
        builder.baseUrl("http://mrhisj23.dothome.co.kr/");
        builder.addConverterFactory(GsonConverterFactory.create());
        Retrofit retrofit = builder.build();

        //2. Service 인터페이스 설계 [원하는 GET/POST 동작을 하는 추상메소드 설계 - 작업요구서]
        // getBoardJsonByPath()

        //3. Service 인터페이스 객체로 생성
        RetrofitService retrofitService = retrofit.create(RetrofitService.class);

        //4. 추상메소드 호출하여 네트워크 작업 수행 Call 객체 받기
        Call<Item> call = retrofitService.getBoardJsonByPath("Retrofit","board.json");

        //5. 네크워크 작업 시작
        call.enqueue(new Callback<Item>() {
            @Override
            public void onResponse(Call<Item> call, Response<Item> response) {
                Item item = response.body();
                binding.tv.setText(item.name + "\n" + item.msg);
            }

            @Override
            public void onFailure(Call<Item> call, Throwable t) {
                binding.tv.setText("failune : "+ t.getMessage());
            }
        });


    }

3) GET방식으로 서버에 값 전달 : @Query("식별자")

하나식 보내는 것

  1. Retrofit 객체 생성 -> static 클래스로 만들기
  • 매번 쓰기 귀찮아서 서버 연동 따로 모듈화
package com.bsj0420.ex85retrofit;

import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;

public class RetrofitHelper {

    public static Retrofit getRetrofitInstance() {
        Retrofit.Builder builder = new Retrofit.Builder();
        builder.baseUrl("http://mrhisj23.dothome.co.kr/");
        builder.addConverterFactory(GsonConverterFactory.create());
        Retrofit retrofit = builder.build();

        return retrofit;
    }

}
  1. Service 인터페이스 설계 - 요구 명세 추상메소드
 //[3] GET방식으로 서버에 값 전달 [@Query]
    @GET("Retrofit/getTest.php")
    Call<Item> getMethodTest(@Query("name") String name, @Query("msg") String msg);
  1. Service 객체 생성

  2. 추상메소드 호출하여 네트워크 작업 수행 Call 객체 받기 [서버로 보낼 값 파라미터로 전달]

  3. 네크워크 작업 시작

////////////////////////////////////////////////////

php 파일 서버에 올리기

echo할 데이터 값 $name $msg를 연관배열로 변경하여 json 문자열로 보내주기

<?php

    header("Content-Type:application/json ; charset=utf-8");

    // 변수 받기
    $name = $_GET["name"];
    $msg = $_GET["msg"];


    //잘 받았는지 확인하기 위해 안드로이드 쪽으로 응답 echo
    //단 , 안드로이드에서 응답결과를
    //json형식으로 받아 처리하도록 되어 있음
    //php언어는 연관배열을 json으로 쉽게 바꿔준다

    //echo할 데이터 값 $name $msg를 연관배열로 변경
    $arr = array(); //빈 배열
    $arr['name'] = $name; //값 넣기
    $arr['msg'] = $msg;

    //연관 배열 --> json 문자열
    echo json_encode($arr);

?>

main.java 총 코드

package com.bsj0420.ex85retrofit;

import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;

import com.bsj0420.ex85retrofit.databinding.ActivityMainBinding;
import com.google.gson.Gson;

import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;

public class MainActivity extends AppCompatActivity {

    //Http 통신을 위한 라이브러리 : OkHttp, Retrofit2, Volley
    //이 세개 중에서 가장 보편적으로 많이 사용하는 Retrolfit2 실습

    ActivityMainBinding binding;

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

        binding = ActivityMainBinding.inflate(getLayoutInflater());
        setContentView(binding.getRoot());

 binding.btnQuery.setOnClickListener(v-> clickBtnQuery());

    }

    private void clickBtnQuery() {

        //Get방식으로 값을 서버에 전달
        //0. 서버에 보낼 데이터들 - 한글 url 인코더 안해줘도됨 레트로핏이 알아서 해줌
        String name="홍길동";
        String msg="안녕";
        
        //1. Retrofit 객체 생성
        Retrofit retrofit = RetrofitHelper.getRetrofitInstance();
        
        //2. Service 인터페이스 설계 - 요구 명세 추상메소드
        // getMethodTest()
        
        //3. Service 객체 생성
        RetrofitService retrofitService = retrofit.create(RetrofitService.class);
        
        //4. 추상메소드 호출하여 네트워크 작업 수행 Call 객체 받기 [서버로 보낼 값 파라미터로 전달]
        Call<Item> call = retrofitService.getMethodTest(name, msg);
        
        //5. 네크워크 작업 시작
        call.enqueue(new Callback<Item>() {
            @Override
            public void onResponse(Call<Item> call, Response<Item> response) {
                Item item = response.body();
                binding.tv.setText(item.name + " - " + item.msg);
            }

            @Override
            public void onFailure(Call<Item> call, Throwable t) {
                binding.tv.setText("error : " + t.getMessage());
            }
        });

    }

4) GET방식으로 값을 보낼 때 Map Collection 을 이용하여 한방에 값을 전달

  1. Retrofit 객체 생성

  2. Service 인터페이스 설계 - 요구 명세 추상메소드

//[4] GET방식으로 값을 보낼 때 Map Collection 을 이용하여 한방에 값을 전달
    @GET("Retrofit/getTest.php")
    Call<Item> getMethodMapTest(@QueryMap Map<String,String> datas);
  1. Service 인터페이스 객체 생성

  2. 원하는 작업 추상메소드 호출

  3. 네트워크 연결


main.java 총 코드

코드를 입력하세요

5) POST방식으로 값 전달 : @body

  • 객체를 전달하면 자동으로 객체 멤버변수를 json문자열로 변환하여 서버로 전송해줌

  • 멤버변수 이름이 자동 식별자가 됨

  1. Retrofit 객체 생성

  2. Service 인터페이스 설계 - 요구 명세 추상메소드

 //[5] POST방식으로 값 전달 [@body]
    // -객체를 전달하면 자동으로 객체 멤버변수를 json문자열로 변환하여 서버로 전송해줌
    @POST("Retrofit/postTest.php")
    Call<Item> postMethodTest(@Body Item item);
  1. Service 인터페이스 객체 생성

  2. 원하는 작업 추상메소드 호출

  3. 네트워크 연결

/////////////////////////////////////////////////////////////

post.php

  • @Boby로 보낸 json문자열은 POST[]라는배열에자동저장되지않는다_POST[]라는 배열에 자동 저장되지 않는다name = $_POST[]; - 안됨
<?php

    header('Content-Type:application/json; charset=utf-8');

    //@Boby로 보낸 json문자열은 $_POST[]라는 배열에 자동 저장되지 않는다
    //$name = $_POST[]; - 안됨

    //json으로 넘어온 데이타는 별도의 임시공간[php://input]에 파일 형태로 보관됨
    //이 파일을 읽어서 $_POST[] 배열변수에 대입해주기

    $data = file_get_contents('php://input');
    //$data는 json으로 된 문자열

    $_POST = json_decode($data, true); //true = 연관배열로 할지 여부

    $name = $_POST['name'];
    $msg = $_POST['msg'];

    //잘 받았는지 확인해 보기 위해 안드로이드로 echo
    //안드로이드는 json으로 보내줘야 하기에
    
    $arr= array();
    $arr['name'] = $name;
    $arr['msg'] = $msg;

    //응답 > json
    echo json_encode($arr);

?>

main.java 총 코드

package com.bsj0420.ex85retrofit;

import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;

import com.bsj0420.ex85retrofit.databinding.ActivityMainBinding;
import com.google.gson.Gson;

import java.util.HashMap;

import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;

public class MainActivity extends AppCompatActivity {

    //Http 통신을 위한 라이브러리 : OkHttp, Retrofit2, Volley
    //이 세개 중에서 가장 보편적으로 많이 사용하는 Retrolfit2 실습

    ActivityMainBinding binding;

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

        binding = ActivityMainBinding.inflate(getLayoutInflater());
        setContentView(binding.getRoot());
        binding.btnPostObject.setOnClickListener(v-> clickBtnPostObject());


    }

    private void clickBtnPostObject() {

        //POST방식으로 전달할 값을 객체로 - retrofit이 자동으로 json문자열로 뱐환하여 전송
        Item item = new Item("kim","good good");

        //1.
        Retrofit retrofit = RetrofitHelper.getRetrofitInstance();

        //2.추상 클래스 명세서

        //3.
        RetrofitService retrofitService = retrofit.create(RetrofitService.class);

        //4.
        Call<Item> call = retrofitService.postMethodTest(item);

        //5.
        call.enqueue(new Callback<Item>() {
            @Override
            public void onResponse(Call<Item> call, Response<Item> response) {
                Item item_result = response.body();

                binding.tv.setText(item_result.name+" - "+item_result.msg);
            }

            @Override
            public void onFailure(Call<Item> call, Throwable t) {

            }
        });

    }

6) POST방식으로 값 하나씩 전달 : @Field

  • @Field를 사용하고 싶다면 반드시 @FormUrlEncoded와 함께 써야함

  1. Retrofit 객체 생성

  2. Service 인터페이스 설계 - 요구 명세 추상메소드

//[6] POST방식으로 값 하나씩 전달 [@Field]
    //단, @Field를 사용하고 싶다면 반드시 @FormUrlEncoded와 함께 써야함
    @FormUrlEncoded
    @POST("Retrofit/postTest2.php")
    Call<Item> postMethodTest2(@Field("name") String name, @Field("msg")String msg);
  1. Service 인터페이스 객체 생성

  2. 원하는 작업 추상메소드 호출

  3. 네트워크 연결

/////////////////////////////////////////////////////////////

post.php

<?php

    header('Content-Type:application/json; charset=utf-8');

    $name = $_POST['name'];
    $msg = $_POST['msg'];

    //잘 받았는지 확인해 보기 위해 안드로이드로 echo
    //안드로이드는 json으로 보내줘야 하기에
    
    $arr= array();
    $arr['name'] = $name;
    $arr['msg'] = $msg;

    //응답 > json
    echo json_encode($arr);

?>

main.java 총 코드

package com.bsj0420.ex85retrofit;

import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;

import com.bsj0420.ex85retrofit.databinding.ActivityMainBinding;
import com.google.gson.Gson;

import java.util.HashMap;

import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;

public class MainActivity extends AppCompatActivity {

    //Http 통신을 위한 라이브러리 : OkHttp, Retrofit2, Volley
    //이 세개 중에서 가장 보편적으로 많이 사용하는 Retrolfit2 실습

    ActivityMainBinding binding;

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

        binding = ActivityMainBinding.inflate(getLayoutInflater());
        setContentView(binding.getRoot());

        binding.btnPostField.setOnClickListener(v-> clickBtnPostField());

    }

    private void clickBtnPostField() {

        // POST방식으로 개별 데이터 전송
        String name = "Rose";
        String msg  = "hello";

        //1.
        Retrofit retrofit = RetrofitHelper.getRetrofitInstance();

        //2. 인터페이스 클래스 : postMethodTest2()

        //3.
        RetrofitService retrofitService = retrofit.create(RetrofitService.class);

        //4.
        Call<Item> call = retrofitService.postMethodTest2(name, msg);

		//5.
        call.enqueue(new Callback<Item>() {
            @Override
            public void onResponse(Call<Item> call, Response<Item> response) {
                Item item = response.body();

                binding.tv.setText(item.name +" : "+ item.msg);
            }

            @Override
            public void onFailure(Call<Item> call, Throwable t) {

            }
        });

    }

7) GET방식으로 jsonArray 값 읽어와서 ArrayList<Item'>로 곧바로 파싱

  1. Retrofit 객체 생성

  2. Service 인터페이스 설계 - 요구 명세 추상메소드

//[7]GET방식으로 jsonArray 값 읽어와서 ArrayList<Item>로 곧바로 파싱
    @GET("Retrofit/boardArr.json")
    Call<ArrayList<Item>> getBoardArr();
  1. Service 인터페이스 객체 생성

  2. 원하는 작업 추상메소드 호출

  3. 네트워크 연결


/////////////////////////////////////////////////////////////

post.php

<?php

[
    {"name" : "sam", "msg" : "hello"},
    {"name" : "robin", "msg" : "Nice"},
    {"name" : "lisa", "msg" : "hoho"},
    {"name" : "jisoo", "msg" : "ohoh"}
]

?>

main.java 총 코드

package com.bsj0420.ex85retrofit;

import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;

import com.bsj0420.ex85retrofit.databinding.ActivityMainBinding;
import com.google.gson.Gson;

import java.util.ArrayList;
import java.util.HashMap;

import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;
import retrofit2.converter.scalars.ScalarsConverterFactory;

public class MainActivity extends AppCompatActivity {

    //Http 통신을 위한 라이브러리 : OkHttp, Retrofit2, Volley
    //이 세개 중에서 가장 보편적으로 많이 사용하는 Retrolfit2 실습

    ActivityMainBinding binding;

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

        binding = ActivityMainBinding.inflate(getLayoutInflater());
        setContentView(binding.getRoot());

        binding.btnArr.setOnClickListener(v-> clickBtnArr());

    }

    private void clickBtnArr() {

        // json array를 읽어와서 곧바로 ArrayList<Item> 파싱
        //1.
        Retrofit retrofit = RetrofitHelper.getRetrofitInstance();

        //2.

        //3.
        RetrofitService retrofitService = retrofit.create(RetrofitService.class);

        //4.
        Call<ArrayList<Item>> call = retrofitService.getBoardArr();

        //5.
        call.enqueue(new Callback<ArrayList<Item>>() {
            @Override
            public void onResponse(Call<ArrayList<Item>> call, Response<ArrayList<Item>> response) {
                ArrayList<Item> item = response.body();

                binding.tv.setText("아이템 개수 : "+ item.size());
            }

            @Override
            public void onFailure(Call<ArrayList<Item>> call, Throwable t) {

            }
        });

    }

8) GSON을 통헤 자동으로 Item 객체로 파싱하지 않고 그냥 문자열로 응답결과 받아보기

  • 결과를 Stirng 으로 받으려면 ScalarsConverter 필요 - 추가 라이브러리!

  1. Retrofit 객체 생성

  2. Service 인터페이스 설계 - 요구 명세 추상메소드

//[8] GSON을 통헤 자동으로 Item 객체로 파싱하지 않고 그냥 문자열로 응답결과 받아보기
    @GET("Retrofit/board.json")
    Call<String> getJsonString();
  1. Service 인터페이스 객체 생성

  2. 원하는 작업 추상메소드 호출

  3. 네트워크 연결


main.java 총 코드

package com.bsj0420.ex85retrofit;

import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;

import com.bsj0420.ex85retrofit.databinding.ActivityMainBinding;
import com.google.gson.Gson;

import java.util.ArrayList;
import java.util.HashMap;

import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;
import retrofit2.converter.scalars.ScalarsConverterFactory;

public class MainActivity extends AppCompatActivity {

    //Http 통신을 위한 라이브러리 : OkHttp, Retrofit2, Volley
    //이 세개 중에서 가장 보편적으로 많이 사용하는 Retrolfit2 실습

    ActivityMainBinding binding;

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

        binding = ActivityMainBinding.inflate(getLayoutInflater());
        setContentView(binding.getRoot());

        binding.btnString.setOnClickListener(v-> clickBtnString());

    }

    private void clickBtnString() {

        //서버의 응답결과가 json이 아닐 때 사용
        //서버의 응답결과를 그냥 String으로 받아보기 [No Parse]
        //결과를 Stirng 으로 받으려면 ScalarsConverter 필요 - 추가 라이브러리!

        //1.
        Retrofit.Builder builder = new Retrofit.Builder();
        builder.baseUrl("http://mrhisj23.dothome.co.kr/");
        builder.addConverterFactory(ScalarsConverterFactory.create());
        Retrofit retrofit= builder.build();

        //2.

        //3.
        RetrofitService retrofitService = retrofit.create(RetrofitService.class);

        //4.
        Call<String> call = retrofitService.getJsonString();

        call.enqueue(new Callback<String>() {
            @Override
            public void onResponse(Call<String> call, Response<String> response) {
                String s = response.body();

                binding.tv.setText(s);
            }

            @Override
            public void onFailure(Call<String> call, Throwable t) {

            }
        });

    }



2. Retrofit으로 Image 업로드

MultipartBody.part 안에 requst body 안에 이미지 들어가 있음

0. 사전준비

  • 이미지를 추가하고 레트로핏을 사용하기 위한 라이브러리들 추가

  • 인터넷 퍼미션 받기


1. 화면 UI 제작

<?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"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:padding="16dp"
    tools:context=".MainActivity">

    <Button
        android:id="@+id/btn_select"
        android:text="select img"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"/>

    <ImageView
        android:id="@+id/iv"
        android:layout_width="match_parent"
        android:layout_height="300dp"/>

    <Button
        android:id="@+id/btn_upload"
        android:text="upload img"
        android:backgroundTint="@color/black"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"/>

</LinearLayout>

2. 서버에 업로드 php 준비 & filezila에 업로드

<?php

    header("Content-Type:text/plain ; charset=utf-8");

    //파일의 실제 데이터는 임시 저장소에 가고
    //php에는 파일정보만 전달됨(택배 송장 같은 거..)

    $file = $_FILES['img'];
    //$file은 파일정보를 가진 배열

    $srcName = $file['name']; // 원본 이름
    $size = $file['size']; // 파일 사이즈
    $tmpName = $file['tmp_name']; //임시 저장소 위치 이름

    //전달이 잘 됐는지 확인
    echo "$srcName \n";
    echo "$size \n";
    echo "$tmpName \n";


    //임시 저장소에 있는 이미지를 영구저장소 위치로 이동
    $dstName = "./".date('YmdHis').$srcName; 
    $result = move_uploaded_file($tmpName, $dstName);

    if($result) echo "업로드 성공";
    else echo "업로드 실패" ;
?>

3. Main.java

1) 사진 앨범에서 불러오기

  • MediaStore.ACTION_PICK_IMAGES : API 29버전 이상에서 동적퍼미션 없이 사용가능함 (이것을 이용해 앨범에서 사진 불러와보기)
  • Intent로 앨범에 접근 MediaStore.ACTION_PICK_IMAGES
  • setActivity하면 그냥 앨범으로 넘어가는 것이니떄문에 액티비티를 스타트 하고 결과같을 받아오는 ActivityResultLauncher 계약서 만들기

  • ActivityResultLauncher 할 일
    ① result.getResultCode() 값이 RESULT_CANCELED 아닐때 , 그냥 앨범에서 사진 고르지 않고 돌아온 게 아닐때
    ② 선택한 사진의 콘텐츠 주소(URI) 얻어오기

    Uri uri = result.getData().getData();
    => getData() 택배기사 , 뒤 getData() 실제 uri

③ Glide를 이용해 사진 이미지뷰에 보여주기
/////-- uri경로 => 파일주소로 변환 --//////
가져온 사진의 실제 경로도 얻어줘야함 파일을 서버에 업로드 하기 위해선 콘텐츠 DB 주소를 가진 uri 가 아닌 실제 사진이 있는 파일의 실 경로를 찾아주어야한다
④ 업로드할 파일의 주소를 저장하는 문자열 변수를 멤버변수로 만들자

String imgPath = null;

⑤ 파일 경로를 찾아오는 함수를 따로 작성 getFilePathFromUri(uri)


	private void clickSelect() {
        //이미지를 선택할 수 있는 앱을 실행
        //29버전 이상에서 퍼미션 없이 사용가능한것 사용할 것 : MediaStore.ACTION_PICK_IMAGES
        Intent intent = new Intent(MediaStore.ACTION_PICK_IMAGES); //사진첩 실행하면
        resultLauncher.launch(intent); //결과 받아오는 애 만들기

    }//////////////////////////////////

    //결과 받는 계약서 만들기 : 액티비티 스타트하고 결과값 받아오는 애
    ActivityResultLauncher<Intent> resultLauncher = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(), result -> {
        if(result.getResultCode() == RESULT_CANCELED) return;

        //선택한 사진의 콘텐츠 주소(URI) 얻어오기
        Uri uri = result.getData().getData(); //앞 getData() 택배기사 , 뒤 getData() 실제 uri
        Glide.with(this).load(uri).into(binding.iv);

        //애석하게 Retrofit을 이용해 서버에 파일을 전송하려면
        //파일의 Uri (즉, 콘텐츠 DB 주소)가 아니라 파일(File)의 주소가 필요함
        //Uri = 특정 DB의 경로 , 가짜경로임
        //서버에 보낼땐 찐 경로인 파일의 주소가 필요함
        //new AlertDialog.Builder(this).setMessage(uri.toString()).create().show(); // => uri 주소
        
        //uri --> 파일주소로 변환
        imgPath = getFilePathFromUri(uri);

        //new AlertDialog.Builder(this).setMessage(imgPath.toString()).create().show(); // => 파일 주소 찍어보기

    });//////////////////////////////////////////

2) Uri 경로에서 파일의 실제 경로로 변경 함수 작성 [ getFilePathFromUri(uri) ]

CursorLoader는 백그라운드에서 쿼리를 수행하는 일을 한다
파일의 실제 경로를 Cursor로 찾아온다

  • 아래 코드 정형화 되어 있음

CursorLoader

응용 프로그램의 UI를 차단하지 않도록 백그라운드 스레드에서 커서 쿼리를 수행하도록 빌드

Cursor

실제 로드를 수행하고 로드 작업의 결과를 반환하기 위해 작업자 스레드에서 호출


String getFilePathFromUri(Uri uri) {

        String[] proj = {MediaStore.Images.Media.DATA};

        //Cursor : 안드로이드 백그라운드를 부르는 애
        CursorLoader loader = new CursorLoader(this,uri,proj,null,null,null);
        //select MediaStore.Images.Media.DATA from uri where selection and selectionArgs order by sortOrder

        Cursor cursor = loader.loadInBackground();

        //cursor.getString();의 ()안 index번호를 식별가능한 글자 써넣기 위한 int 변수
        int colum_index = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);

        cursor.moveToFirst();
        String result = cursor.getString(colum_index);

        return result;
    }

Uri -- > 절대경로로 바꿔서 리턴시켜주는 메소드

String getFilePathFromUri(Uri uri){
    String[] proj= {MediaStore.Images.Media.DATA};
    CursorLoader loader= new CursorLoader(this, uri, proj, null, null, null);
    Cursor cursor= loader.loadInBackground();
    int column_index= cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
    cursor.moveToFirst();
    String result= cursor.getString(column_index);
    cursor.close();
    return  result;
}

3) 사진 서버에 업로드

사진을 업로드 하기 위해서 포장 작업이 필요하다
1. 2)번에서 얻은 file경로로 File 객체를 생성
2. 미디어타입과 1번에서 생성한 파일을 넣어 RequestBody 객체 생성
3. MultipartBody.Part를 create 한다 createFormData의 생성자로 (서버에서 분류할 식별자, 파일명, 2번에서 생성한 RequestBody) 넣어준다

서버업로드 순서

  1. 빌더를 사용해 Retrofit 객체 생성
  2. 서비스 인터페이스 설계 (요구 명세)
  • 이미지 파일은 택배상자(MultipartBody.Part : 이미지 들고 가는 애)에 넣어서 전송한다
  • @Part 어노테이션을 사용할 때 @Mulipart 인코딩 방식 어노테이션 추가
package com.bsj0420.ex86retrofitimgupload;

import okhttp3.MultipartBody;
import retrofit2.Call;
import retrofit2.http.Multipart;
import retrofit2.http.POST;
import retrofit2.http.Part;

public interface RetrofitService {

    //이미지 파일은 택배상자(MultipartBody.Part)에 넣어서 전송한다
    //@Part 어노테이션을 사용할 때 @Mulipart 인코딩 방식 어노테이션 추가
    @Multipart
    @POST("Retrofit/fileUplaod.php")
    Call<String> uploadImg(@Part MultipartBody.Part file); //이미지 들고 가는 애

}
  1. 서비스 인터페이스 객체 생성
  2. 보낼 데이터 택배상자[MultipartBody.Part]로 포장
  • file -> RequestBody -> MultipartBody.Part
  1. 원하는 작업 추상메소드 호출
  2. 네트워크 연결

순서를 생각하며 보기

	private void clickUpload() {

        //Retrofit을 라이브러리를 이용하여 이미지 업로드

        //1. 빌더를 사용해 Retrofit 객체 생성
        Retrofit.Builder builder = new Retrofit.Builder();
        builder.baseUrl("http://mrhisj23.dothome.co.kr/");
        builder.addConverterFactory(ScalarsConverterFactory.create());
        Retrofit retrofit = builder.build();

        //2. 서비스 인터페이스 설계 (요구 명세)

        //3.서비스 인터페이스 객체
        RetrofitService retrofitService = retrofit.create(RetrofitService.class);

        //4. 보낼 데이터 택배상자[MultipartBody.Part]로 포장
        File file = new File(imgPath); //imgPath = 선택한 이미지 경로
        
        RequestBody body = RequestBody.create(MediaType.parse("image/*"),file); //이미지 감싸는 애(진공포장)

        MultipartBody.Part part = MultipartBody.Part.createFormData("img", file.getName(),body); //(식별자 ,파일명, RequestBody) : 상자포장

        //5. 원하는 작업 추상메소드 호출
        Call<String> call = retrofitService.uploadImg(part);

        //6. 네트워크 연결
        call.enqueue(new Callback<String>() {
            @Override
            public void onResponse(Call<String> call, Response<String> response) {
                String s = response.body();

                new AlertDialog.Builder(MainActivity.this).setMessage(s).show();
            }

            @Override
            public void onFailure(Call<String> call, Throwable t) {
                Toast.makeText(MainActivity.this, "error : "+ t, Toast.LENGTH_SHORT).show();
            }
        });

    }



Main.java 총 코드

package com.bsj0420.ex86retrofitimgupload;

import androidx.activity.result.ActivityResult;
import androidx.activity.result.ActivityResultCallback;
import androidx.activity.result.ActivityResultLauncher;
import androidx.activity.result.contract.ActivityResultContracts;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;
import androidx.loader.content.CursorLoader;

import android.content.Intent;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.provider.MediaStore;
import android.widget.Toast;

import com.bsj0420.ex86retrofitimgupload.databinding.ActivityMainBinding;
import com.bumptech.glide.Glide;

import java.io.File;

import okhttp3.MediaType;
import okhttp3.MultipartBody;
import okhttp3.RequestBody;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;
import retrofit2.converter.scalars.ScalarsConverterFactory;

public class MainActivity extends AppCompatActivity {

    ActivityMainBinding binding;
    
    //업로드할 파일의 주소를 저장하는 문자열 변수
    String imgPath = null;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        binding = ActivityMainBinding.inflate(getLayoutInflater());
        setContentView(binding.getRoot());

        binding.btnSelect.setOnClickListener(v->clickSelect());
        binding.btnUpload.setOnClickListener(v->clickUpload());

    }//////////////////////////////////

    private void clickSelect() {
        //이미지를 선택할 수 있는 앱을 실행
        //29버전 이상에서 퍼미션 없이 사용가능한것 사용할 것 : MediaStore.ACTION_PICK_IMAGES
        Intent intent = new Intent(MediaStore.ACTION_PICK_IMAGES); //사진첩 실행하면
        resultLauncher.launch(intent); //결과 받아오는 애 만들기

    }//////////////////////////////////

    //결과 받는 계약서 만들기 : 액티비티 스타트하고 결과값 받아오는 애
    ActivityResultLauncher<Intent> resultLauncher = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(), result -> {
        if(result.getResultCode() == RESULT_CANCELED) return;

        //선택한 사진의 콘텐츠 주소(URI) 얻어오기
        Uri uri = result.getData().getData(); //앞 getData() 택배기사 , 뒤 getData() 실제 uri
        Glide.with(this).load(uri).into(binding.iv);

        //애석하게 Retrofit을 이용해 서버에 파일을 전송하려면
        //파일의 Uri (즉, 콘텐츠 DB 주소)가 아니라 파일(File)의 주소가 필요함
        //Uri = 특정 DB의 경로 , 가짜경로임
        //서버에 보낼땐 찐 경로인 파일의 주소가 필요함
        //new AlertDialog.Builder(this).setMessage(uri.toString()).create().show(); // => uri 주소
        
        //uri --> 파일주소로 변환
        imgPath = getFilePathFromUri(uri);

        new AlertDialog.Builder(this).setMessage(imgPath.toString()).create().show(); // => 파일 주소 찍어보기

    });//////////////////////////////////////////

    //Uri --> 파일경로로 바꿔서 리턴해주는 메소드
    String getFilePathFromUri(Uri uri) {

        String[] proj = {MediaStore.Images.Media.DATA};

        //Cursor : 안드로이드 백그라운드를 부르는 애
        CursorLoader loader = new CursorLoader(this,uri,proj,null,null,null);
        //select DATA from uri where selection

        Cursor cursor = loader.loadInBackground();

        //cursor.getString();의 ()안 index번호를 식별가능한 글자 써넣기 위한 int 변수
        int colum_index = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);

        cursor.moveToFirst();
        String result = cursor.getString(colum_index);

        return result;
    }

    private void clickUpload() {

        //Retrofit을 라이브러리를 이용하여 이미지 업로드

        //1.
        Retrofit.Builder builder = new Retrofit.Builder();
        builder.baseUrl("http://mrhisj23.dothome.co.kr/");
        builder.addConverterFactory(ScalarsConverterFactory.create());
        Retrofit retrofit = builder.build();

        //2. 서비스 인터페이스 설계 (요구 명세)

        //3.서비스 인터페이스 객체
        RetrofitService retrofitService = retrofit.create(RetrofitService.class);

        //4. 보낼 데이터 택배상자[MultipartBody.Part]로 포장
        File file = new File(imgPath); //imgPath = 선택한 이미지 경로
        
        RequestBody body = RequestBody.create(MediaType.parse("image/*"),file); //이미지 감싸는 애(진공포장)

        MultipartBody.Part part = MultipartBody.Part.createFormData("img", file.getName(),body); //(식별자 ,파일명, RequestBody) : 상자포장

        Call<String> call = retrofitService.uploadImg(part);

        //5.
        call.enqueue(new Callback<String>() {
            @Override
            public void onResponse(Call<String> call, Response<String> response) {
                String s = response.body();

                new AlertDialog.Builder(MainActivity.this).setMessage(s).show();
            }

            @Override
            public void onFailure(Call<String> call, Throwable t) {
                Toast.makeText(MainActivity.this, "error : "+ t, Toast.LENGTH_SHORT).show();
            }
        });

    }
}



Volley라이브러리

  • 구글에서 지속적으로 api를 관리하지않는 느낌
profile
보조기억장치

0개의 댓글