Kotlinの紹介:人間のためのAndroidプログラミング

公開: 2022-03-11

完璧なAndroidの世界では、Javaの主要言語は本当に現代的で、明確で、エレガントです。 より多くのことを行うことで書き込みを減らすことができ、新しい機能が登場するたびに、開発者はGradleでバージョンを増やすだけでそれを使用できます。 次に、非常に優れたアプリを作成している間、それは完全にテスト可能で、拡張可能で、保守可能であるように見えます。 私たちの活動はそれほど大きく複雑ではなく、データソースをデータベースからWebに変更することができ、大きな違いはありません。 いいですね。 残念ながら、Androidの世界はこれほど理想的ではありません。 グーグルはまだ完璧を目指して努力していますが、理想的な世界が存在しないことは誰もが知っています。 したがって、Androidの世界での素晴らしい旅に身を投じる必要があります。

Kotlinとは何ですか、なぜそれを使用する必要がありますか?

だから、第一言語。 Javaはエレガンスや明快さの達人ではなく、現代的でも表現的でもないと思います(そしてあなたも同意すると思います)。 欠点は、Android Nより下では、Java 6(Java 7の一部を含む)に制限されていることです。 開発者は、RetroLambdaをアタッチして、コードでラムダ式を使用することもできます。これは、RxJavaを使用しているときに非常に便利です。 Android Nを超えると、Java 8の新機能の一部を使用できますが、それでも古い、重いJavaです。 Android開発者が「iOSがSwiftで行うように、Androidがより良い言語をサポートしたい」と言うのをよく耳にします。 そして、nullの安全性、ラムダ、およびその他の多くの優れた新機能を備えた、非常に優れたシンプルな言語を使用できると言ったらどうでしょうか。 Kotlinへようこそ。

Kotlinとは何ですか?

Kotlinは、JetBrainsチームによって開発された新しい言語(Android用のSwiftと呼ばれることもあります)であり、現在は1.0.2バージョンです。 Android開発で役立つのは、JVMバイトコードにコンパイルでき、JavaScriptにもコンパイルできることです。 Javaと完全に互換性があり、KotlinコードはJavaコードに簡単に変換でき、その逆も可能です(JetBrainsのプラグインがあります)。 つまり、Kotlinは、Javaで記述された任意のフレームワークやライブラリなどを使用できます。 Androidでは、Gradleによって統合されます。 既存のAndroidアプリがあり、アプリ全体を書き直さずにKotlinに新しい機能を実装したい場合は、Kotlinで書き始めるだけで機能します。

しかし、「素晴らしい新機能」とは何ですか? いくつか挙げさせてください。

オプションの名前付き関数パラメーター

fun createDate(day: Int, month: Int, year: Int, hour: Int = 0, minute: Int = 0, second: Int = 0) { print("TEST", "$day-$month-$year $hour:$minute:$second") }

さまざまな方法でメソッドcreateDateを呼び出すことができます

createDate(1,2,2016) prints: '1-2-2016 0:0:0' createDate(1,2,2016, 12) prints: '1-2-2016 12:0:0' createDate(1,2,2016, minute = 30) prints: '1-2-2016 0:30:0'

ヌルセーフティ

変数がnullになる可能性がある場合、強制的に作成しない限り、コードはコンパイルされません。 次のコードにはエラーがあります-nullableVarはnullの可能性があります:

 var nullableVar: String? = “”; nullableVar.length;

コンパイルするには、nullでないかどうかを確認する必要があります。

 if(nullableVar){ nullableVar.length }

または、短い:

 nullableVar?.length

このように、nullableVarがnullの場合、何も起こりません。 それ以外の場合は、次のように入力した後に疑問符を付けずに、変数をnull許容ではないものとしてマークできます。

 var nonNullableVar: String = “”; nonNullableVar.length;

このコードはコンパイルされ、nullをnonNullableVarに割り当てたい場合、コンパイラはエラーを表示します。

非常に便利なエルビス演算子もあります。

 var stringLength = nullableVar?.length ?: 0

