How androidx.tech Worked
I have received inquiries as to how my former androidx.tech
site worked. It offered a catalog
of every Jetpack artifact version, including the source code. It was designed to be a workaround
for the fact that Google did not offer this sort of stuff back in the day. Nowadays,
maven.google.com
kinda has this, insofar as it offers “source”
links (and you need to inspect for yourself which is the actual artifact source and which is
sample code).
So, here is how androidx.tech
worked.
Step 1: Find the POMs
Google’s Maven publications have long had links to source JARs for their artifact versions in their POM files. The POM files also had information about dependencies that I wanted to display. So, I needed the POM files for all the artifacts and their versions. And, to save me having to re-download them, I wanted to store each of those POMs locally.
The simplest solution, given this need plus the need for the source code, would be to mirror
maven.google.com
. I could not figure out how to do this five years ago, and I may also have
been concerned about how much disk space would be needed by all of this.
I created a Kotlin project, reporeader
, that would traverse the artifacts in a Maven
repository and pull their POMs. This involved:
-
Downloading
https://dl.google.com/dl/android/maven2/master-index.xml
and parsing the XML to get the list of artifact groups -
Filtering that list to only worry about
androidx.
groups, as Google’s Maven repository has a lot of other stuff as well, such as Play Services -
Generating the URL for the artifact group index, which involves replacing all the
.
characters in the artifact group with/
and using that to get a group index XML URL, such ashttps://dl.google.com/dl/android/maven2/androidx/activity/group-index.xml
forandroidx.activity
-
Downloading and parsing that XML to get the artifacts and versions that are in that group
-
Generating the URL for the POM, which replaces
group-index.xml
in the group index URL with/$artifact/$version/$artifact-$version.pom
for a given artifact name and artifact version, giving you something likehttps://dl.google.com/dl/android/maven2/androidx/activity/activity-compose/1.10.0-alpha03/activity-compose-1.10.0-alpha03.pom
-
Downloading that POM
This tool also knew to emit a roster of what was new: new artifact groups, new artifact IDs within a group, and new artifact versions. I still use this tool today, to generate the list of changes that go into posts like this one.
Step 2: Download the Sources
A POM file is an XML file with lots of interesting info:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<!-- This module was also published with a richer model, Gradle metadata, -->
<!-- which should be used instead. Do not delete the following line which -->
<!-- is to indicate to Gradle or any Gradle module metadata file consumer -->
<!-- that they should prefer consuming it instead. -->
<!-- do_not_remove: published-with-gradle-metadata -->
<modelVersion>4.0.0</modelVersion>
<groupId>androidx.activity</groupId>
<artifactId>activity-compose</artifactId>
<version>1.10.0-alpha03</version>
<packaging>aar</packaging>
<name>Activity Compose</name>
<description>Compose integration with Activity</description>
<url>https://developer.android.com/jetpack/androidx/releases/activity#1.10.0-alpha03</url>
<inceptionYear>2020</inceptionYear>
<organization>
<name>The Android Open Source Project</name>
</organization>
<licenses>
<license>
<name>The Apache Software License, Version 2.0</name>
<url>http://www.apache.org/licenses/LICENSE-2.0.txt</url>
<distribution>repo</distribution>
</license>
</licenses>
<developers>
<developer>
<name>The Android Open Source Project</name>
</developer>
</developers>
<scm>
<connection>scm:git:https://android.googlesource.com/platform/frameworks/support</connection>
<url>https://cs.android.com/androidx/platform/frameworks/support</url>
</scm>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>androidx.activity</groupId>
<artifactId>activity</artifactId>
<version>1.10.0-alpha03</version>
</dependency>
<dependency>
<groupId>androidx.activity</groupId>
<artifactId>activity-ktx</artifactId>
<version>1.10.0-alpha03</version>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>androidx.compose.runtime</groupId>
<artifactId>runtime-saveable</artifactId>
<version>1.7.0</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>androidx.activity</groupId>
<artifactId>activity-ktx</artifactId>
<version>[1.10.0-alpha03]</version>
<scope>compile</scope>
<type>aar</type>
</dependency>
<dependency>
<groupId>androidx.compose.runtime</groupId>
<artifactId>runtime</artifactId>
<version>1.7.0</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>androidx.compose.ui</groupId>
<artifactId>ui</artifactId>
<version>1.0.1</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>androidx.core</groupId>
<artifactId>core-ktx</artifactId>
<version>1.13.0</version>
<scope>compile</scope>
<type>aar</type>
</dependency>
<dependency>
<groupId>org.jetbrains.kotlinx</groupId>
<artifactId>kotlinx-coroutines-core</artifactId>
<version>1.7.3</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>androidx.lifecycle</groupId>
<artifactId>lifecycle-runtime</artifactId>
<version>2.6.1</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>androidx.savedstate</groupId>
<artifactId>savedstate</artifactId>
<version>1.2.1</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>androidx.lifecycle</groupId>
<artifactId>lifecycle-viewmodel</artifactId>
<version>2.6.1</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-stdlib</artifactId>
<version>1.8.22</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>androidx.lifecycle</groupId>
<artifactId>lifecycle-common</artifactId>
<version>2.6.1</version>
<scope>runtime</scope>
</dependency>
</dependencies>
</project>
However, it does not contain a link to the sources JAR for the artifact. Fortunately, Google
tended to use a consistent naming convention of $artifact-$version-sources.jar
. So, if I did not
already have the sources for that artifact version, I generated the URL for it
(e.g., https://dl.google.com/dl/android/maven2/androidx/activity/activity-compose/1.10.0-alpha03/activity-compose-1.10.0-alpha03-sources.jar
)
and downloaded it. Since JAR files are ZIP files, I could then use ordinary unZIP code to convert
the JAR into a directory tree of source code.
Step 3: Generate the Site
androidx.tech
was a static site, generated by Jekyll. Jekyll is Ruby code and is not designed
for a site of this size, with hundreds of thousands of pages. Still, given a big enough Amazon EC2 instance,
I could plow through it.
I had a separate Kotlin project, gen2
, that would generate the Markdown source for the site, given
the POM files and source trees. My publish
script would then set Jekyll loose to generate the
site, and from there it was a matter of rsync
to push the changes up to the actual host.