首页 > 移动开发 > android中反射技术使用实例

android中反射技术使用实例

在计算机科学领域,反射是指一类应用,它们能够自描述和自控制。也就是说,这类应用通过采用某种机制来实现对自己行为的描述(self-representation)和监测(examination),并能根据自身行为的状态和结果,调整或修改应用所描述行为的状态和相关的语义.反射 是 Java 程序开发语言的特征之一,它允许运行中的 Java 程序对自身进行检查,或者说“自审”,并能直接操作程序的内部属性。Java 的反射机制的实现要借助于4个类:class,Constructor,Field,Method;其中class代表的时类对 象,Constructor-类的构造器对象,Field-类的属性对象,Method-类的方法对象。

 1.通过反射技术可以访问到其他包名下数据方法等,这些为一些APK换皮肤提供了方便

首先初始化skinContext

[java]

  1. try {    
  2.            skinContext = this.createPackageContext("com.skin",     
  3.                    CONTEXT_IGNORE_SECURITY|CONTEXT_INCLUDE_CODE);    
  4.        } catch (NameNotFoundException e) {    
  5.            // TODO Auto-generated catch block    
  6.            skinContext=null;    
  7.            e.printStackTrace();    
  8.        }    


可以通过下面的方法访问到指定包名下的资源ID

[java]

  1. /**  
  2.      * 取得对应包的所有资源的ID  
  3.      * 存在MAP中  
  4.      * @param packageName  
  5.      * @return  
  6.      */    
  7.     private Map<String,Map<String, Object>> getSkinResourcesId(String packageName)    
  8.     {    
  9.         Map<String, Object> temp =  null;    
  10.         Map<String,Map<String, Object>> resMap =new HashMap<String,Map<String,Object>>();    
  11.         try {    
  12.                 //取得皮肤包中的R文件    
  13.                 Class<?> rClass = skinContext.getClassLoader().loadClass(packageName+".R");    
  14.                 //取得记录各种资源的ID的类    
  15.                 Class<?>[] resClass =rClass.getClasses();    
  16.                 String className,resourceName;    
  17.                 int resourceId=0;    
  18.                 for(int i=0;i<resClass.length;i++)    
  19.                 {    
  20.                     className = resClass[i].getName();    
  21.                     //取得该类的资源    
  22.                     Field field[] = resClass[i].getFields();    
  23.                     for(int j =0;j < field.length; j++)    
  24.                     {    
  25.                         resourceName = field[j].getName();    
  26.                         try {    
  27.                             resourceId = field[j].getInt(resourceName);    
  28.                         } catch (IllegalArgumentException e) {    
  29.                             // TODO Auto-generated catch block    
  30.                             e.printStackTrace();    
  31.                         } catch (IllegalAccessException e) {    
  32.                             // TODO Auto-generated catch block    
  33.                             e.printStackTrace();    
  34.                         }    
  35.                         if(resourceName!=null && !resourceName.equals(""))    
  36.                         {    
  37.                             temp =new HashMap<String, Object>();    
  38.                             temp.put(resourceName, resourceId);    
  39.                             Log.i("DDDDD""className:"+className+"  resourceName:"+resourceName+"  " +    
  40.                                     "resourceId:"+Integer.toHexString(resourceId));    
  41.                         }    
  42.                     }    
  43.                     //由于内部类的关系className应该是com.skin.R$layout的形式    
  44.                     //截掉前面的包名和.R$以方便使用    
  45.                     className = className.substring(packageName.length()+3);    
  46.                     resMap.put(className, temp);    
  47.                 }    
  48.             } catch (ClassNotFoundException e) {    
  49.                 // TODO Auto-generated catch block    
  50.                 e.printStackTrace();    
  51.             }    
  52.         return resMap;    
  53.         }   


最后通过资源ID和skinContext可以访问到指定包下的所有资源,例如要访问layout

[java]

  1. /**  
  2.      * 获取皮肤包中的layout  
  3.      * 并转化为VIEW  
  4.      * @param layoutName  
  5.      * @return  
  6.      */    
  7.     private View getLayoutFromSkin(String layoutName)    
  8.     {    
  9.         View view;    
  10.         if(resMap == null)    
  11.             return null;    
  12.         Map<String, Object> temp = resMap.get("layout");    
  13.         int viewId = (Integer) temp.get(layoutName);    
  14.         if(viewId != 0)    
  15.         {    
  16.             //引用皮肤包资源转化View    
  17.             LayoutInflater inflater =LayoutInflater.from(skinContext);    
  18.             view = inflater.inflate(skinContext.getResources().getLayout(viewId), null);    
  19.         }    
  20.         else    
  21.         {    
  22.             view = null;    
  23.         }    
  24.         return view;    
  25.     }    


注:换皮肤思路详见:http://blog.csdn.net/tangnengwu/article/details/22801107

2. 访问android 隐藏的API

Toast信息框的关闭是由系统管理的,因为hide方法是隐藏的开发者没有办法直接调用,这种情况下可以用发射机制获取这个方法,创建一个显示和隐藏都由开发者控制的Toast信息框。

