New issue
Advanced search Search tips

Issue 758615 link

Starred by 1 user

Issue metadata

Status: Fixed
Owner: ----
Closed: Dec 2017
Cc:
Components:
EstimatedDays: ----
NextAction: ----
OS: Android
Pri: 3
Type: Bug

Blocked on:
issue 758626

Blocking:
issue 725019


Show other hotlists

Hotlists containing this issue:
Hotlist-1


Sign in to add a comment

Automate as much as possible of the code creation for the WebView Support Library

Project Member Reported by gsennton@chromium.org, Aug 24 2017

Issue description

The WebView Support library should contain mostly code that can be auto-generated from existing android.webkit code.

Similarly a large part of the new support-library glue layer should be auto-generated from the old glue layer.

Having automated steps for both these tasks would help a lot when we add new APIs to WebView, or make any code changes in android.webkit or the old glue layer.


To be a bit more specific:
1. We will essentially duplicate the code in android.webkit directed towards app developers, so that code itself should be fairly straight-forward to transform.
2. We will create a generic way of calling from the support library into the support library glue layer, we should be able to transform existing android.webkit -> glue-layer calls into using this new kind of call. This could for example mean that for each object that only exists in one of the two classloaders in support library and the glue layer we would probably pass an InvocationHandler instead of the actual object. 
3. The same goes in the other direction - so the support library will also need some auto-generated code for passing InvocationHandlers.
4. The support library glue layer should be very similar to the existing WebView glue layer, so most of the code there should be possible to auto-generate.


It would be great if there was some tool that did these kind of code transformations / code generations, for example:
1. Fetch parameters from a method, if a parameter stems from a certain package, change that into an InvocationHandler, and change any uses of that parameter within the method to use another variable.
2. Given an entire class definition, create an interface corresponding to that class (for an InvocationHandler/Proxy solution we need Interfaces to implement, we can't implement classes through Proxy).
3. Renaming of imports/packages.

Do we know of any tools like this that we can use in Chromium/Android? I would assume Android Studio could do most of these things by parsing the Java code into some kind of AST representation.
 
I'm curious why the decision was made to duplicate the API surface of WebView in this support library? Seems like that's a recipe for bloating apps. Why not just provide a library that provides only new APIs rather than duplicating a tonne of code?
Blockedon: 758626
Good point, IIRC we have not looked into that option much - we should definitely do so. Filed crbug.com/758626
To make a decision on which approach to go with (including all APIs vs. including only new ones) it would be good to know what the size and number of methods would be for either approach.

I have a couple of questions related to this:
1. How do you calculate the size of an Android/java library? (given a number of classes + methods + potentially a few resources)
2. Which methods are counted toward the number of methods in an app that imports the support library - I assume all of the methods defined in the support library are counted, and any methods in its dependencies, not just the methods in the public interface seen by the app?

Andrew / Torne, do you know the answer to any of these questions? :)
I'd use //build/android/method_count.py. E.g.:

$ build/android/method_count.py out/Release/gen/chrome/android/chrome_public_apk/classes.dex
*RESULT chrome_public_apk_methods: total= 51985 methods
*RESULT chrome_public_apk_types: total= 8916 types
*RESULT chrome_public_apk_fields: total= 24628 fields
*RESULT chrome_public_apk_strings: total= 27064 strings
*RESULT chrome_public_apk_DexCache_size: total= 450372 bytes of permanent dirty memory

- You should consider both your method counts and file size. Proguard generally helps a lot with both, but I believe the majority of apps in the wild do not use it, so we can't really rely on post-proguarded numbers.

- You are correct - all methods that you define as well as reference are counted. However, if you reference types that the embedder references, then they will not be double-counted.
Right, do you know how to calculate/estimate the size of the library given the number of method though? (it would be nice to be able to do this without having created the actual library). 
Maybe try defining an android_library() of webview .java files that are similar to what you'd be adding, then run method_count.py on that?

Each android_library() will have a .dex built for it that contains only the .java in its sources.
Blockedon: -725019
Blocking: 725019
Alright, I've managed to build a library like that (the lint check for it is failing - but the library seems to have been built anyway). I can't find the corresponding dex file though, do you know where that would show up Andrew?
They are sneakily hidden within .jar files. E.g.:
gen/base/base_java.dex.jar
Alright, cool!

