Skip to content

Commit

Permalink
Update README.md
Browse files Browse the repository at this point in the history
  • Loading branch information
Catherine22 authored Jun 20, 2021
1 parent 2897004 commit da00066
Showing 1 changed file with 25 additions and 25 deletions.
50 changes: 25 additions & 25 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,35 +2,35 @@

This repository hosts an example of dynamically loading an APK and in-depth documentation.

This repo demonstrates how APKs can be loaded into an installed app via ClassLoader. Users can automatically update their application without reinstalling it. Once developers fix any bug or update new features, there is no need to re-build your APK (ClassLoader APK), developers upload patch APKs to their server.
This repo demonstrates how APKs can be loaded into an installed app via ClassLoader. Users can automatically update their application without reinstalling it. Once developers fix any bug or update new features, there is no need to re-build your APK (ClassLoader APK). Developers upload patch APKs to their server.

## Use case

1. Build a ClassLoader APK and patch APK(s).
2. Upload the patch APK to your server, only users who have installed ClassLoader APK can download it.
2. Upload the patch APK to your server. Only users who have installed ClassLoader APK can download it.
3. Users install ClassLoader APK.
4. Users open ClassLoader app and download the patch APK(s) which include(s) main business logic of your app.
4. Users open the ClassLoader app and download the patch APK(s), which include(s) the main business logic of your app.
5. Verify patch APK(s) and load classes into the ClassLoader app.

Next, you will repeat the following steps again and again in your development life cycle:

1. Add new features or fix bugs
2. Build patch APK
3. Update the patch APK in your server
4. Users open ClassLoader app to download the patch APK and all the updates will be loaded to the installed app.
4. Users open the ClassLoader app to download the patch APK. The app will load all the changes.

To do so, you must create two projects, ClassLoader and patch APK which combines features and/or layouts.
To do so, you must create two projects, ClassLoader and patch APK, which combines features and/or layouts.

### The main job of ClassLoader app
### The main job of the ClassLoader app

- Download and validate the patch APK(s) automatically.
- Load classes of the patch APK(s).
- Shrink your app and hide your code in patch APK(s).
- Switch patch APKs. I.e. You can run each patch APK independently without polluting their resources.
- Switch patch APKs. I.e., You can run each patch APK independently without polluting their resources.

### The main job of patch APK(s)

- Provide features like instant messaging, taking photos, scanning QR codes, login or anything.
- Provide features like instant messaging, taking photos, scanning QR codes, login, or anything.
- UI components and business logic are both supported.

In this demo, ClassLoader app loads [Resource1.apk] and [Resource2.apk].
Expand All @@ -42,21 +42,21 @@ In this demo, ClassLoader app loads [Resource1.apk] and [Resource2.apk].
There are three classLoaders used when running JVM:

1. Bootstrap class loader
Loading classes from <JAVA_HOME>/jre/lib directory
Loads classes from <JAVA_HOME>/jre/lib directory
2. Extension class loader
Loading classes from <JAVA_HOME>/jre/lib/ext directory
Loads classes from <JAVA_HOME>/jre/lib/ext directory
3. System class loader
Loading classes from system class path (which is the same as the environment variable - CLASSPATH).
Loads classes from the system classpath (which is the same as the environment variable - CLASSPATH).

Besides, you could create your own class loader, aka. 'User-defined class loaders'.
Besides, you could create your ClassLoader, aka. 'User-defined class loaders'.

## Android ClassLoader

Android virtual machine loads classes just like the way Java does, but they're slightly different.

### Dex

In an Android device, it packages your classes into one (or more) dex file(s) which is (are) located in an APK, and optimizes those dex files loading with Dalvik.
In an Android device, it packages your classes into one or more dex files located in an APK and optimizes those dex files loading with Dalvik.

![enter description here][1]

