Android 代理模式浅析

12 阅读8分钟

一、基本概念

定义

代理模式为其他对象提供一种代理以控制对这个对象的访问。代理对象在客户端和目标对象之间起到中介作用,可以用于控制访问、延迟加载、增强功能等。

核心思想

  1. 间接访问:客户端不直接访问目标对象,而是通过代理访问
  2. 控制访问:代理可以控制对目标对象的访问
  3. 功能增强:在不修改目标对象的基础上,通过代理增加额外功能

三种代理类型

  • 远程代理:为不同地址空间的对象提供本地代表
  • 虚拟代理:延迟创建开销大的对象
  • 保护代理:控制对原始对象的访问权限

二、基本结构

三个核心角色

// 1. Subject (抽象主题接口)
interface Image {
    fun display()
    fun load(): String
    fun getFileName(): String
}

// 2. RealSubject (真实主题)
class RealImage(private val fileName: String) : Image {
    
    init {
        loadFromDisk()
    }
    
    private fun loadFromDisk() {
        println("从磁盘加载大图片: $fileName")
        Thread.sleep(2000) // 模拟耗时操作
    }
    
    override fun display() {
        println("显示图片: $fileName")
    }
    
    override fun load(): String {
        return "图片内容: $fileName"
    }
    
    override fun getFileName(): String = fileName
}

// 3. Proxy (代理)
class ProxyImage(private val fileName: String) : Image {
    private var realImage: RealImage? = null
    
    override fun display() {
        // 延迟加载:只有真正需要时才创建真实对象
        if (realImage == null) {
            realImage = RealImage(fileName)
        }
        realImage?.display()
    }
    
    override fun load(): String {
        // 缓存:避免重复加载
        if (realImage == null) {
            realImage = RealImage(fileName)
        }
        return realImage?.load() ?: ""
    }
    
    override fun getFileName(): String = fileName
}

// 客户端使用
fun main() {
    val image: Image = ProxyImage("高清大图.jpg")
    
    println("1. 第一次访问图片")
    image.display() // 这里才会真正加载图片
    
    println("\n2. 第二次访问同一张图片")
    image.display() // 直接从内存缓存访问
    
    println("\n3. 访问图片信息(无需加载)")
    println("文件名: ${image.getFileName()}")
}

三、代理模式类型详解

1. 虚拟代理 (Virtual Proxy)

延迟创建开销大的对象

// Android中列表项的图片延迟加载
class LazyImageView(context: Context) : ImageView(context) {
    private var imageUrl: String? = null
    private var placeholder: Drawable? = null
    private var errorImage: Drawable? = null
    private var isLoading = false
    
    fun loadImage(url: String, placeholderResId: Int = R.drawable.placeholder) {
        imageUrl = url
        placeholder = ContextCompat.getDrawable(context, placeholderResId)
        
        // 先显示占位图
        setImageDrawable(placeholder)
        
        // 延迟加载(比如当图片进入屏幕可见区域时)
        postDelayed({
            if (!isLoading) {
                startLoading()
            }
        }, 100)
    }
    
    private fun startLoading() {
        isLoading = true
        
        // 使用Glide等库异步加载
        Glide.with(context)
            .load(imageUrl)
            .placeholder(placeholder)
            .error(errorImage)
            .listener(object : RequestListener<Drawable> {
                override fun onLoadFailed(
                    e: GlideException?,
                    model: Any?,
                    target: Target<Drawable>?,
                    isFirstResource: Boolean
                ): Boolean {
                    isLoading = false
                    return false
                }
                
                override fun onResourceReady(
                    resource: Drawable?,
                    model: Any?,
                    target: Target<Drawable>?,
                    dataSource: DataSource?,
                    isFirstResource: Boolean
                ): Boolean {
                    isLoading = false
                    return false
                }
            })
            .into(this)
    }
}

// 使用示例
val imageView = LazyImageView(context)
imageView.loadImage("https://example.com/large-image.jpg")

2. 保护代理 (Protection Proxy)

控制访问权限

// 文件系统访问权限控制
interface FileSystem {
    fun readFile(fileName: String): String
    fun writeFile(fileName: String, content: String)
    fun deleteFile(fileName: String): Boolean
}

