RoboGuiceまたは「Androidが注射に夢中になった」

画像 RoboGuiceは、Androidでアプリケーションを開発する際にコンテキストと依存関係の注入を最大限に活用できるライブラリです。
ご想像のとおり、RoboGuiceはGoogle Guiceに基づいています
私は、「注入」という言葉の翻訳として、「注入」という言葉を使用するとすぐに言わなければなりません。

なぜ注入するのですか?


多くの読者がすぐに質問をするだろうと思います。「モバイルプラットフォームでのCDIの問題はなぜですか? 確かにすべてが多くのスペースを占有し、ゆっくりと動作します。」
そのような読者に、モバイルプラットフォームでのCDIは非常に実行可能であり、開発者の生活を楽にするという反対を納得させようとします。

ロボギス
叙情的な余談

熱意とXMLの普及の時代の後、誰もがXMLファイルの「フットバッグ」にうんざりしたとき、冷静になりました。 構成コードの量を減らし、開発をスピードアップする新しいアプローチの検索が始まりました。 そのような出力の1つは、注釈ベースのCDIでした。 2つ目は、構成またはコーディングの慣習によるコーディングを 超えるコンベンションと呼ばれます 。 開発者はそれが好きで、アイデアが開発され、いくつかのCDI実装が登場し、多くの大規模なライブラリが注釈を通じてCDIを使用し始めました。
その結果、CDIは、Java EE 6に正式に含まれている( JSR-299 )と同様に、 JSR-330の形でJava用に標準化されました。
個人的に、私は最初にSeamライブラリでCDIを見ました。 そして彼はアプローチのシンプルさと優雅さに喜んで驚いた。 開発時間の節約、構成の簡素化、総量の削減、それに応じて発生する可能性のあるエラーの削減。
時間が経つにつれて、注釈はほぼすべての場所に浸透しました。 XMLマニアの後、注釈の時代が来ました。 そして、彼らがAndroidに到達しなければ、非常に奇妙になります。

では、なぜモバイルプラットフォーム上にあるのでしょうか?

答えは簡単です。開発をより速く簡単にすること、そしてコードはよりクリーンで信頼性が高いことです。
根拠がないようにするために、 Roboguice Get Startedの例を見てみましょう。
通常のActivityクラスは次のようになります。
class AndroidWay extends Activity { TextView name; ImageView thumbnail; LocationManager loc; Drawable icon; String myName; public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); name = (TextView) findViewById(R.id.name); thumbnail = (ImageView) findViewById(R.id.thumbnail); loc = (LocationManager) getSystemService(Activity.LOCATION_SERVICE); icon = getResources().getDrawable(R.drawable.icon); myName = getString(R.string.app_name); name.setText( "Hello, " + myName ); } } 

コードは19行です。 onCreate()メソッドの5行のコードはクラスフィールドの通常の初期化であり、最後の1行のみがセマンティックロードを実行します:name.setText()。 Activityクラスのより複雑な実装には、オブジェクトを初期化するためのさらに多くのコードが含まれる場合があります。
次に、RoboGuiceが許可するものと比較します。
 class RoboWay extends RoboActivity { @InjectView(R.id.name) TextView name; @InjectView(R.id.thumbnail) ImageView thumbnail; @InjectResource(R.drawable.icon) Drawable icon; @InjectResource(R.string.app_name) String myName; @Inject LocationManager loc; public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); name.setText( "Hello, " + myName ); } } 

onCreate()はるかに簡潔でonCreate()やすくなりました。 最小テンプレートコード、アプリケーションロジックに関する最大コード。

最初の注入


「最初のインジェクション」をより実行可能にするために、 developer.android.comからインジェクションに関するよく知られた例を注入します: Notepad Tutorial 。 それは棚で完全に分類され、そのソースコードが利用可能であり、CDIのアイデアを修正して実装するのに十分です。
プロジェクトでRoboGuiceの使用を開始するには、クラスパスにRoboGuiceguice-2.0-no_aop.jar (guice-3.0ではない)の2つのライブラリを追加する必要があります。
Mavenに依存関係として追加することもできます(はい、AndoirdプロジェクトとMavenはまったく互換性がありますが、これは別の記事のトピックです)。
 <dependency> <groupId>org.roboguice</groupId> <artifactId>roboguice</artifactId> <version>1.1</version> </dependency> 

