Android 插件化原理 完胜360插件框架 技术实战
Android 性能优化 (一)APK高效瘦身
http://blog.csdn.net/whb20081815/article/details/70140063
Android 性能优化(三)布局优化 秒变大神
http://blog.csdn.net/whb20081815/article/details/70147958
Android 性能优化<七>自定义view绘制优化
Android AIDL进程IPC通信 一次就好
http://blog.csdn.net/whb20081815/article/details/70766913
3.
Android AIDL进程IPC通信 一次就好
4.
Android反射(Reflect)完全解析--强势来袭
http://blog.csdn.net/whb20081815/article/details/61198050
5.
Android 2分钟学会xUtils 注解 Annotation(实例+原理)
Android 价值千万 java多线程同步 <四> synchronized&Lock&Atomic6种方式
http://blog.csdn.net/whb20081815/article/details/66971983
5).Android 价值千万java多线程同步 <五>CountDownLatch(计数器)和Semaphore(信号量)
Android快速理解Activity、View及Window&WindowManager之间关系
4).Android 性能优化<七>自定义view绘制优化
组件化开发就是将一个app分成多个模块,每个模块都是一个组件(Module),开发的过程中我们可以让这些组件相互依赖或者单独调试部分组件等,但是最终发布的时候是将这些组件合并统一成一个apk,这就是组件化开发。
插件化开发和组件化开发略有不用,插件化开发时将整个app拆分成很多模块,这些模块包括一个宿主和多个插件,每个模块都是一个apk(组件化的每个模块是个lib),最终打包的时候将宿主apk和插件apk分开或者联合打包。
做插件化真正的目的:是为了去适应并行开发,是为了解耦各个模块,是为了避免模块之间的交叉依赖,是为了加快编译速度,从而提高并行开发效率。
加入一个app工程只有一个组件,随着app业务的壮大模块越来越多,代码量超10万是很正常的,这个时候我们会遇到以下问题
- 稍微改动一个模块的一点代码都要编译整个工程,耗时耗力
- 公共资源、业务、模块混在一起耦合度太高
- 不方便测试
- 项目新功能的添加,无法确定与用户匹配性,发生严重异常往往牵一发而动全身,只能紧急发布补丁版本,强制用户进行更新.结果频繁的更新
可以干什么:
热修复,热修复其实也是动态加载原理
换肤,使用动态的资源加载可以实现换肤功能
实现免安装的功能
还可以通过hook系统的一些类做一些你想做的坏事。
插件化最早出现是因为65535问题出现的,用于查分多个dex并动态加载dex来防止65535问题
现在很多公司用插件化来做模块的动态加载,这样既能实现web端一样的随时更新,还能减少apk包的体积,其实就是加载一个未安装的apk。
Android插件化开发框架
5.ACDD插件化框架
8.DL APK动态加载框架
四个月前(2015年8月初) , BunnyBlue开源了一个名为OpenAtlas的插件化框架, 这个插件化的来源可能并非所有人知晓, 我在这里概括一下:
2013年, 朱碧军同学在阿里的一次技术沙龙中分享了一个插件化框架: Atlas ,这个框架用在淘宝的Android客户端,没有开源,沙龙中也没有对该插件化的具体实现做讲解.(阿里技术沙龙)
15年, BunnyBlue同学在研究淘宝客户端时发现,Atlas框架没有混淆的十分彻底(在com.android.atlas下),于是花了一些时间对其中代码的实现进行了逆向,并在8月初开源了这一成果,并取名为OpenAtlas.
后来,由于涉及到一些代码来源方面的问题, OpenAtlas项目关闭,后改名为:ACCD(bunnyblue/ACDD · GitHub).
- 编译期:资源和代码的编译
- 运行时:资源和代码的加载
- 还有一个问题就是对四大组件的生命周期管理
<SDK>/build-tools/<buildToolsVersion>/aapt
ClassLoader
机制,但是对于Android来说,并不是说类加载进来就可以用了,很多组件都是有“生命”的
资源加载
资源加载方案大家使用的原理都差不多,都是用AssetManager
的隐藏方法addAssetPath
;但是,不同插件的资源如何管理?是公用一套资源还是插件独立资源?共用资源如何避免资源冲突?对于资源加载,有的方案共用一套资源并采用资源分段机制解决冲突(要么修改aapt
要么添加编译插件);有的方案选择独立资源,不同插件管理自己的资源。
目前国内开源的较成熟的插件方案有DL和DroidPlugin;但是DL方案仅仅对Frameworl的表层做了处理,严重依赖that
语法,编写插件代码和主程序代码需单独区分;而DroidPlugin通过Hook增强了Framework层的很多系统服务,开发插件就跟开发独立app差不多;就拿Activity生命周期的管理来说,DL的代理方式就像是牵线木偶,插件只不过是操纵傀儡而已;而DroidPlugin则是借尸还魂,插件是有血有肉的系统管理的真正组件;DroidPlugin Hook了系统几乎所有的Sevice,欺骗了大部分的系统API;掌握这个Hook过程需要掌握很多系统原理,因此学习DroidPlugin对于整个Android FrameWork层大有裨益。
接下来的一系列文章将以DroidPlugin为例讲解插件框架的原理,揭开插件化的神秘面纱;同时还能帮助深入理解Android Framewrok;主要内容如下:
需要学习的一些类:
1.ActivityThread
2.Instrumentation
3.ActivityManager
4.ActivityManagerServiche
5.PackageManagerServic
6.ServiceManager
7.ActivityManagerNative
8.ApplicationThread
如果你不清楚ActivityManager
,ActivityManagerService
以及ActivityManagerNative
之间的关系;建议先仔细阅读我之前关于Binder的文章 Binder学习指南。
流程
1.Activity的启动流程
2.service的启动加载
CLassLoader机制,JAVA虚拟机的原理!
public static void attachContext() throws Exception{// 先获取到当前的ActivityThread对象 Class<?> activityThreadClass = Class.forName("android.app.ActivityThread"); Method currentActivityThreadMethod = activityThreadClass.getDeclaredMethod("currentActivityThread"); currentActivityThreadMethod.setAccessible(true); //currentActivityThread是一个static函数所以可以直接invoke,不需要带实例参数 Object currentActivityThread = currentActivityThreadMethod.invoke(null); // 拿到原始的 mInstrumentation字段 Field mInstrumentationField = activityThreadClass.getDeclaredField("mInstrumentation"); mInstrumentationField.setAccessible(true); Instrumentation mInstrumentation = (Instrumentation) mInstrumentationField.get(currentActivityThread); // 创建代理对象 Instrumentation evilInstrumentation = new EvilInstrumentation(mInstrumentation); // 偷梁换柱 mInstrumentationField.set(currentActivityThread, evilInstrumentation); }
mInstrumentation
这个字段为我们的代理对象,我们先实现这个代理对象,
Instrumentation
是一个类,没办法,我们只有手动写静态代理类,覆盖掉原始的方法即可
public class EvilInstrumentation extends Instrumentation {private static final String TAG = "EvilInstrumentation"; // ActivityThread中原始的对象, 保存起来 Instrumentation mBase; public EvilInstrumentation(Instrumentation base) {mBase = base; }public ActivityResult execStartActivity(Context who, IBinder contextThread, IBinder token, Activity target, Intent intent, int requestCode, Bundle options) {// Hook之前, XXX到此一游! Log.d(TAG, "\n执行了startActivity, 参数如下: \n" + "who = [" + who + "], " +"\ncontextThread = [" + contextThread + "], \ntoken = [" + token + "], " +"\ntarget = [" + target + "], \nintent = [" + intent +"], \nrequestCode = [" + requestCode + "], \noptions = [" + options + "]"); // 开始调用原始的方法, 调不调用随你,但是不调用的话, 所有的startActivity都失效了. // 由于这个方法是隐藏的,因此需要使用反射调用;首先找到这个方法 try {Method execStartActivity = Instrumentation.class.getDeclaredMethod("execStartActivity", Context.class, IBinder.class, IBinder.class, Activity.class, Intent.class, int.class, Bundle.class); execStartActivity.setAccessible(true); return (ActivityResult) execStartActivity.invoke(mBase, who, contextThread, token, target, intent, requestCode, options); } catch (Exception e) {// 某该死的rom修改了 需要手动适配 throw new RuntimeException("do not support!!! pls adapt it"); }}
// 招代理 women = (Shopping) Proxy.newProxyInstance(Shopping.class.getClassLoader(), women.getClass().getInterfaces(), new ShoppingHandler(women));
执行启动程序的时候先调用了打印(仅仅为了演示),如下
Log.d(TAG, "\n执行了startActivity, 参数如下: \n" + "who = [" + who + "], " +"\ncontextThread = [" + contextThread + "], \ntoken = [" + token + "], " +"\ntarget = [" + target + "], \nintent = [" + intent +"], \nrequestCode = [" + requestCode + "], \noptions = [" + options + "]");
系统服务的获取过程
ActivityManager activityManager=(ActivityManager)this.getSystemService(Context.ACTIVITY_SERVICE);
@Override public Object getSystemService(String name) {return SystemServiceRegistry.getSystemService(this, name); }
public static Object getSystemService(ContextImpl ctx, String name) {ServiceFetcher<?> fetcher = SYSTEM_SERVICE_FETCHERS.get(name); return fetcher != null ? fetcher.getService(ctx) : null; }
tatic {registerService(Context.ACCESSIBILITY_SERVICE, AccessibilityManager.class, new CachedServiceFetcher<AccessibilityManager>() {@Override public AccessibilityManager createService(ContextImpl ctx) {return AccessibilityManager.getInstance(ctx); }}); registerService(Context.CAPTIONING_SERVICE, CaptioningManager.class, new CachedServiceFetcher<CaptioningManager>() {@Override public CaptioningManager createService(ContextImpl ctx) {return new CaptioningManager(ctx); }});
registerService(Context.POWER_SERVICE, PowerManager.class, new CachedServiceFetcher<PowerManager>() {@Override public PowerManager createService(ContextImpl ctx) {IBinder b = ServiceManager.getService(Context.POWER_SERVICE); IPowerManager service = IPowerManager.Stub.asInterface(b); if (service == null) {Log.wtf(TAG, "Failed to get power manager service."); }return new PowerManager(ctx.getOuterContext(), service, ctx.mMainThread.getHandler()); }});
IBinder b = ServiceManager.getService("service_name"); // 获取原始的IBinder对象 IXXInterface in = IXXInterface.Stub.asInterface(b); // 转换为Service接口
public class BinderHookHelper {public static void hookClipboardService() throws Exception {final String CLIPBOARD_SERVICE = "clipboard"; // 下面这一段的意思实际就是: ServiceManager.getService("clipboard"); // 只不过 ServiceManager这个类是@hide的 Class<?> serviceManager = Class.forName("android.os.ServiceManager"); Method getService = serviceManager.getDeclaredMethod("getService", String.class); // ServiceManager里面管理的原始的Clipboard Binder对象 // 一般来说这是一个Binder代理对象 IBinder rawBinder = (IBinder) getService.invoke(null, CLIPBOARD_SERVICE); // Hook 掉这个Binder代理对象的 queryLocalInterface 方法 // 然后在 queryLocalInterface 返回一个IInterface对象, hook掉我们感兴趣的方法即可. IBinder hookedBinder = (IBinder) Proxy.newProxyInstance(serviceManager.getClassLoader(), new Class<?>[] { IBinder.class }, new BinderProxyHookHandler(rawBinder)); // 把这个hook过的Binder代理对象放进ServiceManager的cache里面 // 以后查询的时候 会优先查询缓存里面的Binder, 这样就会使用被我们修改过的Binder了 Field cacheField = serviceManager.getDeclaredField("sCache"); cacheField.setAccessible(true); Map<String, IBinder> cache = (Map) cacheField.get(null); cache.put(CLIPBOARD_SERVICE, hookedBinder); }
public class BinderProxyHookHandler implements InvocationHandler {private static final String TAG = "BinderProxyHookHandler"; // 绝大部分情况下,这是一个BinderProxy对象 // 只有当Service和我们在同一个进程的时候才是Binder本地对象 // 这个基本不可能 IBinder base; Class<?> stub; Class<?> iinterface; public BinderProxyHookHandler(IBinder base) {this.base = base; try {this.stub = Class.forName("android.content.IClipboard$Stub"); this.iinterface = Class.forName("android.content.IClipboard"); } catch (ClassNotFoundException e) {e.printStackTrace(); }}@Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {if ("queryLocalInterface".equals(method.getName())) {Log.d(TAG, "hook queryLocalInterface"); // 这里直接返回真正被Hook掉的Service接口 // 这里的 queryLocalInterface 就不是原本的意思了 // 我们肯定不会真的返回一个本地接口, 因为我们接管了 asInterface方法的作用 // 因此必须是一个完整的 asInterface 过的 IInterface对象, 既要处理本地对象,也要处理代理对象 // 这只是一个Hook点而已, 它原始的含义已经被我们重定义了; 因为我们会永远确保这个方法不返回null // 让 IClipboard.Stub.asInterface 永远走到if语句的else分支里面 return Proxy.newProxyInstance(proxy.getClass().getClassLoader(),
public class BinderHookHandler implements InvocationHandler {private static final String TAG = "BinderHookHandler"; // 原始的Service对象 (IInterface) Object base; public BinderHookHandler(IBinder base, Class<?> stubClass) {try {Method asInterfaceMethod = stubClass.getDeclaredMethod("asInterface", IBinder.class); // IClipboard.Stub.asInterface(base); this.base = asInterfaceMethod.invoke(null, base); } catch (Exception e) {throw new RuntimeException("hooked failed!"); }}@TargetApi(Build.VERSION_CODES.HONEYCOMB)@Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {// 把剪切版的内容替换为 "you are hooked" if ("getPrimaryClip".equals(method.getName())) {Log.d(TAG, "hook getPrimaryClip"); return ClipData.newPlainText(null, "you are hooked"); }// 欺骗系统,使之认为剪切版上一直有内容 if ("hasPrimaryClip".equals(method.getName())) {return true; }return method.invoke(base, args);
AMS
正如名字所说,管理所有的“活动”,整个系统的Activity堆栈,Activity生命周期回调都是由AMS所在的系统进程system_server帮开发者完成的;Android的Framework层帮忙完成了诸如生命周期管理等
AMS
与ActivityThread
之间对于Activity的生命周期的交互,并没有直接使用Activity对象进行交互,而是使用一个token来标识,
这个token是binder对象,因此可以方便地跨进程传递。Activity里面有一个成员变量mToken
代表的就是它,token可以唯一地标识一个Activity对象,它在Activity的attach
方法里面初始化;
在AMS
处理Activity的任务栈的时候,使用这个token标记Activity
- Hook机制之Binder Hook
- Hook机制之动态代理
- Hook机制之AMS&PMS
- Activity生命周期管理
- 插件加载机制
- 广播的管理方式
- Service的插件化
- ContentProvider的插件化
- DroidPlugin插件通信机制
- 插件机制之资源管理
- 不同插件框架方案对比
- 插件化的未来
Hook机制
hook,又叫钩子,通常是指对一些方法进行拦截
大致思路:
1.找到需要Hook方法的系统类
2.利用代理模式来代理系统类的运行拦截我们需要拦截的方法
3.使用反射的方法把这个系统类替换成你的代理类
Android 插件化原理 完胜360插件框架 技术实战相关推荐
- 【Android 插件化】Hook 插件化框架 ( hook 插件化原理 | 插件包管理 )
Android 插件化系列文章目录 [Android 插件化]插件化简介 ( 组件化与插件化 ) [Android 插件化]插件化原理 ( JVM 内存数据 | 类加载流程 ) [Android 插件 ...
- Android插件化原理—ClassLoader加载机制
前面<Android 插件化原理学习 -- Hook 机制之动态代理>一文中我们探索了一下动态代理 hook 实现了 启动没有在 AndroidManifest.xml 中显式声明的 Ac ...
- Android 插件化原理(一),通过dex文件调用插件app代码
Android插件化原理,从以下三个问题切入: 什么是插件化 如何实现插件类的加载 如何实现插件资源的加载 什么是插件化 插件化技术最初是源于免安装运行APK的想法,这个免安装的APK就可以理解为插件 ...
- Android插件化原理和实践 (一) 之 插件化简介和基本原理简述
1 插件化简介 Android插件化技术是一种这几年间非常火爆的技术,也是只有在中国才流行起来的技术,这几年间每每开发者大会上几乎都会提起关于插件化技术和相关方向.在国内各大互联网公司无不都有自己的插 ...
- Android 插件化原理入门笔记
Android开发笔记 onGithub 笔记,参考7.2中所列参考文章所写,DEMO地址在PluginTestDemoApplication 1.综述 2015年是Android插件化技术突飞猛进的 ...
- Android 插件化原理学习 —— Hook 机制之动态代理
前言 为了实现 App 的快速迭代更新,基于 H5 Hybrid 的解决方案有很多,由于 webview 本身的性能问题,也随之出现了很多基于 JS 引擎实现的原生渲染的方案,例如 React Nat ...
- 【Android 插件化】插件化原理 ( 类加载器 )
Android 插件化系列文章目录 [Android 插件化]插件化简介 ( 组件化与插件化 ) [Android 插件化]插件化原理 ( JVM 内存数据 | 类加载流程 ) [Android 插件 ...
- Android 插件化原理解析——Activity生命周期管理
之前的 Android插件化原理解析 系列文章揭开了Hook机制的神秘面纱,现在我们手握倚天屠龙,那么如何通过这种技术完成插件化方案呢?具体来说,插件中的Activity,Service等组件如何在A ...
- Android 插件化原理解析——Hook机制之AMSPMS
在前面的文章中我们介绍了DroidPlugin的Hook机制,也就是代理方式和Binder Hook:插件框架通过AOP实现了插件使用和开发的透明性.在讲述DroidPlugin如何实现四大组件的插件 ...
最新文章
- QLocalServer与QLocalSocket进程通讯
- SUSTechTripleH队墓志铭
- 二叉树的建立和遍历程序代码(Java,C)
- UVA11021麻球繁衍
- boost::range_result_iterator相关的测试程序
- oracle rman备份慢,诊断Oracle RMAN备份慢的原因
- Mysql 连接的使用
- Linux网络协议栈(三)——网络设备(1)
- [linux]【编译】【高级01】 - 动态库的设定和依赖性的检查 20160921更新 文章没有最终结案
- ajax长轮询的问题,Ajax长轮询
- 二、K8s Cluster详细安装步骤
- 基于springboot小区物业管理系统
- 如何设计沉浸式游戏引导界面?你要知道的七大点
- VMware Cloud Director 10.3.1 - 云计算调配和管理平台
- 硅谷的长生不老产业:炼金术 2.0,满足 CEO 们“拿钱换命”的欲望
- 已知两个向量的坐标求夹角的大小_两个向量的夹角怎么算
- 【Luat-esp32c3】4.3 文件系统——加载jpeg图片并拆包
- python关于q检验
- jq 点击按钮跳转到微信_实现点击复制微信号并自动打开微信加好友
- 计划处理链的很多种情况factory calendar
热门文章
- 线性回归2020年天猫双十一销量
- 云南计算机网络技术排名,2017年云南大学排名
- TX2(Ubuntu16.04)安装TP-link外接网卡驱动及AP热点创建
- Java+SSM(Spring+SpringMVC+Mybatis)个性化购物商城推荐系统 电子商务推荐系统 基于用户、项目、聚类、混合的协同过滤推荐算法WebShopRSMEx 源代码下载
- 《果壳中的C# C# 5.0 权威指南》 (09-26章) - 学习笔记
- Resource.arsc文件格式解析
- 【扫盲】Pulse消除马赛克(老司机福利)
- CentOS 7 VM虚拟机安装docker步骤
- SSL与数字证书,Htpps
- 互联网寒冬,那些不怕失业的程序员们,都有什么技能?