Android P上引入了针对非公开API的限制,对开发者来说,特别是对于需要维护具有众多“黑科技”的项目的开发者来说,这绝对是一个灾难。
目前Google商店对于应用Target SDK版本的要求是29,假设你的应用如果需要在Google商店上架,意味着你不得不去适配非公开API的限制。
要把项目里面所有的非公开API都替换为其它的实现?看起来是最终最好的方案,但是实现起来困难诸多,工作量也非常大。
我们可以通过使用元反射的形式,即借助系统的类,来反射我们需要的东西,直接上代码:
/**
* reflection util
*/
public class ReflectionUtil {
public static final String TAG = "ReflectionUtil";
private static Method sForNameMethod;
private static Method sGetDeclaredMethod;
private static Method sGetFieldMethod;
/**
* init reflection and cache it
*/
static {
try {
sForNameMethod = Class.class.getDeclaredMethod("forName", String.class);
sGetDeclaredMethod = Class.class
.getDeclaredMethod("getDeclaredMethod", String.class, Class[].class);
sGetFieldMethod = Class.class.getDeclaredMethod("getDeclaredField", String.class);
} catch (Throwable t) {
t.printStackTrace();
}
}
/**
* get filed
*
* @param src the instance
* @param clzName the class name
* @param filedName the filed name
* @param defObj the default val
* @return the filed of the instance
*/
@NonNull
public static Object getFiledObj(@NonNull Object src, @NonNull String clzName,
@NonNull String filedName, @NonNull Object defObj) {
Object result = defObj;
try {
Field field = getFiled(clzName, filedName);
if (field != null) {
result = field.get(src);
}
} catch (Throwable t) {
t.printStackTrace();
}
return result;
}
/**
* get filed
*
* @param clzName the class name
* @param filedName the filed name
* @return the class filed
*/
@Nullable
public static Field getFiled(@NonNull String clzName, @NonNull String filedName) {
Field field = null;
if (canReflection()) {
try {
Class<?> clz = (Class<?>) sForNameMethod.invoke(null, clzName);
field = (Field) sGetFieldMethod.invoke(clz, filedName);
field.setAccessible(true);
} catch (Throwable t) {
t.printStackTrace();
}
}
return field;
}
/**
* set filed to the instance
*
* @param src the instance
* @param clzName the class name
* @param filedName the filed name
* @param tarObj target object
*/
public static void setFiled(@NonNull Object src, @NonNull String clzName,
@NonNull String filedName, Object tarObj) {
try {
Field field = getFiled(clzName, filedName);
if (field != null) {
field.set(src, tarObj);
}
} catch (Throwable t) {
t.printStackTrace();
}
}
/**
* get method
*
* @param clzName the class name
* @param methodName the method name
* @param clzArgs method params
* @return method
*/
@Nullable
public static Method getMethod(@NonNull String clzName, @NonNull String methodName,
Class[] clzArgs) {
Method method = null;
if (canReflection()) {
try {
Class<?> clz = (Class<?>) sForNameMethod.invoke(null, clzName);
method = (Method) sGetDeclaredMethod.invoke(clz, methodName, clzArgs);
method.setAccessible(true);
} catch (Throwable t) {
t.printStackTrace();
}
}
return method;
}
/**
* invoke method
*
* @param src the instance
* @param clzName the class name
* @param methodName the method name
* @param clzArgs args class array
* @param objArgs args
* @return obj
*/
public static Object invokeMethod(@NonNull Object src, @NonNull String clzName,
@NonNull String methodName, Class[] clzArgs, Object... objArgs) {
Object result = null;
try {
Method method = getMethod(clzName, methodName, clzArgs);
if (method != null) {
result = method.invoke(src, objArgs);
}
} catch (Throwable t) {
t.printStackTrace();
}
return result;
}
/**
* check can reflation
*
* @return can use reflection or no
*/
private static boolean canReflection() {
boolean canReflection = true;
if (sForNameMethod == null || sGetDeclaredMethod == null || sGetFieldMethod == null) {
canReflection = false;
}
return canReflection;
}
}
使用的方法也不难,例如我们需要反射下面的方法:
package android.telephony;
public class TelephonyManager {
@RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
public int getDataNetworkType(int subId) {
}
}
调用代码如下:
Object obj = ReflectionUtil.invokeMethod(tm, "android.telephony.TelephonyManager",
"getDataNetworkType", new Class[]{int.class}, defaultDataSubId);
int networkType = TelephonyManager.NETWORK_TYPE_UNKNOWN;
if (obj instanceof Integer) {
networkType = (Integer) obj;
}
转载请注明出处:https://www.jianshu.com/p/058fe5a62607
元反射方式来自于文章:另一种绕过 Android P以上非公开API限制的办法
网友评论