1. 基本概念とUML図
1. 標準的な静的プロキシ構造
classDiagram
class Subject {
<<interface>>
+request() void
}
class RealSubject {
+request() void
}
class Proxy {
-realSubject: RealSubject
+request() void
+preRequest() void
+postRequest() void
}
Subject <|.. RealSubject
Subject <|.. Proxy
Proxy --> RealSubject
2. Androidにおける代表的な使用例
- プロセス間通信: Binderプロキシ(AIDLによる生成)
- 権限管理: ダイナミックな権限プロキシ
- パフォーマンス監視: メソッド実行時間計測
- サードパーティSDKのラッピング: 実装の隔離
2. Androidのソースコードにおける実例
1. ActivityManagerProxy(システムレベル実装)
// frameworks/base/core/java/android/app/ActivityManagerNative.java
class ActivityManagerProxy implements IActivityManager {
private final IBinder mRemote;
public ActivityManagerProxy(IBinder remote) {
mRemote = remote;
}
public int startActivity(...) throws RemoteException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
try {
// 引数をシリアライズ
data.writeInterfaceToken(IActivityManager.descriptor);
// Binderを通じてプロセス間呼び出し
mRemote.transact(START_ACTIVITY_TRANSACTION, data, reply, 0);
reply.readException();
return reply.readInt();
} finally {
data.recycle();
reply.recycle();
}
}
}
2. AIDLにおけるプロキシパターンの活用
// 自動生成されたProxyクラス(簡略化版)
public static abstract class Stub extends Binder implements IMyAidlInterface {
private static class Proxy implements IMyAidlInterface {
private IBinder mRemote;
public String getData() throws RemoteException {
Parcel _data = Parcel.obtain();
Parcel _reply = Parcel.obtain();
String _result;
try {
mRemote.transact(TRANSACTION_getData, _data, _reply, 0);
_result = _reply.readString();
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
}
}
3. エンタープライズレベルでの応用
1. ネットワークリクエストのプロキシラッピング
interface ApiService {
@GET("user/info")
suspend fun getUserInfo(): User
}
class ApiProxy(private val realService: ApiService) : ApiService {
private val monitor = PerformanceMonitor()
override suspend fun getUserInfo(): User {
return try {
monitor.recordStart()
val user = realService.getUserInfo()
monitor.recordSuccess()
user
} catch (e: Exception) {
monitor.recordError(e)
throw NetworkException("Get user failed", e)
}
}
}
// 利用方法
val retrofit = Retrofit.Builder().build()
val proxy = ApiProxy(retrofit.create(ApiService::class.java))
2. 権限制御プロキシ
public class PermissionProxy {
private final Activity mTarget;
private final PermissionChecker mChecker;
public PermissionProxy(Activity target) {
this.mTarget = target;
this.mChecker = new PermissionChecker(target);
}
public void requestCamera() {
if (mChecker.hasCameraPermission()) {
mTarget.requestCamera();
} else {
mTarget.showPermissionDialog();
}
}
}
// 元のActivity
public class CameraActivity extends Activity {
private PermissionProxy mProxy;
@Override
protected void onCreate(Bundle savedInstanceState) {
mProxy = new PermissionProxy(this);
}
// プロキシを通じて呼び出し
void onOpenCameraClick() {
mProxy.requestCamera();
}
}
4. パフォーマンス最適化と比較
1. 静的プロキシ vs 動的プロキシ
| 特徴 | 静的プロキシ | 動的プロキシ (Proxy/InvocationHandler) |
|---|---|---|
| 実装の複雑さ | 高い(手動作成が必要) | 低い(自動生成) |
| 柔軟性 | 低い(変更には再コンパイルが必要) | 高い(実行時変更可能) |
| パフォーマンス | 良い(直接呼び出し) | 若干劣る(リフレクションオーバーヘッド) |
| 適用場面 | インターフェースメソッドが少なく安定している場合 | インターフェースメソッドが多く変更がある場合 |
2. メモリ最適化の提案
// プロキシチェーンが長すぎないようにする
public class ProxyFactory {
public static <T> T create(Class<T> clazz) {
RealSubject real = new RealSubject();
// 最大2層のプロキシのみ
return (T) new TimeProxy(new LogProxy(real));
}
}
5. 深層的な問題の解釈
設計パターンに関する質問: Q:"Androidシステムにおいて静的プロキシパターンはどのような場面で使われているか?"
システムレベルでの回答:
- Binder通信:
ActivityManagerProxyがシステムサービスの呼び出しを処理 - ウィンドウ管理:
WindowManagerGlobalがウィンドウ操作を代理 - コンテンツプロバイダー:
ContentProviderProxyがプロセス間データアクセスを担当 - 入力システム:
InputMethodManagerProxyが入力メソッドを管理
パフォーマンス最適化に関する質問: Q:"プロキシパターンによるパフォーマンス低下を回避するにはどうすればよいか?"
解決策:
- プロキシ階層の制限: プロキシのネストを3層以内に抑える
- 遅延初期化戦略:
public class LazyProxy implements ApiService {
private ApiService mReal;
private ApiService getReal() {
if (mReal == null) {
mReal = Retrofit.create(...);
}
return mReal;
}
}
- オブジェクトプールの利用: 高頻度で使用されるプロキシオブジェクトをキャッシュする
アーキテクチャ設計に関する質問: Q:"なぜ動的プロキシではなく静的プロキシを選ぶべきか?"
選択基準:
- インターフェースの安定性: メソッドが変更されにくい場合
- パフォーマンス要求が高い: システムの重要なパス(例:Activity起動など)
- 明確な拡張ポイント: コンパイル時にプロキシロジックをチェックしたい場合
- シンプルなシナリオ: ログ出力や権限確認などの基本機能のみが必要な場合
コード実装に関する質問: Q:"Retrofitの静的プロキシを用いてキャッシュ機能を実装する方法を示せ"
実装例:
public class CacheProxy implements ApiService {
private final ApiService mReal;
private final Cache mCache;
public CacheProxy(ApiService real, Cache cache) {
this.mReal = real;
this.mCache = cache;
}
@Override
public User getUser(String id) {
User cached = mCache.get(id);
if (cached != null) {
return cached;
}
User fresh = mReal.getUser(id);
mCache.put(id, fresh);
return fresh;
}
}