class RealFileSystem : FileSystem {
    override fun readFile(fileName: String): String {
        val file = File(fileName)
        return file.readText()
    }
    
    override fun writeFile(fileName: String, content: String) {
        File(fileName).writeText(content)
    }
    
    override fun deleteFile(fileName: String): Boolean {
        return File(fileName).delete()
    }
}

class ProtectedFileSystemProxy(
    private val realFileSystem: RealFileSystem,
    private val userRole: UserRole
) : FileSystem {
    
    override fun readFile(fileName: String): String {
        // 所有人都可以读
        return realFileSystem.readFile(fileName)
    }
    
    override fun writeFile(fileName: String, content: String) {
        if (userRole == UserRole.ADMIN || userRole == UserRole.EDITOR) {
            realFileSystem.writeFile(fileName, content)
        } else {
            throw SecurityException("用户 ${userRole.name} 没有写权限")
        }
    }
    
    override fun deleteFile(fileName: String): Boolean {
        if (userRole == UserRole.ADMIN) {
            return realFileSystem.deleteFile(fileName)
        } else {
            throw SecurityException("只有管理员可以删除文件")
        }
    }
    
    enum class UserRole { GUEST, USER, EDITOR, ADMIN }
}

// 使用
fun main() {
    val realFS = RealFileSystem()
    val userProxy = ProtectedFileSystemProxy(realFS, UserRole.USER)
    val adminProxy = ProtectedFileSystemProxy(realFS, UserRole.ADMIN)
    
    // 用户只能读,不能写
    println(userProxy.readFile("test.txt")) // 正常
    // userProxy.writeFile("test.txt", "new") // 抛出SecurityException
    
    // 管理员可以读写删
    adminProxy.writeFile("test.txt", "new content") // 正常
    adminProxy.deleteFile("test.txt") // 正常
}

3. 远程代理 (Remote Proxy)

访问远程对象

// 模拟网络API的远程代理
interface UserApi {
    fun getUser(id: String): User
    fun updateUser(user: User): Boolean
    fun deleteUser(id: String): Boolean
}

data class User(val id: String, val name: String, val email: String)

class RemoteUserApiProxy(private val baseUrl: String) : UserApi {
    private val retrofit: Retrofit by lazy {
        Retrofit.Builder()
            .baseUrl(baseUrl)
            .addConverterFactory(GsonConverterFactory.create())
            .build()
    }
    
    private val service: UserApiService by lazy {
        retrofit.create(UserApiService::class.java)
    }
    
    override fun getUser(id: String): User {
        return try {
            val response = service.getUser(id).execute()
            if (response.isSuccessful) {
                response.body()?.toDomain() ?: throw RuntimeException("用户不存在")
            } else {
                throw RuntimeException("网络请求失败: ${response.code()}")
            }
        } catch (e: Exception) {
            // 可以在这里添加重试逻辑、缓存等
            throw e
        }
    }
    
    override fun updateUser(user: User): Boolean {
        return try {
            val response = service.updateUser(user.toDto()).execute()
            response.isSuccessful
        } catch (e: Exception) {
            false
        }
    }
    
    override fun deleteUser(id: String): Boolean {
        return try {
            val response = service.deleteUser(id).execute()
            response.isSuccessful
        } catch (e: Exception) {
            false
        }
    }
    
    // DTO转换
    private fun User.toDto() = UserDto(id, name, email)
    private fun UserDto.toDomain() = User(id, name, email)
    
    // Retrofit接口
    interface UserApiService {
        @GET("/users/{id}")
        fun getUser(@Path("id") id: String): Call<UserDto>
        
        @PUT("/users/{id}")
        fun updateUser(@Body user: UserDto): Call<Void>
        
        @DELETE("/users/{id}")
        fun deleteUser(@Path("id") id: String): Call<Void>
    }
    
    data class UserDto(val id: String, val name: String, val email: String)
}

// 使用
val api: UserApi = RemoteUserApiProxy("https://api.example.com")
val user = api.getUser("123")

4. 智能引用代理 (Smart Reference Proxy)

添加额外操作

// 数据库连接池 - 智能引用代理
interface DatabaseConnection {
    fun executeQuery(sql: String): ResultSet
    fun executeUpdate(sql: String): Int
    fun close()
}

