前言
目前在做一个Android开发板上运行的App,在使用一个第三方的视频库的时候发现他的jar和so库是以apk形式作为一个插件安装在设备上的。看了下他的一些代码,知道了使用的是java 的反射机制实现的。研究了下,把现在项目里的opencv有关的大量so和java也打包成了一个插件用反射去调用。
这样做的好处显而易见,原本35M的apk安装包分离成了一个主程序和两个插件,主程序只有4M左右。插件一般是不需要更新的,那么我们更新是就只需要更新4M的主程序就可以了。大大节省了流量。
下面就说下是怎么玩的,还有一些学习过程中遇到的问题。
知识点
对象
获取对象的三种方式
- 方式一
try {
//直接写Bean.Class获取class对象,然后调用Class对象的newInstance()构造出一个Bean的对象
Class<?> mClass = Bean.class;
//上面一行代码执行完成时,会执行Bean对象的static初始化块
Object o = mClass.newInstance();
//也可以使用泛型
//Class<Bean> mClass = Bean.class;
//Bean o = mClass.newInstance();
} catch (Exception e) {
}
- 方式二
try {
//使用对象的全路径名,此方法也会执行对象的static初始化块
Class<?> mClass = Class.forName("com.***.***.Bean");
Object o = mClass.newInstance();
//如果想获取Class是不执行static初始化块可以使用下面的方法
//Class<?> mClass = Class.forName("com.***.***.Bean",false,getClassLoader());
} catch (Exception e) {
}
- 方式三
try {
//获取当Class时并不会执行静态初始化块,在newInstance时才会执行
Class<?> mClass =getClassLoader().loadClass("com.***.****.Bean");
Object o = mClass.newInstance();
} catch (Exception e) {
}
- 反射其他Apk里的方法
try {
//在android中反射其他apk的类只能使用这个方法(其实就是方式三),其他方法都会抛出ClassNotFoundExcption
//构造要反射apk的Context
Context pgCtx = getApplication().createPackageContext("其他apk的包名", (Context.CONTEXT_INCLUDE_CODE | Context.CONTEXT_IGNORE_SECURITY));
//使用Context获取ClassLoader,并获取Class对象
Class<?> mClass = pgCtx.getClassLoader().loadClass("com.***.***.类名");
//获取对象
Object o = mClass.newInstance();
} catch (Exception e) {
Log.e(TAG, "testD: ", e);
}
createPackageContext()方法里面的flag参数:
CONTEXT_INCLUDE_CODE :把要使用的代码加载到当前进程中(如果加载的代码,发送崩 溃,当前应用也会崩溃),如果代码不能安全的加载到当前进程,将会抛出java.lang.SecurityException 异常。如果不设置这个flag将不限制代码的加载,getClassLoader()将返回默认的加载器(也就是当前应用的类加载器,那么就不能加载其他apk的类了,所以这个flag是必设的)
CONTEXT_IGNORE_SECURITY:忽略一些安全限制
CONTEXT_IGNORE_SECURITY:受限制的
带参数的构造方法
try {
//和获取Class对象和无参数的使用方式一样
Class<?> mClass =getClassLoader().loadClass("com.***.****.Bean");
//参数是参数类型,这里传的是String和Integer
Constructor constructor = mClass.getConstructor(String.class,Integer.class);
//传入两个参数,反射出一个对象
Object o = constructor.newInstance("hello",1);
//如果不知道具体的构造方法可以使用以下方法
//Constructor<?>[] constructors = mClass.getConstructors();获取所有构造方法
//constructors[0].getGenericParameterTypes();获取构造方法的参数类型
// ...
} catch (Exception e) {
}
属性
try {
Class<?> mClass = Bean.class;
Object object = mClass.newInstance();
//对于公开的属性
Field fieldA = mClass.getField("a");
fieldA.set(object, 1);
//对于私有属性
Field fieldB = mClass.getDeclaredField("b");//获取属性
fieldB.setAccessible(true);//设置可访问性
fieldA.set(object, "hello");//设置值
//获取所有公开属性
Field[] publicFields = mClass.getFields();
//获取所有属性
Field[] allFields = mClass.getDeclaredFields();
} catch (Exception e) {
}
方法
try {
//和属性的用法差不多
Class<?> mClass = Class.forName("com.***.***.Bean", false, getClassLoader());
Object o = mClass.newInstance();
//公开方法,无参数,无返回值
Method testA = mClass.getMethod("testA");
testA.invoke(o);
//私有方法,无参数,无返回值
Method testB = mClass.getDeclaredMethod("testB");
testB.setAccessible(true);//设置可访问性
testB.invoke(o);
//私有方法,有参数,有boolean返回值,三个参数(String ,int ,Integer),
//在反射中int和Integer是不能混用的
Method testC = mClass.getDeclaredMethod("testB", String.class, int.class, Integer.class);
testC.setAccessible(true);
boolean result = (Boolean) testC.invoke(o, "hah", 1, 1);//调用
} catch (Exception e) {
}
回调
在android中的应用场景中,可能我们需要一个插件去完成一个任务,完成后返回结果,这时就需要回调了。其实也不能称之为回调。简单说就是传一个对象到其他apk,然后其他apk拿到这个对象后,反射这个对象的方法,并调用。当然,前提是两个apk都是你写的。
例子
调用端
下面是调用端的工具类:
public class ReflectTools {
private static final String TAG = "ReflectTest";
private static Method methodTestA = null;
private static Method methodTestB = null;
private static Object object = null;
public static void init(Application application) {
try {
//com.test.****是被调用apk的包名
Context pgCtx = application.createPackageContext("com.test.****", (Context.CONTEXT_INCLUDE_CODE | Context.CONTEXT_IGNORE_SECURITY));
//com.test.****.ReflectInvoked被反射的类名
Class<?> mClass = pgCtx.getClassLoader().loadClass("com.test.****.ReflectInvoked");
//反射出来一个对象
object = mClass.newInstance();
//反射ReflectInvoked类中的方法,一个参数,类型是String
methodTestB = mClass.getMethod("testA", String.class);
//反射ReflectInvoked类中的方法,两个参数,类型是String和int(顺序不能乱)
methodTestA = mClass.getDeclaredMethod("testB", String.class, int.class);
} catch (Exception e) {
e.printStackTrace();
}
}
public static void testA(String msg) {
try {
//调用反射创建的object对象的testA()方法,传入参数msg,空返回值
methodTestB.invoke(object, msg);
} catch (Exception e) {
Log.e(TAG, "testA: ", e);
}
}
public static int testB(String msg,int code) {
try {
//调用反射创建的object对象的testB()方法,传入参数msg,code,int型返回值
return (int) methodTestA.invoke(object, msg);//调用
} catch (Exception e) {
Log.e(TAG, "send: ", e);
return -1;
}
}
/**
* 设置一个回调
*/
public static void setCallBack(CallBack callBack){
try {
methodCallBack.invoke(object, callBack);
} catch (Exception e) {
Log.e(TAG, "testA: ", e);
}
}
}
这是一个接口,用来让被调用端 回调 调用端。
其实就是两边约定某个类具有某个方法,那么一边有了这个类的对象之后就可以使用反射调用约定的方法
public interface CallBack {
//调用setCallBack()方法,传入一个CallBack的实现类,被调用端便可以回调onResult()方法。
void onResult(String result);
}
使用起来很简单,如下:
public class ReflectTestActivity extends Activity implements View.OnClickListener {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ReflectTools.init(getApplication());
ReflectTools.setCallBack(new CallBack() {
@Override
public void onResult(String result) {
//由被调端 有处理结果时 回调
//do something
}
});
}
@Override
public void onClick(View v) {
if (v.getTag() == "testA") {
//调用testA()
ReflectTools.testA("hello");
} else {
//调用testB()
int a = ReflectTools.testB("hello",0);
}
}
}
被调用端
public class ReflectInvoked {
private static final String TAG = "ReflectInvoked";
private Object callBack;
/**
* 设置一个回调
* @param o 回调对象
*/
public void setCallBack(Object o) {
callBack = o;
}
/**
* 调用本方法10s后回调调用者的methodForCallBack()方法
* @param msg 参数
*/
public void testA(String msg) {
Log.d(TAG, "testA() called with: msg = [" + msg + "]");
new Thread(new Runnable() {
@Override
public void run() {
int i = 0;
while (i++ < 10)
SystemClock.sleep(1000);
callInvoker("hello invoker");
}
}).start();
}
public int testB(String msg, int flag) {
Log.d(TAG, "testB() called with: msg = [" + msg + "], flag = [" + flag + "]");
//do something
return 1;
}
/**
* 回调本类发射的使用者,如果设置了CallBack;
* @param result 事件处理结果
*/
private void callInvoker(String result) {
if (null == callBack)
return;
try {
Method method = callBack.getClass().getMethod("onResult",String.class);
method.invoke(callBack,result);
} catch (Exception ignored) {
}
}
}
over…