组件:指单一的功能组件,如视频组件(VideoSDK)、支付组件(PaySDK)、路由组件(Router),每个组件都能单独抽出来制作成SDK,也有的公司内部叫Lib组件
模块:指的是独立的业务模块,如直播模块(LiveModule)、首页模块(HomeModule)、即时通信模块(IMModule)等。模块相对于组件来说粒度更大,模块可能包含多种不同的组件。有的公司内部也叫UI组件
组件化的好处:
避免重复造轮子,节省开发和维护成本
通过组件和模块为业务基准合理安排人力,提高开发效率
不同的项目可以共用一个组件或者模块,确保整体技术方案的统一性
为未来插件化共用一套底层模型做准备
模块化的好处
业务模块解耦,业务一直更加简单
多团队或者团队内多人根据业务内容进行并行开发和测试
单个业务可以单独编译和打包,加快编译速度
多个APP共用模块,降低了研发和维护成本(eg:同程旅行,同程旅行极速版)
组件化是功能导向的,模块化是业务导向的
总结一句话:组件化是为了重用而拆分模块,模块化是为了业务分离而拆分模块
shareUid,拥有同一个uer id的多个APP可以配置成运行在同一个进程中,所以默认可以互相访问任意数据,比如prefs,且只有在主module里面声明shareUserId,才会最终打包到full文件夹西面的AndroidManifest中!
Application比Acitivity更先创建,重写refisterActicityLifeCycleCallbacks方法可以拿到栈顶的Activity,而且是全局的。
组件间通信方式
LocalBroadcast(APP内部通信)
EventBus(订阅者、发布者、订阅时间、事件总线)Publisher post(Event),Subscriber onEvent,EventBus3.0+用编译时注解替代了2.x的运行时注解,速度快效率高
RxBus是一种响应式编程的模式,而非EventBus的观察者、监听者设计模式
组件间跳转
隐式启动Activity,通过sendintent.resolveActivity(getPackageManager!=null安全的跳转,设置exported=false确保其他APP无法跳转到我们的APP中
Router
ARouter:
通过加载编译时注解创建的Group、Providers、Root三个类型的文件,使用WareHouse将文件保存到三个不同的HashMap对象中,而这个WareHouse就相当于路由表。
加载过程:
通过ARouter.navigation封装postcard对象
tongguo 索引传递到路由中转站,询问是否存在跳转的对象
如果存在则设置绿色通道开关
判断是否绿色同行和是否能通过拦截服务
全部通过就会调用startActivity()
路由最大的好处:
组件间跳转
拦截过滤,登录状态验证等
具体的路由拦截:
路由跳转到需要申请权限的组件或者Activity,则可以实现IInterceptor接口,并重写process()判断postcard.getPath.==("/takePhoto/xxx")则调用权限申请;除此之外还可以登录前、支付前验证,拦截机机制是安全的象征。
反射
获取Class对象的方式:
Class c1 = Class.forName("com.ly.TestA");会装载并进行类的初始化static代码块之类的会走
Class c2 = TestA.class 不会进行初始化
Class c3 = TestA().getClass()会实例化所以static、无参构造都会被调用
Field f = c1.getDeclaredField("name");//可以获取所有属性,包括private的,但是无法获取父类的属性,不过可以通过c1.getSupperClass继续获取父类所有属性。
f.setAccessible(true)取消私有字段的访问限制,public的accessible也是false。
为了安全不管是否是私有属性,全部设置访问权限为true
类型擦除,如果反射的方法的参数的T泛型,则需要用Object.class作为参数。
动态代理,可以在不修改源码的情况下,在方法执行前后做任何事情。
反射框架jOOR
组件化权限:
normal级别的权限申请都放在base module里面,然后各个module中分别申请dangerous权限,这样分配的好处是当添加或者移除单一模块时,隐私权限申请也会跟随移除,做到最大程度的权限解耦。如果权限全部转交到每个module中是可以达到最大程度的权限解耦,但是这样会增加AndroidManifest合并检测的耗时。建议在base module中封装权限申请。
app module中的R文件有final修饰,而lib module中的R文件没有final修饰???
ButterKnife通过gradleplugin构建生成R2文件,目录和R文件一致:build/generated/source/r/debug/xxx/
查看依赖关系命令:
//壳工程:
./gradlew :app:dependencies --configuration developerDebugRuntimeClasspath >> dependency.txt
//某个组件
./gradlew du_publish:dependencies --configuration releaseRuntimeClasspath >> dependency.txt
(如果是别人的项目可能会遇到 bash: ./gradlew: Permission denied)
解决方法: chmod +x gradlew
依赖标注了*号,表示这个依赖被忽略了,这是因为其他顶级依赖也依赖于这个传递的依赖比如:
组件化资源名的冲突解决:
不同module资源命名不一样
android{resourcePrefix "组件名_"}加上前缀,但是只限定于XML,图片资源依旧需要手动修改
混淆:
混淆包括代码压缩、代码混淆、资源压缩等优化过程。
AS使用ProGuard进行混淆,ProGuard是一个压缩、优化和混淆Java字节码文件的工具,可以删除无用的类、字段、方法、属性、无用资源减少APK大小。
混淆具体有四项工作:
Shrinking(压缩)
Optimization(优化)
Obfuscation(混淆)
Preverfication(预校验)
Gradle的生命周期分为三个阶段:
setting.gradle(初始化)根据include信息决定哪些工程会加入构建过程。
build.gralde(配置)配置阶段会按引用数去执行所有工程的build.gradle脚本,配置project对象,一个对象由多个任务组成,此阶段也会去创建、配置Task及相关信息。
Gradle构建,运行阶段会根据Gradle命令传递过来的Task名称,执行相关依赖任务。
两点问题 一个是混淆
另一个是
Submodule test
SubTree test
Gradle的编译步骤:
代码编译--->代码合成--->资源打包--->签名和对齐
Instant Run的运行原理:
Instant Run 的源码可以通过jd-gui反编译获得
优化gradle编译时间的相关配置(效果未知)
org.gradle.parallel=true(开启并行编译)
android.enableBuildCache=true(使用编译缓存)
org.gradle.daemon=true(减少加载JVM和classes的时间)
org.gradle.configureondemand=true(在大型多项目中更快构建)
org.gradle.jvmargs=-Xmx3072M -XX\:MaxPermSize\=512m(加大编译时的AS内存空间)
为Task耗时添加测量,有针对性的采取优化措施。
task任务过滤(如果没有使用aidl、test、ndk、jni、lint等都可以不运行相关任务)
不执行重复的任务,lib module的Debug/release版本单独依赖。
project不支持Annotation processors的增量build,所以在module种减少使用Annotation processor有助于提升编译速度。
View的生命周期:
你去小卖部,想买瓶可乐,你和店家很熟,自己找到放可乐的位置取一瓶,然后结账。
你去小卖部,想买瓶可乐,你告诉店家你要买可乐,然后他帮你取一瓶,然后结账。
下面的一种就是职责分离,更适合服务客户,此为依赖倒置:
依赖倒置原则是指程序要依赖于抽象接口,不依赖于具体实现,依赖倒置原则的核心是面向接口编程。
加载优化的思路:
核心是时间和空间之间是转换:
预加载
线程加载
懒加载
Java通过Executros提供四种线程池:
newCachedThreadPool(可缓存线程池,如果线程池数量超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程)
newFixedThreadPool(定长线程池,可控制线程最大并发数,超出的线程会在队列中等待)
newSingleThreadPool(单线程的线程池,保证所有任务按照指定顺序执行)
newScheduleThreadPool(定长线程池,支持定时及周期性任务执行)
懒加载:
ViewStub的运行原理:视图加载时,在onMeasure方法中调用setMeasuredDimension传递的参数measureWidth和measureHeight的值为0,draw方法被置空。ViewStub只要被inflate后,会实例化指定的布局,ViewStub的setVisibility()方法被调用时,布局实例化。
gradle的默认缓存地址在Mac上是:/User/用户名/.gradle/caches
那么buildscript中的repositories和allprojects的repositories的作用和区别是什么呢?
答:
1、 buildscript里是gradle脚本执行所需依赖,分别是对应的maven库和插件
2、 allprojects里是项目本身需要的依赖,比如我现在要依赖我自己maven库的toastutils库,那么我应该将maven {url 'https://dl.bintray.com/calvinning/maven'}写在这里,而不是buildscript中,不然找不到。
模板工程路径:
Mac: AndroidStudio/Contents/plugins/android/lib/templates
template.xml:模板填写文件。通过此文件指定添加属性来记录新建模板的要求。
globals.xml.ftl: 模板中记录的全局变量,通过tempalte.xml中的属性可以指定。
recipe.xml.ftl: 模板文件操作逻辑,通过template.xml中的属性可以指定。
FreeMarker的文件后缀是ftl。
FreeMarker类似于MVC设计模式,ftl文件是View层,tempalte是Model数据层,而FreeMarker机制是Controller控制器。
AS头部注释:
Setting/Editor/File and Code Templates----Includes/File Header---Create by ${USER}
模块化:
应用层:生成APP和加载初始化操作
模块层:每个模块相当于一个业务,通过module来分割每个业务的逻辑,一个模块由多个不同的页面逻辑组成。
基础层:基础组件的整合,提供基础组件能力给业务层使用。
组件层:将图片记在、网络HTTP、Socket等基础功能划分为一层。
基础库层:更加基础的库类依赖,此层非必须,例如RxJava、EventBus等一些代码结构优化的库,还有自己编写的封装类。
进程化:
进程化是大型APP架构的必然选择,Android系统不是以App为单位分配内存的,而是以进程为单位限制内存和分配资源的。那么开启多个进程会使App获取更多的内存,运行更加流畅。多进程内存分散,既避免了让单个进程太大导致内存过大,在不断回收内存中导致卡顿,也避免了因内存过低被系统杀死。其他进程可以做一些播放视频、播放音乐、拍摄、浏览网页等非常耗费资源的操作。
进程定义需要以四大组件作为入口: AndroidManifest.xml中使用process字段来声明额外进程。
进程化遇到的问题:
静态成员和单例模式会失效。
线程同步机制完全失效,由于JAVA同步机制使用虚拟机来进行调度,因为两个进程会有两个虚拟机,同步在多进程中是无效的,sync、volatile等都是基于虚拟机级别的同步。
SP没有对多进程的支持,可以用MMKV
文件读写、访问同一个文件没有进程锁机制,SQLite很容易被锁,其他进程访问时也会报异常。
Application多次被创建。每个进程在创建时都会新建一个Application,可以通过进程名来区分进程。
类里面logt生成TAG
类似于小程序,多进程,单独的Activity容器
android:taskAffinity=":remote" android:launchMode="singleTask" android:process=":remote" >