十五、迁移到 Kotlin
如果您有一个要迁移到 Kotlin 的遗留项目或现有的 Java 模块,迁移应该很容易。成功的人都想过这个。正如您所记得的,Kotlin 是可互操作的。正因为如此,有些模块不需要完全迁移;相反,它们可以包含在 Kotlin 项目中。由你决定。所以,让我们准备我们的迁移吧!
在本章中,我们将涵盖以下主题:
- 准备迁移
- 转换类
- 重构和清理
准备迁移
正如我们所说的,我们需要决定是将我们的模块完全重写到 Kotlin 中,还是继续用 Kotlin 编写我们的代码,但用纯 Java 保留它的遗产。我们该怎么办?在这一章中,我们将一点点地演示每一个。
我们目前的项目,在这一点上,没有任何东西可以迁移。因此,我们将创建一些代码。如果您没有带有包结构的 Java 源目录,请创建它。现在,添加以下包:
activity
model
这些包相当于我们在 Kotlin 源代码中已经有的包。在activity
包中,添加以下类:
MigrationActivity.java
代码如下:
package com.journaler.activity;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
import com.journaler.R;
public class MigrationActivity extends AppCompatActivity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
@Override
protected void onResume() {
super.onResume();
}
}
MigrationActivity2.java
:确保和MigrationActivity.java
实现完全一样。我们只需要一些代码库来呈现和移植。 在安卓manifest
文件中注册这两个活动,如下所示:
<manifest xmlns:android=
"http://schemas.android.com/apk/res/android"
package="com.journaler">
...
<application
...
>
...
<activity
android:name=".activity.MainActivity"
android:configChanges="orientation"
android:screenOrientation="portrait">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name=
"android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name=".activity.NoteActivity"
android:configChanges="orientation"
android:screenOrientation="portrait" />
<activity
android:name=".activity.TodoActivity"
android:configChanges="orientation"
android:screenOrientation="portrait" />
<activity
android:name=".activity.MigrationActivity"
android:configChanges="orientation"
android:screenOrientation="portrait" />
<activity
android:name=".activity.MigrationActivity2"
android:configChanges="orientation"
android:screenOrientation="portrait" />
</application>
</manifest>
如您所见,Java 代码与 Kotlin 代码站在一起,没有任何问题。你的安卓项目可以两者兼得!现在,想一想,你真的需要做任何转换吗,或者你可以保留现有的 Java 内容吗?让我们在model
包中添加类:
Dummy.java
代码如下:
package com.journaler.model;
public class Dummy {
private String title;
private String content;
public Dummy(String title) {
this.title = title;
}
public Dummy(String title, String content) {
this.title = title;
this.content = content;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
}
Dummy2.java
代码如下:
package com.journaler.model;
import android.os.Parcel;
import android.os.Parcelable;
public class Dummy2 implements Parcelable {
private int count;
private float result;
public Dummy2(int count) {
this.count = count;
this.result = count * 100;
}
public Dummy2(Parcel in) {
count = in.readInt();
result = in.readFloat();
}
public static final Creator<Dummy2>
CREATOR = new Creator<Dummy2>() {
@Override
public Dummy2 createFromParcel(Parcel in) {
return new Dummy2(in);
}
@Override
public Dummy2[] newArray(int size) {
return new Dummy2[size];
}
};
@Override
public void writeToParcel(Parcel parcel, int i) {
parcel.writeInt(count);
parcel.writeFloat(result);
}
@Override
public int describeContents() {
return 0;
}
public int getCount() {
return count;
}
public float getResult() {
return result;
}
}
让我们再次检查项目的 Kotlin 部分是否看到这些类。在 Kotlin 源文件目录的根目录下创建一个新的.kt
文件。姑且称之为kotlin_calls_java.kt
:
package com.journaler
import android.content.Context
import android.content.Intent
import com.journaler.activity.MigrationActivity
import com.journaler.model.Dummy2
fun kotlinCallsJava(ctx: Context) {
/**
* We access Java class and instantiate it.
*/
val dummy = Dummy2(10)
/**
* We use Android related Java code with no problems as well.
*/
val intent = Intent(ctx, MigrationActivity::class.java)
intent.putExtra("dummy", dummy)
ctx.startActivity(intent)
}
如您所见,Kotlin 在使用 Java 代码时没有任何问题。因此,如果您仍然想继续迁移,您可以这样做。没问题。我们将在以下几节中介绍这一点。
危险信号
将庞大而复杂的 Java 类转换成 Kotlin 仍然是一个可以做的选择。无论如何,提供适当的单元或工具测试,以便在转换后重新测试这些类的功能。如果您的任何测试失败,请再次检查失败的原因。
要迁移的类可以通过以下两种方式迁移:
- 自动转换
- 手工重写
在庞大而复杂的类的情况下,这两种方法都会给我们带来某些缺点。全自动转换有时会给你带来不太好看的代码。所以,在你做了之后,你应该重新检查并重新格式化一些东西。第二种选择会花费你很多时间。
结论-您可以始终使用原始的 Java 代码。从你切换到 Kotlin 作为你的主要语言的那一刻起,你就可以用 Kotlin 写所有的新东西了。
更新依赖关系
如果你把安卓项目的 100%纯 Java 代码切换到 Kotlin,你必须从底层开始。这意味着您的第一次迁移工作将是更新您的依赖关系。您必须更改您的build.gradle
配置,以便 Kotlin 被识别并且源代码路径可用。我们已经在第 1 章、从安卓开始,在设置梯度部分解释了如何做到这一点;因此,如果您的项目中没有 Kotlin 相关的配置,那么您必须提供它。
让我们概括一下我们的梯度配置:
build.gradle
根项目代表主build.gradle
文件,如下图所示:
buildscript {
repositories {
jcenter()
mavenCentral()
}
dependencies {
classpath 'com.android.tools.build:gradle:2.3.3'
classpath 'org.jetbrains.kotlin:kotlin-gradle-
plugin:1.1.51'
}
}
repositories {
jcenter()
mavenCentral()
}
- 主应用
build.gradle
解析应用的所有依赖关系,如下图所示:
apply plugin: "com.android.application"
apply plugin: "kotlin-android"
apply plugin: "kotlin-android-extensions"
repositories {
maven { url "https://maven.google.com" }
}
android {
...
sourceSets {
main.java.srcDirs += [
'src/main/kotlin',
'src/common/kotlin',
'src/debug/kotlin',
'src/release/kotlin',
'src/staging/kotlin',
'src/preproduction/kotlin',
'src/debug/java',
'src/release/java',
'src/staging/java',
'src/preproduction/java',
'src/testDebug/java',
'src/testDebug/kotlin',
'src/androidTestDebug/java',
'src/androidTestDebug/kotlin'
]
}
...
}
...
}
repositories {
jcenter()
mavenCentral()
}
dependencies {
compile "org.jetbrains.kotlin:kotlin-reflect:1.1.51"
compile "org.jetbrains.kotlin:kotlin-stdlib:1.1.51"
...
compile "com.github.salomonbrys.kotson:kotson:2.3.0"
...
compile "junit:junit:4.12"
testCompile "junit:junit:4.12"
testCompile "org.jetbrains.kotlin:kotlin-reflect:1.1.51"
testCompile "org.jetbrains.kotlin:kotlin-stdlib:1.1.51"
compile "org.jetbrains.kotlin:kotlin-test:1.1.51"
testCompile "org.jetbrains.kotlin:kotlin-test:1.1.51"
compile "org.jetbrains.kotlin:kotlin-test-junit:1.1.51"
testCompile "org.jetbrains.kotlin:kotlin-test-junit:1.1.51"
...
}
这些都是你应该满足的与 Kotlin 相关的依赖。其中一个是 Kotson,为Gson
库提供 Kotlin 绑定。
转换类
最后,我们将迁移我们的类。我们有两个自动选项。我们将两者都使用。定位MigrationActivity.java
并打开。选择代码|将 Java 文件转换为Kotlin
文件。转换需要几秒钟。现在,将文件从Java
包拖放到Kotlin
源包中。请注意以下源代码:
package com.journaler.activity
import android.os.Bundle
import android.support.v7.app.AppCompatActivity
import com.journaler.R
class MigrationActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
}
override fun onResume() {
super.onResume()
}
}
正如我们提到的,全自动转换并不能给出完美的代码。在下一节中,我们将进行重构和清理。第二种方法是将 Java 代码复制粘贴到Kotlin
文件中。从MigrationActivity2
复制所有源代码。创建一个同名的新 Kotlin 类,并粘贴代码。如果询问,请确认您希望执行自动转换。代码出现后,移除该类的 Java 版本。请注意,源代码与迁移后的MigrationActivity
类相同。
对Dummy
和Dummy2
类重复这两种方法。您得到的类如下所示:
Dummy
,第一Dummy
类示例:
package com.journaler.model
class Dummy {
var title: String? = null
var content: String? = null
constructor(title: String) {
this.title = title
}
constructor(title: String, content: String) {
this.title = title
this.content = content
}
}
Dummy2
,第二Dummy
类示例:
package com.journaler.model
import android.os.Parcel
import android.os.Parcelable
class Dummy2 : Parcelable {
var count: Int = 0
private set
var result: Float = 0.toFloat()
private set
constructor(count: Int) {
this.count = count
this.result = (count * 100).toFloat()
}
constructor(`in`: Parcel) {
count = `in`.readInt()
result = `in`.readFloat()
}
override fun writeToParcel(parcel: Parcel, i: Int) {
parcel.writeInt(count)
parcel.writeFloat(result)
}
override fun describeContents(): Int {
return 0
}
companion object {
val CREATOR: Parcelable.Creator<Dummy2>
= object : Parcelable.Creator<Dummy2> {
override fun createFromParcel(`in`: Parcel): Dummy2 {
return Dummy2(`in`)
}
override fun newArray(size: Int): Array<Dummy2> {
return arrayOfNulls(size)
}
}
}
}
Dummy2
类在转换方面有问题。在这种情况下,你必须自己解决。修复源代码。问题发生在下面一行:
override fun newArray(size: Int): Array<Dummy2> { ...
通过从Array<Dummy2> int Array<Dummy2?>
切换类型进行修复,如下所示:
override fun newArsray(size: Int): Array<Dummy2?> { ...
简单!
这正是您在进行迁移时可能面临的挑战!值得注意的是,在Dummy
和Dummy2
类中,我们都通过切换到 Kotlin 显著减少了代码库。由于不再有 Java 实现,我们可以进行重构和清理。
重构和清理
为了在转换后得到最好的代码,我们必须执行重构和清理。我们将调整我们的代码库,以符合 Kotlin 标准和习惯用法。为此,你必须完整地阅读它。只有这样做了,我们才能认为我们的迁移完成了!
打开你的课程,阅读代码。还有很大的改进空间!做了一些工作后,你应该得到这样的东西:
MigrationActivity
代码如下:
...
override fun onResume() = super.onResume()
...
可以看到MigrationActivity
(和MigrationActivity2
)没有太多的工作。两个班都很小。像Dummy
和Dummy2
这样的班级预计会付出更大的努力:
Dummy
类代码如下:
package com.journaler.model
class Dummy(
var title: String,
var content: String
) {
constructor(title: String) : this(title, "") {
this.title = title
}
}
Dummy2
类代码如下:
package com.journaler.model
import android.os.Parcel
import android.os.Parcelable
class Dummy2(
private var count: Int
) : Parcelable {
companion object {
val CREATOR: Parcelable.Creator<Dummy2>
= object : Parcelable.Creator<Dummy2> {
override fun createFromParcel(`in`: Parcel):
Dummy2 = Dummy2(`in`)
override fun newArray(size: Int): Array<Dummy2?> =
arrayOfNulls(size)
}
}
private var result: Float = (count * 100).toFloat()
constructor(`in`: Parcel) : this(`in`.readInt())
override fun writeToParcel(parcel: Parcel, i: Int) {
parcel.writeInt(count)
}
override fun describeContents() = 0
}
与转换后的第一个 Kotlin 版本相比,这两个类版本在重构后有了显著的改进。尝试将当前版本与我们拥有的原始 Java 代码进行比较。你怎么想呢?
摘要
在这一章中,我们发现了迁移到 Kotlin 编程语言的秘密。我们演示了技术,并就如何以及何时进行迁移给出了建议。幸运的是,对我们来说,这似乎毕竟不是什么困难的事情!下一章将是我们的最后一章,所以,正如你已经知道的,是时候向世界发布我们的应用了!
版权属于:月萌API www.moonapi.com,转载请注明出处