次に、nullableVarがnullの場合(つまり、nullableVar?.lengthはnullを返します)、stringLengthの値は0になります。

可変変数と不変変数

上記の例では、変数を定義するときにvarを使用しています。 これは変更可能であり、いつでも再割り当てできます。 その変数を不変にしたい場合(多くの場合、そうする必要があります)、valを使用します(変数ではなく値として):

 val immutable: Int = 1

その後、コンパイラは不変に再割り当てすることを許可しません。

ラムダ

ラムダとは何かを知っているので、ここではKotlinでラムダを使用する方法を示します。

 button.setOnClickListener({ view -> Log.d("Kotlin","Click")})

または、関数が唯一または最後の引数である場合:

 button.setOnClickListener { Log.d("Kotlin","Click")}

拡張機能

拡張機能は非常に便利な言語機能です。そのおかげで、既存のクラスが最終的なものである場合や、ソースコードにアクセスできない場合でも、既存のクラスを「拡張」できます。

たとえば、editText.text.toString()のたびに書き込む代わりに、編集テキストから文字列値を取得するには、次の関数を記述します。

 fun EditText.textValue(): String{ return text.toString() }

またはそれより短い:

 fun EditText.textValue() = text.toString()

そして今、EditTextのすべてのインスタンスで:

 editText.textValue()

または、同じものを返すプロパティを追加できます。

 var EditText.textValue: String get() = text.toString() set(v) {setText(v)}

演算子のオーバーロード

オブジェクトを追加、乗算、または比較する場合に役立つことがあります。 Kotlinでは、二項演算子(plus、minus、plusAssign、rangeなど)、配列演算子(get、set、get range、set range)、および等号演算と単項演算(+ a、-aなど)のオーバーロードが可能です。

データクラス

copy、equals、hashCode、toStringの3つのプロパティを持つUserクラスをJavaで実装するには、何行のコードが必要ですか? Kaotlinでは、必要な行は1つだけです。

 data class User(val name: String, val surname: String, val age: Int)

このデータクラスは、equals()、hashCode()、copy()メソッド、およびユーザーを次のように出力するtoString()を提供します。

 User(name=John, surname=Doe, age=23)

データクラスは、Kotlinのドキュメントで確認できる他の便利な関数とプロパティも提供します。

アンコエクステンション

