本博客转自:http://blog.csdn.net/tiefeng0606/article/details/51700866

动态语言:

一般认为在程序运行时,允许改变程序结构或变量类型,这种语言称为动态语言。从这个观点看,Perl,Python,Ruby是动态语言,C++,Java,C#不是动态语言。尽管这样,JAVA有着一个非常突出的动态相关机制:反射(Reflection)。运用反射我们可以于运行时加载、探知、使用编译期间完全未知的classes。换句话说,Java程序可以加载在运行时才得知名称的class,获悉其完整构造方法,并生成其对象实体、或对其属性设值、或唤起其成员方法。

反射:

要让Java程序能够运行,就得让Java类被Java虚拟机加载。Java类如果不被Java虚拟机加载就不能正常运行。正常情况下,我们运行的所有的程序在编译期时候就已经把那个类被加载了。 Java的反射机制是在编译时并不确定是哪个类被加载了,而是在程序运行的时候才加载。使用的是在编译期并不知道的类。这样的编译特点就是java反射。

反射的作用:

如果有AB两个程序员合作,A在写程序的时需要使用B所写的类,但B并没完成他所写的类。那么A的代码是不能通过编译的。此时,利用Java反射的机制,就可以让A在没有得到B所写的类的时候,来使自身的代码通过编译。

反射的实质:

反射就是把Java类中的各种存在给解析成相应的Java类。要正确使用Java反射机制就得使用Class(C大写) 这个类。它是Java反射机制的起源。当一个类被加载以后,Java虚拟机就会自动产生一个Class对象。通过这个Class对象我们就能获得加载到虚拟机当中这个Class对象对应的方法、成员以及构造方法的声明和定义等信息。

反射机制的优点与缺点:

为什么要用反射机制?直接创建对象不就可以了吗,这就涉及到了动态与静态的概念: 
静态编译:在编译时确定类型,绑定对象,即通过。 
动态编译:运行时确定类型,绑定对象。动态编译最大限度发挥了java的灵活性,体现了多态的应用,降低类之间的藕合性。 
一句话,反射机制的优点就是可以实现动态创建对象和编译,体现出很大的灵活性,特别是在J2EE的开发中它的灵活性就表现的十分明显。比如,一个大型的软件,不可能一次就把把它设计的很完美,当这个程序编译后,发布了,当发现需要更新某些功能时,我们不可能要用户把以前的卸载,再重新安装新的版本,假如这样的话,这个软件肯定是没有多少人用的。采用静态的话,需要把整个程序重新编译一次才可以实现功能的更新,而采用反射机制的话,它就可以不用安装,只需要在运行时才动态的创建和编译,就可以实现该功能。它的缺点是对性能有影响。使用反射基本上是一种解释操作,我们可以告诉JVM,我们希望做什么并且它满足我们的要求。这类操作总是慢于只直接执行相同的操作。


Android FrameWork中的反射:

一个类中的每个成员都可以用相应的反射API的一个实例对象来表示——反射机制。 
了解这些,那我们就知道了,我们可以利用反射机制在Java程序中,动态的去调用一些protected甚至是private的方法或类,这样可以很大程度上满足我们的一些比较特殊需求。例如Activity的启动过程中Activity的对象的创建。

