Android cleanarchitecture
-
Upload
tomoaki-imai -
Category
Engineering
-
view
15.886 -
download
7
Transcript of Android cleanarchitecture
![Page 1: Android cleanarchitecture](https://reader034.fdocument.pub/reader034/viewer/2022042723/58f9adba760da3da068b9b57/html5/thumbnails/1.jpg)
Android Clean Architecture ことはじめMercari, Inc. Tomoaki Imai
08/08/2015 Android All Stars @Shibuya dots.
![Page 2: Android cleanarchitecture](https://reader034.fdocument.pub/reader034/viewer/2022042723/58f9adba760da3da068b9b57/html5/thumbnails/2.jpg)
Tomoaki ImaiMercari, Inc.
twitter: @tomoaki_imai
github: tomoima525
![Page 3: Android cleanarchitecture](https://reader034.fdocument.pub/reader034/viewer/2022042723/58f9adba760da3da068b9b57/html5/thumbnails/3.jpg)
今日はみなさんに質問が あります
![Page 4: Android cleanarchitecture](https://reader034.fdocument.pub/reader034/viewer/2022042723/58f9adba760da3da068b9b57/html5/thumbnails/4.jpg)
胸に手を当てて振り返って みてください
![Page 5: Android cleanarchitecture](https://reader034.fdocument.pub/reader034/viewer/2022042723/58f9adba760da3da068b9b57/html5/thumbnails/5.jpg)
キレイな設計、できてますか?
![Page 6: Android cleanarchitecture](https://reader034.fdocument.pub/reader034/viewer/2022042723/58f9adba760da3da068b9b57/html5/thumbnails/6.jpg)
Androidにおける設計の難しさ• Life Cycle(画面再生成 etc)
• 数多く存在するApi
• OSごとのバージョン管理
![Page 7: Android cleanarchitecture](https://reader034.fdocument.pub/reader034/viewer/2022042723/58f9adba760da3da068b9b57/html5/thumbnails/7.jpg)
みんな似たようなことで 悩んでる
ライフサイクル 考えるのめんどくさい
画面回転が入ると面倒
Activity, Fragmentが どんどん肥大化する
ディレクトリ構成が プロジェクトでまちまち
Unit/UI Testが難しい
![Page 8: Android cleanarchitecture](https://reader034.fdocument.pub/reader034/viewer/2022042723/58f9adba760da3da068b9b57/html5/thumbnails/8.jpg)
“Android Clean Architecture” という考え方
The Clean Architecture by Uncle Bob
http://blog.8thlight.com/uncle-bob/2012/08/13/the-clean-architecture.html
![Page 9: Android cleanarchitecture](https://reader034.fdocument.pub/reader034/viewer/2022042723/58f9adba760da3da068b9b57/html5/thumbnails/9.jpg)
“Android Clean Architecture” という考え方
The Clean Architecture (Enterprise Application)
(Web Service)+ =
Android Clean Architecture
![Page 10: Android cleanarchitecture](https://reader034.fdocument.pub/reader034/viewer/2022042723/58f9adba760da3da068b9b57/html5/thumbnails/10.jpg)
“Android Clean Architecture” という考え方
http://www.slideshare.net/shinnosukekugimiya/ss-50705888
https://github.com/android10/Android-CleanArchitecture
![Page 11: Android cleanarchitecture](https://reader034.fdocument.pub/reader034/viewer/2022042723/58f9adba760da3da068b9b57/html5/thumbnails/11.jpg)
今日みなさんに持って返って もらいたいもの
• Android Clean Architectureの思想をさっくり理解する
• Android Clean Architectureの実際的な実装方法を理解する
• 既存の設計と比べてどういうメリットがあるか理解する
![Page 12: Android cleanarchitecture](https://reader034.fdocument.pub/reader034/viewer/2022042723/58f9adba760da3da068b9b57/html5/thumbnails/12.jpg)
標準的なWeb設計の指針
“MVC Architecture”
![Page 13: Android cleanarchitecture](https://reader034.fdocument.pub/reader034/viewer/2022042723/58f9adba760da3da068b9b57/html5/thumbnails/13.jpg)
MVC Architecture
Modelアプリケーションデータ ビジネスロジック
View Modelをレンダリングする
Controller ユーザーイベントをハンドルする
View
Model
Controller
User
usessees
![Page 14: Android cleanarchitecture](https://reader034.fdocument.pub/reader034/viewer/2022042723/58f9adba760da3da068b9b57/html5/thumbnails/14.jpg)
Life Cycle
Data source
Frameworks
![Page 15: Android cleanarchitecture](https://reader034.fdocument.pub/reader034/viewer/2022042723/58f9adba760da3da068b9b57/html5/thumbnails/15.jpg)
Android Frameworks
Activity Life Cycle
MVC Architecture on Android
Model
アプリケーションデータ (http, DB, memory色々) ビジネスロジック
ViewModelをレンダリングする (端末の状態を意識しながら)
Controllerユーザーイベントをハンドルする (端末状態をハンドルする) (色んなViewもハンドルする)
View
Model
Controller
User
usessees
![Page 16: Android cleanarchitecture](https://reader034.fdocument.pub/reader034/viewer/2022042723/58f9adba760da3da068b9b57/html5/thumbnails/16.jpg)
MVCでアプリ
![Page 17: Android cleanarchitecture](https://reader034.fdocument.pub/reader034/viewer/2022042723/58f9adba760da3da068b9b57/html5/thumbnails/17.jpg)
Sample App• GithubからFollowerを取ってくるアプリ
• Followerのrepository見れる
• 選択したFollowerはメモリにキャッシュし、2回目以降はトーストで通知する
https://github.com/tomoima525/CleanArchitectureSample
/app配下 -> MVC で実装
/cleanarchitecture配下 -> Clean Architecture で実装
![Page 18: Android cleanarchitecture](https://reader034.fdocument.pub/reader034/viewer/2022042723/58f9adba760da3da068b9b57/html5/thumbnails/18.jpg)
private GithubApi mApi;private UserMemoryCache mUserMemoryCache;…
mApi = createApi();mUserMemoryCache = UserMemoryCache.getInstance();…public void HogeTask(…){ //ビジネスロジック… mApi.listFollowersAsync(…); mUserMemoryCache.put(…); }
Models
• Modelのデータ元は多様
• ModelとControllerのインタラクション(ビジネスロジック)がテストしにくい
![Page 19: Android cleanarchitecture](https://reader034.fdocument.pub/reader034/viewer/2022042723/58f9adba760da3da068b9b57/html5/thumbnails/19.jpg)
public class MainActivity { @Override protected void onCreate(Bundle savedInstanceState) { // create views... }
@Override protected void onResume(){ // resume/update views when system state is resumed }
@Override protected void onPause(){ // pause views when system state is paused }}
Views
• Viewの状態管理が必要
![Page 20: Android cleanarchitecture](https://reader034.fdocument.pub/reader034/viewer/2022042723/58f9adba760da3da068b9b57/html5/thumbnails/20.jpg)
public class MainActivity { // User events public void onClick(){} public void afterTextChanged(){}
// System events public void onSaveInstanceState(Bundle outState){} public void onRestoreInstanceState(Bundle inState){}
// View events public void gotoUserDetailActivity(Context context){}}
Controllers
• ViewとControllerがActivity上に混在している
• 画面再生成などのシステムの状態もハンドルする必要がある
![Page 21: Android cleanarchitecture](https://reader034.fdocument.pub/reader034/viewer/2022042723/58f9adba760da3da068b9b57/html5/thumbnails/21.jpg)
MVC Architecture on Android における課題
View(の状態管理)とControllerがActivity上に混在している
ModelとController間のインタラクション(ビジネスロジック)が システム状態(LifeCycle)に影響を受ける/テストしにくい
Modelのデータ・ソースが多様
各層の依存関係が強い
![Page 22: Android cleanarchitecture](https://reader034.fdocument.pub/reader034/viewer/2022042723/58f9adba760da3da068b9b57/html5/thumbnails/22.jpg)
Clean Way…?
![Page 23: Android cleanarchitecture](https://reader034.fdocument.pub/reader034/viewer/2022042723/58f9adba760da3da068b9b57/html5/thumbnails/23.jpg)
MVC Architecture on Android における課題
View(の状態管理)とControllerがActivity上に混在している
ModelとController間のインタラクション(ビジネスロジック)が システム状態(LifeCycle)に影響を受ける/テストしにくい
Modelのデータ・ソースが多様
各層の依存関係が強い
![Page 24: Android cleanarchitecture](https://reader034.fdocument.pub/reader034/viewer/2022042723/58f9adba760da3da068b9b57/html5/thumbnails/24.jpg)
MVP Architecture
![Page 25: Android cleanarchitecture](https://reader034.fdocument.pub/reader034/viewer/2022042723/58f9adba760da3da068b9b57/html5/thumbnails/25.jpg)
MVP Architecture
• Presenterを介してModelを制御する設計
View
Presenterからの情報を レンダリングする -> 端末の状態は配慮不要になる
Presenter
- Activityから分割 - ユーザーイベントを ハンドルする - Modelの結果をViewに返すAndroid Frameworks
Activity
View
Model
Presenter
Userusessees
![Page 26: Android cleanarchitecture](https://reader034.fdocument.pub/reader034/viewer/2022042723/58f9adba760da3da068b9b57/html5/thumbnails/26.jpg)
public interface ShowUserListView { // UI として表示したい処理を定義する void showLoading(); void hideLoading(); void showNoResultCase(); void hideNoResultCase(); void showResult(Collection<User> usersCollection);}
Views
![Page 27: Android cleanarchitecture](https://reader034.fdocument.pub/reader034/viewer/2022042723/58f9adba760da3da068b9b57/html5/thumbnails/27.jpg)
public abstract class Presenter { // Activity LifeCycle public abstract void initialize(); public abstract void resume(); public abstract void pause(); public abstract void destroy();}
Presenters
public class ShowUserListPresenter extends Presenter { public void setShowUserListView(ShowUserListView view){ mShowUserListView = view; //Viewを監視できるようにする }
public void getFollowerList(String user){ mShowUserListView.showLoading(); // Activity側にCallbackする }}
![Page 28: Android cleanarchitecture](https://reader034.fdocument.pub/reader034/viewer/2022042723/58f9adba760da3da068b9b57/html5/thumbnails/28.jpg)
Activitypublic class MainActivity implements ShowUserListView{
@Override public void onCreate(){ mShowUserListPresenter = new ShowUserListPresenter(…); mShowUserListPresenter.setShowUserListView(this); } @Override public void showLoading() { //実際のUI処理はActivityで実行できる! mListView.setVisibility(View.GONE); mProgress.setVisibility(View.VISIBLE); } @Override public void showResult(Collection<User> usersCollection) { mListView.setVisibility(View.VISIBLE); mUserAdapter.refresh(usersCollection); }}
![Page 29: Android cleanarchitecture](https://reader034.fdocument.pub/reader034/viewer/2022042723/58f9adba760da3da068b9b57/html5/thumbnails/29.jpg)
MVC Architecture on Android における課題
MVP Architecture
ModelとController間のインタラクション(ビジネスロジック)が システム状態(LifeCycle)に影響を受ける/テストしにくい
Modelのデータ・ソースが多様
各層の依存関係が強い
![Page 30: Android cleanarchitecture](https://reader034.fdocument.pub/reader034/viewer/2022042723/58f9adba760da3da068b9b57/html5/thumbnails/30.jpg)
MVC Architecture on Android における課題
MVP Architecture
ModelとController間のインタラクション(ビジネスロジック)が システム状態(LifeCycle)に影響を受ける/テストしにくい
Modelのデータ・ソースが多様
各層の依存関係が強い
![Page 31: Android cleanarchitecture](https://reader034.fdocument.pub/reader034/viewer/2022042723/58f9adba760da3da068b9b57/html5/thumbnails/31.jpg)
Domain Layer
![Page 32: Android cleanarchitecture](https://reader034.fdocument.pub/reader034/viewer/2022042723/58f9adba760da3da068b9b57/html5/thumbnails/32.jpg)
• Domain Layerにビジネスロジック(interactorまたはuse casesと呼ぶ)を集約する
• Modelとの処理はDomain経由で行う
• 処理は非同期で実行する
• Pure Java
Domain Layer
Domain
Android Frameworks
Activity
View
Model
Presenter
Userusessees
![Page 33: Android cleanarchitecture](https://reader034.fdocument.pub/reader034/viewer/2022042723/58f9adba760da3da068b9b57/html5/thumbnails/33.jpg)
Domain Layer
UseCase <<abstract>> ベースとなるスレッド処理を実装
ConcreteUseCaseUseCaseを継承 ロジック処理の実行、Presenterへのコールバック
PostExecutionThread (UIThread) スレッド実行後の処理
• Domain層は大まかにUseCase, Threadにより成立
• 設計方法は多くの手法がある
![Page 34: Android cleanarchitecture](https://reader034.fdocument.pub/reader034/viewer/2022042723/58f9adba760da3da068b9b57/html5/thumbnails/34.jpg)
public abstract class UseCase<T> { // スレッドをキュー処理できるようにする private ExecutorService mExecutorService
= Executors.newSingleThreadExecutor(); public void start(final T params) { mExecutorService.submit(new Runnable() { @Override public void run() { call(params); } }); } //スレッド内でcallされるメソッド abstract protected void call(T params);}
UseCase
https://github.com/kgmyshin/Android-archを参考
![Page 35: Android cleanarchitecture](https://reader034.fdocument.pub/reader034/viewer/2022042723/58f9adba760da3da068b9b57/html5/thumbnails/35.jpg)
public class FollowerListUseCase extends UseCase<String> { @Override public void execute(String user, FollowerListUseCaseCallback callback){ mCallback = callback; this.start(user); // (1) ExecutorServiceのスレッド経由でcall()が呼ばれる }
@Override protected void call(String user) { // (2) Modelからデータ取得、バリデーション等のロジック処理。 // …省略 // (3) 処理後はUIThread( PostExecutionThread )にCallbackを返す mPostExecutionThread.post(() -> {mCallback.onUserListLoaded(usersCollection);}); } // UIThreadに返すCallbackのinterface interface FollowerListUseCaseCallback { onUserListLoaded(final Collection<User> usersCollection); onError(); }}
ConcreteUseCase
![Page 36: Android cleanarchitecture](https://reader034.fdocument.pub/reader034/viewer/2022042723/58f9adba760da3da068b9b57/html5/thumbnails/36.jpg)
Presenterpublic class ShowUserListPresenter extends Presenter implements FollowerListUseCaseCallback { private FollowerListUseCase mFollowerListUseCase; public void getFollowerList(String user){ // (1) usecaseを実行 -> 別スレッドへ mFollowerListUseCase.execute(user, this); }
// (2) FollowerListUseCaseCallback経由で処理結果を受け取る @Override onUserListLoaded(final Collection<User> usersCollection){ // Viewへ結果を渡す } @Override onError(){ // error handling }}
![Page 37: Android cleanarchitecture](https://reader034.fdocument.pub/reader034/viewer/2022042723/58f9adba760da3da068b9b57/html5/thumbnails/37.jpg)
MVC Architecture on Android における課題
MVP Architecture
Domain Layer
Modelのデータ・ソースが多様
各層の依存関係が強い
![Page 38: Android cleanarchitecture](https://reader034.fdocument.pub/reader034/viewer/2022042723/58f9adba760da3da068b9b57/html5/thumbnails/38.jpg)
MVC Architecture on Android における課題
MVP Architecture
Domain Layer
Modelのデータ・ソースが多様
各層の依存関係が強い
![Page 39: Android cleanarchitecture](https://reader034.fdocument.pub/reader034/viewer/2022042723/58f9adba760da3da068b9b57/html5/thumbnails/39.jpg)
Data Layer
![Page 40: Android cleanarchitecture](https://reader034.fdocument.pub/reader034/viewer/2022042723/58f9adba760da3da068b9b57/html5/thumbnails/40.jpg)
Android Frameworks
Data Layer
• データをEntity(メソッドやデータ構造体のかたまり)として扱う
• Domain Layerにデータ元を意識させないためにRepository パターンで実装する
Domain
Android Frameworks
Activity
View
Data
Presenter
Userusessees
![Page 41: Android cleanarchitecture](https://reader034.fdocument.pub/reader034/viewer/2022042723/58f9adba760da3da068b9b57/html5/thumbnails/41.jpg)
Repository Pattern• ドメイン駆動開発で利用されるデザインパターン
• UseCaseで必要なデータソースを集約したRepositoryクラスを実装する
Data
UserRepository DocsRepository XXRepository
![Page 42: Android cleanarchitecture](https://reader034.fdocument.pub/reader034/viewer/2022042723/58f9adba760da3da068b9b57/html5/thumbnails/42.jpg)
public class UserRepository { private GithubApi mApi; // Github API用のインスタンス private UserMemoryCache mUserMemoryCache; //メモリキャッシュ用インスタンス
public UserRepository(){ mUserMemoryCache = UserMemoryCache.getInstance(); mApi = createGithubApi(); }
public void getFollowers(String user,UserListCallback callback) { mApi.listFollowersAsync( ... // Networkから取得 }
public void putUser(User user){ mUserMemoryCache.put(… // メモリにキャッシュ }}
Repository
![Page 43: Android cleanarchitecture](https://reader034.fdocument.pub/reader034/viewer/2022042723/58f9adba760da3da068b9b57/html5/thumbnails/43.jpg)
MVC Architecture on Android における課題
MVP Architecture
Domain Layer
Data Layer(Repository Pattern)
各層の依存関係が強い
![Page 44: Android cleanarchitecture](https://reader034.fdocument.pub/reader034/viewer/2022042723/58f9adba760da3da068b9b57/html5/thumbnails/44.jpg)
MVC Architecture on Android における課題
MVP Architecture
Domain Layer
Data Layer(Repository Pattern)
各層の依存関係が強い
![Page 45: Android cleanarchitecture](https://reader034.fdocument.pub/reader034/viewer/2022042723/58f9adba760da3da068b9b57/html5/thumbnails/45.jpg)
Dependency Inversion Principle
![Page 46: Android cleanarchitecture](https://reader034.fdocument.pub/reader034/viewer/2022042723/58f9adba760da3da068b9b57/html5/thumbnails/46.jpg)
Dependency Inversion Principle
• 依存関係逆転の原則
• 上位モジュールは下位モジュールに依存しない
例) PresenterはDomainの実装がどのようになってても影響を受けない
• interfaceを各層の間に置く
Android Frameworks
Domain
Android Frameworks
Activity
View
Data
Presenter
Userusessees
interface
interface
![Page 47: Android cleanarchitecture](https://reader034.fdocument.pub/reader034/viewer/2022042723/58f9adba760da3da068b9b57/html5/thumbnails/47.jpg)
public interface UserRepository { // Domain層(UseCase)側はこの interfaceの仕様だけ知っていればOK void getFollowers(String userId, UserRepositoryCallback callback); void getUser(String userId, UserRepositoryCallback callback); void putUser(User user);}
Dependency Inversion
//実際の実装は interfaceを継承する public class UserRepositoryImpl implements UserRepository { @Override public void getFollowers(String userId, UserRepositoryCallback callback){ //データ取得 … } @Override public void putUser(User user) { } //…}
![Page 48: Android cleanarchitecture](https://reader034.fdocument.pub/reader034/viewer/2022042723/58f9adba760da3da068b9b57/html5/thumbnails/48.jpg)
MVC Architecture on Android における課題
MVP Architecture
Domain Layer
Data Layer(Repository Pattern)
Dependency Inversion Principle
![Page 49: Android cleanarchitecture](https://reader034.fdocument.pub/reader034/viewer/2022042723/58f9adba760da3da068b9b57/html5/thumbnails/49.jpg)
どう変わった?
![Page 50: Android cleanarchitecture](https://reader034.fdocument.pub/reader034/viewer/2022042723/58f9adba760da3da068b9b57/html5/thumbnails/50.jpg)
こう変わった!
Android Frameworks
Domain
Android Frameworks
Activity
View
Data
Presenter
Userusessees
interface
interface
UIがビジネスロジックから分離して 見通しが良くなった
ビジネスロジックがAndroidのライフサイクルから分離され、テストが容易に
Modelの仕様変更に柔軟に 対応可能になった
モジュールの置き換えが 容易になった
![Page 51: Android cleanarchitecture](https://reader034.fdocument.pub/reader034/viewer/2022042723/58f9adba760da3da068b9b57/html5/thumbnails/51.jpg)
なんか難しい…?
![Page 52: Android cleanarchitecture](https://reader034.fdocument.pub/reader034/viewer/2022042723/58f9adba760da3da068b9b57/html5/thumbnails/52.jpg)
少しずつ手をつけると よいかも
![Page 53: Android cleanarchitecture](https://reader034.fdocument.pub/reader034/viewer/2022042723/58f9adba760da3da068b9b57/html5/thumbnails/53.jpg)
どこから手を付けるべきか
手法 実現難易度
MVP Architecture ☆
Domain Layer ☆☆☆
Data Layer(Repository Pattern) ☆☆
Dependency Inversion Principle ☆☆
![Page 54: Android cleanarchitecture](https://reader034.fdocument.pub/reader034/viewer/2022042723/58f9adba760da3da068b9b57/html5/thumbnails/54.jpg)
Next step…• Dependency Injection
• 簡潔に書ける
• モジュールの載せ替えが容易
![Page 55: Android cleanarchitecture](https://reader034.fdocument.pub/reader034/viewer/2022042723/58f9adba760da3da068b9b57/html5/thumbnails/55.jpg)
Next step…• Implementing Observer Pattern
• Domain Layerの煩雑なスレッド処理, Callback地獄からの開放
参考例: https://github.com/android10/Android-CleanArchitecture
![Page 56: Android cleanarchitecture](https://reader034.fdocument.pub/reader034/viewer/2022042723/58f9adba760da3da068b9b57/html5/thumbnails/56.jpg)
– Clean Architecture, Uncle Bob
Architecture is about Intents, not Frameworks
![Page 57: Android cleanarchitecture](https://reader034.fdocument.pub/reader034/viewer/2022042723/58f9adba760da3da068b9b57/html5/thumbnails/57.jpg)
– Clean Architecture, Uncle Bob
設計とは(良いコードが書きたいっていう)
意志である!
フレームワークなんて関係ねぇ!(意訳)