演技と反射概要

まず、エージェント

静的および動的プロキシエージェントに分かれて演技。帯電防止剤は、すでにそれが識別されたプロキシクラスの前に実行する前に存在し、実行中のクラスの関係を委任します。動的プロキシは、実行時に決定され、生成されたエージェントクラスと委譲クラス反射機構との間の関係に基づいて、実行時に決定することができます。

1、帯電防止剤

バインダー機構を話すときには、非常に一般的な静的なエージェントがあります。ここはおよそました:

public static com.example.runningh.myapplication.phone.IPhoneManager asInterface(android.os.IBinder obj) {
      if ((obj==null)) {
        return null;
      }
      android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
      if (((iin!=null)&&(iin instanceof com.example.runningh.myapplication.phone.IPhoneManager))) {
        return ((com.example.runningh.myapplication.phone.IPhoneManager)iin);
      }
      return new com.example.runningh.myapplication.phone.IPhoneManager.Stub.Proxy(obj);
}
复制代码

返されたメソッドはIInterfaceサービスと現在のプロセスが新しいStub.Proxyプロキシクラスは、同じプロセス内にない場合は、関連するメソッドへのクライアントアクセスは、プロキシクラスのメソッドは、サーバーを呼び出すことであろうと、典型的である、クライアントに返すとき静的プロキシモード。

2、動的プロキシ

実装は、プロキシオブジェクトを指定する必要はありません場合は、あなたはそれがより柔軟かつ便利なの利点があり、実行時に達成するために必要とされています。のは、それがどのように使われるかを見てみましょう。

Object proxy = Proxy.newProxyInstance(ClassLoader loader, Class[] claz , InvocationHanlder handler);
复制代码

動的プロトタイプ剤は、上記の方法でプロキシオブジェクトを返します。たとえば、次の呼び出し:

Class<?> serviceManager = Class.forName("android.os.ServiceManager");
Method getServiceMethod = serviceManager.getDeclaredMethod("getService", String.class);
IBinder binder = (IBinder) getServiceMethod.invoke(null, "alarm");
IBinder myBinder = (IBinder) Proxy.newProxyInstance(serviceManager.getClassLoader()
      , new Class[]{IBinder.class}, new MyProxy(binder));
复制代码

我々は詳細にその三つのパラメータの方法を分析します。

  • クラスローダーローダー。これは、我々は、プロキシクラスをロードするクラスローダを使用することを意味し、クラスローダです。一般的に、我々は、クラスローダの委譲クラスを使用します。
  • クラス[] claz。オブジェクトが実装するためのインタフェース(つまり、私たちのプロキシクラスである)newProxyInstanceリターンを指定します。これは、複数のインタフェースを実装することができる示し、アレイです。例えば上記IBinderインターフェイスを実装するので、IBinder型にキャストすることができます。
  • InvocationHandlerハンドラ。これはインターフェイスです。方法が起動します。実際には、関係なく、あなたはプロキシオブジェクトのメソッドを呼んでいるもの、それは最終的にメソッドを呼び出すために来ないだろう。すべてのメソッドは、我々は、メソッド名のために何をしたいかを達成するために、いくつかの操作を呼び出すことができます。

第二に、反射

カテゴリのような反射機能、それが動作中に知られているすべてのプロパティとメソッドのクラスのいずれであってもよく、目的のためには、属性および方法のいずれかと呼ぶことができます。関連事業の一部を見てみましょう反映:

1、Class对象的获取:
        Class.forName(类名全路径); //这是Class的一个静态方法
        类对象.getClass();  //通过类对象.getClass方法
        boolean.class, Boolean.class, int.class, Integer.class; //基本数据的Class对象获取,注意boolean.class和Boolean.class并不一样

2、实现接口的获取:
        Class<?> clazz  = Class.forName(类名全路径);
        Class<?>[] interfaces = clazz.getInterfaces()

3、通过指定参数获取构造函数及实例化
        Class<?> clazz  = Class.forName(类名全路径);
        Constructor<?> constructor = clazz.getConstructor(Class<?>  ... class);//获取公有的构造函数(public修饰符的)
        Constructor<?> constructor = clazz.getDeclaredConstructor(); //获取构造函数,包括公有和私有的
        constructor.newInstance(Object args);

4、获取所有构造函数和参数类型
        Class<?> clazz  = Class.forName(类名全路径);
        Constructor<?>[] constructors = clazz.getConstructors();//公共的构造函数
        Constructor<?>[] constructors = clazz.getDeclaredConstructors()//所有的构造函数,包括私有的

         for (int i = 0; i < constructors.length; i++) {
            Class<?> clazzs[] = constructors[i].getParameterTypes();//获取参数类型
             Log.d("ABC", "constructors[" + i + "] =" + constructors[i]);
            for (int j = 0; j < clazzs.length; j++) {
                if (j == clazzs.length - 1)
                    Log.d("ABC", clazzs[j].getName() + " ;");
            }
          }