バターナイフやAndroidの拡張機能を使用していますね。 このライブラリを使用する必要がなく、XMLでビューを宣言した後、コードからIDで使用するだけの場合はどうなりますか(C#のXAMLのように):

 <Button android: android:layout_width="match_parent" android:layout_height="wrap_content" />
 loginBtn.setOnClickListener{}

Kotlinには非常に便利なAnko拡張機能があり、これを使用すると、loginBtnとは何かをアクティビティに伝える必要がなく、xmlを「インポート」するだけでわかります。

 import kotlinx.android.synthetic.main.activity_main.*

アンコには、活動を始めたり、乾杯をしたりするなど、他にもたくさんの便利なことがあります。 これはAnkoの主な目標ではなく、コードからレイアウトを簡単に作成できるように設計されています。 したがって、プログラムでレイアウトを作成する必要がある場合は、これが最善の方法です。

これは、Kotlinのほんの一部です。 AntonioLeivaのブログと彼の本-KotlinforAndroid Developers、そしてもちろん公式のKotlinサイトを読むことをお勧めします。

MVPとは何ですか?その理由は?

素晴らしく、強力で、明確な言葉だけでは十分ではありません。 優れたアーキテクチャがなくても、すべての言語で厄介なアプリを作成するのは非常に簡単です。 Android開発者(主に始めたばかりの開発者だけでなく、より高度な開発者)は、多くの場合、周囲のすべてに対してアクティビティの責任を負います。 アクティビティ(またはフラグメント、またはその他のビュー)は、データのダウンロード、保存のための送信、表示、ユーザーインタラクションへの応答、データの編集、すべての子ビューの管理を行います。 。 。 そして多くの場合、はるかに。 アクティビティやフラグメントなどの不安定なオブジェクトには多すぎます(画面を回転させるだけで十分で、アクティビティには「さようなら…」と表示されます)。

非常に良い考えは、責任を見解から分離し、それらを可能な限り愚かにすることです。 ビュー(アクティビティ、フラグメント、カスタムビュー、または画面にデータを表示するもの)は、サブビューの管理のみを担当する必要があります。 ビューには、モデルと通信し、何をすべきかを伝えるプレゼンターが必要です。 つまり、これはModel-View-Presenterパターンです(私にとっては、レイヤー間の接続を表示するためにModel-Presenter-Viewという名前にする必要があります)。

MVCとMVP

「ねえ、私はそのようなことを知っています、そしてそれはMVCと呼ばれています!」 -そう思いませんでしたか? いいえ、MVPはMVCと同じではありません。 MVCパターンでは、ビューはモデルと通信できます。 MVPを使用している間は、これら2つのレイヤー間の通信を許可しません。ViewがModelと通信できる唯一の方法は、Presenterを使用することです。 ViewがModelについて知っているのは、データ構造だけです。 ビューは、たとえば、ユーザーを表示する方法を知っていますが、いつ表示するかはわかりません。 簡単な例を次に示します。

ビューは「私はアクティビティです。2つのEditTextと1つのボタンがあります。 誰かがボタンをクリックしたら、それをプレゼンターに伝えて、EditTextsの値を渡す必要があります。 そして、それだけです。次のクリックまたはプレゼンターが何をすべきかを教えてくれるまで、私は眠ることができます。」

プレゼンターは、どこかにビューがあることを知っており、このビューが実行できる操作を知っています。 彼はまた、2つの文字列を受け取ったら、これら2つの文字列からユーザーを作成し、データをモデルに送信して保存する必要があることも知っています。保存が成功した場合は、ビューに「成功情報を表示」と伝えます。

モデルは、データがどこにあるか、どこに保存するか、データに対してどのような操作を実行するかを知っているだけです。

MVPで記述されたアプリケーションは、テスト、保守、および再利用が簡単です。 純粋なプレゼンターは、Androidプラットフォームについて何も知らないはずです。 純粋なJava(またはこの場合はKotlin)クラスである必要があります。 これにより、プレゼンターを他のプロジェクトで再利用できます。 モデル、ビュー、プレゼンターを個別にテストして、単体テストを簡単に作成することもできます。

MVPパターンは、ユーザーインターフェイスとビジネスロジックを完全に分離することにより、より優れた、より複雑でないコードベースにつながります。

少し余談:MVPは、アプリケーションをさらに柔軟で適切にアーキテクチャ化するために、ボブおじさんのクリーンアーキテクチャの一部である必要があります。 次回はそれについて書こうと思います。

MVPとKotlinを使用したサンプルアプリ

これで十分な理論です。コードを見てみましょう。 さて、簡単なアプリを作成してみましょう。 このアプリの主な目標は、ユーザーを作成することです。 最初の画面には、2つのEditText(名前と名前)と1つのボタン(保存)があります。 名前と名前を入力して[保存]をクリックすると、アプリに[ユーザーが保存されました]と表示され、保存された名前と名前が表示される次の画面に移動します。 名前または名前が空の場合、アプリはユーザーを保存せず、何が問題なのかを示すエラーを表示する必要があります。

Android Studioプロジェクトを作成した後の最初のことは、Kotlinを構成することです。 Kotlinプラグインをインストールする必要があります。再起動後、[ツール]> [Kotlin]で、[プロジェクトにKotlinを構成する]をクリックできます。 IDEはKotlinの依存関係をGradleに追加します。 既存のコードがある場合は、(Ctrl + Shift + Alt +Kまたはコード>JavaファイルをKotlinに変換)で簡単にKotlinに変換できます。 何かが間違っていてプロジェクトがコンパイルされない場合、またはGradleがKotlinを認識しない場合は、GitHubで利用可能なアプリのコードを確認できます。

Kotlinは、Javaフレームワークおよびライブラリとうまく相互運用できるだけでなく、すでに使い慣れている同じツールのほとんどを引き続き使用できます。

プロジェクトができたので、最初のビューであるCreateUserViewを作成することから始めましょう。 このビューには前述の機能が含まれている必要があるため、そのためのインターフェイスを作成できます。

 interface CreateUserView : View { fun showEmptyNameError() /* show error when name is empty */ fun showEmptySurnameError() /* show error when surname is empty */ fun showUserSaved() /* show user saved info */ fun showUserDetails(user: User) /* show user details */ }

ご覧のとおり、Kotlinは関数の宣言においてJavaに似ています。 これらはすべて何も返さない関数であり、最後の関数には1つのパラメーターがあります。 これが違いです。パラメータタイプは名前の後にあります。 ViewインターフェースはAndroidのものではありません-それは私たちのシンプルで空のインターフェースです:

 interface View

Basic Presenterのインターフェイスには、Viewタイプのプロパティが必要であり、少なくともメソッド(onDestroyなど)では、このプロパティはnullに設定されます。

 interface Presenter<T : View> { var view: T? fun onDestroy(){ view = null } }

ここでは、別のKotlin機能を確認できます。インターフェイスでプロパティを宣言し、そこにメソッドを実装することもできます。

CreateUserViewはCreateUserPresenterと通信する必要があります。 このプレゼンターに必要な唯一の追加関数は、2つの文字列引数を持つsaveUserです。

 interface CreateUserPresenter<T : View>: Presenter<T> { fun saveUser(name: String, surname: String) }

モデル定義も必要です-前述のデータクラス:

 data class User(val name: String, val surname: String)

すべてのインターフェースを宣言したら、実装を開始できます。

CreateUserPresenterは、CreateUserPresenterImplに実装されます。

 class CreateUserPresenterImpl(override var view: CreateUserView?): CreateUserPresenter<CreateUserView> { override fun saveUser(name: String, surname: String) { } }

クラス定義のある最初の行:

 CreateUserPresenterImpl(override var view: CreateUserView?)

コンストラクターです。インターフェースで定義されたビュープロパティを割り当てるために使用します。

CreateUserViewの実装であるMainActivityには、CreateUserPresenterへの参照が必要です。

 class MainActivity : AppCompatActivity(), CreateUserView { private val presenter: CreateUserPresenter<CreateUserView> by lazy { CreateUserPresenterImpl(this) } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) saveUserBtn.setOnClickListener{ presenter.saveUser(userName.textValue(), userSurname.textValue()) /*use of textValue() extension, mentioned earlier */ } } override fun showEmptyNameError() { userName.error = getString(R.string.name_empty_error) /* it's equal to userName.setError() - Kotlin allows us to use property */ } override fun showEmptySurnameError() { userSurname.error = getString(R.string.surname_empty_error) } override fun showUserSaved() { toast(R.string.user_saved) /* anko extension - equal to Toast.makeText(this, R.string.user_saved, Toast.LENGTH_LONG) */ } override fun showUserDetails(user: User) { } override fun onDestroy() { presenter.onDestroy() } }

