Home » Android » java – Can I override a hidden (but public) method and call its super method?

java – Can I override a hidden (but public) method and call its super method?

Posted by: admin May 14, 2020 Leave a comment

Questions:

There is a non public api that I need to override in order to workaround a quirk with Android’s WebView.

The api is hidden but it is public:

/**
 * ...
 *
 * @hide pending API council approval
 */
public boolean selectText() {
    ...
}

So I can override it by simply declaring it in my own WebView class, minus the @Override:

public boolean selectText() {
    ...
}

Is it possible to call the super method from my override? Normally I could write:

public boolean selectText() {
    return super.selectText();
}

But the method is hidden, so super.selectText() is not available. If I use reflection:

public boolean selectText() {
    return (Boolean) WebView.class.getMethod("selectText").invoke(this, (Object[]) null);
}

I get an infinite loop because it calls my overridden method.

Is there anyway to override this method AND be able to call the super method?

Thanks!

How to&Answers:

Is there anyway to override this method AND be able to call the super
method?

No, unfortunately, as is explained in the answer to the question How to call a superclass method using Java reflection you cannot solve this problem with reflection.

Answer:

There are actually a couple ways to do this, but they’re a little labor intensive, so probably should be used as a last resort. With a rooted device, you can extract the symbols from the framework jars on the device and put them into the android.jar in your sdk/platforms/ folder. Used to have a script to do this; lost it in switching jobs.

steps from memory (note you’ll probably have trouble as I don’t remember the steps… for advanced users who understand what I’m trying to get at):

  • adb pull /system/framework/* (these are all jar files). It might be
    enough to get framework.jar. unzip them.
  • for each one I believe will give you a classes.dex and a manifest. If
    you just get class files, skip the next step
  • dex2jar the classes.dex and unjar the classes_dex2jar.jar or whatever the
    output is from dex2jar, which will give you a bunch of .class files
  • take the class files and add them to your android sdk android.jar
    file (sdk/platforms/android-#sdk#/android.jar).

I suggest duplicating your android-#sdk# folder into an android-1#sdk# folder, so android-19 becomes android-119, and then change build.prop in the folder to say ro.build.version.sdk=119, and then build against sdk 119. This way you have the choice of building against a clean 119 or your hacked 119. Just don’t refer to any com.android.internal.R files because those ids are not accurate in my experience.

You’ll now be able to compile against these symbols and access them fine. Though don’t get mad if they change how things work, and they have no obligation to maintain compatibility with 3rd party apps.

This works because the hide tag is only used during the SDK compile step and strips these symbols from the android.jar located in your sdk. If we gather all the symbols that are compiled onto the device and stick them into your sdk jar, it’s like they were never stripped.

Alternatively you could pull down and build the full android tree (https://source.android.com/source/building.html) and then build your app against the full source, which the first step is essentially getting around by taking already compiled symbols and working with them.

Answer:

  1. YES You CAN 🙂 – first part of question
  2. NO You CAN’T – second one

solution 1) – best what you can do here closest to what you want to achieve

// to override method 
// which IDE doesn't see - those that contains {@ hide} text in JavaDoc
// simple create method with same signature
// but remove annotation
// as we don't know if method will be present or not in super class 
//
//@Override      
public boolean callMethod(String arg) {

    //  can we do it ?
    // boolean result = super.callMethod(arg)
    // no  why ? 
    // You may think you could perhaps
    // use reflection and call specific superclass method 
    // on a subclass instance.


/** 
 * If the underlying method is an instance method, it is 
 * invoked using dynamic method lookup as documented in The 
 * Java Language Specification, Second Edition, section 
 * 15.12.4.4; in particular, overriding based on the runtime
 * type of the target object will occur.
 */


   // but always you can see what super class is doing in its method 
   // and do the same by forward call in this method to newly created one
   // return modified  result 
  return doSomthingWhatSuperDo()
}

solutions 2)

does the object (which calls this method) belongs to you? and implements interface?

if so how about proxy ?

  1. extend class
  2. create proxy
  3. use proxy
  4. intercept method call
  5. based on some conditions ? call own implementation or fall back to originally?