5、获取字段,修改字段
        Class<?> clazz  = Class.forName(类名全路径);

        Field field = clazz.getField(String name);//获取公有字段
        Field field = clazz.getDeclaredField(String name);//获取字段,包括私有的
        Field[] field = clazz.getFields();//获取所有公有字段
        Field[] field = clazz.getDeclaredFields();//获取所有字段,包括私有的

        Field field = clazz.getDeclaredField("price");
        field.setAccessible(true);//设置取消访问检查,私有类型也可以访问
        field.set(obj, 100);

6、获取方法
        Class<?> clazz  = Class.forName(类名全路径);

        clazz.getMethod(String name ,Class<?> ... parame);//获取公共指定方法
        clazz.getDeclaredMethod(String name ,Class<?> ... parame)//获取指定方法,包括私有的方法
        clazz.getMethods()//获取所有的公共方法
        clazz.getDeclaredMethods();//获取所有的方法,包括私有方法

        Method method = clazz.getMethod("setPrice", int.class);
        method.setAccessible(true);
        method.invoke(clazz.newInstance(), 200);
复制代码

第三に、実用的なアプリケーション

ここでは、コピーして貼り付けるとき、それは言葉を言わせて、フックシステムのコピー&ペーストの方法でクリップボード。まず、見てサービスシステムを取得する方法です。

Context.getSystemService(Context.CLIPBOARD_SERVICE);
复制代码

そして、我々はのgetSystemService方法ContextImplにその事実でContextImplのContextオブジェクトを知っています:

@Override
public Object getSystemService(String name) {
    return SystemServiceRegistry.getSystemService(this, name);
}
复制代码

今度は静的メソッドSystemServiceRegistryクラスを呼び出します。

public static Object getSystemService(ContextImpl ctx, String name) {
    ServiceFetcher<?> fetcher = SYSTEM_SERVICE_FETCHERS.get(name);
    return fetcher != null ? fetcher.getService(ctx) : null;
}
复制代码

対応ServiceFetcherから取得したSYSTEM_SERVICE_FETCHERS、ServiceFetcherのgetServiceメソッド呼び出しは、対応するサービスを返します。SYSTEM_SERVICE_FETCHERSは、コードSystemServiceRegistryクラスの初期化の静的ブロックに初期化され、我々はコピーしてビューの外のサービスを初期化し、貼り付けます。

registerService(Context.CLIPBOARD_SERVICE, ClipboardManager.class,
      new CachedServiceFetcher<ClipboardManager>() {
      @Override
      public ClipboardManager createService(ContextImpl ctx) {
          return new ClipboardManager(ctx.getOuterContext(),
                ctx.mMainThread.getHandler());
}});
复制代码

あなたは、サービスが実際にコピー&ペーストで見ることができるClipboardManagerオブジェクト。そして、ClipboardManagerオブジェクトは、プロキシオブジェクトの実行のgetServiceによって返されるすべてのメソッドは()それがあります。getService返されたものを、私たちは、ソースコードを見ていき(ここでは①に注意し、私たちはコードのこの部分を参照するのを待つために戻ってきます):

static private IClipboard getService() {
      synchronized (sStaticLock) {
          if (sService != null) {
            return sService;
          }
          IBinder b = ServiceManager.getService("clipboard");
          //注意这里,后面拿到代理的IBinder对象后会执行到asInterface的queryLocalInterface方法。
          sService = IClipboard.Stub.asInterface(b); 
          return sService;
      }
}

public static android.content.IClipboard asInterface(android.os.IBinder obj) {
    if ((obj == null)) {
        return null; 
    }
    android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR); 
    if (((iin != null) && (iin instanceof android.content.IClipboard))) {
        return ((android.content.IClipboard) iin);
    }
    return new android.content.IClipboard.Stub.Proxy(obj);
}
复制代码

sServiceオブジェクトがnullでない場合、それが返されます。そうでなければ、のgetServiceメソッドServiceManagerによりIBinderオブジェクトを取得しIClipboard.Stub.asInterface(IBinderバインダー)が生成されるコール。まったく何もするのgetService道のServiceManager?ソースコードを見て続行します。

public static IBinder getService(String name) {
      try {
            IBinder service = sCache.get(name);
            if (service != null) {
                return service;
            } else {
                return getIServiceManager().getService(name);
            }
        } catch (RemoteException e) {
            Log.e(TAG, "error in getService", e);
        }
        return null;
}
复制代码