class RealDatabaseConnection(private val connection: Connection) : DatabaseConnection {
    override fun executeQuery(sql: String): ResultSet {
        return connection.createStatement().executeQuery(sql)
    }
    
    override fun executeUpdate(sql: String): Int {
        return connection.createStatement().executeUpdate(sql)
    }
    
    override fun close() {
        connection.close()
    }
}

class PooledDatabaseConnectionProxy(
    private val realConnection: RealDatabaseConnection,
    private val connectionPool: ConnectionPool
) : DatabaseConnection {
    
    private var isClosed = false
    
    override fun executeQuery(sql: String): ResultSet {
        checkNotClosed()
        logQuery(sql)
        return realConnection.executeQuery(sql)
    }
    
    override fun executeUpdate(sql: String): Int {
        checkNotClosed()
        logQuery(sql)
        return realConnection.executeUpdate(sql)
    }
    
    override fun close() {
        if (!isClosed) {
            isClosed = true
            // 不是真正关闭,而是归还到连接池
            connectionPool.returnConnection(this)
        }
    }
    
    fun realClose() {
        realConnection.close()
    }
    
    private fun checkNotClosed() {
        if (isClosed) {
            throw IllegalStateException("连接已关闭")
        }
    }
    
    private fun logQuery(sql: String) {
        println("执行SQL: $sql")
        // 可以添加性能监控、SQL注入检查等
    }
}

class ConnectionPool {
    private val pool = mutableListOf<PooledDatabaseConnectionProxy>()
    private val maxSize = 10
    
    fun getConnection(): DatabaseConnection {
        return if (pool.isNotEmpty()) {
            pool.removeAt(0).also { it.isClosed = false }
        } else {
            // 创建新连接
            val realConn = createRealConnection()
            PooledDatabaseConnectionProxy(realConn, this)
        }
    }
    
    fun returnConnection(conn: PooledDatabaseConnectionProxy) {
        if (pool.size < maxSize) {
            pool.add(conn)
        } else {
            conn.realClose() // 真正关闭连接
        }
    }
    
    private fun createRealConnection(): RealDatabaseConnection {
        // 创建真实数据库连接
        return RealDatabaseConnection(DriverManager.getConnection("jdbc:mysql://localhost/test"))
    }
}

四、Android中的代理模式应用

1. Retrofit的动态代理

// Retrofit内部使用Java动态代理
class SimpleRetrofit {
    fun <T> create(service: Class<T>): T {
        return Proxy.newProxyInstance(
            service.classLoader,
            arrayOf(service),
            InvocationHandler { proxy, method, args ->
                // 解析注解
                val annotation = method.getAnnotation(GET::class.java)
                    ?: throw IllegalArgumentException("方法必须有HTTP注解")
                
                val url = annotation.value
                println("将调用方法: ${method.name}, URL: $url")
                
                // 模拟网络请求
                "模拟响应数据"
            }
        ) as T
    }
}

// 使用
interface GitHubApi {
    @GET("/users/{user}/repos")
    fun listRepos(@Path("user") user: String): String
}

fun main() {
    val retrofit = SimpleRetrofit()
    val api = retrofit.create(GitHubApi::class.java)
    val result = api.listRepos("octocat")
    println(result)
}

2. View的点击事件代理

// 点击事件防抖代理
interface OnClickListener {
    fun onClick(view: View)
}

class DebounceClickListenerProxy(
    private val realListener: OnClickListener,
    private val debounceTime: Long = 500L
) : OnClickListener {
    
    private var lastClickTime: Long = 0
    
    override fun onClick(view: View) {
        val currentTime = System.currentTimeMillis()
        if (currentTime - lastClickTime >= debounceTime) {
            lastClickTime = currentTime
            realListener.onClick(view)
        } else {
            Log.d("Debounce", "点击过快,已忽略")
        }
    }
}

// 使用
button.setOnClickListener(object : View.OnClickListener {
    private val proxy = DebounceClickListenerProxy(
        realListener = object : OnClickListener {
            override fun onClick(view: View) {
                // 真正的点击逻辑
                Toast.makeText(view.context, "按钮被点击", Toast.LENGTH_SHORT).show()
            }
        }
    )
    
    override fun onClick(view: View) {
        proxy.onClick(view)
    }
})

五、Kotlin中的代理特性

1. 类委托 (Class Delegation)