次に、 RoboApplicationを継承するクラスを作成する必要があります。
 public class NotepadApplication extends RoboApplication { } 

AndroidManifest.xml登録します:
 <application android:name="com.android.demo.notepad3.NotepadApplication"> 


ここで、小さなNoteEditクラスを書き直します。 NotesDbAdapterから継承したリソースインジェクションとNotesDbAdapterを追加しRoboActivity
それは:
 public class NoteEdit extends RoboActivity { private NotesDbAdapter mDbHelper; private EditText mTitleText; private EditText mBodyText; private Long mRowId; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.note_edit); setTitle(R.string.edit_note); mDbHelper = new NotesDbAdapter(this); mDbHelper.open(); mTitleText = (EditText) findViewById(R.id.title); mBodyText = (EditText) findViewById(R.id.body); Button confirmButton = (Button) findViewById(R.id.confirm); ... } ... } 

次のようになりました:
 public class NoteEdit extends RoboActivity { @Inject NotesDbAdapter mDbHelper; @InjectView(R.id.title) EditText mTitleText; @InjectView(R.id.title) EditText mBodyText; @InjectView(R.id.confirm) Button confirmButton; @InjectResource(R.string.edit_note) String title; private Long mRowId; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.note_edit); setTitle(title); mDbHelper.open(); ... } ... } 

Notepadv3Notepadv3変更しNotepadv3RoboListActivityRoboListActivityの注入を追加し、このNotesDbAdapterの初期化をonCreate()から削除します。
 public class Notepadv3 extends RoboListActivity { ... @Inject private NotesDbAdapter mDbHelper; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.notes_list); mDbHelper.open(); fillData(); registerForContextMenu(getListView()); } ... } 

また、 NotesDbAdapterコンストラクターを更新して、 Context RoboGuiceに自動的に置き換えNotesDbAdapterします。
  @Inject public NotesDbAdapter(Context ctx) { this.mCtx = ctx; } 

実行、確認-動作します!
コードが少なくなり、機能が維持され、読みやすさが向上しました。
RoboGuiceはContextクラスだけでなく、他の多くのAndroidサービスオブジェクトもインジェクションできることに注意してください。 ApplicationActivity 、さまざまなサービス。 完全なリストはこちらから入手できます

線量を増やす


RoboGuiceの機能を示すために、 NotesDbAdapterクラスをリファクタリングします。 私の意見では、それは非常に過負荷であり、 DatabaseHelperNotesDbAdapter自体の分解が必要です。 プロジェクトが成長するにつれて、 DatabaseHelperは間違いなくいくつかのクラスで使用されます。 たとえば、 TasksDbAdapterが表示されます。 特に私は個人的にmDbHelper.open();への強制呼び出しが好きではないのでmDbHelper.open(); 。 このメソッドは、2つの場所で呼び出す必要があります。 この呼び出しを行い、間違いを犯すことを忘れる大きな誘惑。 それでは始めましょう。
NotesDbAdapterから始めましょう。 追加のフィールドは次のとおりです。

実際、 SQLiteDatabase mDbのみを残したいとSQLiteDatabase mDbます。 残りを削除します。 NotesDbAdapterシングルトンにします。
合計では、次の目標があります。
 @Singleton public class NotesDbAdapter { public static final String KEY_TITLE = "title"; public static final String KEY_BODY = "body"; public static final String KEY_ROWID = "_id"; private static final String TAG = "NotesDbAdapter"; private static final String DATABASE_TABLE = "notes"; @Inject private SQLiteDatabase mDb; public NotesDbAdapter() { } ... } 

