Reampライブラリ:Androidアプリケーションの鎮痛剤

EastBanc Technologiesでは、Androidの開発で発生するアーキテクチャ上の問題に苦労することにうんざりし、それを修正することにしました:)。 私たちは、すべての要件を満たすソリューションを探していました。







そして、よくあることですが、既成のソリューションはありませんでした。独自のライブラリを作成する必要がありました。







どのような問題を解決しましたか:















叙情的な余談。 なぜリアンプするのですか?

それはエレキギターを録音するためのそのような迷いのようなものですか?

もちろん、私たちの場合、Reampは録音とは何の関係もありません。 最初は、MとP(モデルとプレゼンター)、A-理由は覚えていませんが、RE-試薬に書かれているため、略語になると考えました。 しかし、私たちはすでに試薬を捨てていて、クールな名前が残っていました。







実装プロセスでは、マニフェストを追跡しようとしました。









その結果、MVP / MVVMライブラリができました。これは1年以上使用されており、まだ変更する予定はありません。 今こそ、それを一般大衆と共有する時だと信じています!







なんで?



ほとんどすべてのモバイルアプリケーションの最も一般的な問題、つまり承認に対する解決策を見てみましょう。







ログインとパスワードの入力フィールド、ログインボタン、操作の進行状況を表示するProgressBar、および結果を表示するTextViewがあります。













このような画面の動作の要件は非常に典型的です。









そのような問題を解決するとき、開発者が考えるべきことを分析しましょう。







検証



何がそんなに複雑なの? changeListener



では、 loginEditText



ハングアップしchangeListener



。これは、 login



空または空でないときにボタンをオンまたはオフにしlogin









 loginEditText.addTextChangeListener = { text -> button.setEnabled(text.length() > 0) }
      
      





はい。ただし、これは1つのフィールドでのみ機能します。 そして、まだパスワードがあります:







 loginEditText.addTextChangeListener = { text -> validate() } passwordEditText.addTextChangeListener = { text -> validate() } private void validate() { boolean loginValid = loginEditText.getText().toString().lenght() > 0 boolean passwordValid = passwordEditText.getText().toString().lenght() > 0 button.setEnabled(loginValid && passwordValid) }
      
      





さて、これですべてです! 非同期ログイン操作もありますが、その間はボタンをロックする必要があります。







OK、リクエストを実行する前にボタンをオフにするだけで... loginEditText



またはpasswordEditText



テキストを変更することでオンにできpasswordEditText









validate()



メソッド内にアクティブなリクエストが存在するかどうかのチェックを追加する方が正しいでしょう。

おそらく、あなたはすでにこのポイントの目的を推測しているでしょう。 UIに影響を与える可能性のあるものとその関係を覚えておく必要があります。







別の入力フィールドまたはスイッチを追加して検証する必要がある場合、それらについて簡単に忘れてしまいます。







ここに新しい工夫があります



入るには、 AsyncTask



であろうとRxJava



+ Scheduler



であろうとAsyncTask



なく、非同期操作が必要です。







重要なことは、画面が回転したときに停止したくないため、 Activity



内に書き込むことができないことです。







後でこのタスクのステータスを確認したり、結果を取得したりできるように、タスクを開始するときにActivity



のスコープから取り出し、何らかの種類の識別子を見つけて覚えておく必要があります。







そして、それらの多くがあるので、そのような操作のために何らかの種類のマネージャーを書くか、既製のものから取る必要があります。







状態



画面の状態は、常に対処しなければならないものです。







逆説的に、それは事実です-多くの開発者は、アプリケーションの画面の状態を無視し続けており、彼のプログラムが一方向のみで動作することを正当化しています。







EditText



は、入力されたテキストを個別に保存できますが、ログインボタンの状態は、入力されたテキストと現在のネットワーク操作に従って復元する必要があります。







Activity



で保存および復元する必要のあるデータが多ければ多いほど、それらを追跡することが難しくなり、何かを見逃しやすくなります。







Reampが提供するソリューションは何ですか?



Reampでは、 Presenter



を使用して画面の動作を実装し、StateModelを使用してこの画面に必要なデータを保存します。







すべてが非常に簡単です。 Presenter



、画面のライフサイクルPresenter



実質的に無関係です。

必要ないくつかの操作を実行して、 Presenter



StateModel



オブジェクトStateModel



さまざまな必要なデータを入力します。







Presenter



は、最新のデータを画面に表示する必要があると考えるたびに、これを自分のView



に報告しView









コードを見せてください!



実際には、これは次のように機能します。







LoginState



画面に表示される内容に関する情報を含むクラス:

ログインボタンの状態、入力テキストフィールドに書かれている内容などをProgressBarに表示する必要があるかどうか







LoginPresenter



は、 LoginActivity



からイベントを受け取ります(テキストを入力し、ボタンを押しました)

必要な操作を実行し、 LoginState



クラスLoginState



必要なデータを入力し、「レンダリング」のために「 LoginActivity



」に送信します。







LoginActivity



は、 LoginState



のデータLoginState