// 使用by关键字实现代理
interface Printer {
    fun print(message: String)
    fun printLine(message: String)
}

class RealPrinter : Printer {
    override fun print(message: String) {
        println("打印: $message")
    }
    
    override fun printLine(message: String) {
        println("打印行: $message")
    }
}

// 代理类,将大部分方法委托给realPrinter
class LoggingPrinter(private val realPrinter: RealPrinter) : Printer by realPrinter {
    
    override fun print(message: String) {
        println("[LOG] 开始打印: $message")
        realPrinter.print(message)
        println("[LOG] 打印完成")
    }
    
    // printLine方法直接使用realPrinter的实现
}

// 使用
val printer: Printer = LoggingPrinter(RealPrinter())
printer.print("Hello")  
printer.printLine("World")  

2. 属性委托 (Property Delegation)

// 自定义属性代理
class ObservableProperty<T>(
    private var value: T,
    private val onChange: (oldValue: T, newValue: T) -> Unit
) {
    operator fun getValue(thisRef: Any?, property: KProperty<*>): T {
        println("获取属性 ${property.name} = $value")
        return value
    }
    
    operator fun setValue(thisRef: Any?, property: KProperty<*>, newValue: T) {
        val oldValue = value
        value = newValue
        println("设置属性 ${property.name}: $oldValue -> $newValue")
        onChange(oldValue, newValue)
    }
}

class User {
    var name: String by ObservableProperty("默认") { old, new ->
        println("名字从 '$old' 改为 '$new'")
    }
    
    var age: Int by ObservableProperty(0) { old, new ->
        require(new >= 0) { "年龄不能为负数" }
        println("年龄从 $old 改为 $new")
    }
}

// 使用
val user = User()
user.name = "张三"  // 输出: 设置属性 name: 默认 -> 张三
println(user.name)  // 输出: 获取属性 name = 张三

3. 标准库中的属性代理

class Configuration {
    // 延迟初始化
    val heavyObject by lazy {
        println("初始化heavyObject")
        HeavyObject()
    }
    
    // 可观察属性
    var username by Delegates.observable("默认用户") { _, old, new ->
        println("用户名从 '$old' 改为 '$new'")
    }
    
    // 带否决权的可观察属性
    var score by Delegates.vetoable(0) { _, old, new ->
        println("尝试修改分数: $old -> $new")
        new >= 0  // 只有新值>=0时才允许修改
    }
    
    // 非空属性,延迟初始化
    var token: String by Delegates.notNull()
}

// 使用
val config = Configuration()
println(config.heavyObject)  // 第一次访问时初始化
config.username = "新用户"
config.score = -10  // 不会被设置
println(config.score)  // 仍然是0

六、动态代理实现

1. Java动态代理

// 接口
interface UserService {
    fun addUser(name: String): Boolean
    fun deleteUser(id: Int): Boolean
    fun getUser(id: Int): String
}

// 真实实现
class UserServiceImpl : UserService {
    override fun addUser(name: String): Boolean {
        println("添加用户: $name")
        return true
    }
    
    override fun deleteUser(id: Int): Boolean {
        println("删除用户: $id")
        return true
    }
    
    override fun getUser(id: Int): String {
        return "用户$id"
    }
}

// 动态代理处理器
class LoggingInvocationHandler(private val target: Any) : InvocationHandler {
    
    override fun invoke(proxy: Any, method: Method, args: Array<out Any>?): Any? {
        println("开始执行方法: ${method.name}")
        println("参数: ${args?.joinToString()}")
        
        val startTime = System.currentTimeMillis()
        val result = if (args != null) {
            method.invoke(target, *args)
        } else {
            method.invoke(target)
        }
        val endTime = System.currentTimeMillis()
        
        println("方法执行完成,耗时: ${endTime - startTime}ms")
        println("返回值: $result")
        
        return result
    }
}

// 使用
fun main() {
    val realService = UserServiceImpl()
    val handler = LoggingInvocationHandler(realService)
    
    val proxyService = Proxy.newProxyInstance(
        UserService::class.java.classLoader,
        arrayOf(UserService::class.java),
        handler
    ) as UserService
    
    proxyService.addUser("张三")
    proxyService.getUser(1)
}

2. 带缓存的动态代理