Note: I had to edit the code of build/android/method_count.py to not make it crash when there was no APK associated with the dex-file (which is a bit odd since the apk name is just used for printing anyway, but w/e).



Here are the results from running build/android/method_count.py on just files from android.webkit (i.e. most of the files in android.webkit with a few exceptions like WebViewFactory and WebViewLibraryLoader). Note that I did include utility-only classes like UrlUtil, but avoided importing internal dependencies like android.net.WebAddress, and libcore.icu.LocaleData).


*RESULT unknown-apk-name_methods: total= 1158 methods
*RESULT unknown-apk-name_types: total= 233 types
*RESULT unknown-apk-name_fields: total= 211 fields
*RESULT unknown-apk-name_strings: total= 1705 strings
*RESULT unknown-apk-name_DexCache_size: total= 13228 bytes of permanent dirty memory

Does this seem right? (and does it seem like a reasonable addition to make to a WebView app?)


In the actual support library we would need to duplicate some/many of the classes here as interfaces for the WebView APK glue layer to implement, so the method count / library size would grow considerably (the count might be doubled or so).
Note that, actually, for classes that are not defined as interfaces in android.webkit but still need to be passed to the WebView APK, we might want/need to (because of our InvocationHandler approach for calling between the support library and the webview apk glue) define both
1. a new interface with the same methods as that class, and
2. an implementation of that interface which just delegates calls to the app-facing class (e.g. WebViewClient is not an interface, so we would declare a WebViewClientInterface to pass to the WebView APK, and have a WebViewClientImpl implement that interface by calling methods on a given WebViewClient).

This would mean we would triple the method counts for those classes - as long as package private methods are counted towards the method count, are they?
Would be happy to see method_count.py updated to take a .dex file (or .dex.jar) (indeed that seems like a silly omission :P).

Visibility doesn't affect the counts, so package private would still count against.

Tough call as to whether this is acceptable or not. Some won't care, and others will probably grumble. Is it dramatically different from the other approach? On the whole, I'd want a goal to just be "as slim as possible", but I conceded that there are other opinions. :P
Since the other approach only includes new APIs it should have a relatively small method count. I'll see if I can estimate this in a good way.

Right now I'm just scoping out the pros and cons of the two approaches. The only drawback of the slimmed approach I've found so far is that it's not clear how we will deal with some certain android.webkit objects in that approach (namely the ones app developers override to implement app callbacks), to avoid app developers making silly mistakes where some callback implemented by the app isn't used because the developer implemented it on an old object instead of support-library one.
Regarding the number of new methods per Android version:

we have added 79 new methods (I created some code for parsing and diffing the [api-level].txt files in [android-master]/prebuilts/sdk/api to get this value) between API levels 21 and 27 (O-MR1), i.e. about 13 methods per version.
Note that once the support library ships we might add more APIs per Android version since any new APIs will then reach more device than before the support library existed.

So assuming we ship this with APIs from Android P, the number of APIs in the new-API-only support library would be about a tenth of that of a support library containing all APIs.
Actually, the number 79 includes all the methods that were later deprecated after being added, ignoring deprecated methods there are only 37 new methods added after L-MR1.
Actually, that number is wrong as well :), 67 is the method count added after L-MR1. But if we allowed WebView to be updated through the play store before that (in L) then we should be counting methods after L instead of after L-MR1..
Ok, so after double-checking this:
WebView is updateable in L, which means that we are interested in all APIs that were introduced after L (i.e. all APIs introduced in L-MR1 and above), since there's no reason to provide APIs from L when the oldest devices that can benefit from the support library will be running L.
The number of methods added in L-MR1 and above (up until O-MR1) are 74.

I've set up a spreadsheet with those methods here:
https://docs.google.com/spreadsheets/d/1pu4WT8Suxyt00P9tRQ_uWIwZjhu0_0pj9zzg_Jb3Rq0/edit#gid=50666148
I'm gonna mark this as fixed, I've added bugs for the specific tasks we need to perform here.
Status: Fixed (was: Available)

Sign in to add a comment