6
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

CBcloudAdvent Calendar 2019

Day 18

Android Architecture Components でAndroid開発のアーキテクチャを知る

Last updated at Posted at 2019-12-22

この記事は CBcloud Advent Calendar 2019 18日目の記事です。

対象読者

  • Android開発を少しでも実施したことがある人
  • Android開発に興味がある方

1. Android開発とアーキテクチャについての歴史

Android開発当初はEclipseと専用プラグイン(Android Development Tools)で開発されていました。
2013年5月15日に開催されたGoogle I/OでAndroid Studioが発表されました。

AndroidをEclipseで開発したことがありますが、重すぎてとても快適な開発とは言えませんでしたね...

その後、2017年のGoogle I/OでJavaに変わってKotlinで開発できるようになったり、2019年にはKotlinファーストと銘打ってどんどんモダンな開発者にやさしい開発環境になっていきました。

Kotlin on Android. Now official

Google I/O 2019: Empowering developers to build the best experiences on Android + Play

その中でもAndroid Architecture Componentsの発表はAndroid開発者にとって大きい発表だったと思います。
それまで公式では存在しなかったアーキテクチャについての公式ライブラリが初めて提供されました。
Architecture Components - Introduction (Google I/O '17)

2. Android Architecture Componentsとは?

アプリのアーキテクチャ ガイドに書いてある下記の一文がAndroid Architecture Componentsを表す上でもっとも適した文章だと思います。

最も重要な原則は関心の分離です。すべてのコードを 1 つの Activity または Fragment 内に記述するのはよくある間違いです。これらの UI ベースのクラスには、UI やオペレーティング システムとのやり取りを処理するロジックのみを含めます。これらのクラスをできる限りシンプルに保つことで、ライフサイクルに関連する多くの問題を回避することができます

アプリのアーキテクチャ ガイド

image.png

## データの取得

データの取得ではRetrofit(ほぼ公式)のAPIClientを実行し、データの取得を行います。
image.png

fun getTeamList(): LiveData<List<Team>> {
    val data = MutableLiveData<List<Team>>()

    footBallService.getTeamList().enqueue(object : Callback<TeamsResponse>{
        override fun onResponse(call: Call<TeamsResponse>, @Nullable response: Response<TeamsResponse>) {
            Log.d("TeamsResponse", response.toString())
            val response: TeamsResponse? = response.body()
            Log.d("TeamsResponse", response?.teams.toString())
            data.postValue(response?.teams)
        }

        override fun onFailure(call: Call<TeamsResponse>, t: Throwable) {
            Log.d("TeamsResponse", t.toString())
            data.postValue(null)
        }
    })

    return data
}

Modelに関しては一般的な形であると思いますが、下記の形でデータを扱います。

data class Team {
    val id:Int
    var area:Area
    var name:String
    var shortName:String
    var crestUrl:String
}

データの取り回し(ViewModel)

取得されたデータをLiveDataで保管し、Fragmentから監視できるようにします。
image.png

class TeamListViewModel(application: Application) : AndroidViewModel(application) {

    val teamListObservable: LiveData<List<Team>>

    init {
        teamListObservable = FootballRepository.instance.getTeamList()
    }
}

データのbinding(Fragment)

ここからはすべて図で言うと一番上位のActivity/Fragment での話になります。
image.png

FragmentでViewModelが保有している監視対象のLivedataをよびだします。
ここでObserverがイベントを検知したタイミングでチーム一覧をAdapterへバインディングします。

private fun observeViewModel(viewModel: TeamListViewModel) {


    //オブザーバーは、STARTED かRESUMED状態である場合にのみ、イベントを受信する
    viewModel.teamListObservable.observe(this, Observer { teams ->
        if (teams != null) {
            requireNotNull(binding).isLoading = false
            teamsAdapter?.setTeamList(teams)
        }
    })
}

Adapter内でのデータセット

Adapterでは受けわたされたデータをDataBindingによってViewにマッピングします。
自動生成されたDataBindingのclassを指定する必要があるので、次にあるxmlを先に用意してビルドしておく必要があります。

// データをsetする
fun setTeamList(teamList: List<Team>) {
       this.projectList = teamList
       result.dispatchUpdatesTo(this)
    }
}

// データをview内のitemに当てはめる
override fun onCreateViewHolder(parent: ViewGroup, viewtype: Int): TeamViewHolder {
    val binding =
            DataBindingUtil.inflate(
                    LayoutInflater.from(parent.context),

                    R.layout.project_list_item, parent,
                    false) as TeamListItemBinding   
                //自動生成されたDataBinding用のjavaclass


    return TeamViewHolder(binding)
}

xmlによるDataBindingのViewの表記

こちらでついにAPIが取得してきたデータがViewに当てはめられます。
xml内ではTeamモデルの形のみ意識すれば良いので

最も重要な原則は関心の分離です。

というアーキテクチャの導入目的を達成できていると言えると思います。

<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">

    <data>
        <variable name="team" type="com.entaku.football.service.model.Team"/>
    </data>

    <androidx.cardview.widget.CardView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        android:layout_marginStart="20dp"
        android:layout_marginEnd="20dp"
        app:cardUseCompatPadding="true">

        <LinearLayout
            android:layout_marginStart="20dp"
            android:layout_marginEnd="20dp"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:padding="5dp"
            android:orientation="vertical">

            <TextView
                android:id="@+id/name"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:contentDescription="test"
                android:textStyle="bold"
                android:textSize="20dp"
                android:text="@{team.name}"/>
        </LinearLayout>

    </androidx.cardview.widget.CardView>
</layout>

ここまでのソースをGitHubにまとめてありますので、宜しければ参考にしてください。
GitHub

3.まとめ

  • Android開発にてアーキテクチャ ガイド発表されたのは2017年とまだ2年程度
  • Android Architecture Components によって「関心の分離」が実現可能
  • Googleは神

以上ここまで読んでいただきありがとうございました!
私がまとめた中で間違いやこんな内容をもう少し書いてもらいたいなどありましたらお気軽にコメントお願いします!

6
3
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
6
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?