プロジェクトでAndroid AACライブラリとAndroidデータバインディングライブラリを使用しています。AuthActivityがあり、AuthViewModelがAndroidのViewModelクラスを拡張しています。場合によっては質問する必要があります。アクティビティがViewModelのいくつかのメソッドを呼び出すため。たとえば、ユーザーがアクティビティクラスで初期化されたGoogleAuthまたはFacebookAuthボタンをクリックすると(GoogleApiClientを初期化するには、ViewModelに渡せないActivityコンテキストが必要なため、ViewModelはActivityを保存できません。フィールド)。Activityクラスに実装されたGoogleApiおよびFacebookAPIのすべてのロジック:
//google api initialization
googleApiClient = new GoogleApiClient.Builder(this)
.enableAutoManage(this, this)
.addApi(Auth.GOOGLE_SIGN_IN_API, gso)
.build();
//facebook login button
loginButton.setReadPermissions(Arrays.asList("email", "public_profile"));
loginButton.registerCallback(callbackManager,
また、アクティビティコンテキストも必要とするサインインインテントを呼び出す必要があります。
Intent signInIntent = Auth.GoogleSignInApi.getSignInIntent(googleApiClient);
startActivityForResult(signInIntent, GOOGLE_AUTH);
FacebookログインとGoogleログイン、またはビューモデルクラスからstartActivityインテントをリクエストできないため、クラスインターフェイスAuthActivityListenerを作成しました。
public interface AuthActivityListener {
void requestSignedIn();
void requestGoogleAuth();
void requestFacebookAuth();
void requestShowDialogFragment(int type);
}
アクティビティクラスにリスナーを実装します。
AuthActivityRequester authRequestListener = new AuthActivityRequester() {
@Override
public void requestSignedIn() {
Intent intent = new Intent(AuthActivity.this, ScanActivity.class);
startActivity(intent);
AuthActivity.this.finish();
}
@Override
public void requestGoogleAuth() {
Intent signInIntent = Auth.GoogleSignInApi.getSignInIntent(googleApiClient);
startActivityForResult(signInIntent, GOOGLE_AUTH);
}
...
そして、ビューモデルクラスでこのリスナーを割り当てて、アクティビティメソッドを呼び出します。
// in constructor
this.authRequester = listener;
// call activity method
public void onClickedAuthGoogle() {
authRequester.requestGoogleAuth();
}
グーグルまたはフェイスブックの認証に合格した後、アクティビティからビューモデルメソッドを呼び出します。
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
callbackManager.onActivityResult(requestCode, resultCode, data);
if (requestCode == GOOGLE_AUTH) {
GoogleSignInResult result = Auth.GoogleSignInApi.getSignInResultFromIntent(data);
if (result.isSuccess()) {
GoogleSignInAccount acct = result.getSignInAccount();
if (acct != null) {
viewModel.onGoogleUserLoaded(acct.getEmail(), acct.getId());
} else {
viewModel.onGoogleUserLoaded("", "");
}
}
}
}
ビューモデルとアクティビティの間の通信のこのアプローチは正しいのか、ビューモデルからアクティビティメソッドを呼び出す別の方法を見つける必要があるのか、誰かが私に説明できますか?
mVVMの最も難しい部分はビューモデルはビューについて認識してはならず、それらを参照してはなりません
これは非常に強い制限です。
あなたはそれについていくつかのオプションがあります
1。コンテキスト引数を受け取るモデルメソッドを表示します
ビューからコンテキストを受け取るメソッドを作成できます(このメソッドはビューから呼び出されます)。
コンテキスト関連の変数をインスタンス化できた後。
メモリリークに気付いた場合は、ビューが一時停止しているときに破棄するか、ライフサイクル対応AACの使用を停止し、アクティビティまたはフラグメントの再開または開始時に元に戻します。
OnActivityResultについては、APIサポートがそのようなものであるため、ソリューションは悪くないと思います。
2。データバインディングを使用してビューからコンテキストを取得します
レイアウトxmlでは、イベントリスナーを使用してビュー自体を送信できます。
<Button
....
Android:onClick=“@{(view) -> vm.onClickFacebookLogin(view)}”
次に、ビューを受け取り、Viewmodelのビューからコンテキストを取得できます
。AndroidViewModelを使用
AndroidViewModelクラスは、アプリケーションコンテキストがないViewModelクラスと同じです。
アプリケーションコンテキストは次のように使用できます
gerApplication()
ありがとうございました
これを行う方法にはいくつかの異なるアプローチがあります。ここで私はあなたと私のアプローチを共有したいと思います。私の意見では、これはMVVMパターンのイデオロギーに最も適しています。
前述のように、「ビューモデルはビューについて何も知らず、それを参照する必要があります」。これにより、ビューモデルがActivityメソッドを呼び出す方法について多くのオプションが残されません。まず、頭に浮かぶのはListenerアプローチです。しかし、このアプローチには、私の意見ではいくつかの欠点があります。
View
は、ViewModel
よりも有効期間が短い可能性が高いため、ViewModel
への/からのサブスクライブ/サブスクライブ解除を処理する必要があります。ViewModel
がView
のメソッドを呼び出す必要があるが、View
がサブスクライブ/サブスクライブ解除の間にあるという状況にもつながります。 ViewModel
は、null
である可能性があるため、空のリスナーの状況にも注意する必要があります。ViewModel
、Activity
、およびListener
インターフェイスを変更する必要があります。したがって、Listenerアプローチはあまり適していません。そしてそれはMVPアプローチのように見えます。上記の欠点(または少なくともそれらのいくつか)を排除するために、私が作成した、ViewModel Eventsアプローチを作成しました。このアプローチでは、ViewModel
はイベントを「発行」(または生成)し、View
にそれらを監視させます。私が話していることを示しましょう。
最初に、ViewModel
イベントの表現が必要になります。
_abstract class ViewModelEvent {
var handled: Boolean = false
private set
fun handle(activity: BaseActivity) {
handled = true
}
}
_
すでにご覧のとおり、handle()
メソッドが魔法を実行します。 Activity
が受信したイベントを処理するとき、そのインスタンスをhandle()
メソッドにパラメーターとして渡します。このメソッド内で、任意のActivity
メソッドを呼び出すことができます(または特定のActivity
に安全にキャストできます)。 handled
プロパティは、Activity
がこのViewModelEvent
を2回処理しないようにすることを目的としています。
さらに、ViewModel
がそのイベントを発行するためのメカニズムを作成する必要があります。 LiveData
は、これらのニーズに最も適しています。ライフサイクルイベントのオブザーバーサブスクリプションをキャンセルし、最後に発行されたイベントを保存します(そのため、ViewModelEvent
には上記のhandled
プロパティが必要です)。
_abstract class BaseViewModel: ViewModel() {
private val observableEvents = MutableLiveData<ViewModelEvent>()
fun observeViewModelEvents(): LiveData<ViewModelEvent> = observableEvents
protected fun postViewModelEvent(event: ViewModelEvent) {
observableEvents.postValue(event)
}
}
_
ここでは複雑なことは何もありません。 MutableLiveData
(LiveData
として公開)とイベントを発行するメソッドだけです。ちなみに、postViewModelEvent
内では、このメソッドが呼び出されたスレッドを確認し、_MutableLiveData.postValue
_または_MutableLiveData.setValue
_を使用できます。
そして最後に、Activity
自体。
_abstract class BaseActivity: Activity() {
override fun onCreate(savedInstanceState: Bundle?) {
// ...
viewModel.observeViewModelEvents().observe(this, Observer {
val event = it.takeUnless { it == null || it.handled } ?: return@Observer
handleViewModelAction(event)
})
}
protected open fun handleViewModelAction(event: ViewModelEvent) {
event.handle(this)
}
}
_
ご覧のとおり、一般的なイベントはBaseActivity
で処理できますが、一部の特定のイベントはhandleViewModelAction
メソッドをオーバーライドすることで処理できます。
このアプローチは、特定のニーズに合わせて変更できます。たとえば、ViewModelEvent
はActivity
インスタンスを操作する必要はなく、「マーカー」イベントとして使用したり、必要なアクションなどの特定のパラメーターを渡すことができます。
ViewModel Eventsアプローチにより、ViewModel-Activity通信が堅牢かつシームレスになります。 Activity
は一度サブスクライブする必要があり、最新のViewModel
のイベントを見逃すことはありません。