クラスの始めに、プレゼンターを定義しました。

 private val presenter: CreateUserPresenter<CreateUserView> by lazy { CreateUserPresenterImpl(this) }

これは不変(val)として定義され、レイジーデリゲートによって作成されます。レイジーデリゲートは、最初に必要になったときに割り当てられます。 さらに、nullにならないことを確信しています(定義後に疑問符はありません)。

ユーザーが[保存]ボタンをクリックすると、ViewはEditTexts値を使用してPresenterに情報を送信します。 その場合、ユーザーを保存する必要があるため、Presenter(およびモデルの関数の一部)にsaveUserメソッドを実装する必要があります。

 override fun saveUser(name: String, surname: String) { val user = User(name, surname) when(UserValidator.validateUser(user)){ UserError.EMPTY_NAME -> view?.showEmptyNameError() UserError.EMPTY_SURNAME -> view?.showEmptySurnameError() UserError.NO_ERROR -> { UserStore.saveUser(user) view?.showUserSaved() view?.showUserDetails(user) } } }

ユーザーが作成されると、有効性を確認するためにUserValidatorに送信されます。 次に、検証の結果に応じて、適切なメソッドが呼び出されます。 when(){}構文は、Javaのswitch/caseと同じです。 ただし、より強力です。Kotlinでは、「case」のenumまたはintだけでなく、範囲、文字列、またはオブジェクトタイプも使用できます。 すべての可能性を含むか、else式を持っている必要があります。 ここでは、すべてのUserError値について説明します。

