Skip to content

Commit

Permalink
added fields and updated README
Browse files Browse the repository at this point in the history
  • Loading branch information
Catherine22 committed Feb 13, 2017
1 parent 3635161 commit 7b230c4
Show file tree
Hide file tree
Showing 2 changed files with 150 additions and 9 deletions.
143 changes: 134 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,6 @@ ClassLoader
===================

Loading apks from external storage by another app (ClassLoader). You can automatically update you app without reinstalling because you do everything in your apk. It's just like 'hotfix'.


----------
# Instruction

This application is used to load classes from another apks, you can launch activities and call methods from another apk, we don't put logic in this app.

----------
# Features

1. Load codes from another Apk, including classes, jars, etc.
Expand All @@ -18,7 +10,133 @@ This application is used to load classes from another apks, you can launch activ
4. Automatic updates
5. Switch apk, it means that you can load more than an apk. But in general, I think you just need to package all of your logic into an apk and loading an apk is fair enough.

----------


# Illustration

This application is used to load classes from another apks, you can launch activities and call methods from another apk, we don't put logic in this app.

Using getClassLoader().loadClass() to get activities from an apk, and calling methods or fields by java reflection.

Here're some reflection examples:

Let's say Utils is what class you want to reflect, your class looks like...
``` java
package com.catherine.resource1;

public class Utils {
public static String myStaticField = "Default field";

public static String getInputStringStatic(String value) {
return value;
}

public static int getInputIntStatic(Integer value) {
return value;
}

public static String getStringValueStatic() {
return "(static) Hello, I'm apk1";
}

public static int getIntValueStatic() {
return 1234;
}

public String getStringValue() {
return "Hello, I'm apk1";
}

public int getIntValue() {
return 4321;
}
}
```

And now you want to call methods of Utils. But you can't find Utils in your project, so you can't just import it like any other classes in your project.
So here we use reflection to resolve the problem.

- First, find the class

``` java
private Class<?> apkUtils;

try {
apkUtils = getClassLoader().loadClass("com.catherine.resource1.Utils");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
```

- Call methods
``` java
try {
//set null as the first parameter of invoke() while invoking a static method.
//static String getInputStringStatic(String value)
Method getInputStringStatic = apkUtils.getDeclaredMethod("getInputStringStatic", String.class);
String returns1 = (String) getInputStringStatic.invoke(null, "Hello, I'm your classLoader");
Log.d("Reflection" , returns1);

//static int getInputIntStatic(Integer value)
Method getInputIntStatic = apkUtils.getDeclaredMethod("getInputIntStatic", Integer.class);
int returns2 = (Integer) getInputIntStatic.invoke(null, 86400);
Log.d("Reflection" , returns2 + "");

//static String getStringValueStatic()
Method getStringValueStatic = apkUtils.getDeclaredMethod("getStringValueStatic");
String returns3 = (String) getStringValueStatic.invoke(null);
Log.d("Reflection" , returns3);

//static int getIntValueStatic()
Method getIntValueStatic = apkUtils.getDeclaredMethod("getIntValueStatic");
int returns4 = (Integer) getIntValueStatic.invoke(null);
Log.d("Reflection" , returns4 + "");


//Get constructor for not-static method
Constructor<?> cons = apkUtils.getConstructor();

//String getStringValue()
Method getStringValue = apkUtils.getDeclaredMethod("getStringValue");
String returns5 = (String) getStringValue.invoke(cons.newInstance());
Log.d("Reflection" , returns5);

//int getIntValue()
Method getIntValue = apkUtils.getDeclaredMethod("getIntValue");
int returns6 = (Integer) getIntValue.invoke(cons.newInstance());
Log.d("Reflection" , returns6 + "");

} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (NullPointerException e) {
e.printStackTrace();
}
```

- Or set fields

``` java
try {
Field myStaticField = apkUtils.getDeclaredField("myStaticField");
Log.d("Reflection" , myStaticField.getName() + ":\t" + myStaticField.get(null));

myStaticField.setAccessible(true);//You can update the field.
myStaticField.set(null, "new value");
myStaticField.setAccessible(false);

Log.d("Reflection" , myStaticField.getName() + " updated:\t" + myStaticField.get(null));
} catch (NullPointerException e) {
e.printStackTrace();
} catch (NoSuchFieldException e) {
e.printStackTrace();
}
```


# Warning
Expand Down Expand Up @@ -56,5 +174,12 @@ Legal
setContentView(View.inflate(getApplicationContext(), R.layout.activity_main, null));
```

# Issues

**It'll be find if you just load an apk.
But if you try to load multi-apks, there still are some problems I haven't fixed.**

If there're some libraries likes support-v4, zxing, whatever, you imported these libraries to both apks (in this case, it means resource1.apk and resource2.apk). And when you call methods or launch activities that are included the same libraries, it'll crash because resources're not found.

So there's a workaround here that you must not use the same libraries in any apks you wanna load or you just load an apk.
[1]: https://raw.githubusercontent.com/Catherine22/ClassLoader/master/screen%20shot.png
16 changes: 16 additions & 0 deletions app/src/main/java/com/catherine/classloader/MainActivity.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import android.widget.TextView;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

Expand Down Expand Up @@ -93,6 +94,19 @@ public void onClick(View v) {
int returns6 = (Integer) getIntValue.invoke(cons.newInstance());
history = tv_console.getText().toString();
tv_console.setText("getIntValue:\t" + returns6 + "\n----\n" + history);

//Fields
Field myStaticField = apkUtils.getDeclaredField("myStaticField");

history = tv_console.getText().toString();
tv_console.setText(myStaticField.getName() + ":\t" + myStaticField.get(null) + "\n----\n" + history);

myStaticField.setAccessible(true);//You can update the field.
myStaticField.set(null, "new value");
myStaticField.setAccessible(false);

history = tv_console.getText().toString();
tv_console.setText(myStaticField.getName() + " updated:\t" + myStaticField.get(null) + "\n----\n" + history);
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
Expand All @@ -103,6 +117,8 @@ public void onClick(View v) {
e.printStackTrace();
} catch (NullPointerException e) {
e.printStackTrace();
} catch (NoSuchFieldException e) {
e.printStackTrace();
}
break;
case R.id.bt_launch_apk:
Expand Down

0 comments on commit 7b230c4

Please sign in to comment.