이 글은 기존에 사용하던 java 프로젝트를 kotlin으로 전환해 보기 위해서 작성하고 있습니다. 이 글은 아래 링크의 개발자 페이지의 내용을 참고하고 있습니다.
https://developer.android.com/kotlin/add-kotlin?hl=ko#kts
먼저 gradle 파일부터 수정해 보겠습니다.
buildscript {
ext { // 추가한 부분
compose_ui_version = '1.5.0-alpha04'
raamcosta_version = '1.9.42-beta'
kotlin_version = '1.8.22'
}
repositories {
google()
mavenCentral()
}
dependencies {
classpath 'com.android.tools.build:gradle:8.0.2'
classpath 'com.google.gms:google-services:4.3.15'
classpath 'com.google.firebase:firebase-crashlytics-gradle:2.9.7'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" // 추가한 부분
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}
다음 모듈 수준의 gradle 파일 수정입니다.
import java.text.SimpleDateFormat
plugins {
id 'com.android.application'
id 'com.google.gms.google-services'
id 'com.google.firebase.crashlytics'
id 'kotlin-android' // 추가한 부분
}
android {
signingConfigs {
upload {
storeFile file(var)
storePassword var
keyAlias = var
keyPassword var
}
}
compileSdkVersion 34
namespace='com.n.......y2kakao'
defaultConfig {
applicationId "com.n......akao"
minSdkVersion 26
targetSdkVersion 34
versionCode 41
versionName '1.4.1'
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
renderscriptTargetApi 18
renderscriptSupportModeEnabled true
}
sourceSets { // 추가한 부분
getByName("main") {
java.srcDir("src/main/kotlin")
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_17
targetCompatibility JavaVersion.VERSION_17
}
buildTypes {
debug {
minifyEnabled false // 2022.06.23 true 가 되었더니, 압축이 되서 null 오류가 난다.
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
release {
minifyEnabled false // 2022.06.23 true 가 되었더니, 압축이 되서 null 오류가 난다.
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
viewBinding{
enabled = true
}
def archiveBuildType = ["release"]
namespace 'com.nari.notify2kakao'
applicationVariants.all { variant ->
variant.outputs.each { output ->
if (variant.buildType.name in archiveBuildType) {
def df = new SimpleDateFormat("yyyyMMdd")
df.setTimeZone(TimeZone.getDefault())
if (variant.versionName != null) {
String name = "Notify2Kakao_${df.format(new Date())}_${defaultConfig.versionCode}_${variant.versionName}.apk"
output.outputFileName = name
}
}
}
}
}
dependencies {
constraints { // 추가한 부분
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.8.0") {
because("kotlin-stdlib-jdk7 is now a part of kotlin-stdlib")
}
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.8.0") {
because("kotlin-stdlib-jdk8 is now a part of kotlin-stdlib")
}
implementation('androidx.work:work-runtime:2.7.1') {
because '''androidx.work:work-runtime:2.1.0 pulled from play-services-ads
has a bug using PendingIntent without FLAG_IMMUTABLE or
FLAG_MUTABLE and will fail in apps targeting S+.'''
}
}
implementation("androidx.core:core-ktx:1.10.1") // 추가한 부분
implementation("org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version") // 추가한 부분
implementation "com.kakao.sdk:v2-all:2.15.0" // 전체
implementation "com.google.android.play:core:1.10.3"
implementation 'androidx.appcompat:appcompat:1.6.1'
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
implementation 'com.google.android.material:material:1.9.0'
implementation 'androidx.legacy:legacy-support-v4:1.0.0'
implementation 'androidx.preference:preference-ktx:1.2.0'
implementation 'com.google.gms:google-services:4.3.15'
implementation platform('com.google.firebase:firebase-bom:30.1.0')
implementation 'com.google.firebase:firebase-crashlytics'
implementation 'com.google.firebase:firebase-analytics'
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.5'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1'
implementation 'androidx.navigation:navigation-fragment-ktx:2.6.0'
implementation 'androidx.navigation:navigation-ui-ktx:2.6.0'
implementation 'com.google.android.gms:play-services-ads:22.2.0'
implementation 'com.google.android.gms:play-services-maps:18.1.0'
implementation 'androidx.mediarouter:mediarouter:1.4.0'
implementation 'com.google.guava:guava:29.0-android'
// 데이터 주고 받기
implementation 'com.squareup.retrofit2:retrofit:2.9.0'
implementation 'com.squareup.retrofit2:converter-gson:2.9.0'
implementation 'com.squareup.retrofit2:converter-simplexml:2.9.0'
}
실행하는 방법은 메뉴에서 Code 메뉴 아래에 보면 Convert Java File To Kotlin File 메뉴가 있는 데, 저는 아래 그림처럼 변환할 대상을 찾고 오른쪽 마우스 클릭해서 해 보도록 하겠습니다.
이제 java 코드를 kotlin 으로 하나씩 변환해 보겠습니다.
변환하면서 쉽게 만나는 오류 표시
이름 그림과 같이 kotlin으로 변환된 뒤에 변수 처리 등에 오류 표시가 나오는 경우를 종종 만나게 됩니다. 사유는 해당 변수를 변환하면서 null 허용 상태를 만들어 가면서 변환을 하기 때문입니다. 선언된 변수를 찾아보겠습니다.
이렇게 null 로 초기화를 해야만 하는 가 봅니다. kotlin 에는 초기화를 나중에 하는 유용한 방법이 있는 데도 말입니다. 그래서 다음과 같이 변경을 해 주면 해결이 됩니다.
이제 소스 코드에 표시 되었던 오류는 사라집니다. 다만, 실제로 source 코드에서는 해당 변수를 사용하기 전에 꼭 초기화를 해야 실행 시 오류가 발생하지 않습니다.
java.lang.NullPointerException: Parameter specified as non-null is null:
또한 java.lang.NullPointerException: Parameter specified as non-null is null 이런 오류를 만나게 되었습니다. 사유는 Recycle View을 이용해서 List에 들어 있는 항목들을 보여주는 화면들이 있는 데, View Adapter 등을 사용했었다면 만나게 될 것 같습니다.
Process: com.nari.notify2kakao, PID: 17268
java.lang.NullPointerException: Parameter specified as non-null is null: method com.nari.notify2kakao.util.ViewStrValueAdapter.getView, parameter convertView
at com.nari.notify2kakao.util.ViewStrValueAdapter.getView(Unknown Source:2)
at android.widget.AbsListView.obtainView(AbsListView.java:2466)
at android.widget.ListView.makeAndAddView(ListView.java:2065)
at android.widget.ListView.fillDown(ListView.java:791)
at android.widget.ListView.fillFromTop(ListView.java:853)
at android.widget.ListView.layoutChildren(ListView.java:1836)
at android.widget.AbsListView.onLayout(AbsListView.java:2263)
at android.view.View.layout(View.java:24421)
아래 Adapter 예시와 같이 getView 의 리턴값이 View? 이 될 수 있도록 조치를 해 주시면 해소가 됩니다.
class ViewWithDrawMonthlyAdapter(oData: ArrayList<ViewWithDrawMonthly>) : BaseAdapter() {
private var viewWithDrawMonthlyls = ArrayList<ViewWithDrawMonthly>()
init {
viewWithDrawMonthlyls = oData
}
override fun getCount(): Int {
return viewWithDrawMonthlyls.size
}
override fun getItem(position: Int): Any {
return viewWithDrawMonthlyls[position]
}
override fun getItemId(position: Int): Long {
return position.toLong()
}
override fun getView(position: Int, containView: View?, parent: ViewGroup): View? {
var containView = containView
val context = parent.context
if (containView == null) {
val inflater =
context.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater
containView = inflater.inflate(R.layout.viewwithdrawmonthly_item, parent, false)
}
val chkTy = containView?.findViewById<CheckBox>(R.id.chkTy)
val monthlyDay = containView?.findViewById<TextView>(R.id.editMonthlyDay)
val remark = containView?.findViewById<TextView>(R.id.editRemark)
val outamt = containView?.findViewById<TextView>(R.id.editOutAmt2)
val textSplit = containView?.findViewById<TextView>(R.id.textSplit)
val textSplit1 = containView?.findViewById<TextView>(R.id.textSplit1)
val viewWithDrawMonthly = viewWithDrawMonthlyls[position]
if (chkTy != null) {
chkTy.isChecked = false
}
if ("Y" == viewWithDrawMonthly.chkTy) {
if (chkTy != null) {
chkTy.isChecked = true
}
}
if (monthlyDay != null) {
monthlyDay.text = viewWithDrawMonthly.getmonthlyDay()
}
if (remark != null) {
remark.text = viewWithDrawMonthly.remark
}
if (outamt != null) {
outamt.text = viewWithDrawMonthly.outAmt
}
return containView
}
}
kotlin에서 non-null 이 null 될 수 없어서 오류가 발생하기 때문이기도 합니다.
다음이야기는 뒤에...
'모바일 앱(안드로이드)' 카테고리의 다른 글
안드로이드 앱 만들기 : jetpack compose IconButton 만들기에서 쉽게 하는 실수 (?) (8) | 2023.07.26 |
---|---|
안드로이드 앱 만들기 : java 에서 kotlin 으로 이전 SharedPreferences 는 어떻게 ? (4) | 2023.07.25 |
안드로이드 앱 만들기 : MediaStore API 활용해 보기 (feat 휴대폰 사진 백업) (6) | 2023.07.18 |
안드로이드 앱 만들기 : 인앱 업데이트 appUpdateManager deprecated 해결해 보기 (8) | 2023.07.16 |
안드로이드 앱 만들기 : FTP Clietn 만들어 보기, 백업 앱 만들기 (2) | 2023.07.10 |