Android 反射实战

前面做了很多个demo去学习如何反射字段,方法,代理之类的等等,有一些给了实际的应用,有一些没有,看起来似乎没有什么实际价值,所以现在给出一个实战,开发条件需要的是一个jar包,这里以com.qti.snapdragon.sdk.display.jar的jar为例.调试设备以Oneplus二代旗舰机为例,jar放在Oneplus二代手机的/system/framework/目录下,普通读者可以放到其他目录下,比如sdcard/下.

下面开始实施实际操作过程:

<1> : 新建一个Android工程,工程树如下:

<2> : 布局文件中放三个按钮,并且在OneplusMainActivity.java中增加对应的事件处理:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <Button 
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="hello"
        android:id="@+id/button"/>
    
    <Button 
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="field"
        android:id="@+id/fieldbutton"/>
    
    <Button 
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="listener"
        android:id="@+id/listenerbutton"/>

</LinearLayout>

注意:

Manifest.xml文件中还需要增加一个权限,因为jar会检查它自己设定的权限的:

<uses-permission android:name="com.qti.snapdragon.sdk.permission.DISPLAY_SETTINGS" />

多说两句,权限不一定只有android系统才有,自己也可以自定义权限限制,然后在被第三方使用的时候,检查第三方是否具备这个权限,检查调用:

Context.checkCallingOrSelfPermission(String);

参数是权限的名字,本案例中的jar检查如下(反编译jar看到的源文件所得),发现jar中会检查调用jar包接口的第三方是否具有权限:

jar反编译出来的信息可以知道,他会检查调用jar包第三方应用的权限设定的,所以必须在app中添加上面的权限.

 

<3> : 主体程序如下:

package com.oneplus.oneplusdisplayjardemo;

import java.io.File;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import dalvik.system.DexClassLoader;

import android.app.Activity;
import android.app.Application;
import android.content.Context;
import android.os.Bundle;
import android.os.Environment;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;

/**
 *  @author : zhibao.liu 
 *  @complay : oneplus.Inc
 *  @Date : 2015/11/30
 *  
 * */

public class OneplusMainActivity extends Activity implements OnClickListener {

    private static final String TAG = "OneplusMainActivity";

//下面是需要反射对应的包和类等信息
    private final static String PACKAGENAME = "com.qti.snapdragon.sdk.display.ColorManager";
    private final static String PACKAGEFIELDNAME = "com.qti.snapdragon.sdk.display.ColorManager$DCM_DISPLAY_TYPE";
    private final static String PACKAGELISTENERNAME = "com.qti.snapdragon.sdk.display.ColorManager$ColorManagerListener";

    private Button mButton;
    private Button mFieldButton;
    private Button mListenerButton;