そこ場合sCache IBinderオブジェクトが返されるか、あるいはそこまでし続けています。我々はそれをしたい効果を達成するために、システムまたはオブジェクトをだます元IBinderのリターンを達成するよりも、他の、私たちは自分自身のためIBinderカスタムプロキシクラスのsCacheを交換することによって知ることができますこちらを参照してください。

以下は、特定のコードです:

ReflectActivity:

public class ReflectActivity extends Activity {
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.reflect_activity);
        ClipHelper.binder();
    }
}
复制代码

EditTextのレイアウトファイルは、アウト投稿されていません。onCreateメソッドClipHelper.binderメソッドを呼び出します。

ClipHelper:

public class ClipHelper {
    public static void binder() {
        try {
            Class<?> serviceManager = Class.forName("android.os.ServiceManager"); //获取ServiceManager对象
            Method getServiceMethod = serviceManager.getDeclaredMethod("getService", String.class); //调用getService方法
            IBinder binder = (IBinder) getServiceMethod.invoke(null, "clipboard"); //调用剪切板的方法,获取原始的IBinder对象
            IBinder myBinder = (IBinder) Proxy.newProxyInstance(serviceManager.getClassLoader()
                    , new Class[]{IBinder.class}, new MyProxy(binder)); //获取代理的IBinder对象
            Field sCache = serviceManager.getDeclaredField("sCache");
            sCache.setAccessible(true);
            HashMap<String, IBinder> map = (HashMap<String, IBinder>) sCache.get(null);
            map.put("clipboard", myBinder); //将该代理对象放到sCache里面
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        }
    }
}
复制代码

ClipHelper主にプロキシオブジェクトは、プロキシオブジェクトをIBinder生成し、内部sCacheを置くこと。

MyProxy:

public class MyProxy implements InvocationHandler {
    private IBinder orginBinder;

    public MyProxy(IBinder binder) {
        this.orginBinder = binder;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        if (method.getName().equals("queryLocalInterface")) {
            Class<?> mStubClass = Class.forName("android.content.IClipboard$Stub");
            Class<?> mClipBoard = Class.forName("android.content.IClipboard");
            return Proxy.newProxyInstance(mStubClass.getClassLoader(), new Class[]{mClipBoard}, new MyClipBoard(orginBinder, mStubClass));
        }
        return method.invoke(orginBinder, args);
    }
}
复制代码

queryLocalInterface方法のフックライブIBinder。あなたは戻って、上記①その部分を注意し、IBinder実行方法を見て、queryLocalInterfaceにIClipboardオブジェクトを返すことができます。このオブジェクトは、我々がフックする必要があるクライアントを、クリップボードにあります。だからここに再びダイナミックプロキシを介してプロキシオブジェクトを返します。プロキシオブジェクトは、IClipboardインタフェースを実装しています。ここでMyClipBoardへのプロキシオブジェクトです。下図のように。

MyClipBoard:

public class MyClipBoard implements InvocationHandler {
    private Object mBase;

    public MyClipBoard(IBinder binder, Class stub) {
        try {
            Method asInterface = stub.getDeclaredMethod("asInterface", IBinder.class);
            mBase = asInterface.invoke(null, binder);
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        if (method.getName().equals("getPrimaryClip")) {
            return ClipData.newPlainText(null, "怎么老是你");
        }
        if (method.getName().equals("hasPrimaryClip")) {
            return true;
        }
        return method.invoke(mBase, args);
    }
}
复制代码

私達はちょうどフッククリップボードgetPrimaryClipとhasPrimaryClip方法、または他のメソッド呼び出しは、通常の方法で生活する必要があります。通常の方法は、クリップボード通常のサービスを必要とする、またはノート①コードの先頭に振り返って、通常のクリップボードサービスは経由でIClipboard.Stub.asInterface(binder)取得するので、上記asInterface静的メソッドを介して利用可能mBaseクリップボードサービスです、そのため、asInterface方法はIClipboard.Stubのコンストラクタを呼び出します。

上記のすべてのコードであり、「あなたは何歳」こと、そして私たちは、クリップボードサービスフックライブシステムを介して取得言葉ますのEditText長押しで貼り付けます。

IVの概要

動的結合剤とは、一般に、私たちの所望の効果を達成するようにシステムを変更するために使用することができるいくつかの方法で反映します。私たちは、ソースコードを変更することはできません取得する場合、そのような改革により行うことができますが、使用する際に注意する必要がありますので、隠された潜在的なバグを変更する可能性がない、これは危険であることに注意してください。この記事では、私はhttp://blog.csdn.net/yulong0809/article/details/56842027博覧エージェント、フック、反射知識、自分自身のいくつかの結論、原作者の無私の共有への感謝を読んだ後、ここで原作者であります。

おすすめ

転載: juejin.im/post/5d73188c518825312330b1e8