上面一篇文章已经实现了Intent解析和targetClass的还原工作,这篇文章会来说说:
- 插件apk的解析
- ClassLoader的问题
- 资源的问题
- 插件的下载,加载机制的问题
插件apk的解析
说到这个我们很容易想到一般的apk的安装过程也是需要解析apk的信息的,下面贴一篇比较好的文章。
Android APK安装过程分析
图片取自上面的博客,apk的安装过程与pms有很大的关系,很多操作都是由pms完成的,有兴趣的可以去了解。
上面一篇文章已经实现了Intent解析和targetClass的还原工作,这篇文章会来说说:
说到这个我们很容易想到一般的apk的安装过程也是需要解析apk的信息的,下面贴一篇比较好的文章。
Android APK安装过程分析
图片取自上面的博客,apk的安装过程与pms有很大的关系,很多操作都是由pms完成的,有兴趣的可以去了解。
上一篇文章里面我们分析了一下Activity插件化并提出了5个问题,然后有的问题给出了解决方案,有的问题没有给出解决方案,不用担心,会有一系列的文章来循序渐进的把Activity插件化过程中遇到的问题慢慢讲清楚。
本文代码:PluginDemo/activity_plugin
这篇文章只讲一个问题,Activity插件化占坑实现方式
后续文章将要讲到的,现阶段不讲的:
了解Activity的启动过程就应该知道启动Activity调用的是Instrumentation
的execStartActivity
方法完成的。等到AMS完成校验,以及在需要的时候创建进程等等一系列的操作之后会回到App进程,最后依旧调用Instrumentation
的另外一个方法newActivity
。所以我们Hook掉ActivityThread
的Instrumentation
的实例mInstrumentation
即可。
本文的编写借鉴参考了大量的文章,有的可能是直接把文字拷贝过来的,我会在文中给出链接,如果有侵权,请联系我删除,谢谢。
我们知道,启动Activity可以是通过Activity或者通过Context,这两种启动没有太大的区别,最终都是调用 Instrumentation
的方法来启动的,当然说是这样说,其实还是有区别滴,Activity的startActivity()方法可使用默认配置的LAUNCH FLAG,而Context的startActivity()须包含FLAG_ACTIVITY_NEW_TASK
的LAUNCH FLAG,原因是该Context可能没有现存的任务栈供新建的Activity使用,必须显式指定生成一个自己单独的任务栈。
Activity启动发起后,通过Binder,最终由system_server进程中的AMS(ActivityManagerService)启动的。这里不打算说Activity的启动过程了,因为套路就是那样,太多的博客文章也分析过了过程。想看启动过程的可以去看看下面的文章:
startActivity启动过程分析
Activity启动过程全解析
还是插件化相关的内容,不过这次说的是反射相关的。插件化的两个基础,动态代理与反射,上次说了动态代理,这次就说反射了。
先说一下Java的内存模型,也就是java虚拟机在运行时的内存。运行时的内存分为线程私有和线程共享两块。
线程私有的有程序计数器,虚拟机栈,本地方法栈,线程共享的有方法区(包含运行时常量池),java堆。
工作目录下的文件都只有两种状态:已跟踪或者未跟踪。
已跟踪的文件指那些已经纳入了版本控制,在上次快照中有他们的记录,工作一段时间后,他们的状态可能处于已修改,未修改或者已放入暂存区。
除此之外的文件就是未跟踪的,他们既不存在快照中,也没放入暂存区。
可以看到文件已被跟踪,并处于暂存状态。
git add 使用文件或者目录作为参数,如果是目录的话,会递归跟踪目录下的所有文件。
这次准备写一系列的关于自己学习插件化的过程了,前面虽然陆陆续续的学习了一些插件化方面的知识,但是都是淡淡续续的,这次要从基础开始了,其实我一直认为基础这个东西挺重要的。
本文代码在我的Github上面:Plugin Demo
这里首先说明一下代理模式在插件化中的作用,代理其实就是劫持一个类,然后在执行指定的方法的时候,做一些事,一些什么事情呢,比如在前面或者后面加个统计,或者直接替换原来的执行逻辑,换成自己的逻辑。
其他比如,我们需要在不同的情况下返回同一个方法的不同实现也可以使用代理模式的,比如在做Push的时候,我们可能在App里面不止使用了一个推送服务,比如很多App会同时集成,个推和小米以及华为的推送,那么我们需要在不同的手机上面使用不同的推送,小米和和华为手机上面使用他们自己的,其余的手机上面使用个推的。那这个时候就可以使用代理模式来实现,因为推送基本就那几个方法,一是开启推送,一是给设备打标签等等方法需要我们实现。那分别用几个实现类实现即可,好吧,其实这个是策略模式,逃。
我们时常会看到别人说hook,特别是在插件化的时候,hook住Activity的启动,通过预先埋在Manifest里面的Activity,替换成我们真正想要启动的Activity等等的说法。通常Hook,又叫钩子,通常是指对一些方法进行拦截。这样当这些方法被调用时,也能够执行我们自己的代码,这也是面向切面编程的思想(AOP)。
Android中,本身并不提供这样的拦截机制,但是有时候,我们可以在一些特殊的场合实现一种的Hook方法。
大致思路:
1.找到需要Hook方法的系统类
2.利用代理模式来代理系统类的运行拦截我们需要拦截的方法
3.使用反射的方法把这个系统类替换成你的代理类
上面的一段话取自插件化知识详细分解及原理 之代理,hook,反射,感觉说的挺好。这里是不是就能感受到代理模式的强大了,下面会细说的。
代理模式的意思就是为其他对象提供一种代理以控制对这个对象的访问,一般当我们无法或者不想直接访问某个对象或者访问某个对象存在困难时,可以用过一个代理对象来间接访问。(下图出自:代理模式及Java实现动态代理)
java中的代理模式大概可以分为两种,一种就是普通的代理也就是静态代理,就是我们生成固定的代码,在我们运行前代理类的class编译文件就已经存在啦,动态代理与静态代理相反,在code阶段压根不需要知道代理谁,代理谁将会在代码的执行阶段通过一些判断来决定代理哪个对象。动态代理其实如果细分也可以分成两类,一类是JDK提供的代理,一类是cglib提供的代理类,他们的区别是:
JDK动态代理只能对实现了接口的类生成代理,而不能针对类 。
CGLIB是针对类实现代理,主要是对指定的类生成一个子类,覆盖其中的方法 。
因为是继承,所以该类或方法最好不要声明成final ,final可以阻止继承和多态。
这里需要注意,因为是代理模式,肯定是需要真正代理某个类的,也就是说需要真正做事得类,然后对这个类进行代理。这点是基础,不然说不定你会越看越懵逼。
搭建开发环境就不说啦。
搭建好开发环境之后,找个目录,创建项目,然后运行项目,创建出来的项目的gradle的版本如果本地没有的话,建议改成本地有的,不然要下载好久。
1 | react-native init 项目名 |
*提示:*你可以使用–version参数创建指定版本的项目。
例如react-native init MyApp –version 0.39.2。注意版本号必须精确到两个小数点。
还有一点,RN的菜单需要按菜单键才能出来,但是现在很多手机都没有菜单键啦,RN肯定早就考虑到啦这一点,摇一摇手机就出来啦。
1 | import React, { Component } from 'react'; |
RN的Hello World程序如上面所示的。
上面的示例代码中的import、from、class、extends、以及() =>箭头函数等新语法都是ES2015(也叫作ES6)中的特性。如果你不熟悉ES2015的话,可以看看阮一峰老师的书,还有论坛的这篇总结。
*提示:*顺便说一句,阮一峰老师的博客写的真的很好,很多东西都讲的非常透彻。
上面的代码定义了一个名为HelloWorldApp
的新的组件(Component),并且使用了名为AppRegistry
的内置模块进行了“注册”操作。你在编写React Native
应用时,肯定会写出很多新的组件。而一个App的最终界面,其实也就是各式各样的组件的组合。组件本身结构可以非常简单——唯一必须的就是在render
方法中返回一些用于渲染结构的JSX
语句。
AppRegistry
模块则是用来告知React Native
哪一个组件被注册为整个应用的根容器。你无需在此深究,因为一般在整个应用里AppRegistry.registerComponent
这个方法只会调用一次。上面的代码里已经包含了具体的用法,你只需整个复制到index.ios.js或是index.android.js文件中即可运行。
*提示:*上面的话出自编写Hello World。
最近在研究Android的插件化,插件化需要解决的问题大概有这样的几个,为什么需要插件化技术这个就不说啦。
参考:
Android资源管理框架(Asset Manager)简要介绍和学习计划
Android应用程序的Activity启动过程简要介绍和学习计划
Android源码分析-Activity的启动过程
这篇文章浅解一下Android的资源管理器的创建过程。这里说一下,其实很多人对插件化的资源加载有一个误区呀,就是一般别人说插件化需要解决一个资源冲突的问题,这里有一个很重要的问题。
这个问题其实不应该放在最前面说的,但是很多人都有一个误区,感觉必须先说一下这个了。
首先看Android的资源分类,Android的资源可分为两大类。分别是Asserts和Res。
res资源大概是这样的啦,当然还有raw以及xml等资源啦。在编译打包的过程中,会把资源文件打包成二进制文件(.xml文件打包成二进制文件,png文件进行优化等)。会对除了assets资源之外所有的资源赋予一个资源ID常量,并且会生成一个资源索引表resources.arsc。
这个resources.arsc文件记录了所有的应用程序资源目录的信息,包括每一个资源名称、类型、值、ID以及所配置的维度信息。我们可以将这个resources.arsc文件想象成是一个资源索引表,这个资源索引表在给定资源ID和设备配置信息的情况下,能够在应用程序的资源目录中快速地找到最匹配的资源。
这些资源ID被终会被定义为Java常量值,保存在一个R.java文件中,与应用程序的其它源文件一起被编译到程序中,这样我们就可以在程序或者资源文件中通过这些ID常量来访问指定的资源。