Skip to content

Commit

Permalink
android build: Update to AndroidX.
Browse files Browse the repository at this point in the history
AndroidX, previously called the Android Support Library, is a
collection of libraries developed by Android upstream which are
bundled into an app at build time, rather than taken from the system
on the device.  Some are quite central to a typical Android app.

Upstream has renamed the whole thing to AndroidX, and this goes
with a renaming of tons of classes.  (Mostly to get them out of
`android.*`, which was always very confusing because that's the
same namespace used by the actual system packages.)  Docs:
  https://developer.android.com/jetpack/androidx/migrate

This could have been sticky, because lots of apps and lots of
third-party libraries refer to these core libraries, and in a
distributed ecosystem it's impossible for everything to update at
once.  And because these are often so central, a given build of an
app must use the same version of them throughout -- including from
any third-party libraries it uses.  So for example if some
third-party library has updated to use AndroidX, then an app cannot
switch to that version of that library until it too has updated to
use AndroidX.

Fortunately, the planners of this migration recognized this could
be a problem, and found a way to make the constraints run only in
that one direction.  When an app updates to use AndroidX, it can do
so even while it's still using third-party libraries that are still
built to use the Android Support Library.  The key is a tool called
"Jetifier", described in detail here:
  https://developer.android.com/studio/command-line/jetifier
To use it, we set the flag `android.enableJetifier=true`, and then
(quoting the AndroidX migration doc linked above):

  The Android plugin automatically migrates existing third-party
  libraries to use AndroidX by rewriting their binaries.

So the migration path is, apps go first; then libraries update at
their leisure.

---

For us, the most important third-party library (third-party from an
Android perspective) is React Native.  RN took the AndroidX update
in v0.60, so we have to do the same before we can complete that RN
upgrade, i.e. #3548.

The Android upstream way to update a given project (as described by
the doc linked above) has two prongs:

 (a) The project's own source code can be updated automatically
     (barring fancy use of classloaders, etc.) by Android Studio,
     with the feature "Refactor > Migrate to AndroidX...".

 (b) Third-party libraries get updated when they're linked in, by
     rewriting them as part of this project's build.

Both of those appear in this commit.  But in an RN context, there's
one other important set of code: third-party RN libraries get
pulled in as part of *our* Gradle build, with build.gradle lines
using `project`, like so:
    implementation project(':react-native-vector-icons')
This means they're not covered by (b).  This contrasts with RN
itself, which is pulled in as a binary artifact:
    implementation "com.facebook.react:react-native:+"

