Today we are going to tell you something interesting. Concise, absolutely no nonsense… Just strange.
I have a Crash in the supermarket downstairs
By the way, I have recently written a small Demo. The development and debugging has been done on a 6.0.1 mobile phone.
However, one day my 6.0.1 phone was on a business trip, so I had to go through my drawers and find the dusty Nexus 5, the classic Android 4.4.2 Api 19. Do you miss it? Since everyone knows what Google did to Android from Api 19 to 21, I probably don’t recognize any of the code I wrote in N5. Time has changed.
And then the most terrible thing happens, and you see for yourself:
What!
ActivityCompatApi23 $SharedElementCallbackImpl does not exist?
Android Support library, are you kidding me?
I thought to myself, well, this is Api 19. Don’t panic, we have a look at the source of this boy to make a decision:
@RequiresApi(23)
@TargetApi(23)
class ActivityCompatApi23 {
.
private static class SharedElementCallbackImpl extends SharedElementCallback {
.
}
}
Copy the code
If you’re familiar with that, you should know that if you can compatyn something like ActivityCompatN, you can choose a suitable class based on the current version of the phone. For example, if Api 19 is available, you should use ActivityCompat. What are you ActivityCompatApi23 doing to compatapi23?
There must be some code in the Android Support framework that isn’t very beautiful, or else this is not happening!
Api 19 does not have SharedElementCallback, which is an interface for compatycompatapi23. For example, Api 19 does not have SharedElementCallback. So SharedElementCallbackImpl parent interface cannot be parsed, and led to the above errors occur.
So now the question is, which kid deserves to be punched is going up against something that’s impossible to use?
Well, I found that the wrong call is in a subclass of AppCompatActivity, so I wrote this code:
this: :class.allSupertypes.flatMap { it.arguments }.forEach {
.
}
Copy the code
What does that mean? In other words, there should be at least AppCompatActivity, Activity, etc. No problem, I am not to take a parent class, as for me to shake a face Error yao?
There must be a ghost in the parent class of AppCompatActivity. Sure enough, the code for its parent class looks like this:
public class FragmentActivity extends BaseFragmentActivityJB implements
ActivityCompat.OnRequestPermissionsResultCallback.
ActivityCompatApi23.RequestPermissionsRequestCodeValidator {
.
}
Copy the code
This.. Just don’t ask for face, ActivityCompatApi23 just can’t find anything like this. At this time, to be quiet is probably useless, I will ask you, how to pacify a wounded heart?
So I’m just trying to get all the parents and interfaces of a subclass of AppCompatActivity and find ActivityCompatApi23.
Who’s the guy who sent Crash?
Well, it’s out, and I can’t send you back, so let’s analyze, why do I just want a parent, but it’s involved in a static inner class that can’t find a parent?
Obviously, my traversal of the parents of AppCompatActivity is causing them to be loaded. Wait, do you guys think that’s true? This is problematic because the code itself is running on a subclass of AppCompatActivity, which means that the AppCompatActivity must have already loaded its classes and any of its parents can be loaded into the virtual machine as it loads links. So what I call convenience that causes these parent classes to load is actually wrong. It’s easy to understand.
So my traversal of its parent doesn’t trigger class loading? If I use Java reflection instead of Kotlin reflection’s allSuperTypes to iterate over the parent class and parent interface, I don’t actually get an error. If you look at the FragmentActivity declaration again, The somebody else is cited ActivityCompatApi23 $RequestPermissionsRequestCodeValidator not really cited ActivityCompatApi23, from the perspective of class loading, Loading the former does not directly cause the latter to be loaded (unless the former references the latter) for the simple reason that the former is an interface and you can think of it as a static inner class that generally has no direct dependencies on external classes at the language level.
In other words,ActivityCompatApi23
It shouldn’t have been loaded in.
But now I call Kotlin’s allSuperType to get all the parent classes and it will trigger it to load.
When Kotlin queries these parent classes through reflection, it runs like this:
That said, generating a classId for this interface seems like a fairly important thing, but just like the class’s full name in Java reflection, there’s no question that it’s necessary.
In Api 19, we saw that the external class was explicitly accessed to create the classId, which caused the external class to be loaded.
Of course, if you’re interested, you can run a different version of the code, for example under Java 8 (to do this, you’ll need to copy a lot of jars into your Java project), and the error will be at simplename.isempty (). Obviously, as a static inner class, its enclosingMethod and enclosingConstructor are both null, so simpleName is definitely empty.
Why doesn’t Api 19 get simpleName and wait for it to get simpleName? Because its simpleName implementation is not quite the same as in later versions:
And let’s look at the new version of the way:
The first sentence of getSimpleBinaryName is to get the enclosing class.
private String getSimpleBinaryName(a) {
Class
enclosingClass = getEnclosingClass(a);
.
}
Copy the code
However, the external classes are going to be loaded anyway.
To summarize, loading a static inner class or interface does not directly trigger the loading of an external class, whereas Kotlin’s reflection creates inner classes or interfacesclassId
Which inevitably triggers the loading of external classes.
A few more jokes
The other thing I really want to say about this is:
-
I just called an API to get all of the parents and interfaces to be lazy, and I’m actually just iterating through the parent recursively, which ends before FragmentActivity, which means no compatapi23 class loading on ActivityCompatApi23 can be triggered at all, which doesn’t cause the previous problem. The lesson of this story is, don’t be lazy!
-
Android Support’s FragmentActivity parent interface contains explicit references to compatible apis.
-
I don’t blame it, Kotlin!
Welcome to Kotlin on wechat