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(); ... } ... }
      
      





Notepadv3



Notepadv3



変更しNotepadv3



RoboListActivity



RoboListActivity



の注入を追加し、この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サービスオブジェクトもインジェクションできることに注意してください。 Application



Activity



、さまざまなサービス。 完全なリストはこちらから入手できます



線量を増やす



RoboGuiceの機能を示すために、 NotesDbAdapter



クラスをリファクタリングします。 私の意見では、それは非常に過負荷であり、 DatabaseHelper



NotesDbAdapter



自体の分解が必要です。 プロジェクトが成長するにつれて、 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.jar



roboguice-1.1.1.jar



のサイズは合わせて約545 Kbであり、単純なアプリケーションではかなり多くなりますが、大きくて複雑なアプリケーションではかなり許容できます。

-生産性の低下

また、論争点。 間違いなく、注射には時間がかかります。 ただし、これに費やされる時間は短く、主にアプリケーションの起動時です。 ループやアニメーションでインジェクションを使用しない場合、ユーザーはパフォーマンスの変化に気付かないでしょう。 さらに、スマートフォンとDalvikVMのパフォーマンスは常に成長しています。



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



All Articles