view?.showEmptyNameError()(ビューの後に疑問符が付いている)を使用することで、NullPointerから保護されます。 onDestroyメソッドではビューをnullにすることができ、この構造では何も起こりません。

ユーザーモデルにエラーがない場合、ユーザーモデルに保存するように指示し、成功を表示して詳細を表示するようにViewに指示します。

前述のように、いくつかのモデルを実装する必要があります。

 enum class UserError { EMPTY_NAME, EMPTY_SURNAME, NO_ERROR } object UserStore { fun saveUser(user: User){ //Save user somewhere: Database, SharedPreferences, send to web... } } object UserValidator { fun validateUser(user: User): UserError { with(user){ if(name.isNullOrEmpty()) return UserError.EMPTY_NAME if(surname.isNullOrEmpty()) return UserError.EMPTY_SURNAME } return UserError.NO_ERROR } }

ここで最も興味深いのはUserValidatorです。 オブジェクトワードを使用することで、スレッドやプライベートコンストラクターなどを気にすることなくシングルトンクラスを作成できます。

次に、validateUser(user)メソッドには、with(user){}式があります。 このようなブロック内のコードは、オブジェクトのコンテキストで実行され、名前と名前で渡されるのはユーザーのプロパティです。

もう一つの小さなことがあります。 列挙型からUserValidatorまで、上記のすべてのコードは、定義が1つのファイルに配置されます(Userクラスの定義もここにあります)。 Kotlinは、各パブリッククラスを単一のファイル(またはファイルとまったく同じ名前のクラス)に含めることを強制しません。 したがって、関連するコードの短い部分(データクラス、拡張機能、関数、定数-Kotlinは関数または定数のクラスを必要としません)がある場合、プロジェクト内のすべてのファイルに分散する代わりに、1つのファイルに配置できます。

ユーザーが保存されると、アプリはそれを表示する必要があります。 別のビューが必要です。Androidビュー、カスタムビュー、フラグメント、アクティビティのいずれでもかまいません。 アクティビティを選択しました。

それでは、UserDetailsViewインターフェースを定義しましょう。 ユーザーを表示できますが、ユーザーがいない場合はエラーも表示されます。

 interface UserDetailsView { fun showUserDetails(user: User) fun showNoUserError() }

次に、UserDetailsPresenter。 ユーザープロパティが必要です。

 interface UserDetailsPresenter<T: View>: Presenter<T> { var user: User? }

このインターフェイスは、UserDetailsPresenterImplに実装されます。 ユーザープロパティを上書きする必要があります。 このプロパティが割り当てられるたびに、ユーザーはビューを更新する必要があります。 これにはプロパティセッターを使用できます。

 class UserDetailsPresenterImpl(override var view: UserDetailsView?): UserDetailsPresenter<UserDetailsView> { override var user: User? = null set(value) { field = value if(field != null){ view?.showUserDetails(field!!) } else { view?.showNoUserError() } } }

UserDetailsViewの実装であるUserDetailsActivityは非常に単純です。 前と同じように、遅延読み込みによって作成されたプレゼンターオブジェクトがあります。 表示するユーザーは、インテントを介して渡される必要があります。 今のところ、これには小さな問題が1つあります。これについては、すぐに解決します。 意図したユーザーがいる場合、Viewはそれをプレゼンターに割り当てる必要があります。 その後、ユーザーは画面上で更新されます。nullの場合は、エラーが表示されます(アクティビティは終了しますが、プレゼンターはそのことを認識していません)。

 class UserDetailsActivity: AppCompatActivity(), UserDetailsView { private val presenter: UserDetailsPresenter<UserDetailsView> by lazy { UserDetailsPresenterImpl(this) } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_user_details) val user = intent.getParcelableExtra<User>(USER_KEY) presenter.user = user } override fun showUserDetails(user: User) { userFullName.text = "${user.name} ${user.surname}" } override fun showNoUserError() { toast(R.string.no_user_error) finish() } override fun onDestroy() { presenter.onDestroy() } }