class CachingInvocationHandler(private val target: Any) : InvocationHandler {
    private val cache = mutableMapOf<String, Any>()
    
    override fun invoke(proxy: Any, method: Method, args: Array<out Any>?): Any? {
        // 生成缓存key
        val key = buildCacheKey(method, args)
        
        // 只缓存get开头的方法
        if (method.name.startsWith("get") && cache.containsKey(key)) {
            println("从缓存获取: $key")
            return cache[key]
        }
        
        println("执行方法: ${method.name}")
        val result = if (args != null) {
            method.invoke(target, *args)
        } else {
            method.invoke(target)
        }
        
        // 缓存结果
        if (method.name.startsWith("get") && result != null) {
            cache[key] = result
            println("缓存结果: $key")
        }
        
        return result
    }
    
    private fun buildCacheKey(method: Method, args: Array<out Any>?): String {
        return method.name + (args?.joinToString(",") ?: "")
    }
}

七、代理模式的优缺点

✅ 优点

  1. 职责清晰:真实主题只关注业务逻辑,代理处理附加功能
  2. 高扩展性:可以在不修改真实主题的情况下,通过代理增加功能
  3. 保护目标对象:代理可以控制对真实对象的访问
  4. 降低耦合度:客户端与真实对象解耦
  5. 开闭原则:可以方便地增加新的代理,无需修改现有代码

❌ 缺点

  1. 增加系统复杂度:引入了额外的代理层
  2. 性能开销:代理调用会增加处理时间
  3. 可能造成请求变慢:特别是远程代理,网络延迟可能较大
  4. 可能过度设计:对于简单场景,使用代理可能过于复杂

八、与其他模式的对比

代理模式 vs 装饰器模式

// 代理模式:控制访问,通常一对一关系
class ImageProxy : Image {
    private val realImage: RealImage // 明确知道具体类型
    
    override fun display() {
        // 控制访问:延迟加载、权限检查等
        if (checkAccess()) {
            realImage.display()
        }
    }
}

// 装饰器模式:动态添加功能,可以多层嵌套
class ImageDecorator(private val image: Image) : Image {
    override fun display() {
        // 增强功能:添加边框、阴影等
        addBorder()
        image.display()
        addShadow()
    }
}

代理模式 vs 适配器模式

// 代理模式:提供相同接口,控制访问
interface Database {
    fun query(sql: String): Result
}

class DatabaseProxy : Database {
    private val realDb = RealDatabase()
    
    override fun query(sql: String): Result {
        // 控制访问:连接池、日志等
        logQuery(sql)
        return realDb.query(sql)
    }
}

// 适配器模式:转换接口,使不兼容的接口协同工作
class NewDatabaseAdapter(private val newDb: NewDatabase) : Database {
    override fun query(sql: String): Result {
        // 转换接口:将旧接口转换为新接口
        val newQuery = convertSql(sql)
        return convertResult(newDb.newQuery(newQuery))
    }
}

九、最佳实践指南

何时使用代理模式?

✅ 适用场景

  1. 远程代理:需要访问远程对象时
  2. 虚拟代理:需要延迟加载开销大的对象时
  3. 保护代理:需要控制访问权限时
  4. 智能引用:需要添加额外操作(如引用计数、缓存)时
  5. AOP场景:需要添加日志、监控、事务等横切关注点时

❌ 避免使用场景

  1. 对象创建简单,无需控制访问
  2. 性能要求极高,不能接受代理开销
  3. 可以直接访问对象,无需中介
  4. 可以使用更简单的解决方案(如回调、观察者)

Android开发建议

  1. 合理使用Kotlin委托:简化代理实现
  2. 注意内存泄漏:代理持有Context或Activity引用时要小心
  3. 性能考虑:远程代理要考虑网络状况,虚拟代理要考虑加载时机
  4. 安全性:保护代理要正确实现权限检查
  5. 与框架结合:合理使用Retrofit、Glide等框架的代理机制

代码示例:综合应用

// 网络图片加载的完整代理实现
interface ImageLoader {
    fun load(url: String, imageView: ImageView)
    fun preload(url: String)
    fun clearCache()
}

class RealImageLoader(private val context: Context) : ImageLoader {
    override fun load(url: String, imageView: ImageView) {
        Glide.with(context)
            .load(url)
            .into(imageView)
    }
    