変更されたイベントを受け取り、それらに従ってレイアウトをカスタマイズします。







 //LoginState public class LoginState extends SerializableStateModel { public String login; public String password; public boolean showProgress; public Boolean loggedIn; public boolean isSuccessLogin() { return loggedIn != null && loggedIn; } } //LoginPresenter public class LoginPresenter extends MvpPresenter<LoginState> { @Override public void onPresenterCreated() { super.onPresenterCreated(); //     getStateModel().setLogin(""); getStateModel().setPassword(""); getStateModel().setLoggedIn(null); getStateModel().setShowProgress(false); sendStateModel(); // LoginState  "" } //   View,     public void login() { getStateModel().setShowProgress(true); //      getStateModel().setLoggedIn(null); //     sendStateModel(); //      "" //      new Handler() .postDelayed(new Runnable() { @Override public void run() { getStateModel().setLoggedIn(true); //     getStateModel().setShowProgress(false); //    sendStateModel(); //      "" } }, 5000); } public void loginChanged(String login) { getStateModel().setLogin(login); //  ,    } public void passwordChanged(String password) { getStateModel().setPassword(password); //  ,    } } //LoginActivity public class LoginActivity extends MvpAppCompatActivity<LoginPresenter, LoginState> { /***/ @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_login); /***/ loginActionView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { getPresenter().login(); //     } }); //   ,    loginInput.addTextChangedListener(new SimpleTextWatcher() { @Override public void afterTextChanged(Editable s) { getPresenter().loginChanged(s.toString()); //     } }); //   ,    passwordInput.addTextChangedListener(new SimpleTextWatcher() { @Override public void afterTextChanged(Editable s) { getPresenter().passwordChanged(s.toString()); //     } }); } //  ,       LoginState @Override public LoginState onCreateStateModel() { return new LoginState(); } //  ,       LoginPresenter @Override public MvpPresenter<LoginState> onCreatePresenter() { return new LoginPresenter(); } //    ,     @Override public void onStateChanged(LoginState stateModel) { progressView.setVisibility(stateModel.showProgress ? View.VISIBLE : View.GONE); //      loginActionView.setEnabled(!stateModel.showProgress); //   ,    successView.setVisibility(stateModel.isSuccessLogin() ? View.VISIBLE : View.GONE); //    ""  } }
      
      





一見、重要な動的データをLoginStateに取り出し、コードの一部(ログインリクエストなど)をActivityからPresenterに転送するだけでした。 一見したところ、これは確かにそうです:)私たちにとって退屈な作業はすべてReampによって行われているためです:









Reampがボイラープレートコードを削除するのにどのように役立つかをリストしましたが、これはライブラリを使用することの全体的な利益とはほど遠いものです。 Reampを使用すると、アプリケーションの安定性が向上します。Reampは、 onStateChanged(...)



メソッドの呼び出しが常にメインスレッドで発生するようにします。







onStateChanged(...)



呼び出し内で発生するすべての例外は、アプリケーションプロセスをドロップしません。 Javaでの例外の正しい作業は高度なスキルですが、最高のUIレベル(レイアウトのセットアップ時)で発生する例外は、意図的なイベントよりも厄介な誤解であることが多く、ここでのプログラムクラッシュは絶対に不要です。







Reampを使用すると、 Activity



リークを恐れることはできません。 常にプレゼンタークラスと状態クラスを直接操作します。







最後になりましたが、Reampを使用すると、コードの品質が向上します。







コードはよりテストしやすくなっています。 実際、 Instrumentation



テストも必要ありません。なぜなら、 プレゼンターをテストし、各操作の後にLoginState



に正しいデータセットがあることを確認してください







状態クラスは、UIロジックを保存するための優れた候補です。 LoginState



がログインの進行状況、入力されたログインとパスワードを知っている場合、ログインボタンを有効にするかどうかを決定するためのすべてのソースデータを既に持っています。







 public class LoginState extends SerializableStateModel { /***/ public boolean isLoginActionEnabled() { return !showProgress && (loggedIn == null || !loggedIn) && !TextUtils.isEmpty(login) && !TextUtils.isEmpty(password); } }
      
      





このアプローチは、責任の分離の原則とよく一致しており、 LoginActicity



クラスコードを大幅にオフロードします。







コードが再利用可能になります。 LoginPresenter



は、この画面のUIコンポーネントを変更するだけで同様の画面を実装する必要がある他のプロジェクトでLoginPresenter



使用できます。







同様のソリューションとの比較



もちろん、ReampだけがMVP / MVVMライブラリではありません。







Reampを始めたとき、私たちは意識的に必要なものを書きたいと思っていました。

そして、もちろん、私たちは最善を尽くし、私たちが好きではなかったものを避けるために、その時に利用可能な代替案を研究しました:)







ホリバーを配置する気はありませんが、誰かに指を突っ込むことはあまりありません。Reampの好きな点と避けたいことを単純にまとめています。







まず、Reampは非常に使いやすいです。 コード生成は使用せず、ライブラリ自体にのみ必要な最小限の新しいクラスを導入しようとします。







たとえば、新しいAndroidアーキテクチャコンポーネントとは異なり、同じ問題を解決するために補助的な技術クラスやアノテーションの動物園全体を必要としません。







2番目のポイントは、最初のポイントの結果の一部です。 アンロードされたアーキテクチャと最小限の依存関係により、多くの一般的な最新技術と簡単に統合できます。







たとえば、DataBindingでは、 StateModel



既にDataBindingが機能する必要があるデータの真髄であるためです。







バイトコードに魔法がない別の例では、Kotlinで問題なくReampプログラミングを使用します。







第三に、既存のプロジェクトをグローバルに変更する必要はありません。既存のプロジェクトでReampの使用を開始できます。







ある記事では、必要なものすべてについて話すことは困難ですが、 デモアプリケーション使用して、最も簡単なソリューションから最も複雑なソリューションまで、Reampのすべての機能を段階的に説明します。









参照資料



GitHubのリアンプ-https ://github.com/eastbanctechru/Reamp







デモアプリ-https ://github.com/eastbanctechru/Reamp/tree/master/sample







プロジェクトでReampを試したい場合、または詳細情報が必要な場合は、

プロジェクトのWiki 、特にFAQセクションをご覧ください








All Articles