[java]

  1. package com.example.reflection;  
  2.   
  3. import java.lang.reflect.Field;  
  4. import java.lang.reflect.Method;  
  5.   
  6. import android.content.Context;  
  7. import android.util.Log;  
  8. import android.view.LayoutInflater;  
  9. import android.view.View;  
  10. import android.widget.TextView;  
  11. import android.widget.Toast;  
  12.   
  13. public class MyToast   
  14. {  
  15.     Context context=null;  
  16.     Object obj =null;  
  17.     public MyToast(Context context,String text)  
  18.     {  
  19.         this.context =context;  
  20.         Toast toast =Toast.makeText(context, text, 1);  
  21.         try {  
  22.             Field field = toast.getClass().getDeclaredField("mTN");  
  23.             field.setAccessible(true);  
  24.             obj =field.get(toast);  
  25.         } catch (Exception e) {  
  26.             // TODO: handle exception  
  27.             Log.d("AAA""MyToast Exception--->"+e.toString());  
  28.         }  
  29.     }  
  30.     public void show()  
  31.     {             
  32.         try {  
  33.             //android4.0以上就要以下处理  
  34. //          Field mNextViewField = obj.getClass().getDeclaredField("mNextView");  
  35. //          mNextViewField.setAccessible(true);  
  36. //          LayoutInflater inflate = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);  
  37. //          View v = inflate.inflate(R.layout.ui_toast, null);            
  38. //          mNextViewField.set(obj, v);  
  39.             Method method =obj.getClass().getDeclaredMethod("show"null);  
  40.             method.invoke(obj, null);  
  41.         } catch (Exception e) {  
  42.             // TODO Auto-generated catch block  
  43.             Log.d("AAA""show Exception--->"+e.toString());  
  44.             e.printStackTrace();  
  45.         }  
  46.     }  
  47.     public void hide()  
  48.     {  
  49.         try {  
  50.             Method method =obj.getClass().getDeclaredMethod("hide"null);  
  51.             method.invoke(obj, null);  
  52.         } catch (Exception e) {  
  53.             // TODO Auto-generated catch block  
  54.             Log.d("AAA""hide Exception--->"+e.toString());  
  55.             e.printStackTrace();  
  56.         }  
  57.     }  
  58.   
  59. }  


显示toast:

[java]

  1. MyToast toast = new MyToast(this"反射机制!");  
  2. toast.show();  

隐藏toast:

toast.hide();

注意在4.0以上的版本中,还需要对Toast 中的View进行处理,如代码中所示

3. 修改某些“不可改” 的系统资源

ListView组件没有提供修改快速滑块图像的API,因此不能直接修改,但可通过反射实现

[java]

  1. package com.example.reflection;  
  2.   
  3. import java.lang.reflect.Field;  
  4.   
  5. import android.content.Context;  
  6. import android.graphics.drawable.Drawable;  
  7. import android.util.AttributeSet;  
  8. import android.widget.AbsListView;  
  9. import android.widget.ListView;  
  10.   
  11. public class MListView extends ListView   
  12. {  
  13.     public MListView(Context context, AttributeSet attrs)   
  14.     {  
  15.         super(context, attrs);  
  16.         // TODO Auto-generated constructor stub  
  17.         setNewDrawable(context);  
  18.     }  
  19.       
  20.     private void setNewDrawable(Context context)  
  21.     {  
  22.         try {  
  23.             Field  field = AbsListView.class.getDeclaredField("mFastScroller");  
  24.             field.setAccessible(true);  
  25.             Object obj = field.get(this);  
  26.             field =field.getType().getDeclaredField("mThumbDrawable");        
  27.             field.setAccessible(true);  
  28.             Drawable drawable = (Drawable)field.get(obj);  
  29.             drawable = context.getResources().getDrawable(R.drawable.ic_launcher);  
  30.             field.set(obj, drawable);  
  31.         } catch (Exception e) {  
  32.             // TODO Auto-generated catch block  
  33.             e.printStackTrace();  
  34.         }  
  35.     }  
  36.   
  37. }  
[java]

  1. Field  field = AbsListView.class.getDeclaredField("mFastScroller");  

FastScroller.mThunbDrawable变量保存了快速滑块图像,但首先要获取AbsListView.mFastScroller变量

[html]

  1. <com.example.reflection.MListView  
  2.         android:id="@+id/listView1"  
  3.         android:layout_width="match_parent"  
  4.         android:layout_height="wrap_content"  
  5.         android:fastScrollEnabled="true"  
  6.         android:scrollbars="none"  
  7.         >  
  8.     </com.example.reflection.MListView>  

[html]

  1. android:fastScrollEnabled="true"  

使用快速滑块

效果图如下:

总结:

      Java中的反射机制,被称为Reflection,它允许运行中的Java程序对自身进行检查,并能直接操作程序的内部属性或方法。Reflection机制允许程序在正在执行的过程中,利用Reflection APIs取得任何已知名称的类的内部信息,包括:package、 type parameters、 superclass、 implemented interfaces、 inner classes、 outer classes、 fields、 constructors、 methods、 modifiers等,并可以在执行的过程中,动态生成Instances、变更fields内容或唤起methods。再次基础上我们可以利用反射机制在Java程序中,动态的去调用一些protected甚至是private的方法或类,这样可以很大程度上满足我们的一些比较特殊需求。

有关反射技术的API:

Class类:

  Class类代表着某个类的字节码,要使用反射,就需要取得对应的Class对象,然后就通过这个对象,就可解剖出类的成员变量,成员方法等等。
获取Class类对象

//通过Class的forName()方法,此方法最为常用
Class class1 = Class.forName(className);
//通过 .class
Class class2 = XXX.class;
//通过对象获得
Class class3 = new XXX().getClass();
  Class类的常用方法:
  getConstructor() 获取构造函数
  getMethod()  获取成员方法
  getField() 获取成员变量
  getDeclaredConstructor() 获取私有的构造函数
  getDeclaredMethod()  获取私有的成员方法
  getDeclaredField() 获取私有的成员变量

取得method对象之后

调用

[java]

  1. method.invoke(obj, null)  

使用该方法


本文固定链接: http://www.devba.com/index.php/archives/5666.html | 开发吧

报歉!评论已关闭.