AnKo 라이브러리는 코틀린을 개발한 젯브레인에서 개발했습니다.
AnKo 라이브러리는 아래와 같이 4가지 라이브러리로 구성됩니다
- AnKo Commons : 인텐트, 다이얼로그, 로그 등을 편리하게 사용하는 라이브러리
- AnKo Layouts : 안드로이드 레이아웃을 코드로 쉽게 작성하는 라이브러리
- AnKo SQLite : SQLite를 쉽게 사용하는 라이브러리
- AnKo Coroutines : 코루틴을 쉽게 사용하는 라이브러리
AnKo 라이브러리의 깃허브 ( https://github.com/Kotlin/anko )
AnKo 라이브러리를 추가하려면 프로젝트 창에서 모듈 수준의 build.gradle 파일을 더블 클릭하여 엽니다. 프로젝트에는 build.gradle 파일이 두 개 있는데 옆에 Module 또는 Project 라고 써져
있으니 이를 보고 모듈 수준 또는 프로젝트 수준의 그레이들 파일이라고 부릅니다.
dependencies{} 항목에 Anko 라이브러리를 추가합니다.
이러한 것을 라이브러리 의존성 추가 라고 합니다.
dependencies {
implementation "org.jetbrains.anko:anko:$anko_version"
}
그런 다음 프로젝트 수준의 build.gradle 파일을 더블클릭하여 열어줍니다.
buildscript {
ext.kotlin_version = '1.2.71'
ext.anko_version='0.10.5'
repositories {
google()
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.2.1'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}
buildscript 안에 Anko라이브러리 버전을 변수에 지정합니다. 그후에 오른쪽 위에서 "Sync Now"
-------------------------------------------------------------------------------------------------
라이브러리별 상세 설명
Anko Commons
Dialog와 관련된 유틸 클래스를 Kotlin에서 사용할 수 있는 일종의 모음집입니다. toast에 문자열만 넘겨서 사용하거나, alert나 selector 창을 띄우거나 할 수 있습니다. 인텐트도 간단하게 쓸 수 있도록 도와줍니다. 브라우저를 띄우거나 텍스트를 보내거나 메일을 보내면 이 라이브러리를 사용하면 쉽게 쓸 수 있습니다. 안드로이드를 개발할 때 가독성도 높아집니다.
Anko Coroutines
kotlinx,coroutines 라이브러리를 기본으로 한 유틸리티입니다.
Anko SQLite
ORM의 일종인데, 저는 Realm이라는 좋은 라이브러리를 사용하고 있어서 따로 사용해보진 않았습니다.
Anko layouts
Anko layouts는 Kotlin으로 작성된 안드로이드 DSL(Domain-Specific Language)라고 정의하고 있습니다. 기존에 안드로이드에서 XML로 작업하는 뷰는 다음과 같은 단점을 가졌습니다.
- 코드 재활용 불편
- 불필요한 XML 파싱으로 인한 CPU 및 배터리 소모
Anko는 이런 단점을 극복하는 라이브러리로, 작성이 쉽고 가독성이 높으며, 런타임에 XML 파싱에 따르는 오버헤드를 없앨 수 있습니다.
토스트 표시하기
- toast() 및 toastLong() 함수를 사용하면 토스트 메시지를 간편하게 표시할 수 있습니다.
- 토스트를 표시하려면 Context 클래스의 인스턴스가 필요하므로, 이 클래스 혹은 이를 상속하는 클래스(액티비티, 프래그먼트)내부에서만 사용할 수 있습니다.
// 다음 코드와 동일한 역할을 합니다. // Toast.makeText(Context, "Hello, Kotlin!", Toast.LENGTH_SHORT).show() toast("Hello, Kotlin!") // 다음 코드와 동일한 역할을 합니다. // Toast.makeText(Context, R.string.hello, Toast.LENGTH_SHORT).show() toast(R.string.hello) // 다음 코드와 동일한 역할을 합니다. // Toast.makeText(Context, "Hello, Kotlin!", Toast.LENGTH_LONG).show() longToast("Hello, Kotlin!)
다이얼로그 생성 및 표시하기
- alert() 함수를 사용하면 AlertDialog를 생성할 수 있습니다.
- 토스트와 마찬가지로 Context 클래스 혹은 이를 상속하는 클래스(액티비티, 프래그먼트) 내부에서만 사용할 수 있습니다.
// 다이얼로그의 제목과 본문을 지정합니다. alert(title = "Message", message = "Let's learn Kotlin!") { // AlertDialog.Builder.setPositiveButton()에 대응합니다. positiveButton("Yes") { // 버튼을 클릭했을 때 수행할 동작을 구현합니다. toast("Yay!") } // AlertDialog.Builder.setNegativeButton()에 대응합니다. negativeButton("No") { // 버튼을 클릭했을 때 수행할 동작을 구현합니다. longToast("No way...") } }.show()
- 프레임워크에서 제공하는 다이얼로그가 아닌 서포트 라이브러리에서 제공하는 다이얼로그(android.support.v7.app.AlertDialog)를 생성하려면,
- anko-appcompat-v7-commons을 의존성에 추가한 후 다음과 같이 Appcompat을 함께 인자에 추가하면 됩니다.
// import 문이 추가됩니다. import org.jetbrains.anko.appcompat.v7.Appcompat // Appcompat을 인자에 추가합니다. alert(Appcompat, title = "Message", message = "Let's learn Kotlin!") { ... }.show()
- 여러 항목 중 하나를 선택하도록 할 떄 사용하는 리스트 다이얼로그는 selector() 함수를 사용하여 생성할 수 있습니다.
// 다이얼로그에 표시할 목록을 생성합니다. val cities = listOf("Seoul", "Tokyo", "Mountain View", "Singapore") // 리스트 다이얼로그를 생성하고 표시합니다. selector(title = "Select City", items = cities) { dig, selection -> // 항목을 선택했을 때 수행할 동작을 구현합니다. toast("You selected ${cities[selection]}!") }
- 작업의 진행 상태를 표시할 때 사용하는 프로그레스 다이얼로그는progressDialog()와 indeterminateProgressDialog() 함수를 사용하여 생성할 수 있습니다.
- progressDialog() 함수는 파일 다운로드 상태와 같이 진행률을 표시해야 하는 다이얼로그를 생성할 때 사용합니다.
- indeterminateProgressDialog() 함수는 진행률을 표시하지 않는 다이얼로그를 생성할 때 사용합니다.
// 진행률을 표시하는 다이얼로그를 생성합니다. val pd = progressDialog(title = "File Download", message = "Downloading...") // 다이얼로그를 표시합니다. pd.show() // 진행률을 50으로 조정합니다. pd.progress = 50 // 진행률을 표시하지 않는 다이얼로그를 생성하고 표시합니다. indenterminateProgressDialog(message = "Please wait...").show()
인텐트 생성 및 사용하기
- 인텐트는 컴포넌트 간에 데이터를 전달할 때에도 사용하지만 주로 액티비티나 서비스를 실행하는 용도로 사용합니다.
- 다른 컴포넌트를 실행하기 위해 인텐트를 사용하는 경우, 이 인텐트는 대상 컴포넌트에 대한 정보와 기타 부가 정보를 포함합니다.
// DetailActivity 액티비티를 대상 컴포넌트로 지정하는 인텐트 val intent = Intent(this, DetailActivity::class.java) // DetailActivity를 실행합니다. startActivity(intent)
- 이 인텐트에 부가 정보를 추가하거나 플래그를 설정하는 경우 인텐트를 생성하는 코드는 다음과 같습니다.
val intent = Intent(this, DetailActivity::class.java) // 인텐트에 부가정보를 추가합니다. intent.putExtra("id", 150L) intent.putExtra("title", "Awesome item") // 인텐트에 플래그를 생성합니다. intent.setFlag(Intent.FLAG_ACTIVITY_NO_HISTORY)
- intentFor() 함수를 사용하면 훨씬 간소한 현태로 동일한 역할을 하는 인텐트를 생성할 수 있습니다.
val intent = intentFor<DetailActivity>( // 부가 정보를 Pair 형태로 추가합니다. "id" to 150L, "title" to "Awesome item") // 인텐트 플래그를 설정합니다. .noHistory()
- 인텐트에 플래그를 지정하지 않는다면, startActivity() 함수나 startService() 함수를 사용하여 인텐트 생성과 컴포넌트 호출을 동시에 수행할 수 있습니다.
- 이들 함수는 모두 Context 클래스를 필요로 하므로, 이 클래스 혹은 이를 상속하는 클래스(액티비티, 프래그먼트) 내부에서만 사용할 수 있습니다.
// 부가정보 없이 DetailActivity를 실행합니다. startActivity<DetailActivity>() // 부가정보를 포함하여 DetailActivity를 실행합니다. startActivity<DetailActivity>("id" to 150L, "title" to "Awesome item") // 부가정보 없이 DataSyncService를 실행합니다. startService<DataSyncService>() // 부가정보를 포함하여 DataSyncService를 실행합니다. startService<DataSyncService>("id" to 1000L)
- 이 외에도, 자주 사용하는 특정 작업을 바로 수행할 수 있는 함수들을 제공합니다.
// 전화를 거는 인텐트를 실행합니다. makeCall(number = "01012345678") // 문자메시지를 발송하는 인텐트를 실행합니다. sendSMS(number = "01012345678", text = "Hello, Kotlin!") // 웹 페이지를 여는 인텐트를 실행합니다. browse(url = "https://google.com") // 이메일을 발송하는 인텐트를 실행합니다. email(email = "jyte82@gmail.com", subject = "Hello, Taeho Kim", text = "How are you?")
로그 메시지 기록하기
- 안드로이드 애플리케이션에서 로그메시지를 기록하려면 android.util.Log 클래스에서 제공하는 메서드를 사용해야 합니다.
- 하지만 로그를 기록하는 함수를 호출할 때마다 매번 태그를 함께 입력해야 하므로 다소 불편합니다.
- Anko 라이브러리에서 제공하는 AnkoLogger를 사용하면 훨씬 편리하게 로그 메시지를 기록할 수 있습니다.
- AnkoLogger에서는 다음과 같이 android.util.Log 클래스의 로그 기록 메서드에 대응하는 함수를 제공합니다.
android.util.LogAnkoLogger
v() | verbose() |
d() | debug() |
i() | info() |
w() | warn() |
e() | error() |
wtf() | wtf() |
- AnkoLogger를 사용하려면 이를 사용할 클래스에서 AnkoLogger 인터페이스를 구현하면 됩니다.
- AnkoLogger 인터페이스를 구현한 액티비티에서의 사용 예는 다음 코드에서 확인할 수 있습니다.
- 출력할 메시지의 타입으로 String만 허용하는 android.util.Log 클래스와 달리 모든 타입을 허용하는 모습을 확인할 수 있습니다.
// AnkoLogger 인터패이스를 구현합니다. class MainActivity: AppCompatActivity(), AnkoLogger { fun doSomething() { // Log.INFO 레벨로 로그 메시지를 기록합니다. info("doSomething() called") } fun doSomethingWithParameter(number: Int) { // Log.DEBUG 레벨로 로그 메시지를 기록합니다. // String 타입이 아닌 인자는 해당 인자의 toString() 함수 변환값을 기록합니다. debug(number) } ... }
- AnkoLogger에서 제공하는 함수를 사용하여 로그 메시지를 기록하는 경우, 로그 태그로 해당 함수가 호출되는 클래스의 이름을 사용합니다.
- 따라서 앞의 예제에서는 "MainActivity"를 로그 태그로 사용합니다.
- 로그 태그를 바꾸고 싶다면 loggerTag 프로퍼티를 오버라이드하면 됩니다.
class MainActivity: AppCompatActivity(), AnkoLogger { // 이 클래스 내에서 출력되는 로그 태그를 "Main"으로 지정합니다. override val loggerTag: String get() = "Main" ... }
단위 변환하기
- 안드로이드는 다양한 기기를 지원하기 위해 픽셀(px) 단위 대신 dip(혹은 dp; device independent pixels)나 sp(scale independent pixels)를 사용합니다.
- dp나 sp 단위는 각 단말기의 화면 크기나 밀도에 따라 화면에 표시되는 크기를 일정 비율로 조정하므로, 다양한 화면 크기나 밀도를 가진 단말기에 대응하는 UI를 작성할 때 유용합니다.
- 커스텀 뷰 내뷰와 같이 뷰에 표시되는 요소의 크기를 픽셀 단위로 다루는 경우 dp나 sp단위를 픽셀 단위로 변환하기 위해 복잡한 과정을 거쳐야 합니다.
class MainActivity : AppCompatActivity() { fun doSomething() { // 100dp를 픽셀 단위로 변환합니다. val dpInPixel = TypedValue.applyDimension( TypedValue.COMPLEX_UNIT_DIP, 100f, resources.displayMetrics) // 16sp를 픽셀 단위로 변환합니다. val spInPixel = TypedValue.applyDimension( TypedValue.COMPLEX_UNIT_SP, 16f, resources.displayMetrics) } ... }
- Anko에서 제공하는 dip() 및 sp() 함수를 사용하면 이러한 단위를 매우 간단히 변환할 수 있습니다.
- 단위를 변환하기 위해 단말기의 화면 정보를 담고 있는 DisplayMetrics 객체가 필요하므로, 이 함수들은 단말기 화면 정보에 접근할 수 있는 클래스인 Context를 상속한 클래스 혹은 커스텀 뷰 클래스 내에서 사용할 수 있습니다.
- dip() 함수 및 sp() 함수를 사용하여 앞의 코드를 간단하게 표현한 모습입니다.
- TypedValue.applyDimension() 메서드는 Float 형 인자만 지원했지만, dip() 및 sp() 함수는 Int 형 인자도 지원합니다.
// 100dp를 픽셀 단위로 변환합니다. val dpInPixel = dip(100) // 16sp를 픽셀 단위로 변환합니다. val spInPixel = sp(16)
- 반대로, 픽셀 단위를 dp나 sp 단위로 변환하는 함수도 제공합니다. 각각 px2dip(), px2sp() 함수를 사용합니다.
// 300px를 dp 단위로 변환합니다. val pxInDip = px2dip(300) // 80px를 sp 단위로 변환합니다. val pxInSp = px2sp(80)
기타
- 여러 단말기 환경을 지원하는 애플리케이션은, 단말기 환경에 따라 다른 형태의 UI를 보여주도록 구현하는 경우가 많습니다.
- 이러한 경우, configuration() 함수를 사용하면 특정 단말기 환경일 때만 실행할 코드를 간단하게 구현할 수 있습니다.
매개변수 이름단말기 환경 종류
density | 화면 밀도 |
language | 시스템 언어 |
long | 화면 길이 |
nightMode | 야간모드 여부 |
orientation | 화면 방향 |
rightToLeft | RTL(Right-to-Left)레이아웃 여부 |
screenSize | 화면 크기 |
smallestWidth | 화면의 가장 작은 변의 길이 |
uiMode | UI 모드(일반, TV, 차량, 시계, VR 등) |
- configuration() 또한 단말기 환경에 접근해야 하므로 이 정보에 접근할 수 있는 Context 클래스 혹은 이를 상속한 클래스(액티비티, 프래그먼트)에서만 사용할 수 있습니다.
class MainActivity : AppCompatActivity() { fun doSomething() { configuration(orientation = Orientation.PORTRAIT) { // 단말기가 세로 방량일 때 수행할 코드를 작성합니다. ... } configuration(orientation = Orientation.LANDSCAPE, language = "ko") { // 단말기가 가로 방향이면서 시스템 언어가 한국어로 설정되어 있을 때 // 수행할 코드를 작성합니다. ... } } ... }
- 단순히 단말기의 OS 버전에 따라 분기를 수행하는 경우 doFromSdk()와 doIfSdk()를 사용할 수 있습니다.
doFromSdk(Build.VERSION_CODES.0) { // 안드로이드 8.0 이상 기기에서 수행할 코드를 작성합니다. ... } doIfSdk(Build.VERSION_CODES.N) { // 안드로이드 7.0 기기에서만 수행할 코드를 작성합니다. ... }
Anko Layouts
- 안드로이드 애플리케이션을 작성할 때, 대부분 XML 레이아웃을 사용하여 화면을 구성합니다.
- 소스 코드(Java 혹은 Kotlin)를 사용하여 화면을 구성하는 것도 가능하지만 XML 레이아웃에 비해 복잡하고 까다라로워 대다수의 사람들이 선호하지 않습니다.
- 하지만 XML로 작성된 레이아웃을 사용하려면 이 파일에 정의된 뷰를 파싱하는 작업을 먼저 수행해야 합니다.
- 때문에 소스 코드를 사용하여 화면을 구성한 경우에 비해 애플리케이션의 성능이 저하되고, 파싱 과정에서 자원이 더 필요한 만큼 배터리도 더 많이 소모합니다.
- Anko Layouts는 소스 코드로 화면을 구성할 때 유용하게 사용할 수 있는 여러 함수들을 제공하며, 아룰 사용하면 XML 레이아웃을 작성하는 것처럼 편리하게 소스코드로도 화면을 구성할 수 있습니다.
- Anko Layouts을 사용하려면 이를 사용할 모듈의 빌드스크립트에 의존성을 추가하면 되며, 애플리케이션의 minSdkVersion에 따라 사용하는 라이브러리가 달라집니다.
- 애플리케이션의 minSdkVersion에 대응하는 Anko Layouts 라이브러리는 다음과 같습니다.
minSdkVersionAnko Layouts 라이브러리
15이상 19미만 | anko-sdk15 |
19이상 21미만 | anko-sdk19 |
21이상 23미만 | anko-sdk21 |
23이상 25미만 | anko-sdk23 |
25이상 | anko-sdk25 |
android { defaultConfig { // minSdkVersion이 15로 설정되어 있습니다. minSdkVersion 15 targetSdkVersion 27 ... } ... } dependencies { // minSdkVersion에 맞추어 Anko Layouts 라이브러리를 추가합니다. compile "org.jetbrains.anko:anko-sdk15:0.10.2" }
- 서프트 라이브러리에 포함된 뷰를 사용하는 경우, 사용하는 뷰가 포함된 라이브러리에 대응하는 Anko Layouts 라이브러리를 의존성에 추가하면 됩니다.
- 각 서프트 라이브러리에 대응하는 Anko Layouts 라이브러리는 다음과 같습니다.
서포트 라이브러리Anko Layouts 라이브러리
appcompat-v7 | anko-appcompat-v7 |
cardview | anko-cardview-v7 |
design | anko-design |
gridlayout | anko-gridlayout-v7 |
recyclerview-v7 | anko-recyclerview-v7 |
support-v4 | anko-support-v4 |
- 다음은 서포트 라이브러리와 이에 대응하는 Anko Layouts 라이브러리를 의존성으로 추가한 예입니다.
android { ... } dependencies { // appcompat-v7 서포트 라이브러리 추가 implementation "com.android.support:appcompat-v7:27.0.1" // appcompat-v7용 Anko Layouts 라이브러리를 추가합니다. implementation "org.jetbrains.anko:anko-appcompat-v7:0.10.2" }
DSL로 화면 구성하기
- Anko Layouts을 사용하면 소스 코드에서 화면을 DSL(Domain Specific Language) 형태로 정의할 수 있습니다.
- 다음은 DSL을 사용하여 화면을 구성하는 간단한 예를 보여줍니다. XML 레이아웃으로 정의할 때보다 더 간단하게 화면을 구성할 수 있는 것을 확인할 수 있습니다.
verticalLayout { padding = dip(12) textView("Enter Login Credentials") editText { hint = "E-mail" } editText { hint = "Password" } button("Submit") }
- 앞의 코드에서 사용한 verticalLayout(), textView(), editText(), button()은 Anko Layout에서 제공하는 함수로, 뷰 혹은 다른 뷰를 포함할 수 있는 레이아웃을 생성하는 역할을 합니다.
- 다음은 여기에서 제공하는 함수 중 자주 사용하는 함수 몇 개의 목록입니다.
함수생성하는 뷰비고
button() | android.widget.Button | |
checkBox() | android.widget.CheckBox | |
editText() | android-widget.EditText | |
frameLayout() | android-widget.FrameLayout | |
imageView() | android-widget-ImageView | |
linearLayout() | android.widget.LinearLayout | |
radioButton() | android.widget.RadioButton | |
relativeLayout() | android-widget.RelativeLayout | |
switch() | android-widget.Switch | 서포트 라이브러리에서 제공하는 뷰는 switchCompat() 사용 |
verticalLayout() | android-widget-LinearLayout | orientation 값으로 LinearLayout.VERTICAL을 갖는 LinearLayout |
webView() | android-webkit.WebView |
- XML 레이아웃 파일에 XML로 구성한 레이아웃을 저장하듯이, DSL로 구성한 뷰는 AnkoComponent 클래스를 컨테이너로 사용합니다.
- AnkoComponent에는 정의되어 있는 화면을 표시할 대상 컴포넌트의 정보를 포함합니다.
- 다음은 MainActivity 액티비티에 표시할 뷰의 정보를 가지는 AnkoComponent의 코드 예시를 보여줍니다.
class MainActivityUI : AnkoComponent<MainActivity> { override fun createView(ui: AnkoContext<MainActivity>) = ui.apply { vertivalLayout { // LinearLayout의 padding을 12dp로 설정합니다. padding = dip(12) // TextView를 추가합니다. textView("Enter Login Credentials") // EditText를 추가하고, 힌트 문자열을 설정합니다. editText { hint = "E-mail" } editText { hint = "password" } // 버튼을 추가합니다. button("Submit") } }.view }
- 추가로, 액티비티에서는 AnkoComponent 없이 직접 액티비티 내에서 DSL을 사용하여 화면을 구성할 수 있습니다.
- 다음은 앞의 코드와 동일한 레이아웃을 AnkoComponent 없이 구성하는 예입니다. 이 방식으로 화면을 구성하는 경우 setContentView()를 호출하지 않아도 됩니다.
class MainActivity: AppcompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) // setContentView()가 없어도 됩니다. verticalLayout { padding = dip(12) textView("Enter Login Credentials") editText { hint = "E-mail" } editText { hint = "Password" } button("Submit") } } }
프래그먼트에서 사용하기
- 프래그먼트에서 Anko Layouts을 사용하려면 프래그먼트를 위한 AnkoComponent를 만들고, onCreateView()에서 createView()를 직접 호출하여 프래그먼트의 화면으로 사용할 뷰를 반환하면 됩니다.
- createView()를 직접 호출하려면 AnkoContext 객체를 직접 만들어 인자로 전달하면 됩니다.
class MainFragment : Fragment() { override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View?{ // AnkoComponent.createView() 함수를 호출하여 뷰를 반환합니다. return MainFragmentUI().createView(AnkoContext.create(context, this)) } } // 프래그먼트를 위한 AnkoComponent를 만듭니다. class MainFragmentUI : AnkoComponent<MainFragment> { override fun createView(ui: AnkoContext<MainFragment>) = ui.apply { verticalLayout { textView("Enter Login Credentials") editText { hint = "E-mail" } editText { hint = "Password" } button("Submit") } }.view }
Anko Support Plugin
-
Anko Support Plugin은 Anko와 같이 사용할 수 있는 부가 기능을 제공하는 IDE 플러그인입니다.
-
플러그인을 설치하려면 코틀린 IDE 플러그인을 설치하는 과정과 동일하게 진행하면 되며, 플러그인 검색 다이얼로그에서 다음과 같이 'Anko Support'를 선택하여 설치하면 됩니다.
-
Anko Support Plugin에서는 AnkoComponent로 작성한 화면이 어떻게 표시되는지 미리 확인할 수 있는 레이아웃 프리뷰 기능을 제공합니다.
-
레이아웃 프리뷰를 사용하려면, 먼저 프리뷰 기능으로 확인하고 싶은 AnkoComponent가 구혀되어 있는 파일을 연 후 AnkoComponent의 구현부 내부 아무곳에 커서를 둡니다.
-
그 다음, [View > Tools Windows > Anko Layout Preview]를 선택하여 레이이웃 프리뷰 창을 띄웁니다.
-
레이아웃 프리뷰 창은 XML 레이아웃 프리뷰 창과 거의 유사한 형태로 구성되어 있습니다.
-
앞에서 선택한 AnkoComponent의 레이아웃 프리뷰를 보여주며, 화면이 표시되지 않거나 바뀐 내용이 반영되지 않았다면 프로젝트를 다시 빌드하면 됩니다.
Anko SQLite
- Android 커서를 사용하여 SQLite 쿼리 결과를 파싱하는 것은 힘듭니다.
- 쿼리의 결과 행을 구문 분석하기 위해 많은 상용구 코드를 작성하고 이를 열거 된 모든 리소스를 적절하게 닫으려면 셀 수없는 try..finally 블록으로 묶어야합니다.
- Anko는 SQLite databases와 함께 간단하게 작동할 수 있도록 많은 기능들을 제공합니다.
Using Anko SQLite in your project
- build.gradle의 dependency에 anko-sqlite를 추가합니다.
dependencies { implementation "org.jetbrains.anko:anko-sqlite:$anko_version" }
Accessing the database
- SQLiteOpenHelper를 사용하는 경우 일반적으로 getReadableDatabase () 또는 getWritableDatabase ()를 호출합니다.
- 결과는 실제로 프로덕션 코드에서 동일하지만 수신 된 SQLiteDatabase에서 close () 메서드를 호출해야합니다.
- 또한 어딘가에 도우미 클래스를 캐시해야하며 여러 스레드에서이 클래스를 사용하는 경우 동시 액세스를 인식하고 있어야합니다.
- 이 모든 것은 꽤 힘듭니다. 그래서 안드로이드 개발자는 디폴트 SQLite API에 열중하지 않고 대신 ORM과 같은 값 비싼 래퍼를 선호합니다.
- Anko는 기본 클래스를 완벽하게 대체하는 ManagedSQLiteOpenHelper 클래스를 제공합니다. 사용 방법은 다음과 같습니다.
class MyDatabaseOpenHelper(ctx: Context) : ManagedSQLiteOpenHelper(ctx, "MyDatabase", null, 1) { companion object { private var instance: MyDatabaseOpenHelper? = null @Synchronized fun getInstance(ctx: Context): MyDatabaseOpenHelper { if (instance == null) { instance = MyDatabaseOpenHelper(ctx.getApplicationContext()) } return instance!! } } override fun onCreate(db: SQLiteDatabase) { // Here you create tables db.createTable("Customer", true, "id" to INTEGER + PRIMARY_KEY + UNIQUE, "name" to TEXT, "photo" to BLOB) } override fun onUpgrade(db: SQLiteDatabase, oldVersion: Int, newVersion: Int) { // Here you can upgrade tables, as usual db.dropTable("User", true) } } // Access property for Context val Context.database: MyDatabaseOpenHelper get() = MyDatabaseOpenHelper.getInstance(getApplicationContext())
- try 블록에 코드를 포함하는 대신 이제 다음과 같이 작성할 수 있습니다.
database.use { // 'this' is a SQLiteDatabase instance }
-
{} 안에 모든 코드를 실행 한 후에 데이터베이스가 완전히 닫힙니다.
-
비동기 호출 예제 :
class SomeActivity : Activity() { private fun loadAsync() { async(UI) { val result = bg { database.use { ... } } loadComplete(result) } } }
- 아래 언급된 메서드들과 모든 메서드를은 SQLiteException에 throw 할 수 있습니다.
- Anko가 오류가 발생하지 않는 것처럼 가장하는 것은 무리일 수 있으므로 직접 처리해야합니다.
Creating and dropping tables
- Anko와 함께라면 쉽게 새로운 테이블을 만들고 삭제할 수 있습니다. 구문은 간단합니다.
database.use { createTable("Customer", true, "id" to INTEGER + PRIMARY_KEY + UNIQUE, "name" to TEXT, "photo" to BLOB) }
- SQLite에서는 NULL, INTEGER, REAL, TEXT, BLOB 총 5개의 메인 타입들이 있습니다.
- 하지만 각 열에는 PRIMATY KEY 또는 UNIQUE와 같은 수정자가 있을 수 있습니다. 이러한 수정자는 기본 유형 이름에 "추가"와 함께 추가 할 수 있습니다.
- 테이블을 지울 때에는 dropTable 함수를 사용합니다.
dropTable("User", true)
Inserting data
- 일반적으로 테이블에 행을 삽입하려면 ContentValues 인스턴스가 필요합니다. 다음은 그 예입니다.
val values = ContentValues() values.put("id", 5) values.put("name", "John Smith") values.put("email", "user@domain.org") db.insert("User", null, values)
- Anko는 insert() 함수의 인자로 값을 직접 전달하여 이러한 코드의 거치래을 제거 할 수 있습니다.
// Where db is an SQLiteDatabase // eg: val db = database.writeableDatabase db.insert("User", "id" to 42, "name" to "John", "email" to "user@domain.org" )
- database.use를 사용하면
database.use { insert("User", "id" to 42, "name" to "John", "email" to "user@domain.org" ) }
Querying data
- Anko는 편리한 쿼리 빌더를 제공합니다. db.select (tableName, vararg columns)를 사용하여 만들 수 있습니다. db는 SQLiteDatabase의 인스턴스입니다.
함수기능
column(String) | 검색어 선택하는 열 추가하기 |
distinct(Boolean) | 고유한 쿼리 |
whereArgs(String) | 원시 문자열 where 쿼리를 지정하기 |
whereArgs(String, args) (중요) | 인자를 사용하여where 쿼리를 지정하기 |
whereSimple(String, args) | ? '마크 인자를 가진where` 쿼리를 지정하기 |
orderBy(String, [ASC/DESC]) | 이 열의 순서 |
groupBy(String) | 이 열의 그룹화하기 |
limit(count: Int) | 쿼리 결과 행 수 제한하기 |
limit(offset: Int, count: Int) | 오프셋이있는 쿼리 결과 행 수 제한하기 |
having(String) | 원시 'having'표현식 지정하기 |
having(String, args) (중요) | 인자로 `having' 표현식을 지정하기 |
- (중요) 로 표시된 함수는 특별한 방법으로 인자를 구문 분석합니다. 어떤 순서로든 값을 제공하고 원활한 escaping을 지원합니다.
db.select("User", "name") .whereArgs("(_id > {userId}) and (name = {userName})", "userName" to "John", "userId" to 42)
- 여기에서 {userId} 부분은 42, {userName}은 'John'으로 바뀝니다. 형식이 숫자가 아닌 경우 (Int, Float 등) 또는 Boolean 값이면 escaped됩니다. 다른 형식의 경우 toString() 표현이 사용됩니다.
- whereSimple 함수는 String 자료형을 허용합니다. 이것은 SQLiteDatavase의 query() 와 같은 일을 합니다. (질문 표시 ?는 자료형의 실제 값과 대체됩니다.)
- 이 쿼리를 어떻게 실행할까요? exec() 함수를 씁니다. Cursor. () -> T 형식의 확장 기능을 허용합니다. 수신한 확장 기능을 시작한 다음 Cursor를 닫아 스스로 수행할 필요가 없도록합니다.
db.select("User", "email").exec { // Doing some stuff with emails }
Parsing query results
- 그래서 우리는 Cursor를 가지고 있고, 그것을 정규 클래스로 어떻게 파싱할 수 있을까요?
- Anko는 parseSingle, parseOpt 및 parseList 함수를 제공하여 훨씬 쉽게 처리 할 수 있습니다.
MethodDescription
parseSingle(rowParser): T | Parse exactly one row |
parseOpt(rowParser): T? | Parse zero or one row |
parseList(rowParser): List | Parse zero or more rows |
- 수신 된 Cursor가 둘 이상의 행을 포함하면 parseSingle () 및 parseOpt ()가 예외를 throw합니다.
- rowParser 란 무엇일까요? 각 함수는 RowParser와 MapRowParser인 두 가지 유형의 파서를 지원합니다.
interface RowParser<T> { fun parseRow(columns: Array<Any>): T } interface MapRowParser<T> { fun parseRow(columns: Map<String, Any>): T }
-
매우 효율적인 방법으로 쿼리를 작성하려면 RowParser를 사용합니다 (하지만 각 열의 인덱스를 알아야합니다).
-
parseRow는 Any의 타입을 받아들입니다 (Any 형은 Long, Double, String 또는 ByteArray 이외의 것일 수 있음).
-
반면에 MapRowParser를 사용하면 열 이름을 사용하여 행 값을 가져올 수 있습니다.
-
Anko는 이미 간단한 단일 열 행에 대한 파서를 보유하고 있습니다.
- ShortParser
- IntParser
- LongParser
- FloatParser
- DoubleParser
- StringParser
- BlobParser
-
또한 클래스 생성자에서 행 파서를 만들 수 있습니다. 클래스가 있다고 가정합니다.
class Person(val firstName: String, val lastName: String, val age: Int)
- 파서는 간단해집니다.
val rowParser = classParser<Person>()
- 현재로서는 기본 생성자에 선택적 매개 변수가있는 경우 Anko는 이러한 파서 작성을 지원하지 않습니다.
- 또한 생성자는 Java Reflection을 사용하여 호출되므로 커다란 데이터 세트의 경우 사용자 정의 RowParser를 작성하는 것이 더 합리적입니다.
- Anko db.select () 빌더를 사용하는 경우에는 parseSingle, parseOpt 또는 parseList를 직접 호출하고 적절한 파서를 전달할 수 있습니다.
Custom row parsers
- 예를 들어, 열 (Int, String, String)에 대해 새 파서를 만들어 봅시다. 가장 일반적인 방법은 다음과 같습니다.
class MyRowParser : RowParser<Triple<Int, String, String>> { override fun parseRow(columns: Array<Any>): Triple<Int, String, String> { return Triple(columns[0] as Int, columns[1] as String, columns[2] as String) } }
- 자, 이제 코드에 3가지 명시적 캐스트가 있습니다. rowParser 함수를 사용하여 제거해보겠습니다.
val parser = rowParser { id: Int, name: String, email: String -> Triple(id, name, email) }
- 이게 다 입니다. rowParser는 모든 캐스트를 생성하고 원하는대로 람다 매개 변수의 이름을 지정할 수 있습니다.
Cursor streams
- Anko는 SQLite Cursor에 기능적으로 접근하는 방법을 제공합니다.
- cursor.asSequence () 또는 cursor.asMapSequence () 확장 함수를 호출하여 일련의 행을 가져옵니다. 커서를 닫는 것을 잊지 마세요 :)
Updating values
- 사용자 중 한 명에게 새로운 이름을 줍니다.
update("User", "name" to "Alice") .where("_id = {userId}", "userId" to 42) .exec()
- 또한 전통적인 방식으로 쿼리를 제공하려는 경우 update에는 whereSimple () 메서드가 있습니다.
update("User", "name" to "Alice") .`whereSimple`("_id = ?", 42) .exec()
Delete Data
- 행을 삭제 해 봅시다 (delete 메소드에는 whereSimple () 메소드가 없으며, 대신 인수에 직접 쿼리를 제공합니다).
val numRowsDeleted = delete("User", "_id = {userID}", "userID" to 37)
Transaction
- transaction ()이라는 특별한 함수가 있는데, 여러 개의 데이터베이스 연산을 하나의 SQLite 트랜잭션으로 묶을 수 있습니다.
transaction { // Your transaction code }
- {} 블록 내에 예외가 발생하지 않으면 트랜잭션은 성공으로 표시됩니다.
- 어떤 이유로 트랜잭션을 중단하려면 TransactionAbortException을 throw 하세요. 이 경우에는이 예외를 직접 처리 할 필요가 없습니다.
원문
Anko Coroutines
Using Anko Coroutines in your project
- build.gradle의 dependency에 anko-coroutines를 추가합니다.
dependencies { implementation "org.jetbrains.anko:anko-coroutines:$anko_version" }
Listener helpers
asReference()
- 비동기 API가 취소를 지원하지 않으면 코루틴이 무기한 정지 될 수 있습니다.
- 코루틴은 캡처 된 객체에 대한 강력한 참조를 보유하므로 Activity 또는 Fragment 인스턴스의 인스턴스를 캡처하면 메모리 누수가 발생할 수 있습니다.
- 이러한 경우에는 직접 캡처 대신 asReference ()를 사용하면 됩니다.
suspend fun getData(): Data { ... } class MyActivity : Activity() { fun loadAndShowData() { // Ref<T> uses the WeakReference under the hood val ref: Ref<MyActivity> = this.asReference() async(UI) { val data = getData() // Use ref() instead of this@MyActivity ref().showData(data) } } fun showData(data: Data) { ... } }
bg()
- bg ()를 사용하여 백그라운드 스레드에서 코드를 쉽게 실행할 수 있습니다.
fun getData(): Data { ... } fun showData(data: Data) { ... } async(UI) { val data: Deferred<Data> = bg { // Runs in background getData() } // This code is executed on the UI thread showData(data.await()) }
'IT 통신 수학 과학' 카테고리의 다른 글
java string related function (자바 문자열 관련 함수) (0) | 2020.03.09 |
---|---|
Life time Free Web hosting Service 무료 인터넷 호스팅서버 (0) | 2020.02.21 |
win10과 office2019 licence key(정품 인증키) 쉽게 구매하기 (0) | 2020.02.12 |
노트북 싸게 사는 방법 (0) | 2020.01.20 |
스칼라곱 벡터곱 (0) | 2019.10.08 |
Python으로 홈페이지를 시작하며 환경구성을 하다. (Django 설치) (1) | 2019.08.24 |
컴퓨터를 조립할때 부품을 골라 구매하는 방법 (0) | 2019.05.04 |
windows 10 pro 설치 USB 제작방법 (0) | 2019.05.01 |