4

I'm aware you cannot actually Swizzle in Java.

I was doing some research and I think 'maybe' you can do Reflection in Java to accomplish Swizzle like behaviour (that you can do on iOS).

The culprit (and one of the worst design decisions I've ever seen) is the addView() function on all Android ViewGroup objects. You must explicitly check if the parent is null (you sometimes even need to cast the parent to get the behaviour you need!). Gross.

I want to change the behaviour of this (without creating a million subclasses) by having the addView() method do this check automatically so the client code can ignore this.

Is this something I can do with Reflection (from what I grasp it would require special runtime calling, instead of actually changing the root method call [so maybe not good enough]), or something else? Or am I barking up the wrong tree?

Aggressor
  • 13,323
  • 24
  • 103
  • 182
  • Thats when you override the behaviour of a native function. For example the AppDelegate class is a key class on iOS that responds to lots of events. You could create an AppDelegate swizzle that overrides the behaviour of the AppDelegate but doesn't actually change the code in the AppDelegate. It 'takes control' of the method you want to change and can return control at your discretion, but while you have control you make it do what you want. So for this, I'm saying I want to create my own addVIew function that takes control whenever an addView is called, and I do my stuff in it. – Aggressor Jul 17 '15 at 19:09
  • There is [Aspect-Oriented Programming](http://docs.jboss.org/aop/1.0/aspect-framework/userguide/en/html/what.html) which allows you to 'weave in' code to existing methods. I'm not sure if this will work for Android though, since you don't actually compile the native `Views`. – nhaarman Jul 17 '15 at 19:12
  • Interesting, this gives me something to research, thank you! – Aggressor Jul 17 '15 at 19:13
  • 1
    Method Swizzling is also quite unsafe and should only be used in extreme cases where there are no alternatives. What you are trying to solve is really a non issue, and here's why: Adding a `null` to a `ViewGroup` makes absolutely no sense! I don't see how you would even end up in a situation with a `null` reference to a `View`. It would be nice if you included example code that leads to wanting this behaviour. – Nicklas Jensen Jul 17 '15 at 19:16
  • If you try to add a view to a view, and that first view already has a parent, the app crashes. Very common scenario. – Aggressor Jul 17 '15 at 19:27
  • Common? Not really... Only if you're doing something really wrong... – Selvin Jul 17 '15 at 19:30
  • Then you're no longer using the method / class for it's intended and documented purpose, and you shouldn't expect it to work correctly. A `View` can have 0 or 1 parent, and it wouldn't make sense for a `View` to have more than one parent, which would be the case if it were added to a `ViewGroup` while it's already inside another `ViewGroup`. – Nicklas Jensen Jul 17 '15 at 19:53
  • In this (and many) other apps I have situations where views constantly need to change parents. I dont want to waste code checking if the parent exists before changing parents. – Aggressor Jul 17 '15 at 19:59
  • You should use a Helper class for this kind of situation. Here's a snippet that solves your problem: [pastebin.com](http://pastebin.com/ciT5wqcD) – Nicklas Jensen Jul 17 '15 at 20:27
  • Yes thank you, thats something I already have. I was looking for a way to change the system behaviour with this question. – Aggressor Jul 17 '15 at 20:29

1 Answers1

3

As you mentioned, without creating a million subclasses (or better put, one subclass for every class where you would like to override the addView implementation) this doesn't seem possible. This would actually be the right way to do it, and it also means that you can use these subclasses in XML layout files.

Reflection would allow you to inspect classes/interfaces/fields/methods at runtime, but it won't allow you to change their already defined behaviour provided by the underlying implementation which has been compiled. Although some changes are possible, such as making private methods/fields accessible.

Grey-area options incoming...

One direction worth looking into would be Mockito, which allows you to create spies around existing object instances that would allow you to override the default implementation. This is achieved through Java's proxies and InvocationHandlers. However, at this point I'll stop and say that your production code should not contain any test code and Mockito is clearly meant for that. More so considering that, at least in the past, it used to be hard (if not impossible) to put proxies around some of Android's classes such as Views and Contexts. I believe this has been resolved by DexMaker, which I have less knowledge of myself but it does allow you to perform runtime code generation. This would be another direction worth looking at. I personally think that neither of these should be taken as possible solutions worthy of production code, but something to have a play with for your own curiosity and to learn from.

Martin
  • 1,775
  • 1
  • 13
  • 10
  • Thanks for the good info. I'm going to keep digging but this was very helpful – Aggressor Jul 17 '15 at 19:16
  • Another thing which came to me is other JVM languages, which can run on Android, that support class extensions. I think that both Scala and Kotlin support this functionality, but I have zero idea as to how feasible this is for classes coming from the Android framework. But again, I would put this under the 'grey' area as well. – Martin Jul 17 '15 at 19:20