前言
此处为语雀内容卡片,点击链接查看:www.yuque.com/youer-ycy0r…
简介
Java的反射是指程序在运行期可以拿到一个对象的所有信息
使用
反射主要分为以下几个步骤
1. 获取Class对象
JVM在加载类的时候,会为每个类生成一个独一无二的Class对象
获取方式有以下几种
//name = Test.class.getDeclaredField("name");
//name = test.getClass().getDeclaredField("name");
name = Class.forName("com.example.app.MainActivity$Test").getDeclaredField("name");
2. 操作fileds
Test test = new Test("xxx");
Field name;
try {
//name = Test.class.getDeclaredField("name");
//name = test.getClass().getDeclaredField("name");
name = Class.forName("com.example.app.MainActivity$Test").getDeclaredField("name");
name.setAccessible(true);
Log.d("test", (String)name.get(test));
} catch (NoSuchFieldException | IllegalAccessException | ClassNotFoundException e) {
e.printStackTrace();
}
class Test {
private String name;
public Test(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
-
getDeclaredField :获取本类的任何field
-
getField:获取本类和基类的public field
-
获取基类非public值,只能通过基类class的getDeclaredField
3. 调用method
// 无参数的方法
Method getName = Class.forName("com.example.app.MainActivity$Test").getMethod("getName");
Log.d("test", (String)getName.invoke(test));
// 有参数的方法
Method setName = Class.forName("com.example.app.MainActivity$Test").getMethod("setName", String.class);
setName.invoke(test, "sdaasda");
Log.d("test", test.getName());
动态代理
在程序运行期动态创建某个interface的实例,通过动态代理可以实现一个方法/类的hook
比如hook点击事件
public class HookOnClickListenerHelper {
public static View.OnClickListener hook(Context context, final View v) {//
return (OnClickListener)Proxy.newProxyInstance(v.getClass().getClassLoader(),
new Class[] {OnClickListener.class},
new ProxyHandler(new ProxyOnClickListener()));
}
static class ProxyHandler implements InvocationHandler {
private View.OnClickListener listener;
public ProxyHandler(OnClickListener listener) {
this.listener = listener;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
return method.invoke(listener, args);
}
}
static class ProxyOnClickListener implements View.OnClickListener {
@Override
public void onClick(View v) {
Log.d("HookSetOnClickListener", "点击事件被hook到了");
}
}
}
findViewById(R.id.service).setOnClickListener(HookOnClickListenerHelper.hook(this, findViewById(R.id.service)))
实践
目标:动态代理应用版本号的返回
分析:
动态代理的实现相对来说是简单的,困难的部分在于通过读源码了解到功能是如何实现的,通过代理哪个类可以修改目标代码的返回
- Android是如何获取应用版本号的?
通过getPackageManager()的getPackageInfo()
PackageManager pm = getPackageManager();
PackageInfo pi = pm.getPackageInfo(getPackageName(), 0);
versionName = pi.versionName;
versioncode = pi.versionCode;
- getPackageManager()如何获取 ?
getPackageManager在Context中实现,Context是一个abstract Class,所有的实现都在 ContextImpl中,通过ContextImpl我们发现getPackageManager()是从 ActivityThread.getPackageManager()拿到的
// ContextImp.java
@Override
public PackageManager getPackageManager() {
if (mPackageManager != null) {
return mPackageManager;
}
final IPackageManager pm = ActivityThread.getPackageManager();
if (pm != null) {
// Doesn't matter if we make more than one instance.
return (mPackageManager = new ApplicationPackageManager(this, pm));
}
return null;
}
- ActivityThread如何获取?
ActivityThread内部有静态方法currentActivityThread()来获取
// ActivityThread.java
public static ActivityThread currentActivityThread() {
return sCurrentActivityThread;
}
- ActivityThread中的packageManager怎么获取?
在ActivityThread中定义了sPackageManager,通过它我们就能拿到sPackageManager
public static IPackageManager getPackageManager() {
if (sPackageManager != null) {
//Slog.v("PackageManager", "returning cur default = " + sPackageManager);
return sPackageManager;
}
IBinder b = ServiceManager.getService("package");
//Slog.v("PackageManager", "default service binder = " + b);
sPackageManager = IPackageManager.Stub.asInterface(b);
//Slog.v("PackageManager", "default service = " + sPackageManager);
return sPackageManager;
}
代理:
-
获取ActivityThread
// 获取ActivityThread activityThreadClz = Class.forName("android.app.ActivityThread"); Method currentActivityThread = activityThreadClz.getDeclaredMethod("currentActivityThread"); currentActivityThread.setAccessible(true); Object activityThread = currentActivityThread.invoke(null);
-
获取packageManager
// 获取packageManager Field packageManagerField = activityThreadClz.getDeclaredField("sPackageManager"); packageManagerField.setAccessible(true); final Object packageManager = packageManagerField.get(activityThread);
-
动态代理,处理getPackageInfo方法
// 动态代理处理数据 Class<?> packageManagerClazz = Class.forName("android.content.pm.IPackageManager", false, getClassLoader()); Object proxy = Proxy.newProxyInstance(getClassLoader(), new Class[] {packageManagerClazz}, new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Object result = method.invoke(packageManager, args); if ("getPackageInfo".equals(method.getName())) { PackageInfo packageInfo = (PackageInfo)result; packageInfo.versionName = "sdsds"; } return result; } });
-
给packManger设置hook的对象
//hook sPackageManager packageManagerField.set(activityThread, proxy);
测试:
//越早 hook 越好,推荐在 attachBaseContext 调用
PackageManager pm = getPackageManager();
try {
PackageInfo pi = pm.getPackageInfo(getPackageName(), 0);
Log.d(TAG, pi.versionName);
} catch (NameNotFoundException e) {
e.printStackTrace();
}
推荐封装库
使用介绍 www.jianshu.com/p/1ba3680c1…
参考文档
《Android工程化最佳实践》
找到我
稀土掘金:悠二
Github:悠二