preface

Here is the finch content card, click the link to view: www.yuque.com/youer-ycy0r…

Introduction to the

Reflection in Java means that an application can retrieve all information about an object at runtime

use

Reflection is divided into the following steps

1. Obtain the Class object

When the JVM loads a Class, it generates a unique Class object for each Class

Access method has the following several / / name = Test. Class. GetDeclaredField (" name "); //name = test.getClass().getDeclaredField("name"); name = Class.forName("com.example.app.MainActivity$Test").getDeclaredField("name");Copy the code

2. Operating 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; }}Copy the code
  • GetDeclaredField: Gets any fields of this class

  • GetField: Gets the public field of this class and base class

  • Get non-public base class values only through getDeclaredField of the base class

3. The method is called

/ / parameterless Method getName = Class. Class.forname (" com. Example. App. MainActivity $Test "). The getMethod (" getName "); Log.d("test", (String)getName.invoke(test)); / / a parameter of Method elegantly-named setName = Class. Class.forname (" com. Example. App. MainActivity $Test "). The getMethod (" elegantly-named setName ", String. Class); setName.invoke(test, "sdaasda"); Log.d("test", test.getName());Copy the code

A dynamic proxy

Dynamically create an instance of an interface during program runtime, and implement a method/class hook through dynamic proxy

Like hook click events

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", "click event was hooked "); } } } findViewById(R.id.service).setOnClickListener(HookOnClickListenerHelper.hook(this, findViewById(R.id.service)))Copy the code

practice

Target: return of the version number of the dynamic proxy application

Analysis:

Dynamic proxies are relatively simple to implement. The hard part is to read the source code to understand how the functionality is implemented. Which class can modify the return of object code through the proxy

  1. How does Android get the app version number?

GetPackageInfo () via getPackageManager()

PackageManager pm = getPackageManager();
PackageInfo pi = pm.getPackageInfo(getPackageName(), 0);
versionName = pi.versionName;
versioncode = pi.versionCode;
Copy the code
  1. How to get getPackageManager()?

GetPackageManager is implemented in Context, which is an abstract Class, and all implementations are in ContextImpl, Through ContextImpl we found getPackageManager () from 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; }Copy the code
  1. How do I get ActivityThread?

ActivityThread has a static currentActivityThread() method

// ActivityThread.java
public static ActivityThread currentActivityThread() {
     return sCurrentActivityThread;
 }
Copy the code
  1. How to obtain the packageManager in ActivityThread?

The sPackageManager is defined in ActivityThread, and it is used to get the 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; }Copy the code

Agent:

  1. Get ActivityThread

    / / get ActivityThread activityThreadClz = Class. Class.forname (” android. App. ActivityThread “); Method currentActivityThread = activityThreadClz.getDeclaredMethod(“currentActivityThread”); currentActivityThread.setAccessible(true); Object activityThread = currentActivityThread.invoke(null);

  2. Get packageManager

    / / get packageManager Field packageManagerField = activityThreadClz. GetDeclaredField (” sPackageManager “); packageManagerField.setAccessible(true); final Object packageManager = packageManagerField.get(activityThread);

  3. Dynamic proxy, which handles the getPackageInfo method

    // Dynamic proxy processing data 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; }});

  4. Set the hook object to the packManger

    //hook sPackageManager packageManagerField.set(activityThread, proxy);

Testing:

// The earlier you hook the better, it is recommended to call PackageManager PM = getPackageManager() at attachBaseContext; try { PackageInfo pi = pm.getPackageInfo(getPackageName(), 0); Log.d(TAG, pi.versionName); } catch (NameNotFoundException e) { e.printStackTrace(); }Copy the code

Recommended encapsulation library

JOOR

Used to introduce www.jianshu.com/p/1ba3680c1…

Reference documentation

Android Engineering Best Practices

Liao Xuefeng — Reflection

Find me

Android Skill tree lighting project Git library

Language finches

Rare earth mining: You two

Making: 2