So for those third-party RN libraries, we need to edit the *source*
code, like (a), before it gets built.  Happily, someone has written
a tool to do just that, and we use it:

 (a') Third-party RN libraries get rewritten in place in
      node_modules/, after `yarn` installs them and before the
      Gradle build.  This uses a different "jetifier" tool:
        https://github.com/mikehardy/jetifier

Specifically, the latter "jetifier" will crawl node_modules; find all
.java, .kt, and .xml files; and apply a list of string substitutions
(about 1900 of them) to rewrite old names to new ones.  That sure is
kind of heuristic... but it's good enough that it will in fact find
any normal import of the affected classes, and on the other hand the
patterns being substituted are all long boring fully-qualified Java
class names, unlikely to be found in spurious places.  For us, I
also inspected (below) the exact edits the tool makes.

---

In this commit, we take the upgrade.

 (a) I used "Refactor > Migrate to AndroidX..." in Android Studio.
     This updated a few spots in our own source code.

 (b) The same auto-refactor added the `gradle.properties` settings
     (in particular enabling Jetifier, the Android-upstream tool)
     that handle libraries we incorporate as binary artifacts,
     including RN itself.

     It also updated dependencies in our `app/build.gradle`.  This
     made the `supportLibVersion` variable unused, and I deleted it.

 (a') We add `jetifier`, the NPM-world tool, as a dev dependency,
      and invoke it in our `postinstall` hook.  This means any run
      of `yarn install` (or bare `yarn`) will end by running it.

I inspected manually (by making copies of `node_modules`, and
comparing with `git diff --no-index`) the effect of `jetifier`.
All the edits look appropriate.  Excluding a few that have no
effect, the edits appear in the following packages:
  react-native
  react-native-image-picker
  react-native-webview
  react-native-photo-view
  rn-fetch-blob
  @react-native-community/netinfo
  @unimodules/react-native-adapter
  unimodules-image-loader-interface
  expo-constants
  expo-file-system
  expo-permissions

(FTR the other edits were in react-native-notifications, which on
Android we don't use or build, and in react-native-device-info and
react-native-sound, limited to IDE metadata plus copies of parts of
the support library itself in build-intermediate directories.)

So once we've upgraded all of those to versions that use AndroidX
in the first place, the `jetifier` tool should become a no-op and
we can delete our references to it.  (It won't go away entirely,
because RN v0.60 has `react-native run-android` run the tool; but
it'll stop mattering.)

I tested the app in both debug and release builds, and exercised
relevant dependencies: picked and uploaded an image; opened the
lightbox; downloaded an image there; entered and left airplane mode
to see the "No Internet connection" banner.  Together those cover all
but the unimodules and expo dependencies; I'm not sure we actually
use each of those anywhere at all, but I exercised the core unimodules
machinery by going to the diagnostics screen to view the app version,
which uses an Expo unimodule.  Everything I tested works.
  • Loading branch information
gnprice committed May 8, 2020
1 parent 91e38a8 commit e433197
Show file tree
Hide file tree
Showing 9 changed files with 20 additions and 7 deletions.
4 changes: 2 additions & 2 deletions android/app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -207,12 +207,12 @@ dependencies {
implementation project(':react-native-vector-icons')
implementation fileTree(dir: "libs", include: ["*.jar"])
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
implementation "com.android.support:appcompat-v7:${rootProject.ext.supportLibVersion}"
implementation 'androidx.appcompat:appcompat:1.0.0'
implementation "com.google.firebase:firebase-messaging:17.3.4"
implementation "me.leolin:ShortcutBadger:1.1.16@aar"
implementation "com.facebook.react:react-native:+" // From node_modules
implementation 'com.facebook.fresco:animated-gif:1.10.0' // For animated GIF support
implementation "com.android.support:customtabs:${rootProject.ext.supportLibVersion}"
implementation 'androidx.browser:browser:1.0.0'
addUnimodulesDependencies()

testImplementation 'junit:junit:4.12'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import android.content.Intent;
import android.content.pm.ResolveInfo;
import android.net.Uri;
import android.support.customtabs.CustomTabsIntent;
import androidx.browser.customtabs.CustomTabsIntent;
import android.widget.Toast;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import android.content.Context;
import android.content.Intent;
import android.content.pm.ResolveInfo;
import android.support.annotation.NonNull;
import androidx.annotation.NonNull;
import java.util.List;

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package com.zulipmobile.notifications;

import android.os.Bundle;
import android.support.annotation.Nullable;
import androidx.annotation.Nullable;
import android.util.Log;
import com.facebook.react.bridge.*;
import com.google.firebase.iid.FirebaseInstanceId;
Expand Down
2 changes: 0 additions & 2 deletions android/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,6 @@ buildscript {
// https://developer.android.com/studio/releases/platforms
// Do edit .travis.yml when updating this.
compileSdkVersion = 29

supportLibVersion = "28.0.0"
}
repositories {
google()
Expand Down
3 changes: 3 additions & 0 deletions android/gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,6 @@
# This option should only be used with decoupled projects. More details, visit
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
# org.gradle.parallel=true

android.enableJetifier=true
android.useAndroidX=true
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@
"jest-environment-jsdom": "^24.9.0",
"jest-environment-jsdom-global": "^1.2.0",
"jest-extended": "^0.11.2",
"jetifier": "^1.6.5",
"lolex": "^5.1.1",
"metro-react-native-babel-preset": "^0.54.1",
"prettier": "^1.18.2",
Expand Down
6 changes: 6 additions & 0 deletions tools/postinstall
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,12 @@ EOF
pod install --project-directory="$ROOT_DIR/ios"
}

jetify() {
node_modules/.bin/jetify
}

pod_install

jetify

echo "tools/postinstall finished with no errors!"
5 changes: 5 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -5568,6 +5568,11 @@ jest@^24.9.0:
import-local "^2.0.0"
jest-cli "^24.9.0"

jetifier@^1.6.5:
version "1.6.5"
resolved "https://registry.yarnpkg.com/jetifier/-/jetifier-1.6.5.tgz#ea87324a4230bef20a9651178ecab978ee54a8cb"
integrity sha512-T7yzBSu9PR+DqjYt+I0KVO1XTb1QhAfHnXV5Nd3xpbXM6Xg4e3vP60Q4qkNU8Fh6PHC2PivPUNN3rY7G2MxcDQ==

js-levenshtein@^1.1.3:
version "1.1.6"
resolved "https://registry.yarnpkg.com/js-levenshtein/-/js-levenshtein-1.1.6.tgz#c6cee58eb3550372df8deb85fad5ce66ce01d59d"
Expand Down

0 comments on commit e433197

Please sign in to comment.