일반적으로 서버 연동 개발을 위해 로컬 호스트 아이피(127.0.0.1)를 사용하여 로컬에서 가동중인 서버와의 통신을 시도한다.
하지만 이 로컬 호스트 아이피를 사용하여 아래와 같이 서버 api 호출을 요청하면 오류가 발생한다.
init{
val gson = GsonBuilder().setLenient().create()
val retrofit = Retrofit.Builder()
.baseUrl("http://127.0.0.1:8080/") // 통신 오류 발생
.addConverterFactory(GsonConverterFactory.create(gson))
.build()
api = retrofit.create(ApiManager::class.java)
}
안드로이드에서 사용하는 에뮬레이터 상의 로컬호스트 주소와 내 컴퓨터의 로컬 호스트 주소가 서로 다른 것이 원인으로
다시말해 안드로이드에서 내 컴퓨터가 아닌 가상 운영체제인 에뮬레이터 안에서 작동중인 로컬 서버로의 접속을 시도하고 있기 때문에 오류가 발생하는 것이다.
따라서 안드로이드 에뮬레이터에서 내 컴퓨터의 서버를 가리키려면 기존에 사용하던 localhost 가 아닌 별도의 주소를 사용해야 하며 종류는 아래와 같다.
10.0.2.1
라우터 또는 게이트웨이 주소
10.0.2.2
호스트 루프백 인터페이스의 특수 별칭(개발 머신의 127.0.0.1)
10.0.2.3
첫 번째 DNS 서버
10.0.2.4/10.0.2.5/10.0.2.6
두 번째, 세 번째, 네 번째 DNS 서버(선택사항)
10.0.2.15
이더넷을 사용하여 연결된 경우 에뮬레이션된 기기 네트워크
10.0.2.16
Wi-Fi를 사용하여 연결된 경우 에뮬레이션된 기기 네트워크
127.0.0.1
에뮬레이션된 기기 루프백 인터페이스
만약 내 컴퓨터에서 작동중인 서버가 스프링 서버라면 아래와 같은 형식으로 기존 코드를 고쳐야한다.
init{
val gson = GsonBuilder().setLenient().create()
val retrofit = Retrofit.Builder()
.baseUrl("http://10.0.2.2:8080/") // 개발 머신의 127.0.0.1에 해당
.addConverterFactory(GsonConverterFactory.create(gson))
.build()
api = retrofit.create(ApiManager::class.java)
}
추가로 안드로이드에서는 네트워크 통신시 허가된 아이피로의 통신만 허용하므로 res - xml - network_security_config.xml 에 해당 아이피를 추가해야 한다.
실습의 목표는 MainActivity에서 버튼을 클릭하면 Activity2로 전환되며, Activity2로 1,2를 넘겨서 Activity2의 Answer부분을 변화시키는 것이다.
MainAcitivity에 아래와 같이 코드를 작성한다.
package com.example.androidstudy
import android.content.Intent
import android.os.Bundle
import android.widget.Button
import androidx.appcompat.app.AppCompatActivity
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main) //화면을 그려주는 부분(해당 레이아웃으로 화면을그려주겠다)
var change_activity : Button = findViewById(R.id.button)
//Intent에 정보 담아서 보내기
change_activity.setOnClickListener{
//0. Intent 생성(요청 생성)
val intent = Intent(this@MainActivity,Activity2::class.java)
//1. Intent에 정보 담기 key:value방식(하나 이상의 값을 보낼 수 있음)
intent.putExtra("number1",1) // "number1" : 1 형태로 값 담기
intent.putExtra("number2",2) // "number2" : 2 형태로 값 담기
startActivity(intent) //요청 보내기
}
}
}
버튼에 onClickListener를 달아서 버튼을 클릭했을때의 동작을 정의한다.
버튼을 클릭하면 MainActivity에서 Activity2로 요청하는 Intent객체를 생성하고, putExtra를 통해 인텐트 객체에 정보를 담는다.
정보는 {key:value} 형식으로 담기게 되므로, 키 값만 다르게 한다면 여러개의 값을 한번에 전달 할 수 있다.
위 코드의 경우 number1이라는 이름으로 정수 1과 number2라는 이름으로 정수 2를 삽입하였다.
이후 startActivity를 통해 요청 intent를 시스템에 요청한다.
위 코드는 apply를 사용하여 아래와 같이 깔끔하게 개선 할 수 있다.
apply를 사용하면 블럭으로 묶은 이하의 코드에서 this 키워드를 사용함으로서 intent에 대한 작업을 한눈에 알아보기 쉽게 해준다는 장점이 있으며, 위의 코드와 아래 코드는 문법적으로 완벽히 동일한 코드이다.
package com.example.androidstudy
import android.content.Intent
import android.os.Bundle
import android.widget.Button
import androidx.appcompat.app.AppCompatActivity
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main) //화면을 그려주는 부분(해당 레이아웃으로 화면을그려주겠다)
var change_activity : Button = findViewById(R.id.button)
//Intent에 정보 담아서 보내기
change_activity.setOnClickListener{
//apply를 사용하는 경우
val intent = Intent(this@MainActivity,Activity2::class.java)
//apply -> this키워드를 사용할 수 있도록 해줌(intent에 대한 작업을 한눈에 알아보기 용이)
intent.apply {
this.putExtra("number1",1) // "number1" : 1 형태로 값 담기
this.putExtra("number2",2) // "number2" : 2 형태로 값 담기
}
startActivity(intent) //요청 보내기
}
}
}
MainActivity에서 값을 전달하면 Activity2에서는 값을 전달 받아야 한다.
Activity2에 아래와 같이 작성한다.
package com.example.androidstudy
import android.os.Bundle
import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity
class Activity2 : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_2)
//값 꺼내기 : number1, number2라는 키 값이 없을 수 있으므로 예외 처리 필요
val number1 = intent.getIntExtra("number1",0)
val number2 = intent.getIntExtra("number2",0)
var answer : TextView = findViewById(R.id.txt_answer)
answer.text = " 1 + 2 = " + (number1+number2).toString()
}
}
Intent에 값을 넣어줄 때는 타입을 지정하지 않았지만, 값을 뺄때는 얻고자하는 데이터의 타입을 명시하여 함수를 사용해야 한다.
키워드 number1과 number2로 넣어준 데이터는 모두 Integer타입이므로 getIntExtra()함수를 통해 값을 얻을 수 있으며, 해당 키워드에 대한 값이 없을 수 있기 때문에, 두번째 인자로 0을 넣어줌으로서 Default값을 0으로 지정한다.