前言
在看此文章前,请先查看下面几篇文章,理解一下reflect和Systemproperties系统属性的操作:
1.java核心技术之reflect(一):一个系统学习reflect的Demo(精)
2.github项目之读取系统属性
3.android开发笔记之system.prop使用(1)
问题
公司在搞应用apk化,也就是把android提供的应用导入androidstudio中,使用androidstudio来开发,并生成apk,内置到各个应用中。
这就有一些问题:
比如在androidstudio中,系统属性应用Systemproperties的操作api就使用不了。
再比如一些应用的功能,调用的接口的库文件不能导入到androidstudio,还有一些底层的比较特殊的接口,与硬件平台相关的接口(比如和MTK或高通相关的camera接口),当然了,还有就是一些private,protected相关的方法的调用。
此时,我们就可以使用reflect来解决这些要在androidstudio单独直接使用而不过依赖平台的接口,这样就可以直接在androidstudio中编译生成apk,直接内置到不同的平台上使用了。
说明,这是公司别的部门写的代码,我将其提出来,再重构了一下,方便自己来使用.
关键api
我们提供一个帮助类来专门处理此类问题ApiHelper.java:
此类,只是将reflect类的方便再封装了一下,没有什么特别的地方.
import android.util.Log;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class ApiHelper {
private static final String TAG = "ApiHelper";
public static final String FEATURE_CONFIG_BOOL_VALUE = "getBooleanValue";
public static final String FEATURE_CONFIG_INT_VALUE = "getIntValue";
public static final String SYSTEMPROPERTIES = "android.os.SystemProperties";
public static Class<?> getClass(String className) {
Class<?> class1 = null;
try {
class1 = Class.forName(className);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
return class1;
}
public static boolean hasField(Class<?> klass, String fieldName) {
try {
klass.getDeclaredField(fieldName);
return true;
} catch (NoSuchFieldException e) {
return false;
}
}
public static boolean hasField(String className, String fieldName) {
Class<?> cClass = getClass(className);
try {
cClass.getDeclaredField(fieldName);
return true;
} catch (NoSuchFieldException e) {
return false;
}
}
public static boolean hasMethod(String className, String methodName, Class<?>... parameterTypes) {
Class<?> klass = null;
try {
klass = Class.forName(className);
return hasMethod(klass,methodName,parameterTypes);
} catch (ClassNotFoundException e) {
e.printStackTrace();
return false;
}
}
public static boolean hasMethod(Class<?> klass, String methodName, Class<?>... paramTypes) {
try {
klass.getDeclaredMethod(methodName, paramTypes);
return true;
} catch (NoSuchMethodException e) {
return false;
}
}
public static Object getField(String className, String fieldName) {
Class<?> clazz = getClass(className);
Object result = null;
if (clazz != null) {
Field field = null;
try {
field = clazz.getField(fieldName);
// field = clazz.getDeclaredField(fieldName);
} catch (NoSuchFieldException e) {
e.printStackTrace();
Log.e(TAG, "getField:" + fieldName + " failed");
}
if (field != null) {
try {
result = field.get(null);
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
return result;
}
public static int getIntFieldIfExists(Class<?> klass, String fieldName, Class<?> obj, int defaultVal) {
try {
Field field = klass.getDeclaredField(fieldName);
return field.getInt(obj);
} catch (Exception e) {
return defaultVal;
}
}
public static Method getMethod(String className, String methodName, Class<?>... parameterTypes) {
Method method = null;
try {
Class<?> clazz = Class.forName(className);
method = clazz.getMethod(methodName, parameterTypes);
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (NoSuchMethodException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return method;
}
/* <p>If the underlying method is static, then the specified {@code obj}
* argument is ignored. It may be null.
*/
public static Object invokeMethod(Method method, Object obj, Object... parameters) {
Object retObj = null;
if (null == method /* || null == obj */)
return null;
try {
method.setAccessible(true);
retObj = method.invoke(obj, parameters);
} catch (IllegalArgumentException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InvocationTargetException e) {
// TODO Auto-generated catch block
System.out.print(e.getTargetException());
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
return retObj;
}
public static Constructor<?> getConstructor(String className, Class<?>... parameterTypes) {
Class<?> clazz = null;
try {
clazz = Class.forName(className);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
return getConstructor(clazz,parameterTypes);
}
public static Constructor<?> getConstructor(Class<?> class1, Class<?>... parameterTypes) {
Constructor<?> constructor = null;
if (class1 != null) {
try {
constructor = class1.getConstructor(parameterTypes);
} catch (NoSuchMethodException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalArgumentException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
return constructor;
}
public static Object getInstance(Constructor<?> constructor, Object... parameters) {
if (null == constructor) return null;
Object resultInstance = null;
if (null != constructor) {
if (null != parameters)
try {
resultInstance = constructor.newInstance(parameters);
} catch (InstantiationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InvocationTargetException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
else
try {
resultInstance = constructor.newInstance();
} catch (InstantiationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InvocationTargetException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
return resultInstance;
}
}
使用Demo
我在一个Activity中测试了一下主要api:
public class MainActivity extends AppCompatActivity {
private static final String TAG = "debug_hxm";
private AppCompatActivity context = null;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
testReflect();
}
private void testReflect() {
context = this;
String className = "android.os.SystemProperties";
String fieldName = "PROP_NAME_MAX";
String methodNameSet = "set";
String methodNameGet = "get";
String propFinger = "ro.build.fingerprint";
//String propTest = "ro.pt.setMTPdefault";
String propKey = "bluetooth.fw.rpa";
//test field
boolean hasField = ApiHelper.hasField(className,fieldName);
Log.i(TAG,className+"----"+fieldName+"---"+hasField);
Log.i(TAG,"---------------------------------------");
//test method
boolean hasMethod = ApiHelper.hasMethod(className,methodNameGet,String.class);
Log.i(TAG,className+"----"+methodNameGet+"---"+hasMethod);
hasMethod = ApiHelper.hasMethod(className,methodNameSet,String.class,String.class);
Log.i(TAG,className+"----"+methodNameSet+"---"+hasMethod);
hasMethod = ApiHelper.hasMethod(className,methodNameSet,String.class,int.class);
Log.i(TAG,className+"----"+methodNameSet+"---"+hasMethod);
Log.i(TAG,"---------------------------------------");
//test invoke static method
Method method = ApiHelper.getMethod(className,methodNameGet,String.class);
String fingerprint = (String) ApiHelper.invokeMethod(method,null,propFinger);
Log.i(TAG,"fingerprint:"+fingerprint);
Log.i(TAG,"---------------------------------------");
String propValue = (String) ApiHelper.invokeMethod(method,null,propKey);
Log.i(TAG,"propValue:"+propValue);
method = ApiHelper.getMethod(className,methodNameSet,String.class,String.class);
ApiHelper.invokeMethod(method,null,propKey,"false");
method = ApiHelper.getMethod(className,methodNameGet,String.class);
propValue = (String)ApiHelper.invokeMethod(method,null,propKey);
Log.i(TAG,"after set ,the propValue is:"+propValue);
Log.i(TAG,"---------------------------------------");
//test invoke method
//context.onDestroy();
className = "android.support.v7.app.AppCompatActivity";
fieldName = "onDestroy";
method = ApiHelper.getMethod(className,fieldName);
ApiHelper.invokeMethod(method,context);
Log.i(TAG,"--call android.support.v7.app.AppCompatActivity onDestroy()---");
Log.i(TAG,"---------------------------------------");
}
}
日志输出
I/debug_hxm: android.os.SystemProperties----PROP_NAME_MAX---true
I/debug_hxm: ---------------------------------------
I/debug_hxm: android.os.SystemProperties----get---true
I/debug_hxm: android.os.SystemProperties----set---true
I/debug_hxm: android.os.SystemProperties----set---false
I/debug_hxm: ---------------------------------------
I/debug_hxm: fingerprint:TINNO/k200/k200:7.0/NRD90M/1513592190:userdebug/release-keys
I/debug_hxm: ---------------------------------------
I/debug_hxm: propValue:true
I/debug_hxm: after set ,the propValue is:true
I/debug_hxm: ---------------------------------------
I/debug_hxm: --call android.support.v7.app.AppCompatActivity onDestroy()---
I/debug_hxm: ---------------------------------------
日志分析
日志:
I/debug_hxm: android.os.SystemProperties----PROP_NAME_MAX---true
I/debug_hxm: ---------------------------------------
此部分日志,我们测试类android.os.SystemProperties中是否有PROP_NAME_MAX变量,测试结果表明有.事实了也是,在类android.os.SystemProperties中:
public static final int PROP_NAME_MAX = 31;
相关的测试api为:
//test field
boolean hasField = ApiHelper.hasField(className,fieldName);
Log.i(TAG,className+"----"+fieldName+"---"+hasField);
Log.i(TAG,"---------------------------------------");
日志:
I/debug_hxm: android.os.SystemProperties----get---true
I/debug_hxm: android.os.SystemProperties----set---true
I/debug_hxm: android.os.SystemProperties----set---false
此剖分日志为我们测试类android.os.SystemProperties中的方法:
是否存在get(String),set(String,String),set(String,int)三种类型的方法.
测试结果为存在get(String),set(String,String)方法,但是不存在set(String,int)类型的方法.
相关的测试api为:
boolean hasMethod = ApiHelper.hasMethod(className,methodNameGet,String.class);
Log.i(TAG,className+"----"+methodNameGet+"---"+hasMethod);
hasMethod = ApiHelper.hasMethod(className,methodNameSet,String.class,String.class);
Log.i(TAG,className+"----"+methodNameSet+"---"+hasMethod);
hasMethod = ApiHelper.hasMethod(className,methodNameSet,String.class,int.class);
Log.i(TAG,className+"----"+methodNameSet+"---"+hasMethod);
日志:
I/debug_hxm: fingerprint:TINNO/k200/k200:7.0/NRD90M/1513592190:userdebug/release-keys
此部分日志为我们测试如何读取系统属性ro.build.fingerprint的值:
相关测试api:
Method method = ApiHelper.getMethod(className,methodNameGet,String.class);
String fingerprint = (String) ApiHelper.invokeMethod(method,null,propFinger);
Log.i(TAG,"fingerprint:"+fingerprint);
日志:
I/debug_hxm: propValue:true
I/debug_hxm: after set ,the propValue is:true
此部分日志为我们测试如何读写系统属性bluetooth.fw.rpa的值,测试的日志表明,reflect只能成功的读取系统属性的值,但是修改系统属性的值是做不到的,这是因为和系统权限相关的限制.
我查看了一下所有app源码,确实是只使用这种方式来读取系统属性性,没有修改系统属性值的地方.这是此方式的一个局限性吧.
相关测试api:
String propValue = (String) ApiHelper.invokeMethod(method,null,propKey);
Log.i(TAG,"propValue:"+propValue);
method = ApiHelper.getMethod(className,methodNameSet,String.class,String.class);
ApiHelper.invokeMethod(method,null,propKey,"false");
method = ApiHelper.getMethod(className,methodNameGet,String.class);
propValue = (String)ApiHelper.invokeMethod(method,null,propKey);
Log.i(TAG,"after set ,the propValue is:"+propValue);
Log.i(TAG,"---------------------------------------");
日志:
I/debug_hxm: --call android.support.v7.app.AppCompatActivity onDestroy()---
上面的测试,都是测试类的static方法.所以我加了一个测试对应对象的方法的api.这其实也是reflect使用的非常多的一种情况.
在类android.support.v7.app.AppCompatActivity中,onDestroy方法为protected 类型.使用对象直接调用是会报错的.
protected void onDestroy() {
super.onDestroy();
getDelegate().onDestroy();
}
所以我们使用context对象使用reflect的方式调用其方法,就可以正常调用.
相关测试api:
//test invoke method
//context.onDestroy();
className = "android.support.v7.app.AppCompatActivity";
fieldName = "onDestroy";
method = ApiHelper.getMethod(className,fieldName);
ApiHelper.invokeMethod(method,context);
Log.i(TAG,"--call android.support.v7.app.AppCompatActivity onDestroy()---");