유디의 공간정보·개발일기

1. 0830_ Android Studio에서 Kotlin으로 데이터연동 본문

Kotlin + Android Studio

1. 0830_ Android Studio에서 Kotlin으로 데이터연동

55yudi 2021. 8. 31. 14:32

Android Studio 실행

language를 Kotlin으로 바꾼다

Java와 Kotlin의 차이

- extends가 없고 : (콜론)이 붙는다.

- 부모클래스에 ()가 붙는다. : primary constructor(기본 생성자)를 호출하는 소괄호이다.

- ; (세미콜론)이 없다.

 

 

* activity_main.xml --> 화면구성

* ActivityMain.kt --> 소스코드

 

- build.gradle에 코드 추가

buildFeatures {
        viewBinding true
    }

- MainActivity.kt에 코드 추가 수정

- plaintext 칸에 글자를 입력하고 확인 버튼을 누르면 입력한 글자가 뜨도록 함

binding.showButton.setOnClickListener {
            var input1 = binding.input1.text.toString()
            //binding.output1.text = "안녕하세요!"
            binding.output1.text = "입력한 글자 : ${input1}"
        }

 

- 새 empty 화면 만들기 

MenuActivity.kt 코드 추가

 

[확인1] 버튼

- Wrapping / Unwrapping

- null값 넣기

    // 널허용 자료형, 랩핑/언랩핑!!
    // String name4 == null; //Java
    var name4:String? = null
    name4 = "홍길동4"

System.out.println("name4의 값 : ${name4!!}")
binding.output2.append("name4의 값 : ${name4}\n") //append : 글자를 붙여줌

- var name4:String? = null

   ? 를 사용해서 null값을 넣을 수 있음

- ${name4!!}

   !! 를 붙이면 Unwrapping을 하는 것

 

 

[확인2] 버튼

- 자바에서의 함수 생성과는 방식이 다름 주의

- 코틀린에선 매개변수의 타입과 리턴타입을 다음처럼 사용한다.

    // int add(int a, int b) {
    //     return a + b;
    // }
    fun add(a:Int, b:Int):Int {
        return a + b
    }
binding.showButton2.setOnClickListener {
	// 함수상자
	var result1 = add(10, 10)
	var result2 = add(b=10, a=10) //이름을 명시해 주면 순서도 바꿀 수 있음 (자바에서는 불가)
	binding.output2.append("result2의 값 : ${result2}")
}

- 위와 같은 결과를 다른 방식으로

  binding.showButton2.setOnClickListener {
    // 함수상자
    var result1 = add(10, 10)
    var result2 = add(b=10, a=10) //이름을 명시해 주면 순서도 바꿀 수 있음 (자바에서는 불가)
    binding.output2.append("result2의 값 : ${result2}")
    printLog("result2의 값 : ${result2}")
  }
}

fun printLog(message:String) {
	binding.output2.append("${message}\n")
}

- \n : 줄바꿈하여 결과값 표시

- 값이 화면 밖으로 넘어가서 추가되면 스크롤바가 활성화 됨 (.xml에서 scroll bar를 사용했기 때문에)


[람다식]

<코드 줄이기>

1. 람다식을 만들어서 변수상자에 넣기

- 람다식 : 함수를 일급객체로 다룬다(= 함수를 변수에 할당할 수 있다)

             매개변수에 함수를 넣을 수 있다

             요즘 만들어진 언어들은 이를 제공함

             그래서 함수를 축약해서 표시할 필요가 있고 람다식은 {}중괄호를 쓰고 화살표를 붙인다.

   * 람다식의 형태 : {  ->  }

var add2 = { a:Int, b:Int -> a + b } //return은 생략, a+b가 반환됨
fun add(a:Int, b:Int):Int { //에서 ()안의 내용이 람다식 화살표 왼쪽으로
    return a + b            //에서 {}안의 내용이 람다식 화살표 오른쪽으로
}

 

- 람다식 add2 함수 화면에 출력하기

var result3 = add(20, 20)
printLog("result3의 값 : ${result3}")

 

2. 익명함수로 만들어서 변수상자에 넣기

var add4 = fun (a:Int, b: Int):Int {
	return a + b
}

 

3. 람다식의 다른 형태-1

var add5:(Int,Int)->Int = {a:Int, b:Int -> a + b}

 

4. 람다식의 다른 형태-2 (권장)

var add6:(Int,Int)->Int = {a, b -> a + b}

- a와 b가 Int형임을 추론할 수 있어서 생략


- java 파일로 class Calc 만들기

  이런식으로 필요할 땐 자바를 파일단위로 코틀린을 쓰면서 같이 쓸 수 있다.

package com.lx.hello;

public class Calc {

    public int add(int a, int b) {
        return a + b;
    }
}

<MenuActivity.kt>

var calc1 = Calc() //붕어빵(객체) 만들 때 new 안씀
var result4 = calc1.add(30, 30)

