前面做了很多个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方法去连接服务,连接成功后,回调回来,这个通过代理来实现,然后正式操作的程序放在代理里面开始执行或者初始化.
基本思路如下: