推荐下载Android框架基础知识梳理.docx
《推荐下载Android框架基础知识梳理.docx》由会员分享,可在线阅读,更多相关《推荐下载Android框架基础知识梳理.docx(19页珍藏版)》请在冰豆网上搜索。
![推荐下载Android框架基础知识梳理.docx](https://file1.bdocx.com/fileroot1/2022-11/28/07e86643-ba98-4c7f-a026-812670c813ec/07e86643-ba98-4c7f-a026-812670c813ec1.gif)
推荐下载Android框架基础知识梳理
Android框架基础知识梳理
2017/02/220转载自大神谷歌小弟的博客
经过几年的发展和沉淀,Android开发中涌现出许多优秀的框架,比如:
Retrofit、Afinal、OKHttp、ButterKnife、AndFix等等。
这些框架的出现极大地简化
了开发流程,提高了工作效率。
在项目开发的过程中我们主要是使用这些轮子完成
项目,很难有时间去顾及框架的内部实现。
在项目交付之后我们可能就要去看看这
些框架的源码了。
这些主流框架的功能各不相同,但每当打开浩繁的源码时我们几乎都可以看到反
射,注解,泛型的广泛应用;也正是这些技术使得框架具有了高度的灵活性,优良
的扩展性和健壮的稳定性。
鉴于这些框架必备知识的重要性故在此对这部分内容做
一个全面的梳理和总结
主要内容:
ClassLoaderClassLoader的分析几种不同ClassLoader的介绍ClassLoader的应用泛
型泛型的由来自定义泛型泛型的擦除反射class常用反射技术Type以及
ParameterizedType反射与泛型的结合使用注解常用注解的介绍和使用元注解自定义
注解及其使用1.ClassLoader在程序运行时首先要将类加载到内存中,这个加载工作
就是由ClassLoader完成的,故在中文文档中将其翻译为“类加载器”
那么我们代码中所用到的类有什么不同呢?
——它们的“来源”是不一样的
有的类是属于系统提供的类,比如:
String、Date、Object等因此,在Android
系统启动时会自动创建一个Boot类型的ClassLoader,该ClassLoader用于加载一些
系统层级的类
有的类属于我们自己写的类,比如:
User、Girl、Beauty等等因此,每个APP会
创建一个自己的ClassLoader实例,该ClassLoader用于加载dex
privatevoidgetClassLoaders(){ClassLoaderclassLoader=getClassLoader();while
(null!
=classLoader){System.out.println(“----classLoader=“+classLoader);classLoader
=classLoader.getParent();}}
此处一共展示了三个ClassLoader
第一个ClassLoader,如下:
dalvik.system.PathClassLoader[DexPathList[[zip_file”/data/app/com.lizi.classloaderdemo-
1/base.apk”],nativeLibraryDirectories=[/data/app/com.lizi.classloaderdemo-1/lib/x86,
/vendor/lib,/system/lib]]]
该PathClassLoader在应用启动时创建,用于加载/data/app/cc.testreflection-
2/base.apk中的类
ClassLoader是一个抽象类,它有三个常用的子类:
PathClassLoader、
URLClassLoader、DexClassLoader
PathClassLoader它只能加载已经安装的apk中的资源,比如dex文件
URLClassLoader它只能用于加载jar文件中的资源。
但是dalvik不能直接识别jar,
因此这个加载器极少使用。
DexClassLoader它用于从.jar和.apk类型的文件内部加
载classes.dex。
该类加载器常用来完成动态加载apk的需求第二个ClassLoader,我
不是很清楚,嘿嘿,欢迎指点。
第三个ClassLoader,如下:
java.lang.BootClassLoader@112e4ef该BootClassLoader在系统启动的时候创建,用
于加载系统层级的类
看看解释的这两个类加载器的应用
privatevoidtestClassLoader(){try{Classclazz=
Class.forName(“cc.testreflection.Girl”);ClassLoaderclassLoader=
clazz.getClassLoader();System.out.println(“----classLoader=“+classLoader);
classLoader=mContext.getClass().getClassLoader();InputStreaminputStream=
classLoader.getResourceAsStream(“assets/ic_launcher.png”);System.out.println(“----
classLoader=“+classLoader);clazz=Class.forName(“java.lang.String”);classLoader=
clazz.getClassLoader();System.out.println(“----classLoader=“+classLoader);}catch
(Exceptione){}}
我们自己的类com.lizi.classloaderdemo和assets文件夹中的图片ic_launcher.png都
是由PathClassLoader加载的,而java.lang.String是由BootClassLoader加载的
2.泛型2.1概念泛型始现于JDK1.5,从那以后大家在项目常常使用泛型,比
如:
ArrayListGirlarrayList=newArrayListGirlfor(inti=0;i10;i++){Girlgirl=new
Girl();arrayList.add(girl);}在与此类似的场景中利用泛型限定了集合中的输入类型,
从而让编译器屏蔽了源程序中的非法数据输入,比如此时往ArrayList中add一个
Boy就无法通过编译器的编译
泛型主要是给编译器看的;那么在编译完成之后生成的字节码里泛型会发生什么
变化呢?
来看个例子:
privatevoidtestArraylistClass(){Classclazz1=newArrayListInteger().getClass();
Classclazz2=newArrayListString().getClass();booleanisEqual=(clazz1==clazz2);
System.out.println(“----isEqual=“+isEqual);}输出结果:
—-isEqual=true带不同泛型的ArrayList在编译后生成的Class是相同的!
也就是
说,泛型在编译生成字节码文件时会被”擦除”;不管ArrayList带什么泛型,在编译
后都是ArrayList所对应的字节码文件
privatevoidtestArraylistGeneric(){try{ArrayListIntegerarrayList=newArrayList
IntegerarrayList.add(9527);arrayList.add(9528);Method
method=arrayList.getClass().getMethod(“add”,Object.class);
method.invoke(arrayList,”hello,java”);for(inti=0;iarrayList.size();i++){
System.out.println(“----arrayList.get(“+i+”)=“+arrayList.get(i));}}catch(Exception
e){}}输出结果如下图所示:
看到了吧,之因此能把一个字符串add到该ArrayList中,究其原因还是因为泛型
的擦除所致
2.2自定义泛型方法publicstaticTTgenericMethod1(Tt){returnnull;}publicK,V
KgenericMethod2(Kk,Vv){returnnull;}publicK,VStringgenericMethod3(Kk,Vv)
{returnnull;}在自定义泛型方法时,请注意在方法的返回值之前声明一个泛型,比
如:
这就表示该方法使用到了泛型T。
在此之后,在方法的输入参数中和方法体中
均可以使用该泛型
2.3自定义泛型接口publicinterfaceUserInfoT{publicvoidprintUserInfo(T
t);}privateclassUserInfoImplTimplementsUserInfoT{@Overridepublicvoid
printUserInfo(Tt){}}在自定义泛型接口时,请注意在接口名之后声明一个泛型,比
如:
这就表示该接口使用到了泛型T。
在此之后,在接口定义方法时就可以使用该
泛型了
2.4自定义泛型类publicclassCollectionK,V{privateKkey;privateVvalue;
privateKgetValue(Kk){returnnull;}privatevoidprintValue(Vv){}}自定义泛型类
与自定义泛型接口非常类似,不再赘述
3.反射我们知道Java代码会被编译成字节码文件,当需要用一个类创建其对象的
时候就会将其对应的字节码文件装载到内层,然后新建对象。
也就是说,当一个类编译完成后,在生成的.class文件中会产生一个Class对象,
该对象用于表示这个类的信息,比如类的属性,字段,构造方法等等
既然Class中包含了这么多有用的信息,那么我们可以用什么方式获取Class呢?
3.1获取Class//第一种方式Classclazz=Girl.class;System.out.println(“----“+
clazz.getName());//第二种方式Girlgirl=newGirl();clazz=girl.getClass();
System.out.println(“----“+clazz.getName());//第三种方式clazz=
Class.forName(“com.lizi.classloaderdemo.Girl”);System.out.println(“----“+
clazz.getName());三种方式:
1.利用类名.class获取2.利用对象.getClass()获取3.利用Class.forName(“类名”)获取
在获取到Class之后,就可以利用newInstance()方法生成一个对象
Objectobject=clazz.newInstance();其实,在调用newInstance()方法时实际上是调
用了该类的无参构造方法。
当然,我们的目的不仅仅是利用newInstance()生成一个对象,更重要的是要采用
反射技术结合Class获取到该类的构造方法,属性,方法等信息
在该类中有一些简单的属性,比如年龄,姓名,国家,城市,腰围,胸围,臀
围。
还有一些简单的方法比如,构造方法Girl(Stringname,Integerage),获取电话号
码getMobile();看到这里获取大家可能发现了:
这些属性和方法有的是公有的,有
的是私有的。
访问属性的不同会带来哪些差异呢?
带着这个小疑问,我们来看看常
见的反射使用方法
3.2获取构造方法/***利用反射获取类的构造器**1getConstructors()获取类
的构造器,但获取不到私有构造器*2getDeclaredConstructors()获取类的所有构造
器*3getDeclaredConstructor()获取指定的构造器*/privatevoidtestGetConstructor()
{try{Classclazz=Class.forName(“com.lizi.classloaderdemo.Girl”);Constructor[]
Constructors=clazz.getConstructors();for(Constructorconstructor:
Constructors){
System.out.println(“----constructor=“+constructor);}System.out.println(“-----------------
-----------”);Constructor[]declaredConstructors=clazz.getDeclaredConstructors();for
(ConstructordeclaredConstructor:
declaredConstructors){System.out.println(“----
declaredConstructor=“+declaredConstructor);}System.out.println(“-------------2----------
-----”);Constructorconstructor=clazz.getDeclaredConstructor(String.class,Integer.class);
constructor.setAccessible(true);Girlgirl=(Girl)constructor.newInstance(“liuyan”,
Integer.valueOf(22));System.out.println(“----girl=“+girl);}catch(Exceptione){
e.printStackTrace();}}获取类所有的构造器,这个没啥可说的。
那么怎么获取指定的
构造器呢?
一个类可能有多个重载的构造方法,它们的方法名都是一样的;因此此
时需要从构造器的输入参数入手,比如:
clazz.getDeclaredConstructor(String.class,Integer.class);就可以获取到如下的构造方
法:
privateGirl(Stringname,Integerage){}但是请注意该构造方法是private的,因此需
要将该方法的accessible标志设置为true表示取消语言访问检查。
即:
constructor.setAccessible(true);在获取构造方法后即可利用newInstance()创建对
象,即:
Girlgirl=(Girl)constructor.newInstance(“liuyan”,Integer.valueOf(22));3.3利用反射
获取字段/***利用反射操作类的字段*1getFields()获取类的字段,但是获取不到
私有字段*2getDeclaredFields()获取类的所有字段*3获取指定的字段及其type*
4获取指定对象的某个字段值*5设置指定对象的某个字段值*/privatevoid
testGetField(){try{Classclazz=Class.forName(“com.lizi.classloaderdemo.Girl”);
Field[]fields=clazz.getFields();for(Fieldfield:
fields){System.out.println(“----field=“
+field);}Field[]declaredFields=clazz.getDeclaredFields();for(FielddeclaredField:
declaredFields){System.out.println(“-----declaredField=“+declaredField);}//获取指定
的字段及其typeFieldfield=clazz.getDeclaredField(“name”);Classtype=
field.getType();System.out.println(“----field=“+field+“,type=“+type);
System.out.println(“-----------------------“);//获取指定对象的某个字段值Girlgirl=new
Girl(“lucy”,100,100,100,18);Fieldfield2=clazz.getDeclaredField(“name”);
field2.setAccessible(true);Stringname=(String)field2.get(girl);System.out.println(“----
name=“+name);//设置指定对象的某个字段值Fieldfield3=
clazz.getDeclaredField(“name”);field3.setAccessible(true);field3.set(girl,“hanmeimei”);
System.out.println(“----girl=“+girl);}catch(Exceptione){}}3.4利用反射获取类中
的方法/***利用反射获取类的方法*1getMethods()获取该类及其父类的方法,但
不能获取到私有方法*2getDeclaredMethods()获取该类本身所声明的所有方法*3
反射出类中的指定方法*/privatevoidtestGetMethod(){try{Classclazz=
Class.forName(“com.lizi.classloaderdemo.Girl”);Objectobject=clazz.newInstance();
Method[]methods=clazz.getMethods();for(Methodmethod:
methods){
System.out.println(“----method=“+method);}Method[]declaredMethods=
clazz.getDeclaredMethods();for(MethoddeclaredMethod:
declaredMethods){
System.out.println(“----declaredMethod=“+declaredMethod);}Methodmethod=
clazz.getDeclaredMethod(“getMobile”,String.class);ClassreturnType=
method.getReturnType();System.out.println(“----method=“+method+”,returnType=“+
returnType);method.setAccessible(true);Stringmobile=(String)method.invoke(object,
“678”);System.out.println(“----mobile=“+mobile);}catch(Exceptione){}}3.5利用
反射操作数组/***利用反射操作数组*1利用反射修改数组中的元素*2利用反
射获取数组中的每个元素*/privatevoidtestArrayClass(){int[]intArray=new
int[]{5,7,9};Array.set(intArray,0,9527);Classclazz=intArray.getClass();if
(clazz.isArray()){intlength=Array.getLength(intArray);for(inti=0;ilength;i++){
Objectobject=Array.get(intArray,i);StringclassName=object.getClass().getName();
System.out.println(“----object=“+object+”,className=“+className);}}}3.6利用反射
获取泛型的参数类型在许多框架中有这样的需求:
根据不同的泛型参数响应不同的
操作。
一说到泛型参数类型,可能大家立马就想到了刚才说的泛型擦除,比如ArrayList
在编译后就变成了ArrayList,它原本的泛型被”擦除”了。
但是我们有时确实需要知
道泛型的参数类型,又该怎么来实现呢?
按照刚才的那些思路恐怕是走不通了,得
另辟蹊径了
第一步:
定义getGenericHelper()方法其输入参数为带泛型的参数,比如ArrayList
String,Integer
第二步:
利用反射获取到该getGenericHelper()方法,即:
Method
method=getClass().getDeclaredMethod(“getGenericHelper”,HashMap.class);
第三步:
获取到该方法的带泛型的输入参数,即:
Type[]genericParameterTypes=method.getGenericParameterTypes();注意
getGenericParameterTypes()方法返回的是一个数组,因为方法可能有多个参数,但
是依据我们的需求这个数组中是仅有一个元素的
4.2ParameterizedTypeParameterizedType称为参数化类型,比如HashMapK,
Integer
privateHashMapK,IntegerhashMap=null;publicvoidtestParameterizedType()
throwsE