以下代码位于ActivityThread中:

 private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {

         。。。。。。
Activity activity = null;
try {
java.lang.ClassLoader cl = r.packageInfo.getClassLoader();
activity = mInstrumentation.newActivity(
cl, component.getClassName(), r.intent);
StrictMode.incrementExpectedActivityCount(activity.getClass());
r.intent.setExtrasClassLoader(cl);
r.intent.prepareToEnterProcess();
if (r.state != null) {
r.state.setClassLoader(cl);
}
}
。。。。。。

上面代码可知Activity在创建对象的时候调用了mInstrumentation.newActivity(); 
以下代码位于Instrumentation中:

     public Activity newActivity(ClassLoader cl, String className,
Intent intent)
throws InstantiationException, IllegalAccessException,
ClassNotFoundException {
//这里的className就是在manifest中注册的Activity name.
return (Activity)cl.loadClass(className).newInstance();
}

最终在newActivity()里返回的是利用cl.loadClass返回的Activity对象。可知,Activity对象的创建是通过反射完成的。java程序可以动态加载类定义,而这个动态加载的机制就是通过ClassLoader来实现的,所以可想而知ClassLoader的重要性如何。

ClassLoader和DexClassLoader

上面说到JAVA的动态加载的机制就是通过ClassLoader来实现的,ClassLoader也是实现反射的基石。ClassLoader是JAVA提供的一个类,顾名思义,它就是用来加载Class文件到JVM,以供程序使用的。

但是问题来了,ClassLoader加载文件到JVM,但是Android是基于DVM的,用ClassLoader加载文件进DVM肯定是不行的。于是Android提供了另外一套加载机制,分别为 dalvik.system.DexClassLoader 和 dalvik.system.PathClassLoader,区别在于 PathClassLoader 不能直接从 zip 包中得到 dex,因此只支持直接操作 dex 文件或者已经安装过的 apk(因为安装过的 apk 在 cache 中存在缓存的 dex 文件)。而 DexClassLoader 可以加载外部的 apk、jar 或 dex文件,并且会在指定的 outpath 路径存放其 dex 文件。

ClassLoader在JAVA中的应用

下面利用反射来调用另一个类中的方法

 //定义一个测试类,用来被反射调用
package com.izzy;
public class Test {
private String s;
//构造方法
public Test(String s) {
this.s = s;
}
//定义一个方法,用来输出,构造方法中传递进来的参数
public void display() {
System.out.println(s);
} }
 package com.izzy;
import java.lang.reflect.Constructor; public class Client { public static void main(String[] s) {
try {
//首先拿到系统ClassLoader,并加载Class,返回的是一个Class对象clazz
Class clazz = ClassLoader.getSystemClassLoader().loadClass(
"com.izzy.Test");
//通过clazz 拿到构造方法并转换成对象
Constructor constructor = clazz.getConstructor(String.class);
Object obj = constructor.newInstance("I AM IZZY");
//通过clazz 拿到成员方法
Method method = clazz.getMethod("display", null);
method.invoke(obj, null);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
} }

DexClassLoader在Android中的应用

以一个例子来说明DexClassLoader用法(本例采用两个已安装的Apk),现在有两个Apk:Share和Test,利用Test来调用Share 里面的方法。

首先在Share apk中定义Share类,其中有一个display()方法提供给远程调用

 public class Share {
public void display(String s) {
Log.e("IZZY", s);
} }

接着在manifest文件中配置action和category,方便调用这找到

   <activity
android:name=".MainActivity"
android:theme="@android:style/Theme.Light.NoTitleBar">
<intent-filter>
<action android:name="com.IZZY"/>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>

接下来就是在Test Apk中编写调用代码了

  public void getFromRemote() {
Intent intent = new Intent("com.IZZY");
PackageManager pm = getPackageManager();
List<ResolveInfo> resolveInfos = pm.queryIntentActivities(intent, 0); ResolveInfo resolveInfo = resolveInfos.get(0);
ActivityInfo activityInfo = resolveInfo.activityInfo;
//拿到目标类的包名
String packageName = activityInfo.packageName; //拿到目标类所在的apk或者jar存放的路径
String dexPath = activityInfo.applicationInfo.sourceDir;
//该路径为拿到目标类dex文件存放在调用者里的路径
String dexOutputDir = getApplicationInfo().dataDir;
//拿到目标类所使用的C/C++库存放路径
String nativeLibraryDir = activityInfo.applicationInfo.nativeLibraryDir;
//拿到类装载器
ClassLoader classLoader = getClassLoader(); //DexClassLoader参数分别对应以上四个参数
DexClassLoader dcl = new DexClassLoader(dexPath,dexOutputDir,nativeLibraryDir,classLoader);
try {
//装载目标类
Class<?> clazz = dcl.loadClass(packageName + ".Share");
//拿到构造器并实例化对象
Constructor<?> constructor = clazz.getConstructor();
Object o = constructor.newInstance();
//拿到成员方法
Method display = clazz.getMethod("display", String.class);
display.invoke(o, "I AM IZZY");
} catch (Exception e) {
e.printStackTrace();
}
}

在该调用方法中首先利用Intent查询到目标activityInfo,然后利用查询到的activityInfo得到目标Apk的包名,目标Apk所在的apk或者jar存放的路径dexPath,目标Apk所使用的C/C++库存放路径nativeLibraryDir。然后利用这些参数实例化DexClassLoader加载器。之后反射调用目标类中的方法。
(ps:此处使用的目标Apk是已经安装过的,因此采用Intent查询来拿到dexPath和nativeLibraryDir,如果是未安装过的jar包或Apk,则直接传入该jar包活Apk的文件存放路径和C/C++库存放路径)。

运行结果: 
 
从结果看,调用者Apk拿到了目标Apk的方法并成功执行。

DexClassLoaderde 在Android中的使用场景

上面是是使用的已经安装过的Apk,如果采用未安装过的jar包或者Apk,则实例化DexClassLoader的时候把相应路径改为需要加载的jar包或者Apk路径亦可拿到结果。这就使得DexClassLoaderde可以应用在HotFix(热修复),动态加载框架等等 一些基于插件化的架构中。

反射技术在Android中的应用的更多相关文章

  1. Java反射及其在Android中的应用学习总结

    一. Java反射机制 Reflection 是Java被视为动态(或准动态)语言的一个关键性质.这个机制同意程序在执行时透过Reflection APIs取得不论什么一个已知名称的class的内部信 ...

  2. c#中反射技术在Unity中的运用

    反射技术给类赋值的好处就是可以简化代码,封装的好处就显而易见了.最直接的用途就是用在在显示配置文件的时候,个人习惯性做法是做一个VO来存储需要的数据,其代码如下: internal class Bas ...

  3. 【转】Android 学习笔记——利用JNI技术在Android中调用、调试C++代码

    原文网址:http://cherishlc.iteye.com/blog/1756762 在Android中调用C++其实就是在Java中调用C++代码,只是在windows下编译生成DLL,在And ...

  4. [转][android][利用JNI技术在Android中调用、调试C++代码]

    在Android中调用C++其实就是在Java中调用C++代码,只是在windows下编译生成DLL,在Android中会生成Linux系统下的.so文件(好吧,其实我基本没用过Linux). 没写过 ...

  5. 利用JNI技术在Android中调用C++形式的OpenGL ES 2.0函数

    1.                 打开Eclipse,File-->New-->Project…-->Android-->AndroidApplication Projec ...

  6. Android技术——在Android中的随意视图中找控件

    1.在非常多情况下,我们可能不知道控件的id,可是我们却希望在包括这个控件的视图中找到它,能够採用例如以下做法: 例:在Activity的根视图中找出当中全部的Button控件 private voi ...

  7. 利用JNI技术在Android中调用、调试C++代码

    参考:http://blog.micro-studios.com/?p=4212 代码:http://pan.baidu.com/s/1sjukSDf

  8. 6、Android中的NFC技术

    Android对NFC技术的支持 Android2.3.1(API Level = 9)开始支持NFC技术,但Android2.x和Android3.x对NFC的支持非常有限.而从Android4.0 ...

  9. Android中插件开发篇之----应用换肤原理解析

    一.前言 今天又到周末了,感觉时间过的很快呀.又要写blog了.那么今天就来看看应用的换肤原理解析.在之前的一篇博客中我说道了Android中的插件开发篇的基础:类加载器的相关知识.没看过的同学可以转 ...

随机推荐

  1. linux跨主机复制文件或文件夹

    复制文件基本格式:(本地到远程) scp 文件名 用户名@ip:文件全目录 如果是文件夹加上参数 -r scp -r 基础目录 用户名@ip:目录 栗子: scp local_file remote_ ...

  2. 函数求值一<找规律>

    函数求值 题意: 定义函数g(n)为n最大的奇数因子.求f(n)=g(1)+g(2)+g(3)+-+g(n).1<=n<=10^8; 思路: 首先明白暴力没法过.问题是如何求解,二分.知道 ...

  3. 解决TortoiseGit 推送 拉取需要密码的问题

    找到解决了方法: 1)运行PuTTYGen,在Conversions菜单中点击Import key,选择ssh-keygen生成的私钥文件所在位置,比如id_rsa文件. 2)点击Save priva ...

  4. CentOS下编译安装Apache2(新)

    官网下载apache,apr, apr-util,pcre httpd-2.4.16.tar.gz http://httpd.apache.org/download.cgi#apache24 apr- ...

  5. CNS数据库网站只用mysql自带的fulltext index功能就可实现了。

    1)编辑脚本script.sql如下 ALTER TABLE `table_name` ADD FULLTEXT (`column_name`) 2)在mysql console下输入命令 SOURC ...

  6. JSP标准标签库(JSTL)--核心标签库 c

    核心标签库是JSTL中最重要的部分,可以完成输出,判断,迭代等操作 功能分类: 1. 基本标签: <c:out>:输出属性内容 <c:set>:设置属性内容 <c:rem ...

  7. C++零食:使用Unicode版的预定义宏__FUNCTION__

    在C++中支持一种预定义宏.比如: __FILE__: 就是当前源代码文件名 __LINE__: 就是当前源代码的行号 这些宏可以在printf等语句中直接作为字符串使用,调试的时候很方便. 如下代码 ...

  8. VC分发包版本问题

    来源:http://www.cnblogs.com/mixiyou/archive/2010/02/09/1663620.html 之前曾经写过一篇个人经历,是关于VC2005分发包版本不一致而引起应 ...

  9. 模态对话框 bootstrap-modal.js

    调用方式 通过data属性 无需编写JavaScript代码即可生成一个对话框.在一个主控元素,例如按钮,上设置data-toggle="modal",然后再设置data-targ ...

  10. 素数路(prime)

    素数路(prime) 题目描述 已知一个四位的素数,要求每次修改其中的一位,并且要保证修改的结果还是一个素数,还不能出现前导零.你要找到一个修改数最少的方案,得到我们所需要的素数. 例如把1033变到 ...