扫二维码与项目经理沟通
我们在微信上24小时期待你的声音
解答本文疑问/技术咨询/运营咨询/技术建议/互联网交流
原理:实现原理上都选择尽量少的hook,通过在manifest上预埋一些组件实现四大组件的插件化。其中Small更形成了一个跨平台、组件化的框架。
成都创新互联坚持“要么做到,要么别承诺”的工作理念,服务领域包括:成都网站建设、网站建设、企业官网、英文网站、手机端网站、网站推广等服务,满足客户于互联网时代的盘州网站设计、移动媒体设计的需求,帮助企业找到有效的互联网解决方案。努力成为您成熟可靠的网络建设合作伙伴!
VirtulApp:
能够完全模拟app的运行环境,能够实现免安装应用和双开技术。
Atlas:
阿里出品,号称是一个容器化框架,结合了组件化和热更新技术。
Android中有两种类加载器,DexClassLoader和PathClassLoader,它们都继承于BaseDexClassLoader。
两者的区别:DexClassLoader多了一个optimizedDirectory的路径参数,这个目录必须是内部存储路径,用于缓存系统创建的Dex文件。
所以我们可以使用DexClassLoader去加载外部Apk中的类。
ClassLoader调用loadClass方法加载类采用了双亲委托机制来避免重复加载类。
首先,ClassLoader会查看自身已经加载的类中是否已经存在此类,如不存在,然后,则会使用父类来加载此类,如不能成功加载,则会使用自身重载于BaseDexClassLoader的findClass()方法来加载此类。
DexClass的DexPathList在DexClass的构造器中生成,findClass()方法则是从DexPathList下面找出对应的DexFile,循环DexElements,通过dexElement.dexFile取出对应的DexFile,再通过DexFile.loadClassBinaryName()加载对应的类。
作用:使用插件DexClassLoader加载出需要的类。
通过每一个插件的DexClassLoader加载出自身所需要的类,当每一个插件需要加载相同的类库时,可采用该类库的不同版本来使用。
通过把每一个插件的pathList(DexFile)合并到主app的DexClassLoader上,来使各个插件和主app直接能够相互调用类和方法,并且各个插件中相同的功能可以抽取出来作为一个Common插件供其它插件使用。
插件调用主工程
在ClassLoader构造时指定主工程的DexClassLoader为父加载器即可直接调用主工程中的类和方法。
主工程调用插件
如果是多DexClassLoader的情况,则需要通过插件的DexClassLoader加载对应的类并反射调用其方法。此种情况,主工程一般会在一个统一的地方对访问插件中的类和方法做一些访问权限的管理及配置。
如果是单DexClassLoader的情况,则可以直接调用插件中的类和方法。但是当多个插件引用的库的版本不同时,会出现错误,因此,建议采用Gradle版本依赖管理统一处理主工程及各个插件的库依赖。
Android通过Resource来加载资源,只要有插件apk,就可以使用assertManager.addAssertPath(apkPath)的方式来生成assertManager,再使用其new出对应的Resource对象即可。
注意:由于AssertManager并不是Public,所以需要通过反射的方式去调用它。并且由于一些Rom对Resource的处理,所以,需要兼容处理。
有2种处理方式:
产生的原因:由于主工程和各个插件引用的Resource id重复产生的冲突。
解决思路:Android中的资源在系统中是以8位16进制0XPPTTRRRR的方式存在,其中PP即是资源区分的区域(Android系统只用它来区分系统资源和应用资源),只要让每一个插件的PP段取不同的值即可解决资源id冲突的问题。
具体解决方式:
1.修改aapt源码,编译期修改PP段。
2.修改Resource的arsc文件,其中的每一条都包含了资源id和映射路径。
Activity的处理最为复杂,有两种处理方式:
1.ProxyActivity的方式。
2.预埋StubActivity,hook系统启动Activity的过程。
原理:VirtualAPK通过替换了系统的Instrumentation,hook了Activity的启动和创建,省去了手动管理插件Activity生命周期的繁琐,让插件Activity像正常的Activity一样被系统管理,并且插件Activity在开发时和常规一样,即能独立运行又能作为插件被主工程调用。
Android插件化方向主要有2个方向:
Android 插件化
《Android插件化开发指南》百度网盘pdf最新全集下载:
链接:
?pwd=qn54 提取码: qn54
简介:本书不仅详细介绍Android插件化技术如何实现,而且包含大量Android系统的底层知识,有助于App开发人员深入理解Android系统,从而写出更健壮的代码。
使用Android Studio开发Gradle插件的步骤:
1 创建Gradle Module
AndroidStudio中是没有新建类似Gradle Plugin这样的选项的,那我们如何在AndroidStudio中编写Gradle插件,并打包出来呢?
(1) 首先,你得新建一个Android Project
(2) 然后再新建一个Module,这个Module用于开发Gradle插件,同样,Module里面没有gradle plugin给你选,但是我们只是需要一个“容器”来容纳我们写的插件,因此,你可以随便选择一个Module类型(如PhoneTablet Module或Android Librarty),因为接下来一步我们是将里面的大部分内容删除,所以选择哪个类型的Module不重要。
(3) 将Module里面的内容删除,只保留build.gradle文件和src/main目录。
由于gradle是基于groovy,因此,我们开发的gradle插件相当于一个groovy项目。所以需要在main目录下新建groovy目录
(4) groovy又是基于Java,因此,接下来创建groovy的过程跟创建java很类似。在groovy新建包名,如:com.hc.plugin,然后在该包下新建groovy文件,通过new-file-MyPlugin.groovy来新建名为MyPlugin的groovy文件。
(5) 为了让我们的groovy类申明为gradle的插件,新建的groovy需要实现org.gradle.api.Plugin接口。如下所示:
package com.hc.plugin
import org.gradle.api.Plugin
import org.gradle.api.Project
public class MyPlugin implements Pluginproject {/project
void apply(Project project) {
System.out.println("========================");
System.out.println("hello gradle plugin!");
System.out.println("========================");
}
}
因为我本人对groovy也不是特别熟悉,所以我尽可能的用Java语言,使用System.out.println而不是用groovy的pintln "",我们的代码里面啥也没做,就打印信息。
(6) 现在,我们已经定义好了自己的gradle插件类,接下来就是告诉gradle,哪一个是我们自定义的插件类,因此,需要在main目录下新建resources目录,然后在resources目录里面再新建META-INF目录,再在META-INF里面新建gradle-plugins目录。最后在gradle-plugins目录里面新建properties文件,注意这个文件的命名,你可以随意取名,但是后面使用这个插件的时候,会用到这个名字。比如,你取名为com.hc.gradle.properties,而在其他build.gradle文件中使用自定义的插件时候则需写成:
apply plugin: 'com.hc.gradle'
然后在com.hc.gradle.properties文件里面指明你自定义的类
implementation-class=com.hc.plugin.MyPlugin
现在,你的目录应该如下:
(7) 因为我们要用到groovy以及后面打包要用到maven,所以在我们自定义的Module下的build.gradle需要添加如下代码:
apply plugin: 'groovy'
apply plugin: 'maven'
dependencies {
compile gradleApi()
compile localGroovy()
}
repositories {
mavenCentral()
}
2 打包到本地Maven
前面我们已经自定义好了插件,接下来就是要打包到Maven库里面去了,你可以选择打包到本地,或者是远程服务器中。在我们自定义Module目录下的build.gradle添加如下代码:
group='com.hc.plugin'
version='1.0.0'
uploadArchives {
repositories {
mavenDeployer {
repository(url: uri('D:/repos'))
}
}
}
其中,group和version后面会用到,我们后面再讲。虽然我们已经定义好了打包地址以及打包相关配置,但是还需要我们让这个打包task执行。点击AndroidStudio右侧的gradle工具,如下图所示:
可以看到有uploadArchives这个Task,双击uploadArchives就会执行打包上传啦!执行完成后,去我们的Maven本地仓库查看一下:
其中,com/hc/plugin这几层目录是由我们的group指定,myplugin是模块的名称,1.0.0是版本号(version指定)。
步骤1:注册ApkPlug官网账号:
打开Apkplug官网后,点击右上角的“注册”,在跳转页面填入相关信息,注册界面如下:
确认后注册成功,使用你的账号登录网站。你就可以用Apkplug开发应用了
END
步骤2:开发插件
Apkplug中的插件也是一个完整的apk,它与普通应用的区别有以下3点:
1, 插件assets目录下有一个plugin.xml文档,通过它可判断一个工程是主应用还是插件。
2, 插件有一个入口类BundleActivator
3, 插件会外部引用一个osgi.jar文件
开发插件的步骤有如下4步:
1,引入osgi.jar库文件
Apkplug中插件需要导入的库文件只有一个osgi.jar。
导入osgi.jar库文件需要注意一下
osgi.jar文件只能引用不能编译到apk文件中,否则会出现类冲突的情况
异常代码:had used a different Lorg/osgi/framework/BundleActivator; during pre-verification。
osgi.jar包导入方法:
这文件在Apkplug SDK中可以找到。
2,编写插件入口类BundleActivator
插件启动时首先调用BundleActivator,其功能类似android中的application类。
public class SimpleBundle implements BundleActivator
{
private BundleContext mcontext = null;
public void start(BundleContext context) throws Exception
{
System.err.println("你好我是插件,我将为你展示启动acitivty我已经启动了 我的BundleId为:"+context.getBundle().getBundleId());
}
public void stop(BundleContext context)
{
System.err.println("你好我是插件,我被停止了 我的BundleId为:"+context.getBundle().getBundleId());
}
}
3,编写plugin.xml配置文件
plugin.xml
是一个配置表,它跟AndroidManifest.xml作用类似。 plugin.xml文档放置在assets中即可 重要属性说明:
Bundle-Name 插件名称 Bundle-SymbolicName 插件包名
-与应用packagename可一一对应 Bundle-Version 插件版本 -1.0.0
Bundle-Activator 插件入口 -与Appliction 类似
Bundle-Activity 插件界面 -多个Activity可用 , 分割
Bundle-Service 插件Service -多个Service可用 , 分割
(v2.0.0新增) Bundle-Receiver 插件广播 -多个广播类可用 , 分割
(v2.0.0新增)
4, 编译生成插件apk文件
插件工程中添加的文件目录结构如下:
最后编译运行插件工程,生成的apk文件即为插件文件
END
步骤3:开发主应用
Apkplug 主应用开发分两步集成:
1. 获取主应用授权AppAuth。
登录账号进入Apkplug后台后,切换到“应用授权页面”,按要求填写好应用信息,然后确定,你就拥有了一个等待开发的应用授权AppAuth。应用授权界面如下:
进入“授权列表”页面,点击“查看详情”链接,进入“应用详情界面”,就可以看到已申请的AppAuth,点击其后面的“复制”,即可直接复制AppAuth,如下图所示
2. 对接Apkplug SDK 导入相关库文件。
①配置应用权限
主应用需要几个基础的权限配置,请将以下的几个权限加入到主应用的AndroidManifest.xml中。
!-- 插件平台需要的权限! --
uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/uses-permission
uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/
uses-permission android:name="android.permission.INTERNET"/
uses-permission android:name="android.permission.READ_PHONE_STATE"
/uses-permission
另外将一下加入到application/application节点中
!-- 插件平台需要的配置! --
activity
android:name="org.apkplug.app.apkplugActivity"
android:theme="@style/android:Theme.Light"
android:configChanges="orientation|keyboardHidden"
/
最后将我们从Apkplug管理后台申请到的AppAuth加入到配置文件中。
meta-data android:name="apkplug-auth" android:value="xxxxxxxx" /meta-data
注:由于3.2.2节中我们直接复制了AppAuth,此处直接粘贴到AndroidManifest文档中。
如下图:
②导入SDK库文件
主应用需要导入两个文件,将其放入libs目录中即可。
1, libndkfoo.so
2, Bundle2.0.0.jar
如下图:
这两个库文件在Apkplug SDK中可以找到。
然后:
主应用启动Apkplug最简只需要一段代码即可,建议在Application中启动框架。
FrameworkInstance frame=FrameworkFactory.getInstance().start(ListBundleActivator,Context);
将上一步骤开发好的插件apk,放置在主应用工程里的assets路径下。
如下图:
END
步骤4:启动主应用
最后启动主应用即可。简单的插件化apk的方法就讲完了,有兴趣的关注我,下次讲云端托管插件实现应用内更新。
首先由于我自己也是个新手,也是在学习各种框架然后给公司项目选定相应自动化框架,研究移动自动化测试框架也就近段时间而已,所以我只能从我自己今天为止的认知角度给各个框架抒发我自己的拙见,你看是否能从中接纳一二吧(对于我自己的话还需要再花一段时间去学习各个框架才能确定哪个/些是适合我们项目的了,也许到时我会写个正式的总结)。
根据你的要求,应该不会考虑MonkeyRunner和Robotium,但我还是想跟你说下其实Robotium还是挺不错的,如果你没有考虑跨进程调用其他APP的话。至于MonkeyRunner我就不大推荐了,你可以看下我对金阳光老师的一个评论的回复《MonkenRunner通过HierarchyViewer定位控件的方法和建议》(文章最后我干脆也贴出来了)。至于Robotium,你对比下本人博客里面各个框架编写的Note的测试示例就可以看出来Robotium相对其他框架会简介很多,况且发展的比UIAutomator和Appium长久很多,所以也应该会更成熟,和Eclipse集成调试起来也很方便。比起后两者如果有不足的话我觉得就以下几点吧:
1. 所有的操作抽象到一个Solo类里面,缺乏面向对象的编程思想,有时会让人不适应。如果你熟悉C语言等面向过程的语言思想的话应该没有问题。
2. 获取控件的方法比较缺乏,大概就几种:通过Text,ID, ClassName,Index。没有后两者的多种多样
3. 跨进程:因为底层使用Instrument框架,测试包和被测应用包打包在一起作为一个进程运行而线程间通过instrumentaiton进行通信,导致了逃不出这个进程设沙箱(sandbox)
4. 做不了模拟键盘的测试(但同时这个也是Robotium非常巨大的优点,因为不像后两者那样需要调用键盘导致输入的各种各样的问题),因为Robotium输入读出其实是直接对控件的text属性进行操作没有通过键盘驱动的,你如果做过UI编程应该就明白我的意思了,因为记住你的测试代码和目标应用是打包在同一个进程中的,同一个进程中想访问另外一个线程的某个变量,运用相应的IPC(Interprocess Communication)机制当然是没有问题的了。
然后到了你问的主题UIAutomator和Appium的对比,我个人是这样看的:
1. UIAutomator是亲爹(google)生的,所以可以保证后续的开发维护力量,除非google倒闭(这里我有点不懂的是为什么google对Monkeyrunner的态度这么让人摸不着头脑,具体请看以上我说的对MonkeyRunner的评论)
2. Appium虽然不是亲爹生的,但是干爹实力雄厚把它武装的无所不能(android,ios,firefox,browser通杀),单单以android来说,底层用得还是UIAutomator,所以只要它能及时跟上UIAutomator的更新,功能上面我不是很担心。
3. 但是也这是Appium的这种架构:UIautomator/seledroid-Appium Server-Selenium/AppiumDriver-Test Case (《Appium架构框架图整理》),导致框架有点复杂,当问题出现的时候调试起来比较难以定位,不知道哪个模块出错了。但是说道调试,总比UIAutomator好,起码Appium可以直接集成到eclipse上面进行debug,UiAutomator却每次都要push到目标机器然后再去执行,怎么调试呢?到现在为止我知道的只能原始的print了。
4. 向下兼容问题:Appium可以通过底层UIAutomator/Selendroid(不记得是不是这名字了)通杀;UIAutomator只能在API Level
17(包含)以上使用
5.语言支持:appium基本通杀,UIAutomator用java足矣
6.跨平台:如你所说的只是android两者都没有问题,如果往后需要扩展到ios,那么建议appium
7.bug数量:UIAutomator有的问题Appium都会有,UIAutomator没有的问题Appium也有可能有^_^(不过我还是很看好Appium的)
8. 输入问题,都有bug,具体请查看我相应blog,特别是中文输入,这就是为什么我刚才特意提出Robotum的原因之一
9. WebView支持:UIAutomator据说今年年初已经开始支持,个人没有这方面要求所以没研究;Appium的框架用的Selenium本身就是PC上最流行的开源Web测试框架,所以必然支持了。注意这你你要有点android编程知识了,WebView指的不仅是WebView控件还包含如用sencha+phonegap把webview封装成一个跨平台app的情况了,具体如果不清楚请google。
其他区别我现在就没有想到了,希望能帮助到你,从我自己的角度来看,我觉得UIAutomator继续往前发展是必然的了,但是它不可能最终支持ios。至于Appium我同样有很大的信心它会继续往好的方向发展,且考虑到它的跨平台支持,基于node.js(现在非常流行哦),兼容性等,我如果是你的话我会考虑用Appium的(抛开Robotium不说,如果你又要考虑的话就需要你根据我之前说的再总结下了^_^)。
我觉得这个可以类比之前的微软和Borland的关系,API是Windows,但是IDE是Borland的,各专所长了。可惜(或者庆幸)后来微软发力一下把Borland打得满地找牙一蹶不振,不过这是题外话了,略过......
对了,我有可能会对这封邮件整理下发到博客了,也希望其他网友能评点一二给你出主意。今晚本来想看下easy_monkey的知识了,给你写这个email变成临时性总结了。^_^
给金阳光老师评论的回复如下(关于MonkeyRunner的个人观点)
-----------------------------------------------------------------------------------------------------------------
回复haorenmin2008:首先膜拜下,金老师大驾光临蓬荜生辉啊!
对于后者,确实如此,UIAutomator需要API Level17(包含)以上。
对于前者,因为还没有MonkeyRunner的项目经验,所以是否很强大我就不敢妄加评论了,但是在我近来的tryout过程中,鄙人有以下的一些不成熟的认知:
1. 感觉功能不是很稳定,之前尝试一个MonkeyDevice的getProperty方法,竟然有时成功有时失败。
2. 性能不好,特别是当我们要用到hierarchyviewer的功能的时候很明显。
3. 只能用MonkeyImage的sameAs做截屏的对比,虽然加上hierarchyviewer后可以用它的getText,但还是很有限。
4. 控件定位方面主要是坐标点和HierarchyViewer提供的根据ID。前这儿在UI布局稍微有调整位置的话就需要跟着变动,没有像其他控件类框架那样做高层抽象除非换控件不然都不需要怎么变动;后者的话很多控件是没有id或者是有多个控件id相同的。
5. 可调试性也不强(起码我摸索了这几天没有发现一个很好的调试方法,比如IDE Ecilpse等的集成调试方法)
6. HierarchyViewer的稳定性也让我担忧,碰到过几次取控件信息的时候报exception的。
7. 资料稀缺,不仅百度,google也一样
8. Google支持让人觉得摸不着头脑,sdk给出的API和官方提供的API竟然不一致,以MonkeyDevice为例子,而sdk多出来的API竟然还不能用,google出来的信息不超过10个page,还要很多都是重复的石沉大海的网友报的问题。
9. 再一个的我真心搞不懂为什么本身java写的库非要搞个jython来调用,首先我不说性能损耗(这点肯定是有的,native库当然用native语言调用效率最好嘛),我想在eclipse上对以下的"device."做自动补全是做不到的“device = MonkeyRunner.waitForConnection()\n device.",而只有直接调用个构造函数实例化的device = MonkeyDevice(xxx)才能做到,这个我不相信是我配置的问题,换了个jython标准编译器以调用标准库问题同样存在。
简答的说插件是给android studio这个开发IDE用的,依赖库是给你的项目用的。下面详细解释下。
插件:(Plug-in,又称addin、add-in、addon或add-on,又译外挂)是一种遵循一定规范的应用程序接口编写出来的程序。其只能运行在程序规定的系统平台下(可能同时支持多个平台),而不能脱离指定的平台单独运行。android studio中插件就是其平台下帮助开发的小程序,比如:
ButterKnife Zelezny:ButterKnife 注解生成器,使用起来非常简单方便。
SelectorChapek:设计师给我们提供好了各种资源,每个按钮都要写一个selector是不是很麻烦?这么这个插件就为解决这个问题而生,你只需要做的是告诉设计师们按照规范命名就好了,其他一键搞定。
GsonFormat:现在大多数服务端api都以json数据格式返回,而客户端需要根据api接口生成相应的实体类,这个插件把这个过程自动化了。
Android Parcelable Code Generator:Android中的序列化有两种方式,分别是实现Serializable接口和Parcelable接口,但在Android中是推荐使用Parcelable,只不过我们这种方式要比Serializable方式要繁琐,那么有了这个插件一切就ok了。
依赖库:从编程的角度出发,库是指封装了各种功能的函数,用户在编写新程序时,无需再重新开发相关的功能。android studio的依赖库就是你在做android功能时用到的功能函数库。如下图片:
我们在微信上24小时期待你的声音
解答本文疑问/技术咨询/运营咨询/技术建议/互联网交流