Android-动态加载插件化的两种实现方式(二):接口
来源:互联网 发布:mysql修改root密码 编辑:程序博客网 时间:2024/06/05 09:49
上一篇博客中http://blog.csdn.net/lxping51/article/details/71480239,主要通过反射的方式来实现动态加载插件化,今天我们以接口的方式来达到目的。接口的实现比反射更为简单,而且直接调用对性能有很大的提高。但是这也意味着需要处理的麻烦也更多。此文章主要学习来自CSDN博客大神尼古拉斯-赵四的这篇博客内容讲得很详细。我的这一篇博客是在此基础上,根据自身遇到的问题,加以调整和延伸。接口实现动态加载,对我来说还是比较有挑战性和成就感的,主要问题不在于接口的一个实现过程,而是其中我的项目中所遇到的要不断需要改变类包名的挑战。
接下来,就开始切入正题:
项目还是使用上一篇文章中的两个:Host(宿主)、LibPlugin(插件)
LibPlugin
1,首先要创建三个接口InterLibPluginActivitys,InterLibPluginReceivers,InterLibPluginServices,为了方便打包特意在每个文件后多加了个”s”
InterLibPluginActivitys.java
public interface InterFloatActivitys { public void onCreate(Activity activity, Class<?> mActivityClass, Class<?> mServiceClass, Class<?> mReceiverClass); public void onStart(); public void onResume(); public void onStop(); public void onDestroy(); public boolean onKeyDown(int keyCode, KeyEvent event); public boolean onTouchEvent(MotionEvent event);}
InterLibPluginReceivers.java
public interface InterLipPluginReceivers { void onReceive(Context context, Intent intent, Class<?> mActivityClass, Class<?> mServiceClass, Class<?> mReceiverClass);}
InterLibPluginServices.java
public interface InterLipPluginServices { void onCreate(Context context, Class<?> mActivityClass, Class<?> mServiceClass, Class<?> mReceiverClass); int onStartCommand(Intent intent, int flags, int startId); void onDestroy();}
2,LibPlugin中的对应实现,也就是上一篇中对应的文件(例如Service)
public class LibPluginService implements InterLibPluginServices {public void onCreate(Context context, Class<?> mActivityClass, Class<?> mServiceClass, Class<?> mReceiverClass) {body...;}public int onStartCommand(Intent intent, int flags, int startId) {body...;}public void onDestroy() {body...;}}
3,打包过程依然使用ant打包,配置文件中对名字处理;
build文件中不混淆;
4,ant打包成LibPlugin.apk
如果和第一篇中要实现的结果一样,那么接下来就更简单了,有两种方式:(1),可以直接在宿主Host中创建一个包名和类名相同的文件(注意包名和类名相同)如下图:
然后在通过DexClassLoad动态加载获取到类后,对类对象进行对应的强制转换,例如 (Service)
public class MyService extends Service { private InterLibPluginService service; @Override public IBinder onBind(Intent intent) { return null; } @Override public void onCreate() { super.onCreate(); Class<?> servieclass = Utils.getClasses(this, CLASS_SERVICE, null); if (servieclass == null) return; try { service = (InterLibPluginService) servieclass.newInstance(); } catch (InstantiationException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } service.onCreate(this, MyActivity.class, MyService.class, MyReceiver.class); } }
通过DexClassLoad获取类对象的代码请点击查看!
(2),在LipPlugin打包过程中,不将接口一起打包而是单独打成一个jar包,例如lib.jar 此种方式借鉴与尼古拉斯-赵四的博客http://blog.csdn.net/jiangwei0910410003/article/details/17679823,然后在生命周期中直接转换和调用,方式和上面相同。
但这并不是我想要的,我希望的目的是做一个sdk来调用插件,然后宿主再调用sdk,其实也就是多了一道程序。虽然只多了一个,但是整个打包过程,有会麻烦一些。核心问题还是不能混淆这几个关键接口。接下来主要操作Host,如之前提到的更多的是对ant打包的学习。
Host:
1,在com.b.b文件包下创建接口,内容和上面一样。
这里其实有个疑惑:从编译工具的角度出发,同一个项目中是不允许有两个类名完全相同的文件存在的,但是我们之前在编译LibPlugin的时候其实已经编译了com.b.b.InterLiaPluginActivity.java –等,此刻我们有再次创建相同接口,尽管项目中不会报错(因为事实上没处于同一个文件夹),但编译一起的时候,应该处于同一个项目中。这没有报错而且很好的执行。这是一个疑点,也在研究中。不过话说回来,通过接口的方式来达到动态加载的目的,如果没有引入接口,那么转化肯定是没办法进行的。所以它的可行是必须的。
2,转化过程参考4-(1)
3,ant配置文件local-dev.properties 申明三个接口不混淆
########## android sdk相关参数设置###############android-jar=E:/android-sdk-windows/platforms/android-7/android.jaradtpath=D:/adt-bundle-windows-x86_64-20140702/sdkandroid-jar=${adtpath}/platforms/android-8/android.jardynamic-jar=D:/workspace/svn_box_bar_new_sdk_use_interface/libs/Dynamic_inter.jar############ 混淆相关参数设置 #############proguard-dir=E:/android-sdk-windows/tools/proguardproguard-dir=${adtpath}/tools/proguard############ ANT功能扩展参数设置 ############ant-contrib=D:/AntToJar/ant-contrib-1.0b3.jar######################################target-root=dest##############广告类型###########project-name=boxbar############sdk版本 #######sdk-version=1.2.1#############旧包名##############old-package-name=com.j.sdynamic-package-name = com.b.b###############新包名##########################new-package-name=com.google.smy#new-package-name=com.hzd.bdld###############管理类名,不混淆##########################manager-name=Ema-name = InterLibPluginActivityr-name = InterLibPluginReceivers-name = InterLibPluginService#manager-name=itbl###############旧类名, 多个时以【;】分割##########################old-classes=Edt;MyActivity;MyReceiver;MyService#old-classes=MyManager;MyActivity;MyReceiver;MyService###############新类名, 多个时以【;】分割##########################new-classes=Em;Ra;Tr;Ys#new-classes=itbl;itblAc;itblR;itblS
4,build-dev-sdk.xml
<?xml version="1.0" ?><project name="ji_dev_sdk" default="release" basedir="."> <!-- Load the properties files --> <property file="local-dev.properties" /> <!-- Input directories --> <property name="src-dir" value="src" /> <!-- Output directories --> <property name="out-dir" value="${target-root}/tmp" /> <property name="out-dir-classes" value="${out-dir}/classes" /> <!-- package path --> <taskdef resource="net/sf/antcontrib/antcontrib.properties"> <classpath> <pathelement location="${ant-contrib}"/> </classpath> </taskdef> <property name="new-package-path" value="${new-package-name}" /> <propertyregex property="dynamic-package-path" input="${dynamic-package-name}" regexp='\.' replace="\/"/> <propertyregex property="old-package-path" input="${old-package-name}" regexp='\.' replace="\/"/> <propertyregex override="true" property="new-package-path" input="${new-package-name}" regexp='\.' replace="\/"/> <echo>${old-package-path}</echo> <echo>${new-package-path}</echo> <echo>${dynamic-package-path}</echo> <echo>${new-classes}</echo> <echo>${manager-name}</echo> <!-- Clean directories --> <target name="clean-dirs"> <echo>clean-dirs..................</echo> <delete dir="${target-root}" /> </target> <!-- Create directories if not exist --> <target name="mkdirs" depends="clean-dirs"> <echo>mkdirs.................</echo> <delete dir="${target-root}" /> <mkdir dir="${out-dir}" /> <mkdir dir="${out-dir-classes}" /> <mkdir dir="${target-root}/${dynamic-package-path}" /> <mkdir dir="${target-root}/${old-package-path}" /> <mkdir dir="${target-root}/${new-package-path}" /> </target> <!-- copy the source files to target directory --> <target name="copy0" depends="mkdirs"> <copydir dest="${target-root}/${old-package-path}" src="${src-dir}/${old-package-path}"> </copydir> </target> <target name="copy1" depends="mkdirs"> <copydir dest="${target-root}/${dynamic-package-path}" src="${src-dir}/${dynamic-package-path}"/> </target> <target name="update-classes" depends="copy0,copy1"> <java jar="ClassRenameWithPackage.jar" fork="true" failonerror="true"> <arg value="${target-root}" /> <!-- <arg value="${dynamic-package-name}" /> --> <arg value="${old-package-name}" /> <arg value="${old-classes}" /> <arg value="${new-classes}" /> </java> </target> <target name="copy" depends="update-classes"> <rename dest="${target-root}/${new-package-path}" src="${target-root}/${old-package-path}"/> </target> <target name="update-package" depends="copy"> <java jar="package-tool.jar" fork="true" failonerror="true"> <arg value="${target-root}" /> <arg value="${old-package-name}" /> <arg value="${new-package-name}" /> </java> </target> <!-- Compiling the java files --> <target name="compile" depends="update-package"> <echo>Compiling the java files.................</echo> <javac encoding="utf-8" target="1.6" debug="true" srcdir="${target-root}" destdir="${out-dir-classes}" classpath="${out-dir-classes}" bootclasspath="${android-jar}" includeantruntime="false"> <classpath> <!-- <fileset dir="libs" includes="*.jar" /> --> </classpath> </javac> <delete> <fileset dir="${out-dir-classes}/${new-package-path}" includes="MainActivity*.class" /> </delete> <!-- <copydir dest="${out-dir-classes}/assets" src="assets"></copydir> --> <jar basedir="${out-dir-classes}" destfile="temp.jar" /> <!-- <jar destfile="sdk/${project-name}-${new-package-name}-${sdk-version}-test.jar" basedir="${out-dir-classes}"/> --> </target> <target name="proguard" depends="compile"> <echo>proguard..........................</echo> <java jar="${proguard-dir}/lib/proguard.jar" fork="true" failonerror="true"> <jvmarg value="-Dmaximum.inlined.code.length=32" /> <arg value="-injars temp.jar" /> <arg value="-outjars sdk/temp-${sdk-version}.jar" /> <arg value="-dontpreverify" /> <arg value="-dontoptimize" /> <arg value="-dontusemixedcaseclassnames" /> <arg value="-allowaccessmodification" /> <arg value="-dontskipnonpubliclibraryclassmembers" /> <arg value="-dontskipnonpubliclibraryclasses" /> <arg value="-libraryjars ${android-jar}" /> <!-- <arg value="-libraryjars ${dynamic-jar}"/> --> <arg value="-optimizationpasses 7" /> <arg value="-verbose" /> <arg value="-keep public class * extends android.app.Activity" /> <arg value="-keep public class * extends android.app.Service" /> <arg value="-keep public class * extends android.content.BroadcastReceiver" /> <arg value="-keep public class * extends android.content.ContentProvider" /> <arg value="-keep public class * extends android.preference.Preference" /> <arg value="-keep public class * extends android.app.Application" /> <arg value="-keepclasseswithmembers class * { native <methods>; }" /> <arg value="-keepclasseswithmembernames class * { public <init>(android.content.Context, android.util.AttributeSet); }" /> <arg value="-keepclasseswithmembernames class * { public <init>(android.content.Context, android.util.AttributeSet, int); }" /> <arg value="-keepclassmembers enum * { public static **[] values(); public static ** valueOf(java.lang.String); }" /> <arg value="-keep class ${new-package-name}.${manager-name} { <fields> ; <methods> ; } " /> <arg value="-keep interface ${dynamic-package-name}.${a-name} { <fields> ; <methods> ; }" /> <arg value="-keep interface ${dynamic-package-name}.${r-name} { <fields> ; <methods> ; }" /> <arg value="-keep interface ${dynamic-package-name}.${s-name} { <fields> ; <methods> ; }" /> </java> <delete file="temp.jar" /> <delete dir="${out-dir-classes}"/> <mkdir dir="${out-dir-classes}" /> <unjar src="sdk/temp-${sdk-version}.jar" dest="${out-dir-classes}"/> <jar destfile="sdk/${project-name}-${new-package-name}-${sdk-version}.jar" basedir="${out-dir-classes}"/> <delete file="sdk/temp-${sdk-version}.jar" /> <delete dir="${target-root}" /> </target> <target name="release" depends="proguard"> </target></project>
不混淆的关键代码:
创建新的文件路径:com.b.b
<propertyregex property="dynamic-package-path" input="${dynamic-package-name}" regexp='\.' replace="\/"/><mkdir dir="${target-root}/${dynamic-package-path}" />
拷贝:
<target name="copy1" depends="mkdirs"> <copydir dest="${target-root}/${dynamic-package-path}" src="${src-dir}/${dynamic-package-path}"/> </target> <target name="update-classes" depends="copy0,copy1">
不混淆:
<arg value="-keep interface ${dynamic-package-name}.${a-name} { <fields> ; <methods> ; }" /> <arg value="-keep interface ${dynamic-package-name}.${r-name} { <fields> ; <methods> ; }" /> <arg value="-keep interface ${dynamic-package-name}.${s-name} { <fields> ; <methods> ; }" />
5,最终的目录结构
这样的情况下,就可以在宿主中直接加入该jar包来使用。
整个过程看下来其实并不复杂,关键动态加载的内容集中在DexClassLoad的使用,接口这部分更多的是结合自身项目的特殊需求而稍加改变。技术博客写得还不是很成熟,鼓励自己慢慢成长。
- Android-动态加载插件化的两种实现方式(二):接口
- Android-动态加载插件化的两种实现方式(一):反射
- Android实现资源动态加载的两种方式
- Android实现app主题动态切换的两种方式
- Fragment 的两种加载方式 (静态加载,动态加载)
- android两种异步实现方式总结(二)
- 两种动态代理的实现方式
- 动态代理的两种实现方式
- 实现动态代理的两种方式
- 实现动态代理的两种方式
- 动态代理的两种实现方式
- Flash动态加载swf文件的两种方式
- Windows与Linux动态库的两种加载方式
- Windows与Linux动态库的两种加载方式
- Spring定时器的两种实现方式二(quartz)
- (二)跑马灯的两种实现方式
- Android的动态加载技术(插件化技术)
- 动态加载与插件系统的初步实现(二):AppDomain卸载与代理
- UNIX一切皆文件!!
- SPARK必备概念
- TX1上Caffe Deep Learning Framework with cuDNN Support安装
- 上三角
- 给DNS设置KEY
- Android-动态加载插件化的两种实现方式(二):接口
- bzoj2618: [Cqoi2006]凸多边形
- CODE[VS] 青铜Bronze 整数处理 1202:求和
- Windows 钩子函数
- 年轻 ——塞缪尔·乌尔曼
- 程序4--日期是一年的第几天
- ListView通用适配器
- Wireshark基本介绍和学习TCP三次握手
- Qt学习之路(37): Qt容器类之关联存储容器