Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Proxy Final Method In Non Final Class

I need to proxy methods on various view classes in the Android UI Framework such as TextView. Particularly TextView#setText(int resId). This method is not part of an interface. Therefore, Java Proxy will not work since it only works for interfaces. I need to use bytecode manipulation.

I found a library called dexmaker that seemed promising. I am assuming that I need to do runtime byte code manipulation since the Android View classes are only actually available on the device. Dexmaker can proxy public methods on concrete classes. Then I noticed that TextView#setText(int resId) is inexplicably final. The TextView class itself is non-final.

I think I could fork dexmaker to support final methods in non-final classes. Is this possible? I don't want to start this project if it isn't. It would be a huge win for my library though because developers would not need to have subclasses, interfaces, or manual static method calls for their views. My library needs to know when text is set on particular View. A proxy is the perfect design pattern for this.

like image 920
jophde Avatar asked Sep 29 '22 23:09

jophde


2 Answers

As far as I know, this is not possible on Android.

Dexmaker creates dex files that contain new classes. These classes are then added to an application by using dex class loaders. Such dex files can however not be used to replace classes, only to add new subclasses that serve as a proxy.

In this sense, dexmaker is rather like cglib than javassist.

Note that Android does neither provide similar instrumentation capabilities as a regular Jvm where you can instrument final classes and methods by class redefinition via an agent. This is not provided by Android: http://developer.android.com/reference/android/app/Instrumentation.html

like image 165
Rafael Winterhalter Avatar answered Oct 23 '22 20:10

Rafael Winterhalter


The intent of "final" is that the method cannot be overridden. That effectively puts a halt to proxying by extension. However, you can still proxy by wrapping, the way Spring handles it.

That is one reason why it is a best practice to separate the interface from the implementation.

In more concrete terms ...

// 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);
    }
}

Does this help?

like image 36
pojo-guy Avatar answered Oct 23 '22 19:10

pojo-guy