    override fun preload(url: String) {
        Glide.with(context)
            .load(url)
            .preload()
    }
    
    override fun clearCache() {
        Glide.get(context).clearMemory()
        GlobalScope.launch(Dispatchers.IO) {
            Glide.get(context).clearDiskCache()
        }
    }
}

class SmartImageLoaderProxy(
    private val realLoader: RealImageLoader,
    private val context: Context
) : ImageLoader {
    
    private val memoryCache = LruCache<String, Bitmap>(10 * 1024 * 1024)
    private val diskCacheDir = File(context.cacheDir, "image_cache")
    private val loadingUrls = mutableSetOf<String>()
    
    init {
        if (!diskCacheDir.exists()) {
            diskCacheDir.mkdirs()
        }
    }
    
    override fun load(url: String, imageView: ImageView) {
        // 1. 检查内存缓存
        memoryCache.get(url)?.let {
            imageView.setImageBitmap(it)
            return
        }
        
        // 2. 检查磁盘缓存
        getFromDiskCache(url)?.let {
            memoryCache.put(url, it)
            imageView.setImageBitmap(it)
            return
        }
        
        // 3. 避免重复加载
        if (loadingUrls.contains(url)) {
            return
        }
        loadingUrls.add(url)
        
        // 4. 显示占位图
        imageView.setImageResource(R.drawable.placeholder)
        
        // 5. 异步加载
        CoroutineScope(Dispatchers.IO).launch {
            try {
                val bitmap = downloadImage(url)
                
                withContext(Dispatchers.Main) {
                    imageView.setImageBitmap(bitmap)
                }
                
                // 缓存
                memoryCache.put(url, bitmap)
                saveToDiskCache(url, bitmap)
            } catch (e: Exception) {
                withContext(Dispatchers.Main) {
                    imageView.setImageResource(R.drawable.error)
                }
            } finally {
                loadingUrls.remove(url)
            }
        }
    }
    
    override fun preload(url: String) {
        // 预加载到内存缓存
        if (memoryCache.get(url) == null) {
            CoroutineScope(Dispatchers.IO).launch {
                try {
                    val bitmap = downloadImage(url)
                    memoryCache.put(url, bitmap)
                } catch (e: Exception) {
                    // 忽略错误
                }
            }
        }
    }
    
    override fun clearCache() {
        memoryCache.evictAll()
        diskCacheDir.listFiles()?.forEach { it.delete() }
        realLoader.clearCache()
    }
    
    private fun downloadImage(url: String): Bitmap {
        // 模拟下载
        Thread.sleep(1000)
        return Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888)
    }
    
    private fun getFromDiskCache(url: String): Bitmap? {
        val file = File(diskCacheDir, url.hashCode().toString())
        return if (file.exists()) {
            BitmapFactory.decodeFile(file.absolutePath)
        } else {
            null
        }
    }
    
    private fun saveToDiskCache(url: String, bitmap: Bitmap) {
        val file = File(diskCacheDir, url.hashCode().toString())
        file.outputStream().use {
            bitmap.compress(Bitmap.CompressFormat.PNG, 100, it)
        }
    }
}

十、总结

代理模式是Android开发中极其重要的设计模式,在以下场景中特别有用:

  1. 性能优化:通过虚拟代理实现延迟加载,提升应用响应速度
  2. 权限控制:通过保护代理实现安全访问控制
  3. 网络通信:通过远程代理简化远程调用
  4. 缓存管理:通过智能引用代理实现缓存策略
  5. 横切关注点:通过代理统一处理日志、监控、事务等

关键点

  • 代理是对象的代表,客户端通过代理间接访问真实对象
  • 控制访问是核心:代理可以控制何时、如何访问真实对象
  • Kotlin委托简化实现:善用by关键字和属性委托
  • 合理选择代理类型:根据需求选择虚拟、保护、远程等代理

在Android中的实际应用

  • 图片加载:Glide、Picasso内部使用了代理模式
  • 网络请求:Retrofit使用动态代理
  • 权限管理:通过代理检查运行时权限
  • 数据库访问:Room等ORM框架使用代理实现延迟加载

代理模式是构建健壮、可维护Android应用的重要工具,合理使用可以显著提升代码质量和应用性能。