    private Context mContext;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.oneplus_main);

        mButton = (Button) findViewById(R.id.button);
        mFieldButton = (Button) findViewById(R.id.fieldbutton);
        mListenerButton = (Button) findViewById(R.id.listenerbutton);
        mButton.setOnClickListener(this);
        mFieldButton.setOnClickListener(this);
        mListenerButton.setOnClickListener(this);

        mContext = OneplusMainActivity.this;

    }

    @Override
    public void onClick(View v) {
        // TODO Auto-generated method stub

        int id = v.getId();
        switch (id) {
        case R.id.button:
            // OneplusDisplaySDKReflex.OneplusReflex(PACKAGENAME,mContext);
            // OneplusDisplaySDKReflex.OneplusReflexClass(PACKAGENAME);
            OneplusReflex(PACKAGENAME, "getInstance");
            break;
        case R.id.fieldbutton:
            OneplusFieldReflex(PACKAGEFIELDNAME, "DISP_PRIMARY");
            break;
        case R.id.listenerbutton:
			//这个才是我们的重点,前面两个只是测试包信息.
			//下面才是连接包并且工作的.
            OneplusReflexListener(PACKAGELISTENERNAME);
            break;

        }

    }

    public void OneplusReflexListener(String packagenamelistener) {
		//下面的这个环境,是直接读取/system/framework/目录了
		//其实它本身已经系统jar路径下,只需要包名就可以反射了.
		//下面只是给出一个更加通用的,无论是Android系统的,还是将来第三方的,放在任何地方,只需要调整这个路径就可以了.
        String dexPath = Environment.getRootDirectory().getPath()
                + File.separator + "framework" + File.separator
                + "com.qti.snapdragon.sdk.display.jar";

        File file = new File(dexPath);

        if (!file.exists()) {
            return;
        }

        String dexOutputDirs = Environment.getExternalStorageDirectory()
                .toString(); // this is not access permission to read/write
                             // random file

        final File optimizedDexOutputPath = getDir("outdex", 0);

        DexClassLoader cl = new DexClassLoader(dexPath,
                optimizedDexOutputPath.getAbsolutePath(), null,
                getClassLoader());

        try {
            Class clazzlis = cl.loadClass(packagenamelistener);
            Object listener = Proxy.newProxyInstance(clazzlis.getClassLoader(),
                    new Class<?>[] { clazzlis }, new InvocationHandler() {

                        @Override
                        public Object invoke(Object proxy, Method method,
                                Object[] args) throws Throwable {
                            // TODO Auto-generated method stub
							//运行了一个代理
//下面反射connect方法,并且调用,如果连接成功
//就会通过这个回调调用下面的逻辑了.
                            Log.i(TAG,
                                    "connect to service sucessfully , todo your work here !");
                            // following continue initial
                            /* OneplusReflex(PACKAGENAME,"getInstance"); */
                            return null;
                        }

                    });

            Class clazzcm = cl.loadClass(PACKAGENAME);
            Object object;

            try {

                /*
                 * start up connecting to qual service jar
                 */
                Method conMethod = clazzcm.getDeclaredMethod("connect",
                        new Class[] { Context.class, clazzlis });
                conMethod.setAccessible(true);
                try {
                    conMethod.invoke(/* object */null, new Object[] { mContext,
                            listener });
                } catch (IllegalAccessException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                } catch (IllegalArgumentException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                } catch (InvocationTargetException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            } catch (NoSuchMethodException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }

        } catch (ClassNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

    }

    public void OneplusReflex(String packagename, String methodname) {

        String dexPath = Environment.getRootDirectory().getPath()
                + File.separator + "framework" + File.separator
                + "com.qti.snapdragon.sdk.display.jar";

        File file = new File(dexPath);

        if (!file.exists()) {
            return;
        }

        String dexOutputDirs = Environment.getExternalStorageDirectory()
                .toString(); // this is not access permission to read/write
                             // random file

        final File optimizedDexOutputPath = getDir("outdex", 0);

        DexClassLoader cl = new DexClassLoader(dexPath,
                optimizedDexOutputPath.getAbsolutePath(), null,
                getClassLoader());

        try {
            Class clazz = cl.loadClass(packagename);// Class.forName(packagename);
            Method methods[] = clazz.getDeclaredMethods();

            try {

                Class DIS_TYPE = OneplusFeildTypeReflex(PACKAGENAME,
                        "DCM_DISPLAY_TYPE");

                System.out.println("class type : " + DIS_TYPE.toString());

                Method method = clazz.getDeclaredMethod(methodname,
                        new Class<?>[] { Application.class, Context.class,
                                DIS_TYPE });

                try {

                    Object ret = OneplusFieldReflex(PACKAGEFIELDNAME,
                            "DISP_PRIMARY");
                    method.setAccessible(true);
                    method.invoke(null, new Object[] { this.getApplication(),
                            OneplusMainActivity.this, ret });

                    /*
                     * after init colormanager try to set value it to system
                     */
                    Method methodset = clazz.getDeclaredMethod(
                            "setColorBalance", new Class[] { int.class });
                    methodset.setAccessible(true);
                    try {
                        methodset.invoke(clazz.newInstance(),
                                new Object[] { 56 });
                    } catch (InstantiationException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }

                } catch (IllegalAccessException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                } catch (IllegalArgumentException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                } catch (InvocationTargetException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }

            } catch (NoSuchMethodException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }

        } catch (ClassNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

    }

    public Class OneplusFeildTypeReflex(String packagename, String fieldname) {

        String dexPath = Environment.getRootDirectory().getPath()
                + File.separator + "framework" + File.separator
                + "com.qti.snapdragon.sdk.display.jar";

        File file = new File(dexPath);

        if (!file.exists()) {
            return null;
        }

        String dexOutputDirs = Environment.getExternalStorageDirectory()
                .toString(); // this is not access permission to read/write
                             // random file

        final File optimizedDexOutputPath = getDir("outdex", 0);

        DexClassLoader cl = new DexClassLoader(dexPath,
                optimizedDexOutputPath.getAbsolutePath(), null,
                getClassLoader());

        try {
            Class clazz = cl.loadClass(PACKAGEFIELDNAME);

            return clazz;

        } catch (ClassNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

        return null;

    }

    public Object OneplusFieldReflex(String packagename, String fieldname) {

        String dexPath = Environment.getRootDirectory().getPath()
                + File.separator + "framework" + File.separator
                + "com.qti.snapdragon.sdk.display.jar";

        File file = new File(dexPath);

        if (!file.exists()) {
            return null;
        }

        String dexOutputDirs = Environment.getExternalStorageDirectory()
                .toString(); // this is not access permission to read/write
                             // random file

        final File optimizedDexOutputPath = getDir("outdex", 0);

        DexClassLoader cl = new DexClassLoader(dexPath,
                optimizedDexOutputPath.getAbsolutePath(), null,
                getClassLoader());

        try {
            Class clazz = cl.loadClass(packagename);
            Object obj = null;

            Field fields[] = clazz.getDeclaredFields();
            for (int i = 0; i < fields.length; i++) {

                fields[i].setAccessible(true);

                try {
                    Field field = clazz.getDeclaredField(fieldname);
                    field.setAccessible(true);
                    try {
                        Object ret = null;
                        ret = field.get(null);
                        if (ret == null) {
                            continue;
                        }
                        System.out.println("value : " + ret.toString());
                        return ret;
                    } catch (IllegalAccessException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    } catch (IllegalArgumentException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                } catch (NoSuchFieldException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }

            }

        } catch (ClassNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

        return null;

    }

}

大致的解释放在上面的程序中,通过上面的程序达到一个综合的运用,运行结果如下:


从结果看的出来,我们通过先反射connect方法去连接服务,连接成功后,回调回来,这个通过代理来实现,然后正式操作的程序放在代理里面开始执行或者初始化.

基本思路如下:




你可能感兴趣的:(Android 反射实战)