Android 反射

前言

目前在做一个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…

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值