インテントを介してオブジェクトを渡すには、このオブジェクトがParcelableインターフェイスを実装している必要があります。 これは非常に「汚い」仕事です。 個人的には、すべてのCREATOR、プロパティ、保存、復元などのために、これを行うのは嫌いです。 幸い、Kotlin用のParcelableという適切なプラグインがあります。 インストール後、ワンクリックでParcelableを生成できます。

最後に行うことは、MainActivityにshowUserDetails(user:User)を実装することです。

 override fun showUserDetails(user: User) { startActivity<UserDetailsActivity>(USER_KEY to user) /* anko extension - starts UserDetailsActivity and pass user as USER_KEY in intent */ }

そしてそれがすべてです。

KotlinのデモAndroidアプリ

ユーザーを保存し(実際には保存されませんが、プレゼンターやビューに触れることなくこの機能を追加できます)、画面に表示するシンプルなアプリがあります。 将来、ユーザーが画面に表示される方法を変更する場合(1つのアクティビティで2つのアクティビティから2つのフラグメント、または2つのカスタムビューなど)、変更はViewクラスでのみ行われます。 もちろん、機能やモデルの構造を変更しない場合。 ビューが正確に何であるかを知らないプレゼンターは、変更を加える必要はありません。

Androidアプリのパフォーマンスに問題がありますか? これらの最適化のヒントとテクニックを確認してください。

次は何ですか?

私たちのアプリでは、アクティビティが作成されるたびにプレゼンターが作成されます。 このアプローチ、またはプレゼンターがアクティビティインスタンス間で存続する必要がある場合はその逆であり、インターネット全体で多くの議論の対象となっています。 私にとって、それはアプリ、そのニーズ、そして開発者に依存します。 プレゼンターを破壊する方が良い場合もあれば、そうでない場合もあります。 1つを永続化することにした場合、非常に興味深い手法は、そのためにLoaderManagerを使用することです。

前述のように、MVPはボブおじさんのクリーンアーキテクチャの一部である必要があります。 さらに、優れた開発者はDaggerを使用して、プレゼンターの依存関係をアクティビティに注入する必要があります。 また、将来のコードの保守、テスト、および再利用にも役立ちます。 現在、KotlinはDagger(公式リリース以前はそれほど簡単ではありませんでした)や他の便利なAndroidライブラリと非常にうまく連携しています。

要約

私にとって、Kotlinは素晴らしい言語です。 現代的で、明確で、表現力豊かでありながら、偉大な人々によって開発されています。 また、Androidデバイスとバージョンで新しいリリースを使用できます。 Javaに腹を立てるものは何でも、Kotlinは向上します。

もちろん、私が言ったように、何も理想的ではありません。 Kotlinにはいくつかの欠点もあります。 最新のgradleプラグインバージョン(主にアルファ版またはベータ版)は、この言語ではうまく機能しません。 多くの人が、ビルド時間は純粋なJavaよりも少し長いと不満を漏らしており、apkには追加のMBがいくつかあります。 しかし、Android StudioとGradleはまだ改善されており、スマートフォンにはアプリ用のスペースがますます増えています。 そのため、KotlinはすべてのAndroid開発者にとって非常に優れた言語になると思います。 試してみて、下のコメントセクションであなたの考えを共有してください。

サンプルアプリのソースコードはGithubで入手できます:github.com/tomaszczura/AndroidMVPKotlin