how to build an APK and separate libraries that the app loads dynamically

The short summary is: How do I build an APK and separate libraries (by which I mean sets of classes (and ideally, resources too) in some form, such as JAR, AAR or DEX files), but not include those libraries in the APK; instead, the app loads them at run time?

Detail

So my main question is how to build such an app (e.g. Gradle configuration). How do I specify which classes go into which JAR or DEX files? Do I create an Android Studio module for each DEX file I want to end up with?

A closely related question is how the Java code should then load the external libraries and access their classes at run time. For the latter, I'm hopeful that the approach shown at accessing to classes of app from dex file by classloader would work.

I've tried the instructions at https://developer.android.com/studio/projects/android-library.html, but that builds an APK that does include the dependency library.

I've also tried Multidex (https://developer.android.com/studio/build/multidex.html), but that doesn't seem to leave the developer any control over which classes go in which DEX file, and furthermore, packages them all into a single APK. AFAICT there is no way to control the loading of these DEX files at run time.

Background

There's a possibility of the "X-Y problem" here, so I'd better explain the background.

I'm building an app for a client. It's not going to be distributed through an app store, so it won't have access to the normal mechanism for updates. Instead, the client wants the app to be able to update itself by downloading new components of itself to replace the old components, without a need to manually sideload a new APK. The primary motive here is that the updates have to be easy for non-technical users. If the app can control the update process, it can make it smooth and guide the user.

Moreover, the app will be used in areas where internet access is scarce and expensive, so the client wants to be able to issue app updates in smaller chunks (e.g. 2MB) rather than forcing the user to re-download the whole app to receive a small update.

One aspect of the requirements I should mention, in case it matters, is that the libraries to be loaded at run time are supposed to live on a microSD card. This can also help with distribution of updates without internet access.

The current status of the app is that it's about 50% written: That is, a couple of earlier versions have been released, but the app now needs to be modified (restructured) to meet the above requirements, as well as others.


Solution 1:

This tutorial is a good start for external loading of DEX files. Only three small files of source (MainActivity.java, LibraryInterface.java, LibraryProvider.java) and it copies secondary_dex.jar from the assets folder, into internal application storage [outdex/dex] (the internet is also stated as possible in the tutorial). You have to build it with ant, because it uses custom build steps. I tried it, it works fine. Worth a look.
custom class loading in Dalvik and ART


UPDATE this code has been ported to Android Studio gradle (no need for ant). https://github.com/timrae/custom-class-loader
Tested ok. Copies com.example.toastlib.jar from the SDcard into internal application storage [outdex/dex],(not assets folder). ( you must read the README.md file in the project to build it).

Q: How do I add an Activity, I cannot add it to the manifest ?
A: Use Fragments, they don't need entries in the manifest.

Q: A Jar with resources that is meant to be added to an existing project needs to be able to merge its resources with the project's own resources (R.).
A: Hacks are available, Data file...
Packaging Android resource files within a distributable Jar file

Q: The external file has wrong permissions.
A: Import it.

Q: I need to add uses-permission.
A: Use API23 you can programmatically add uses-permissions (but they still need to be declared in the Manifest, so the new permissions model is probably not much use to us).

This section is for more general users (@LarsH has more specific requirements about updates), The example above is 17kb apk and 1 kb jar. You could put the bulk of you code in the one-off jar, and updates would involve just loading an new Apk (and then importing the bulk code jar, to minimise the data transfer). When the Apk gets too big, start again with a small Apk and everything migrated to another jar (import 2 jar's). You need to balance coding effort, user experience, maintainability, supportability, bandwidth, android rules, play store rules (if these words even exist ;O)).

NOTE Dalvik is discontinued

The successor of Dalvik is Android Runtime (ART), which uses the same bytecode and .dex files (but not .odex files), with the succession aiming at performance improvements transparent to the end users. The new runtime environment was included for the first time in Android 4.4 "KitKat" as a technology preview, and replaced Dalvik entirely in later versions; Android 5.0 "Lollipop" is the first version in which ART is the only included runtime.

Solution 2:

You could try to build multiple apk's with the same sharedUserId and the same process.

This is the plugin mechanism used by Threema

Edit: More about Theema

Threema has one main app and two plugins:

  • main app: https://play.google.com/store/apps/details?id=ch.threema.app
  • QR-Code Plugin: https://play.google.com/store/apps/details?id=ch.threema.qrscannerplugin
  • Voicemessage Plugin: https://play.google.com/store/apps/details?id=ch.threema.voicemessageplugin

Doing so the main app does not need the permissions for accessing the camera or microphone