[콜백함수 경량화(람다식) 이해하기]

 

1. 콜백함수 실행하기

doAction(fun ():Int {
	printLog("콜백함수 안에서 출력됨")
	return 10
})

2. 콜백함수의 다른 형태-1

doAction({ ->
	printLog("콜백함수 안에서 출력됨")
	10
})

3. 콜백함수의 다른 형태-2

doAction({
	printLog("콜백함수 안에서 출력됨")
	10
})

4. 콜백함수의 다른 형태-3

doAction() {
	printLog("콜백함수 안에서 출력됨")
	10
}

5. 콜백함수의 다른 형태-4 (권장)

doAction {
	printLog("콜백함수 안에서 출력됨")
	10
}

[생성자]

 

<Calc1.kt> kotlin class 생성

마우스로 쓰려니 개발새발됨ㅠ

 

- 생성자는 2가지 형태가 있다.


[상속] open과 override

 

<Person.kt> kotlin class생성

<Student.kt> kotlin class생성

<Baby.kt> kotlin class생성

package com.lx.hello

open class Person(open var name:String?) {

    open fun walk() {

    }
}
package com.lx.hello

class Student(override var name:String?) : Person(name) {

    override fun walk() {

    }
}
package com.lx.hello

class Baby : Person {

    constructor(name:String?):super(name) { //2차 생성자 함수를 생성, super는 부모

    }
}

[인터페이스] abstract(추상클래스), override

 

<Calculator.kt> kotlin interface 생성

<Calc3.kt> kotlin class 생성

package com.lx.hello

interface Calculator {

    fun add(a:Int, b:Int):Int

    fun subtract(a:Int, b:Int):Int
}
package com.lx.hello

class Calc3 : Calculator {

    override fun add(a:Int, b:Int):Int {
        return a + b
    }

    override fun subtract(a:Int, b:Int):Int {
        return a - b
    }
}

 

<Calc4.kt> kotlin class 생성 - 인터페이스(약속)를 구현

package com.lx.hello

abstract class Calc4 : Calculator { //추상클래스

    override fun add(a:Int, b:Int):Int {
        return a + b
    }

    abstract override fun subtract(a:Int, b:Int):Int
}

 

<Calc5.kt> kotlin class생성 - Calc4를 상속

package com.lx.hello

class Calc5 : Calc4() { //()안이 빈 기본 생성자

    override fun subtract(a: Int, b: Int): Int {
        return a - b
    }
}

인터페이스는 생성자 없어서 소괄호가 붙지 않는다.

- MenuActivity.kt

binding.showButton3.setOnClickListener {
            // 붕어빵 틀에서 붕어빵 만들기
            var calc1 = Calc1("이름")
            calc1.name = "홍길동1"

            var calc2 = Calc1("홍길동1")
            printLog("calc2의 이름 : ${calc2.name}") //기본 생성자

            // 완성 안된 붕어빵 틀에서 1마리 만들기 (추상클래스에는 ()가 붙음)
            var calc3 = object: Calc4() {
                override fun subtract(a: Int, b: Int): Int {
                    return a - b
                }
            }

            // 약속만 한 인터페이스에서 1마리 만들기 (인터페이스에는 () 안붙음)
            var calc4 = object: Calculator {
                override fun add(a: Int, b: Int): Int {
                    return a + b
                }

                override fun subtract(a: Int, b: Int): Int {
                    return a - b
                }
            }

       }

new project : CustomerApp 생성

- 웹서버 연결

- File - Project Structure - Add Library Dependency

- AndroidManifest.xml에  코드 추가

  외부라이브러리 넣었고 manifest에서 권한 넣어준 것

- Postman도 실행

- ubiaccess-framework > index.html 의 터미널창에서 node index.js로 서버 실행

- Postman의 코드를 android studio의 data폴더에서 마우스 우클릭 - New - Kotlin data class File from JSON 클릭 - 코드 붙여넣기 - (첫 실행시에만 설정) Advanced 클릭 설정 - Annotation메뉴에서 GSON 체크, Other에서 다음처럼 체크

 

CustomerApp <MainActivity.kt>

package com.lx.customerapp

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import com.lx.customerapp.api.CustomerClient
import com.lx.customerapp.data.CustomerListResponse
import com.lx.customerapp.data.CustomerResponse
import com.lx.customerapp.databinding.ActivityMainBinding
import retrofit2.Call
import retrofit2.Callback
import retrofit2.Response

