私はオンラインコースでAndroid=プロジェクトを行っています。そのプロジェクトでDIを使用したいので、dagger2を使い始めましたが、今、典型的な初心者の問題が発生し始めています。戻ってきた。
プロジェクトの期限が近づいているので、このプロジェクトではdagger2を放棄することにしましたが、それでも何らかのDIを使用したいです。
だから私はこの記事に来ました:
純粋な依存性注入または貧しい人の依存性注入
しかし、私はこれをAndroidに実装する方法を正確に理解していませんでした。コンストラクターまたは依存クラス内でオブジェクトのインスタンスを作成しないようにすることもできますが、どこかでインスタンス化する必要があります。
Androidでのこの「純粋なDI」のアイデアまたは実装戦略を探しています。可能であれば、適切に実装された例。
あなたはおそらくこれまでにコースを終了しましたが、まだ検索している場合、または他の誰かがそうである場合:独自のDIをローリングすることは、実際にAndroidでそれを行う方法を知っていれば非常に簡単です。
私は通常、最初にカスタムアプリケーションクラスから始めます(Androidマニフェストに登録することを忘れないでください)。このクラスは、Android =アプリであり、コードがその依存関係にアクセスできる場所です。次のようなもの:
public class CustomApp extends Application {
private static ObjectGraph objectGraph;
@Override
public void onCreate() {
super.onCreate();
objectGraph = new ObjectGraph(this);
}
// This is where your code accesses its dependencies
public static <T> T get(Class<T> s) {
Affirm.notNull(objectGraph);
return objectGraph.get(s);
}
// This is how you inject mock dependencies when running tests
public <T> void injectMockObject(Class<T> clazz, T object) {
objectGraph.putMock(clazz, object);
}
}
(Affirm.notNull()は、何かがnullの場合に爆発するだけで、使用する必要はありません)。実際の依存関係はすべて、基本的に次のようなObjectGraphクラスにあります。
class ObjectGraph {
private final Map<Class<?>, Object> dependencies = new HashMap<>();
public ObjectGraph(Application application) {
// Step 1. create dependency graph
AndroidLogger logger = new AndroidLogger();
Wallet wallet = new Wallet(logger);
//... this list can get very long
// Step 2. add models to a dependencies map if you will need them later
dependencies.put(Wallet.class, wallet);
}
<T> T get(Class<T> model) {
Affirm.notNull(model);
T t = model.cast(dependencies.get(model));
Affirm.notNull(t);
return t;
}
<T> void putMock(Class<T> clazz, T object) {
Affirm.notNull(clazz);
Affirm.notNull(object);
dependencies.put(clazz, object);
}
}
これで、アプリ内のどこにいても(たとえば、アクティビティで)、次のように依存関係を注入できます。
private Wallet wallet;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
wallet = CustomApp.get(Wallet.class);
//...
}
Daggerはいくつかの方法で使用できますが、最も近いDagger2の同等物は次のようになります。
AppComponent appComponent = CustomApp.getAppComponent();
Wallet wallet = appComponent.getWallet();
ここで注入するのは、applicationレベルのスコープを持つクラスです。 ローカルスコープオブジェクトが必要な場合は、ビューまたはアクティビティでそれへの参照を保持している間のみ存在し、まったく同じことを行いますあなたが注入するものはfactoryクラスです:
ObjectGraph内:
WalletFactory walletFactory = new WalletFactory(logger);
例えばあなたのフラグメントで:
Wallet wallet = CustomApp.get(WalletFactory.class).getNewWallet();
これはすべて、ビューレイヤーコードを簡単にテストできるようにするために行われます。たとえば、エスプレッソテストを実行する場合は、アプリケーションを作成しますが、アクティビティを表示する前に、ウォレットインスタンスをモックに置き換えます。
CustomApp.injectMockObject(Wallet.class, mockedWalletWith100Dollars);
これは、テスト中に残りのコードによって取得されるウォレットインスタンスです。
ある意味では、このDIのスタイルはDagger2ほど柔軟ではありませんが、私はそれがはるかに明確であると思います。DIを複雑にする必要がないだけで、実際には非常に基本的なものです。このスタイルは、多くの場合、DIフレームワークを使用するよりもlessボイラープレートになります(コンポーネントおよびモジュールクラスを含めた後)。
私が公開したフレームワークの一部として、5つのサンプルアプリ用に同様のものを書きました(サンプルをできるだけ広くアクセスできるようにしたい-誰もがDaggerを好むわけではない)。ここで完全な例を見ることができます: https://github.com/erdo/asaf-project/blob/master/example01databinding/src/main/Java/foo/bar/example/asafdatabinding/ObjectGraph.Java
単純なJavaのクラスAndroidのクラスの場合、純粋なDIは、コンストラクタパラメータを使用して通常どおり実装されます。たとえば、次のようになります。
interface UserService extends Parcelable {
User getUser(String name);
}
class DbUserService implements UserService {
private final DbHelper db;
public RestUserService(DbHelper db) {
this.db = db;
}
User getUser(String name) {
// use db to get user by name
}
}
Android=で純粋なDIを行うことの複雑さは、フレームワークの統合に付属しています。アクティビティがAndroidで構築されている場合に、Activity
sでDIを行うには簡単ではありませんが、コンストラクタパラメータに最も近い相関はBundle
sであり、これを Intent
sに付加できます。例を使用して、うまくいくでしょう:
たとえば、UserActivity
からユーザーをフェッチしてビューに表示するアクティビティUserService
があるとします。 UserService
をUserActivity
に注入して、よりテストしやすくします(DIの他のすべての利点と共に)。
class UserActivity extends Activity {
private Optional<UserService> userService = Optional.empty();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// get UserService from bundle
userService = Optional.of(getIntent().getExtras().getParcelable<UserService>("userService"));
setContentView(R.layout.activity_user);
}
@Override
protected void onResume() {
super.onResume();
userService.map(us -> us.getUser("Bill"));
}
}
Intent
はParcelable
を実装しているため、次のように合成ルートでオブジェクトグラフを作成できます。
UserService userService = new DbUserService(...);
Intent userActivityIntent = new Intent(this, UserActivity.class);
userActivity.putExtra("userService", userService);
Intent mainIntent = new Intent(this, MainActivity.class);
mainIntent.putExtra("userActivityIntent", userActivityIntent);
startActivity(mainIntent);
このアプローチの制限は、引数を渡すことがParcelable
に大きく依存することです。すべての依存関係はマーシャリング可能である必要があります。多くのAndroidのクラスはParcelable
を実装していない(してはいけない)ので、それらを渡す必要がないようにアプリケーションを設計する必要があります。