【问题标题】:Proxy Final Method In Non Final Class非最终类中的代理最终方法
【发布时间】:2015-01-17 15:51:44
【问题描述】:

我需要在 Android UI 框架中的各种视图类上代理方法,例如 TextView。特别是TextView#setText(int resId)。此方法不是接口的一部分。因此,Java Proxy 将不起作用,因为它仅适用于接口。我需要使用字节码操作。

我发现了一个名为dexmaker 的库,看起来很有希望。我假设我需要进行运行时字节码操作,因为 Android View 类仅在设备上实际可用。 Dexmaker 可以代理具体类的公共方法。然后我注意到TextView#setText(int resId)莫名其妙地是final。 TextView 类本身不是最终的。

我想我可以派生 dexmaker 以支持非最终类中的最终方法。这可能吗?如果不是,我不想开始这个项目。不过,这对我的库来说将是一个巨大的胜利,因为开发人员不需要为他们的视图提供子类、接口或手动静态方法调用。我的图书馆需要知道何时在特定视图上设置了文本。代理是完美的设计模式。

【问题讨论】:

  • 随机拍摄(因为它没有被提及):布局充气器允许set a factory class 这可能是你所追求的。这在 probe 中使用,工厂使用 dexmaker 动态创建代理来拦截调用。
  • 我的库的一部分实际上使用了布局充气工厂技术。您是说可以在通过 dexmaker 创建的视图对象上设置代理?
  • 非常感谢斯特凡。探针源正是我所需要的:)。如果我理解正确,Probe 会创建全新的 View 类,那么 dexmaker Proxybuild 无法处理最终方法的限制不是问题吗?来自 dexmaker ProxyBuilder 文档:“此过程仅适用于具有公共和受保护可见性级别的类。”
  • 这应该与dexmaker无关。首先,我将围绕各自的 View 创建包装类,将所有调用转发到真实的 View 实例。然后,您可以根据自己的喜好随意添加呼叫。不知道这条路是否可行,感觉既费力又脆弱。
  • 我不认为包装会起作用,因为对象肯定仍然需要正确投射。我怀疑 TextView tv = (TextView) findViewById(R.id.textView) 否则会起作用。最后的方法真的很杀手。除了修补 Android View 类以使用接口之外,还有其他选择吗?

标签: java android javassist bytecode-manipulation


【解决方案1】:

据我所知,这在 Android 上是不可能的。

Dexmaker 创建包含新类的 dex 文件。然后使用 dex 类加载器将这些类添加到应用程序中。但是,此类 dex 文件不能用于替换类,只能添加用作代理的新子类。

从这个意义上说,dexmaker 更像是 cglib 而不是 javassist。

请注意,Android 不提供与常规 Jvm 类似的检测功能,您可以通过代理通过类重新定义来检测最终类和方法。这不是 Android 提供的:http://developer.android.com/reference/android/app/Instrumentation.html

【讨论】:

    【解决方案2】:

    “final”的意图是该方法不能被覆盖。这有效地阻止了扩展代理。但是,您仍然可以通过包装进行代理,Spring 处理它的方式。

    这就是为什么将接口与实现分开是最佳实践的原因之一。

    具体来说...

    // This snip is not supposed to be functional, only to demonstrate a concept
    interface TextViewInterface {
        void setText (int resId);
    }
    
    class TextView implements TextViewInterface {
        public final void setText (int resId) {
        ... snip ...
        }
    }
    
    class Proxy$TextView implements TextViewInterface 
    extends View { // Added this for Android hierarchy
        private TextView textView;
    
        public void setText (int  resId) {
            textView.setText(resId);
        }
    }
    

    这有帮助吗?

    【讨论】:

    • 这通常可以工作,但对象需要进入 Android View Tree Hierarchy,它只接受 View 或 ViewGroup 的子类,我确信在某些地方使用了实例。如果我从 TextView 扩展,接口方法会干扰类方法吗?
    • 接口机制允许你包装类方法。您可以扩展 View(称为 MyTextViewProxy)并包装 TextView,前提是可以接受任何扩展 View。我更新了上面的示例以显示扩展视图。
    • 您遇到了为什么某些事情被认为是 java 中的最佳实践。抱歉,我不能在这里为您提供更多帮助。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-03-19
    • 2015-12-19
    • 2020-05-12
    相关资源
    最近更新 更多