AndroidでDIxAOP

  • 1,778 views
Uploaded on

 

More in: Technology
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Be the first to comment
No Downloads

Views

Total Views
1,778
On Slideshare
0
From Embeds
0
Number of Embeds
0

Actions

Shares
Downloads
12
Comments
0
Likes
3

Embeds 0

No embeds

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
    No notes for slide

Transcript

  • 1. Android で DIxAOP   Android で DIxAOP1 Android で DIxAOP
  • 2. Android で DIxAOP  自己紹介 ・名前    robo (兼高理恵) ・お仕事   Java 技術者         設計から実装まで ・好きなもの  モバイル端末2 Android で DIxAOP
  • 3. Android を取り巻く環境の変遷 国内携帯電話に占めるスマートフォン率は、 過半数を超えたそうですが、 国内初のAndroid端末販売(2009年7月)から2年が過ぎ、 Android アプリも、 黎明期・普及期から、充実期に至っていると思います。参考先2009年夏 docomo PRO/他 series HT-03A (発売日:2009.07.10)http://www.doplaza.jp/products/docomo/ht03a.html携帯電話販売に占めるスマートフォン比率、初めて5割超に (2011/04/08 記事)http://itpro.nikkeibp.co.jp/article/Research/20110408/359254/3 Android で DIxAOP
  • 4. Android アプリ開発もまた… 限られた開発情報から、 少数の先進開発者が 独自に解析や開発を行う時代を経て、 開発情報の蓄積と洗練による、 多数の一般開発者が 参加する時代になりました。参考先個人ディベロッパーの時代は終わったのか? iPhoneアプリ業界の現状を追えhttp://itlifehack.jp/archives/5704588.html4 Android で DIxAOP
  • 5. Android アプリ開発もまた…アプリ供給が増えたことによる競争力をつけるため、アプリには、高度なデザインや性能を豊富に含むことを迫られ、Android アプリ開発は、組織的な開発と大規模化の時代を迎えています。 小さくてもOKな例外は、「特定目的のみに特化」したアプリでしょうか。参考先個人ディベロッパーの時代は終わったのか? iPhoneアプリ業界の現状を追えhttp://itlifehack.jp/archives/5704588.html5 Android で DIxAOP
  • 6. 高機能化・大規模化の波 画面周りでは、 デザイン重視のカスタムViewや、アニメーション表現 機能面では、 各種センサー情報や、Webサービスとの連携強化 実装方式では、 実装への内部規約や設計思想の統一反映要求…6 Android で DIxAOP
  • 7. 高機能化・大規模化の波 追い討ちをかける、 Android 特有のフラグメンテーション対応 画面サイズや解像度、CPU速度・搭載センサー… という表面的な違いだけでなく、 端末機種によっては、API挙動の違いまであり、 これに対応するには、 端末ごとの対応(別実装)が必要になります。参考先Android 機種互換性問題対応顛末記http://typea.info/blg/glob/2011/01/android-4.htmlAndroidのカメラアプリは難しいhttp://www.toyship.org/archives/767 Android で DIxAOP
  • 8. DIxAOPフレームワークの登場 Android アプリの高機能化・大規模化に伴い、 開発形態は、多人数のチームが多分野のグループ 下で行うようになりました。 クラス数やソースコード、 画面数や画面挙動パターンが増大するにつれ、 テストやソース・コード管理のコスト(メンテナンスや実装統一)も、 益々増大しています。8 Android で DIxAOP
  • 9. DIxAOPフレームワークの登場 Java界隈では、 複雑化したJavaEE開発に対応するため、 Android に先んじること数年前(2004年頃)より、 Spring(Spring2)、Seaser(Seaser2)、Guiceなどの DIコンテナやDIxAOPフレームワークが 使われています。参考先Spring Frameworkで理解するDI The seaser project (Seaser 本家サイト)http://www.atmarkit.co.jp/fjava/index/index_springdi.html http://www.seasar.org/このバランス感覚、さすが - GoogleのDIフレームワーク"Guice"を使ってみるhttp://journal.mycom.co.jp/articles/2007/03/14/googleguice/menu.html9 Android で DIxAOP
  • 10. DIxAOPについて (DIとは)DI:Dependency Injection依存性注入共通に扱いたいコンポーネントに対し、依存関係を分離して、プログラム実行時に、外部から依存関係を決定(依存性注入)させるプログラム技巧を表します。 (インターフェースと実装の分離) ※説明を短くするため、一般的な用語解説を意訳しています。参考先DI(依存性の注入)×AOP(アスペクト指向)の常識http://www.atmarkit.co.jp/fjava/rensai4/enterprise_jboss03/01.htmlJava フレームワーク開発入門 著者:木村聡 Chapter 4 DIxAOPを学ぶ(書籍)ISBN97804-7973-5340-210 Android で DIxAOP
  • 11. DIxAOPについて (DIとは)DI活用例端末ごとに対応実装が異なってしまうメソッドがある場合、当該メソッド用のインターフェースを定義(依存関係分離)して、端末ごとのインターフェース実装クラスを作成しておき、実行時に端末ごとのインスタンスを間接的に与えてもらうことで、コンポーネント利用処理が共通でありながら、端末ごとの対応ができるようになります。参考先DI(依存性の注入)×AOP(アスペクト指向)の常識http://www.atmarkit.co.jp/fjava/rensai4/enterprise_jboss03/01.htmlJava フレームワーク開発入門 著者:木村聡 Chapter 4 DIxAOPを学ぶ(書籍)ISBN97804-7973-5340-211 Android で DIxAOP
  • 12. DIxAOPについて (DIとは)DI実装例イメージ interface Camera { 端末名により、 public void capture();} Xperia用かIS03用の class XperiaCamera implements Camera { いずれかのインスタンスを public void capture(){//Xperia用の実装};} 返します。 class IS03Camera implements Camera { public void capture(){//IS03用の実装};} /*利用側*/Camera camera = Factory.getCamera(端末名); camera.capture(); (※)挙動を固定化させてしまう、特定クラスを new する実装記述を避けます。【補足】 依存性注入形式の違いコンポーネントへのインスタンス注入をフレームワークが自動的に行う場合は、DIパターンになり、利用者側が自発的に行う場合は、Factory パターンになります。12 Android で DIxAOP
  • 13. DIxAOPについて (AOPとは)AOP:Aspect-Oriented Proguramingアスペクト(局面)指向プログラミング関心事物中心視点での開発を実現するため、複数のコンポーネント間で共通に行いたい処理(関心事物)をひとまとまりの処理で扱えるようにするプログラム技巧です。 ※説明を短くするため、一般的な用語解説を意訳しています。参考先DI(依存性の注入)×AOP(アスペクト指向)の常識http://www.atmarkit.co.jp/fjava/rensai4/enterprise_jboss03/01.htmlJava フレームワーク開発入門 著者:木村聡 Chapter 4 DIxAOPを学ぶ(書籍)ISBN97804-7973-5340-213 Android で DIxAOP
  • 14. DIxAOPについて (AOPとは)AOPの特色テンプレート・パターンを用いて、基盤クラスを継承させることで共通処理を行わせる場合、共通処理を組み込むメソッドのシグニチャが固定されますがAOPでは、複数の異なるメソッド・シグニチャであっても、Proxy技法を利用して対応できる点が大きく異なります。参考先DI(依存性の注入)×AOP(アスペクト指向)の常識http://www.atmarkit.co.jp/fjava/rensai4/enterprise_jboss03/01.htmlJava フレームワーク開発入門 著者:木村聡 Chapter 4 DIxAOPを学ぶ(書籍)ISBN97804-7973-5340-214 Android で DIxAOP
  • 15. DIxAOPについて (AOPとは)AOPの特色drawDisplay(View view)、animation(int patternNo)など、シグニチャの異なるメソッドに対して、実行時の引数と結果の内容や実行時間のデバッグ出力を後から追加したい場合でも、一括して共通処理が追加できるのです。 各メソッド実装へのデバッグ出力の追加は、不要です。参考先DI(依存性の注入)×AOP(アスペクト指向)の常識http://www.atmarkit.co.jp/fjava/rensai4/enterprise_jboss03/01.htmlJava フレームワーク開発入門 著者:木村聡 Chapter 4 DIxAOPを学ぶ(書籍)ISBN97804-7973-5340-215 Android で DIxAOP
  • 16. Android 対応のDIコンテナroboguice RoboGuice は、 Android にシンプルで 簡易な依存性注入(DI)を提供する、 Google Guice Library を用いた (2011/09/09での最新版) DI フレームワークです。 roboguice-1.1.2.jar 102.8 KB (105289 バイト)参考先roboguice 本家サイト Android MockとRoboGuiceでTDDhttp://code.google.com/p/roboguice/ http://d.hatena.ne.jp/thorikawa/20101127/p1Android、DI、roboguiceなど Unit Testing in Dependency Injectionhttp://d.hatena.ne.jp/akitoh/20101203/1291340779 http://d.hatena.ne.jp/esmasui/20100904/128361791416 Android で DIxAOP
  • 17. Android 対応のDIコンテナRoboguice利用例:Roboguice 利用で、典型的なボイラープレート・コードが不要になります。テストのMock注入にも有効だそうですが、時間の関係上ここまでとします。 //roboguice 適用前 //roboguice 適用後 class AndroidWay extends Activity { class RoboWay extends GuiceActivity { TextView name; @InjectView(R.id.name) TextView name; ImageView thumbnail; @InjectView(R.id.thumbnail) ImageView thumbnail; LocationManager loc; @InjectResource(R.drawable.icon) Drawable icon; Drawable icon; @InjectResource(R.string.app_name) String myName; String myName; @Inject LocationManager loc; public void onCreate(Bundle savedInstanceState) { public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); super.onCreate(savedInstanceState); name = (TextView) findViewById(R.id.name); name.setText( "Hello, " + myName ); 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 ); } } (※)onCreateメソッドの中では、典型的な決まりきった記述 (※)各コンポーネントに外部から依存性注入(DI)してもらい (ボイラープレートコード)が行なわれています。 定型的なリソース読込処理の記述が不要になりました。17 Android で DIxAOP
  • 18. Android における DIxAOP フレームワーク導入の問題 本格的な DIxAOP フレームワークへの取り組みに 積極的になれない理由設計当初から、採用するDIxAOPフレームワーク対応を考慮する必要がある各実装者には、採用するDIxAOPフレームワーク規約を学習済な必要がある携帯端末のリソース制限から、導入(オーバーヘッド)を避けたい心理がある ですが、本格的に取り組みたくない(取り組むリソースがない)場合でも、 デバッグ時の統一的なデバッグログ出力など、DIxAOP機能を一時的・ 仮設的・限定的でも利用したいことはあるので、 簡単なDIxAOP機能は、内製対応できることが必要でしょう。18 Android で DIxAOP
  • 19. DIxAOP機能の内製実験 簡単なDIxAOP機能の内製対応の実験をしてみます。  依存を分離するために、AOP対象のメソッドは、  インターフェースの定義と実装が必要になっていますが、  funcFactory メソッドで、依存性の注入(DI)を行い、  MyProxyクラスで、共通処理の処理先一括化(AOP)を  行なうことで、  実装内容を変えずに外部からデバッグ出力を追加できる  ようにして、  DIxAOP機能のエッセンスのみを実現してみました。  (※)ソース全体は、資料巻末を御参照ください。 19 Android で DIxAOP
  • 20. DIxAOP機能の内製実験 内製実験として、階乗関数や平均関数の実装を変えずに、 外部からのデバッグ出力を追加してみます。 DIxAOP利用実装部(抜粋) //階乗関数実装 public int factorial(int in){ 階乗関数と平均関数の実装に、 public void aopTest(){ int arg = in; デバッグ出力の処理は、 通常とAOPの違いは、 int result = 1; //依存性注入(DI)オブジェクト 依存性注入 funcFactoryで ありません。 Func func = null; for(;in > 0; in--){ Proxy有無を指定するだけ result *= in; } //(階乗関数/平均関数)通常の実行 System.out.println(message+" factorial("+arg+") is "+result); func = funcFactory("TEST1 normal", false); return result; func.factorial(5); } func.average(new int[]{1, 2, 3, 4, 5}); //平均関数実装 public float average(int[] ins){ System.out.println(); StringBuilder args = new StringBuilder(); String sep = ""; //AOP的、(階乗関数/平均関数)関心事物追加プロキシ実行 int counts = ins.length; func = funcFactory("TEST2 AOP", true); int result = 0; func.factorial(5); for(int in: ins){ func.average(new int[]{1, 2, 3, 4, 5}); args.append(sep).append(in); sep=", "; } result += in; AOP対応のオブジェクトを取得するには、 } //依存性注入ファクトリ MyProxy に元オブジェクトを渡すのみ。 result /= counts; public Func funcFactory(String arg0, boolean System.out.println(message+ isProxy){ " average(["+args.toString()+"]) is "+result); Object instance = new FuncImpl(arg0); return result; if(isProxy) return } (Func)MyProxy.getProxyObject(instance); (※)通常実行部もプロキシ実行部も、ソース記述的には変化がありません。 return (Func)instance; 階乗関数や平均関数の実装中には、デバッグ出力処理がありません。 }20 Android で DIxAOP
  • 21. DIxAOP機能の内製実験 テスト実行結果では、階乗関数や平均関数の実装を変えずに、 外部からのデバッグ出力追加ができています。 実行結果: TEST1 normal factorial(5) is 120 TEST1 normal average([1, 2, 3, 4, 5]) is 3 DEBUG OUT method start name=factorial, arg[0]=5 TEST2 AOP factorial(5) is 120 DEBUG OUT method end name=factorial, return=120, execute time =780361nsec DEBUG OUT method start name=average, arg[0]=[I@863399 TEST2 AOP average([1, 2, 3, 4, 5]) is 3 DEBUG OUT method end name=average, return=3.0, execute time =470793nsec AOP的メソッド実行では、前後に デバッグ出力が追加されました。 MyProxy クラスによるAOP機能は、Android アプリ中でも動作することを確認しています。21 Android で DIxAOP
  • 22. DIxAOPでも設計が大切です  最後になりましたが、  DIxAOP を利用すれば、  共通の実装を変えずに個別の内容(挙動)を変更したり(DI)、  個別の実装を変えずに共通の処理を一括追加する(AOP)  ことができます。 これにより、テストやソースコードの記述と管理のコストが 低減することになりますが、これは、設計がおろそかでも 後から対応できることでは、ありません。 共通部と独立部を明確にするために、 設計が重要なのは変わりません。22 Android で DIxAOP
  • 23. Android で DIxAOP   ご清聴、ありがとうございました。23 Android で DIxAOP
  • 24. DIxAOP機能の内製実験ソース import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.lang.reflect.InvocationHandler; public class DIxAOPxTest { public static void main(String[] args){ DIxAOPxTest test = new DIxAOPxTest(); test.aopTest(); } public void aopTest(){ //(階乗関数/平均関数)通常の実行 func = funcFactory("TEST1 normal", false); func.factorial(5); func.average(new int[]{1, 2, 3, 4, 5}); System.out.println(); //AOP的、(階乗関数/平均関数)関心事物追加プロキシ実行 func = funcFactory("TEST2 AOP", true); func.factorial(5); func.average(new int[]{1, 2, 3, 4, 5}); } //依存性注入ファクトリ public Func funcFactory(String arg0, boolean isProxy){ Object instance = new FuncImpl(arg0); if(isProxy) return (Func)MyProxy.getProxyObject(instance); return (Func)instance; } }24 Android で DIxAOP
  • 25. DIxAOP機能の内製実験ソース //階乗/平均関数インターフェース interface Func { public int factorial(int in); public float average(int[] ins); } //階乗/平均関数実装 class FuncImpl implements Func { private String message = ""; public FuncImpl(String message){ this.message = message; } public int factorial(int in){ int arg = in; int result = 1; for(;in > 0; in--){ result *= in; } System.out.println(message+" factorial("+arg+") is "+result); return result; } public float average(int[] ins){ StringBuilder args = new StringBuilder(); String sep = ""; int counts = ins.length; int result = 0; for(int in: ins){ args.append(sep).append(in); sep=", "; result += in; } result /= counts; System.out.println(message+" average(["+args.toString()+"]) is "+result); return result; } }25 Android で DIxAOP
  • 26. DIxAOP機能の内製実験ソース //汎用プロキシ・オブジェクト取得クラス class MyProxy implements InvocationHandler { private Object originalObject = null; private MyProxy(Object originalObject){ this.originalObject = originalObject; } public static Object getProxyObject(Object obj){ //プロキシ対象のメソッド・インターフェースを取得します。 Class<?>[] interfaces = obj.getClass().getInterfaces(); //プロキシ対象でない場合は、元オブジェクトを返します。 if(interfaces == null || interfaces.length == 0) return obj; //プロキシ・オブジェクトを返します。 MyProxy instance = new MyProxy(obj); Object proxy = Proxy.newProxyInstance(obj.getClass().getClassLoader(), interfaces, instance); return proxy; } @Override public Object invoke(Object interfaceProxy, Method method, Object[] args) throws Throwable { long startNanoTime = System.nanoTime(); StringBuilder sb = new StringBuilder(); for(int index = 0; args != null && index < args.length; index++){ sb.append(", ").append("arg[").append(index).append("]=").append(args[index].toString()); } //メソッド実行前の任意処理 System.out.println("DEBUG OUT method start name="+method.getName()+sb.toString()); //メソッド実行 Object ret = method.invoke(originalObject, args); //メソッド実行後の任意処理 System.out.println("DEBUG OUT method end name="+method.getName()+", return=" +((ret==null)?"void or null":ret.toString()) +", execute time ="+(System.nanoTime()-startNanoTime)+"nsec"); return ret; } }26 Android で DIxAOP