Skip to content

Understanding the Binding Pipeline

Jonathan Pobst edited this page Mar 23, 2021 · 6 revisions

In order to troubleshoot issues with binding a specific library, it can be helpful to understand the bindings pipeline, and how to verify results at each step in order to narrow down the cause of an issue.

There are 4 main stages in the bindings pipeline:

Extracting the Java API from a .jar/.aar ("class-parse")

This step decompiles compiled Java, extracts the Java API, and writes it to an XML file. Additionally, if the Java library is actually a Kotlin library, it will read the additional annotations that Kotlin places in the API, and updates the Java API to be closer to the Kotlin API. (Kotlin supports more features than Java (like internal classes), which are encoding in the Kotlin annotations.)

If the input binary is an .aar, this file is unzipped and the classes.jar file inside is used as the input.

The output of this step is an XML file in the /obj/$(Configuration)/$(TargetFramework) directory called api.xml.class-parse. There is a lot of noise in this file needed by future steps, but if you ignore the noise you can see it is a description of the Java API:

<api
  api-source="class-parse">
  <package
    name="com.example">
    <interface
      deprecated="not deprecated"
      name="MyInterface"
      visibility="public">
      <method
        abstract="true"
        deprecated="not deprecated"
        final="false"
        name="doStuff"
        return="void"
        static="false"
        visibility="public">
        <parameter
          name="p0"
          type="java.lang.String" />
      </method>
    </interface>
  </package>
</api>

There isn't much interesting output in a diagnostic MSBuild log from this step. However if you are binding a Kotlin library you will see messages describing the changes made due to the Kotlin annotations:

Kotlin: Hiding internal class Com.Example.MyInternalClass
Kotlin: Renaming parameter Com.Example.MyClass - GetByName - p0 -> name

Resolving the Java types in the API ("ApiXmlAdjuster")

The next step takes the api.xml.class-parse created in the previous step, attempts to resolve all Java types mentioned in the API, removes types that rely on other types it cannot resolve, and outputs the result as another XML file called api.xml.

For example, imagine the following method was found in the previous step:

<method
  abstract="true"
  deprecated="not deprecated"
  final="false"
  name="getActivityByName"
  return="android.app.Activity"
  static="false"
  visibility="public">
    <parameter
      name="p0"
      type="java.lang.String" />
</method>

We need to ensure that we can find the definition for all Java types this method requires:

  • Parameter with type java.lang.String
  • Return type android.app.Activity