LoginSignup
3
6

More than 1 year has passed since last update.

この記事では、Androidアプリのアーキテクチャについて、改めて纏めています。

ポイント

Androidに関わらず、オブジェクト思考ではよく言われる、下記を守ることです。

  • クラスをシンプルに
  • 依存を少なく

技術要素

ここでは、下記の技術要素を使用しています。

実装テーマ

  • メモ画面。
  • 保存先は、DataStore。

クラス構成

構成は、下記を参考に、MemoFragment、MemoViewModel、MemoRepositoryです。

依存方向:MemoFragment → MemoViewModel → MemoRepository

実装内容

 MemoFragment

ポイントはDataBinding。
DataBindingで、UI(xml)とソース(ここでは、ViewModel)を繋ぎます。

MemoFragment
class MemoFragment : Fragment() {
    private lateinit var binding: DataStoreFragmentBinding
    private val viewModel: MemoViewModel by viewModels()

    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
                              savedInstanceState: Bundle?): View? {
        binding = DataBindingUtil.inflate(inflater, R.layout.data_store_fragment, container, false)
        binding.lifecycleOwner = viewLifecycleOwner
        binding.viewModel = viewModel
        return binding.root
    }
    ...

DataBindingを使うために、app/build.gradleに下記を追加します。

app/build.gradle
    dataBinding {
        enabled = true
    }

layoutファイルは、layout階層を追加します。
dataには、MemoViewModelを追加します。
EditTextにandroid:text="@={viewModel.text}"を記載して、MemoViewModelのLiveDataと双方向にバインドさせます。(双方向のポイントは@と{の間の=
Buttonには、android:onClick="@{() -> viewModel.onClick()}"を記載して、クリック時のメソッドをバインドします。

data_store_fragment.xml
<layout xmlns:tools="http://schemas.android.com/tools"
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">
    <data>
        <variable
            name="viewModel"
            type="com.ykato.sample.kotlin.datastore.MemoViewModel" />
    </data>
        ...
        <EditText
            android:layout_width="match_parent"
            android:layout_height="0dp"
            android:gravity="top"
            android:text="@={viewModel.text}"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintBottom_toTopOf="@id/guideline" />
            ...
            <Button
                android:id="@+id/button"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="@string/save_button"
                android:onClick="@{() -> viewModel.onClick()}"/>
            ...
 MemoViewModel

ViewModelはDI(Dependency Injection)を使用することで、依存度を下げます。
MemoRepositoryをメンバ変数として保持する場合、コンストラクタの引数としてInjectionすることで依存が下がります。(Mockもしやすく、テストもしやすくなります。)

MemoViewModel.kt
@HiltViewModel
class MemoViewModel @Inject constructor(
    private val store: MemoRepository
): ViewModel() {
...

DI(Hilt, Dagger)を行うために、依存関係をgradleファイルに追加します。

build.gradle
buildscript {
    ...
    dependencies {
        ...
        classpath 'com.google.dagger:hilt-android-gradle-plugin:2.28-alpha'
        ...
    }
}
app/build.gradle
...
apply plugin: 'kotlin-kapt'
apply plugin: 'dagger.hilt.android.plugin'
...
android {
  ...
  compileOptions {
    sourceCompatibility JavaVersion.VERSION_1_8
    targetCompatibility JavaVersion.VERSION_1_8
  }
  ...
}
...
dependencies {
    implementation "com.google.dagger:hilt-android:2.35"
    kapt "com.google.dagger:hilt-android-compiler:2.35"
}
...

また、DIを行う為には、Applicationクラスに@HiltAndroidAppを追加する必要があります。

KotlinSampleApplication.kt
@HiltAndroidApp
class KotlinSampleApplication: Application()

MemoFragmentとMainActivityには@AndroidEntryPointを追加する必要があります。
(MemoFragmentをMainActivity上に追加しているため、MainActivityにも追加する必要がある。)

MainActivity.kt
@AndroidEntryPoint
class MainActivity : AppCompatActivity() {
...
MemoFragment.kt
@AndroidEntryPoint
class MemoFragment : Fragment() {
...
 MemoRepository

MemoRepositoryはDataStoreを使用して、メモの内容を保存します。
また、メモの内容をキャッシュする(DataStoreの読み込み・保存は非同期処理なので、キャッシュして同期メソッドを実装する)ため、Singletonにしています。

MemoRepository.kt
val Context.dataStore: DataStore<Preferences> by preferencesDataStore(name = "memo")
val MEMO_STRING = stringPreferencesKey("memo_string")

@Singleton
class MemoRepository @Inject constructor(
    @ApplicationContext private val context: Context,
) {
    private val cache by lazy { AtomicReference<String?>(null) }
...

DataStoreを使用するために、依存に下記を追加

app/build.gradle
...
dependencies {
    implementation("androidx.datastore:datastore-preferences:1.0.0-rc01")
...

参考

今回のコードは下記にあります。
https://github.com/yoshihiro-kato/android-samples/tree/05a032ade61babbc8cf133418fc367a5a091a33f/KotlinSampleApplication/app/src/main/java/com/ykato/sample/kotlin/datastore

3
6
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
6