KEY_TITLE, KEY_BODY, KEY_ROWID, DATABASE_TABLE, TAG残っているため、 これらはNotesDbAdapter最も関連していNotesDbAdapter
新しいクラスにはopen()メソッドはありません。 このメソッドのロジックをDatabaseHelper転送しましょう。
ここでSQLiteDatabase mDbを取得する必要があります。 明らかに、 DatabaseHelperはこれを行う必要があります。 彼にこれを行う方法を教えるために、 com.google.inject.Providerインターフェースを実装します。これは、 get()メソッドを使用して作成された特定のオブジェクトを返します。
さらに、アプリケーションコンテキストをコンストラクタに注入します。
その結果、 DatabaseHelperクラスは次のようになります。
 @Singleton public class DatabaseHelper extends SQLiteOpenHelper implements Provider<SQLiteDatabase>{ private static final String TAG = "DatabaseHelper"; private static final String DATABASE_CREATE = "create table notes (_id integer primary key autoincrement, " + "title text not null, body text not null);"; private static final String DATABASE_NAME = "data"; private static final int DATABASE_VERSION = 2; private SQLiteDatabase mDb; @Inject public DatabaseHelper(Context context) { super(context, DATABASE_NAME, null, DATABASE_VERSION); } public SQLiteDatabase get() { if (mDb == null || !mDb.isOpen()) { mDb = getWritableDatabase(); } return mDb; } @Override public void onCreate(SQLiteDatabase db) { db.execSQL(DATABASE_CREATE); } @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { ... } } 

次に、RoboGuiceに、 SQLiteDatabaseクラスSQLiteDatabaseプロバイダークラスDatabaseHelperによって提供されることを指摘させてSQLiteDatabase 。 これを行うには、いわゆる構成クラスを作成します モジュール、それをシステムに登録します( NotepadApplication
 public class NotepadModule extends AbstractAndroidModule { @Override protected void configure() { bind(SQLiteDatabase.class).toProvider(DatabaseHelper.class); } } 

 public class NotepadApplication extends RoboApplication { @Override protected void addApplicationModules(List<Module> modules) { modules.add(new NotepadModule()); } } 

実行してみてください-動作します!

NotepadModule.configure()メソッドを拡張して、他のタイプをバインドするNotepadModule.configure()ができます。たとえば、特定のインターフェース実装を置き換えます。
  protected void configure() { bind(SQLiteDatabase.class).toProvider(DatabaseHelper.class); bind(FilterDao.class).to(FilterDaoHttp.class); bind(ItemDao.class).annotatedWith(Http.class).to(ItemDaoHttp.class); } 

つまり、 Google Guiceのあらゆる種類のバインディングが利用可能です

いくつかの単語とテストを追加する価値があります。 RoboGuiceには、単体テストを容易にし、スピードアップするためのいくつかのクラスが含まれています。 RoboUnitTestCaseは、Androidエコシステムに依存しないクラスのテストを提供します。 RoboActivityUnitTestCase使用RoboActivityUnitTestCaseと、アクティビティの単体テストを実行できRoboActivityUnitTestCase 。 将来、Androidでのテストに関する別の記事を作成し、さまざまなアプローチとライブラリを比較したいと思います。 Androidでのテストは、開発自体ほど分解されていません。

最終診断


したがって、注射の利点について最終診断を行います。
+オブジェクトの初期化とライフサイクルの管理を行うコードの削減
+その他のビジネスロジックコード
+オブジェクトの接続性を減らす
+依存関係の構成とオブジェクトの初期化のための最小スペース
+使いやすさとシステムオブジェクトとリソースの置換(コンテキスト、アプリケーション、SharedPreferences、リソースなど)
+結果として、開発を加速し、エラーを減らします

-ライブラリを追加すると、アプリケーションのサイズが大きくなります。
物議を醸すマイナス。 guice-2.0-no_aop.jarroboguice-1.1.1.jarのサイズは合わせて約545 Kbであり、単純なアプリケーションではかなり多くなりますが、大きくて複雑なアプリケーションではかなり許容できます。
-生産性の低下
また、論争点。 間違いなく、注射には時間がかかります。 ただし、これに費やされる時間は短く、主にアプリケーションの起動時です。 ループやアニメーションでインジェクションを使用しない場合、ユーザーはパフォーマンスの変化に気付かないでしょう。 さらに、スマートフォンとDalvikVMのパフォーマンスは常に成長しています。

私の個人的な診断:RoboGuiceは彼らのプロジェクトで使用されるべきです。 プロは間違いなく短所を上回ります。

Source: https://habr.com/ru/post/J116945/


All Articles