Expand Down Expand Up @@ -100,11 +100,11 @@ public DexClassLoader(String dexPath, String optimizedDirectory,

It is noticeable that:

1. optimizedDirectory references to optimizes dex files.
2. PathClassLoader does not accept optimizedDirectory argument, so it is forced to associate the original optimized dex files.
3. You can pass the optimizedDirectory argument to DexClassLoader(). I.e. you can cache optimized dex files placed in internal storage.
1. The optimizedDirectory references to optimize dex files.
2. PathClassLoader does not accept the optimizedDirectory argument, so it is forced to associate the original, optimized dex files.
3. You can pass the optimizedDirectory argument to DexClassLoader(). I.e., you can cache optimized dex files placed in internal storage.

That's why you can call DexClassLoader to load the user-defined APK, dex and jar files whereas PathClassLoader is responsible for loading the installed Apk.
That's why you can call DexClassLoader to load the user-defined APK, dexes, and .jar files, whereas PathClassLoader is responsible for loading the installed Apk.

The code snippet shows how ClassLoader works:

Expand Down Expand Up @@ -198,16 +198,16 @@ Play with some scenarios of class loaders.
- User-defined classes and libraries are loaded via PathClassLoader.
- Core java libraries such as `java.lang.String` are loaded via BootClassLoader. Thus, you cannot create a String class and replace `java.lang.String` no matter they share the same package name and class name. **Android believes that they are two different classes because they are from different class loaders.**

> In Java, object A equates to Object B when they share the same package name, class name and **ClassLoader**
> In Java, object A equates to Object B when they share the same package name, class name, and **ClassLoader**
## How to use this app

- This app is used to load classes from another APK, you can launch activities or call methods wrapped in another APK. That's why there is nothing but updating loading and verifying APKs in this app.
- This app is used to load classes from another APK. You can launch activities or call methods wrapped in another APK. That's why there is nothing but updating loading and verifying APKs in this app.
- Use `getClassLoader().loadClass()` to get activities from another APK, and access methods or fields by using Java reflection.

Here are some reflection examples:

Assume Utils is the latest released feature, it will be loaded to your ClassLoader app.
Assume Utils is the latest released feature. It will be loaded to your ClassLoader app.

```java
package com.catherine.resource1;
Expand Down Expand Up @@ -241,7 +241,7 @@ public class Utils {
}
```

However, you cannot find the `Utils` class in your ClassLoader app. You cannot import it like any other classes as usual. You need to leverage Java reflection to access it.
However, you cannot find the `Utils` class in your ClassLoader app. You cannot import it like any other classes as usual. You will need to leverage Java reflection to access it.

1. Find the class

Expand Down Expand Up @@ -331,7 +331,7 @@ try {
}
```

4. Or maybe you don't want to use any methods or fields, you just launch the activity
4. Or maybe you don't want to use any methods or fields. You launch the activity

```java
try {
Expand Down Expand Up @@ -367,7 +367,7 @@ multiDexEnabled false

4. Register patch APK's activities and permissions in ClassLoader app's Manifest

Android studio probably throws some errors likes 'Unresolved package...', just ignore them. And don't forget to add the prefix of your activity name with its package.
Android studio probably throws some errors likes 'Unresolved package...', ignore them. And don't forget to add the prefix of your activity name with its package.

E.g.

Expand All @@ -378,7 +378,7 @@ E.g.

5. Load layouts

In your patch APK, you cannot map the view by calling `setContentView(@LayoutRes int layoutResID)`. Your ClassLoader app cannot find your resources via that method. You most use `View.inflate()`. Because applications access resources via the instance of Resource, new loaded resources will not be found in the original Resource object.
In your patch APK, you cannot map the view by calling `setContentView(@LayoutRes int layoutResID)`. Your ClassLoader app cannot find your resources via that method. You must use `View.inflate()`. Because applications access resources via Resource, they cannot refer to new resources in the original Resource object.

E.g.

Expand All @@ -398,7 +398,7 @@ setContentView(View.inflate(getApplicationContext(), R.layout.activity_main, nul

Typically, you don't need to load multiple patch APKs. Multiple patch APKs might cause resource conflicts.

Assuming you imports the `support-v4` library in both APK1 and APK2, and then you load APK1 first, then you are going to load APK2.
Assuming you import the `support-v4` library in both APK1 and APK2, and then you load APK1 first, you will load APK2.
You will find your ClassLoader app crashes or some resource errors happens. To fix it, you must have APK2 run on another process so that you can perfectly release loaded resources by terminating the process before you switch to another patch APK.

```java
Expand Down

0 comments on commit da00066

Please sign in to comment.