Androidにおける静的プロキシパターンの詳細解説

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システムにおいて静的プロキシパターンはどのような場面で使われているか?"

システムレベルでの回答

  1. Binder通信: ActivityManagerProxyがシステムサービスの呼び出しを処理
  2. ウィンドウ管理: WindowManagerGlobalがウィンドウ操作を代理
  3. コンテンツプロバイダー: ContentProviderProxyがプロセス間データアクセスを担当
  4. 入力システム: InputMethodManagerProxyが入力メソッドを管理

パフォーマンス最適化に関する質問: Q:"プロキシパターンによるパフォーマンス低下を回避するにはどうすればよいか?"

解決策

  1. プロキシ階層の制限: プロキシのネストを3層以内に抑える
  2. 遅延初期化戦略
public class LazyProxy implements ApiService {
    private ApiService mReal;
    
    private ApiService getReal() {
        if (mReal == null) {
            mReal = Retrofit.create(...);
        }
        return mReal;
    }
}
  1. オブジェクトプールの利用: 高頻度で使用されるプロキシオブジェクトをキャッシュする

アーキテクチャ設計に関する質問: Q:"なぜ動的プロキシではなく静的プロキシを選ぶべきか?"

選択基準

  1. インターフェースの安定性: メソッドが変更されにくい場合
  2. パフォーマンス要求が高い: システムの重要なパス(例:Activity起動など)
  3. 明確な拡張ポイント: コンパイル時にプロキシロジックをチェックしたい場合
  4. シンプルなシナリオ: ログ出力や権限確認などの基本機能のみが必要な場合

コード実装に関する質問: 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;
    }
}

タグ: Android 静的プロキシ 設計パターン aidl Binder

7月12日 17:04 投稿