Contenu connexe Similaire à AndroidでDIxAOP (20) AndroidでDIxAOP3. 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.html
4 Android で DIxAOP
6. 高機能化・大規模化の波
画面周りでは、
デザイン重視のカスタムViewや、アニメーション表現
機能面では、
各種センサー情報や、Webサービスとの連携強化
実装方式では、
実装への内部規約や設計思想の統一反映要求…
6 Android で DIxAOP
7. 高機能化・大規模化の波
追い討ちをかける、
Android 特有のフラグメンテーション対応
画面サイズや解像度、CPU速度・搭載センサー…
という表面的な違いだけでなく、
端末機種によっては、API挙動の違いまであり、
これに対応するには、
端末ごとの対応(別実装)が必要になります。
参考先
Android 機種互換性問題対応顛末記
http://typea.info/blg/glob/2011/01/android-4.html
Androidのカメラアプリは難しい
http://www.toyship.org/archives/76
7 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.html
9 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
15. DIxAOPについて (AOPとは)
AOPの特色
drawDisplay(View view)、animation(int patternNo)など、
シグニチャの異なるメソッドに対して、実行時の引数と結果
の内容や実行時間のデバッグ出力を後から追加したい場合
でも、一括して共通処理が追加できるのです。
各メソッド実装へのデバッグ出力の追加は、不要です。
参考先
DI(依存性の注入)×AOP(アスペクト指向)の常識
http://www.atmarkit.co.jp/fjava/rensai4/enterprise_jboss03/01.html
Java フレームワーク開発入門 著者:木村聡 Chapter 4 DIxAOPを学ぶ
(書籍)ISBN97804-7973-5340-2
15 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でTDD
http://code.google.com/p/roboguice/ http://d.hatena.ne.jp/thorikawa/20101127/p1
Android、DI、roboguiceなど Unit Testing in Dependency Injection
http://d.hatena.ne.jp/akitoh/20101203/1291340779 http://d.hatena.ne.jp/esmasui/20100904/1283617914
16 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
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