class MainActivity : AppCompatActivity() {
    lateinit var binding: ActivityMainBinding

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)

        binding.button.setOnClickListener {
            // 웹서버로 고객 목록 요청하기
            CustomerClient.api.getCustomerList(
                user="john"
            ).enqueue(object: Callback<CustomerListResponse> {
                override fun onResponse(call: Call<CustomerListResponse>, response: Response<CustomerListResponse>) {
                    printLog("onResponse 호출됨")

                    var total = response.body()?.output?.header?.total
                    printLog("고객 수 : ${total}")
                }

                override fun onFailure(call: Call<CustomerListResponse>, t: Throwable) {
                    printLog("onFailure 호출됨")
                }

            })

        }

        binding.button2.setOnClickListener {
            CustomerClient.api.postCustomerCreate(
                name="새 홍길동1",
                age="23",
                mobile="010-3000-3000"
            ).enqueue(object: Callback<CustomerResponse> {
                override fun onResponse(call: Call<CustomerResponse>, response: Response<CustomerResponse>) {
                    printLog("onResponse 호출됨")

                    if (response.body()?.output?.body?.affectedRows!! > 0) {
                        printLog("고객 정보 추가 성공")
                    }
                }

                override fun onFailure(call: Call<CustomerResponse>, t: Throwable) {
                    printLog("onFailure 호출됨")
                }

            })
        }

        binding.button3.setOnClickListener {
            CustomerClient.api.postCustomerCreate(
                name="새 홍길동2",
                age="24",
                mobile="010-4000-4000"
            ).enqueue(object: Callback<CustomerResponse> {
                override fun onResponse(call: Call<CustomerResponse>, response: Response<CustomerResponse>) {
                    printLog("onResponse 호출됨")

                    if (response.body()?.output?.body?.affectedRows!! > 0) {
                        printLog("고객 정보 수정 성공")
                    }
                }

                override fun onFailure(call: Call<CustomerResponse>, t: Throwable) {
                    printLog("onFailure 호출됨")
                }

            })
        }

    }

    fun printLog(message:String) {
        binding.output1.append("${message}\n")
    }

}

 

<CustomerApi.kt>

package com.lx.customerapp.api

import com.lx.customerapp.data.CustomerListResponse
import com.lx.customerapp.data.CustomerResponse
import okhttp3.Interceptor
import okhttp3.OkHttpClient
import okhttp3.logging.HttpLoggingInterceptor
import retrofit2.Call
import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory
import retrofit2.http.*
import java.util.concurrent.TimeUnit

interface CustomerApi {

    @GET("/test1")
    fun getCustomerList(
        @Query("user") user:String? = null
    ): Call<CustomerListResponse>

    @FormUrlEncoded
    @POST("/test1")
    fun postCustomerCreate(
        @Field("name") name:String? = null,
        @Field("age") age:String? = null,
        @Field("mobile") mobile:String? = null
    ): Call<CustomerResponse>

    @FormUrlEncoded
    @PUT("/test1/1")
    fun putCustomerUpdate(
        @Field("name") name:String? = null,
        @Field("age") age:String? = null,
        @Field("mobile") mobile:String? = null
    ): Call<CustomerResponse>
}

class CustomerClient {

    // 붕어빵 틀에 진짜 변수상자나 함수상자를 붙여두는 것 (CustomerClient.count 으로 접근 가능)
    companion object {

        private var instance:CustomerApi? = null

        val api:CustomerApi
            get() {
                return getInstance()
            }

        @Synchronized
        fun getInstance():CustomerApi {
            if (instance == null) {
                instance = create()
            }

            return instance as CustomerApi
        }


        // 기본 URL
        private const val BASE_URL = "http://본인PC의 ip주소:8001"

        // 헤더 속성
        private const val CLIENT_ID = "MOMS"
        private const val CLIENT_SECRET = "123456"
        var userId:String = ""

        fun create(): CustomerApi {
            val httpLoggingInterceptor = HttpLoggingInterceptor()
            httpLoggingInterceptor.level = HttpLoggingInterceptor.Level.BODY

            val headerInterceptor = Interceptor {
                val request = it.request()
                    .newBuilder()
                    .addHeader("X-Client-Id", CLIENT_ID)
                    .addHeader("X-Client-Secret", CLIENT_SECRET)
                    .addHeader("X-Client-UserId", userId)
                    .build()
                return@Interceptor it.proceed(request)
            }

            val client = OkHttpClient.Builder()
                .addInterceptor(headerInterceptor)
                .addInterceptor(httpLoggingInterceptor)
                .connectTimeout(40, TimeUnit.SECONDS)  // 타임아웃 시간 설정 40초
                .build()

            return Retrofit.Builder()
                .baseUrl(BASE_URL)
                .client(client)
                .addConverterFactory(GsonConverterFactory.create())
                .build()
                .create(CustomerApi::class.java)
        }

    }

}

 

- Android Studio에서 가상 디바이스 실행시켜서 버튼 눌렀을 때 데이터가 잘 전송되어 나타나는지 확인

'Kotlin + Android Studio' 카테고리의 다른 글

Layout-ScrollView 사용할 때  (0) 2021.10.09
[Kotlin] val 과 var의 차이점  (0) 2021.10.01