android插件化介绍

#插件化是什么?
说到插件化,其实在我们生活是很常见,比如u盘,用的时候插上,不用的时候拔下,任何品牌的u盘都可以插在电脑上,无线鼠标,无线键盘,外界屏幕等等,这些都是插件化的例子。

那么android中的插件化技术,目前已经比较成熟,比如微信,淘宝,携程,360手机助手中都应用到了插件化。他的特点是无需单独安装apk,即可运行,即插即用,无需升级宿主应用,减少app的更新频率,除此之外他还可以降低模块耦合,按需加载,节省流量等特点。

拿我们自己的考试app举例,我们的app主要做英语考试,那么英语考试分很多种,sat、toefl、ielts考试题目呈现方式都不相同,有的学生需要sat,有的学生需要sat、toefl,目前的解决方案是每个考试都是一个单独app成了很多功能的耦合。

为了解决这种问题,考虑引入插件化技术,按需引入所需考试类型的安装包。

#介绍名词

插件化 – apk 分为宿主和插件部分,插件在需要的时候才加载进来

热修复 – 更新的类或者插件粒度较小的时候,我们会称之为热修复,一般用于修复bug

热更新 – 2016 Google 的 Android Studio 推出了Instant Run 功能 同时提出了3个名词

“热部署” – 方法内的简单修改,无需重启app和Activity。 “暖部署” – app无需重启,但是activity需要重启,比如资源的修改。 “冷部署” – app需要重启,比如继承关系的改变或方法的签名变化等。

所以站在app开发者角度的“热”是指在不发版的情况来实现更新,而Google提出的“热”是指值是否需要重新启动。 同时在开发插件化的时候也有两种情景,一种是插件与宿主apk没有交互,只是在用户使用到的时候进行一次吊起,还有一种是与宿主有很多的交互。

##基本原理
插件化的原理实际是 Java ClassLoader 的原理,看其他资料前请先看:Java ClassLoader基础

###Java类加载器
Android应用程序的.java文件在编译期会通过javac命令编译成.class文件,最后再把所有的.class文件编译成.dex文件放在.apk包里面。那么动态加载就是在运行时vm把插件apk直接加载到classloader里面的技术

###反射原理
主要是指程序可以访问,检测和修改它本身状态或行为的一种能力,并能根据自身行为的状态和结果,调整或修改应用所描述行为的状态和相关的语义

###代理模式及Java实现动态代理
定义:给某个对象提供一个代理对象,并由代理对象控制对于原对象的访问,即客户不直接操控原对象,而是通过代理对象间接地操控原对象。

#为什么用?

##65535方法数天花板
随着业务的不断发展,外部库的引入,导致方法数的增加,应用中的Dex 文件方法数超过了最大值65536的上限,就会引爆android系统。这种情况可以通过削减重复方法数来治标,也有谷歌官方在API 21中提供了通用的解决方案,那就是android-support-multidex.jar. 这个jar包最低可以支持到API 4的版本(Android L及以上版本会默认支持mutidex),同时插件化也是解决方案的一种。

##减少主包大小

##编译提速

##可选模块按需下载
​例如模块可以在需要时进行加载,减少App Size,如只用到sat就只下载sat,只用toefl就只下载toefl的插件

##并行开发,独立Testing
可以独立开发A、B版本的模块,而不是将A、B版本代码写在同一个模块中。

app架构

##崩溃隔离
某一插件的崩溃,影响仅局限当前插件,不会导致其他插件以及宿主的崩溃

#有什么限制?

##通知栏限制
无法在插件中发送具有自定义资源的Notification,例如: a. 带自定义RemoteLayout的Notification b. 图标通过R.drawable.XXX指定的通知(插件系统会自动将其转化为Bitmap)

##系统适配
Dex的加载与系统版本依赖严重,可能会导致新版SDK不支持等问题

##需要预先注册权限
宿主的权限要多于插件的权限, 否则会权限不足,无法在插件中注册一些具有特殊Intent Filter的Service、Activity、BroadcastReceiver、ContentProvider等组件以供Android系统、已经安装的其他APP调用。

##国外资料少的原因
国外andrid以及所有app应用都上传google play store或apple store,这两个市场的审核较为严格,不经审核的热更新的应用很容易被下架

#限制解决办法

##占坑
对于service,ContentProvider需要提前在宿主注册

##自己实现包管理服务PMS

##使用方法与原生方式差异大
写一个“正常”的模块和写一个动态加载模块,写法是不一样的,插件内部对资源的访问只能通过自己定义的方式,包括对layout文件的inflate等,使用getResouces的方式,分分钟crash给你看,而且内部实现有些复杂,容易出现莫名其妙的

已有插件化框架对比
360 DroidPlugin
是360手机助手在Android系统上实现了一种新的插件机制,多进程实现,更新插件时,真正热更新. 后面会针对DroidPlugin详细学习

DL 动态加载框架

阿里ACCD(原OpenAtlas)
非代理Android动态部署框架

携程 DynamicAPK
实现Android App多apk插件化和动态加载,支持资源分包和热修复.携程App的插件化和动态加载框架.

#未来趋势
插件化作为近几年比较火的技术,有他的适用场景,但是有很大的局限性,在扩展性,适应性方面都赶不上react-native,未来的趋势还是移动web化更占优势

#其他资料
包建强:android插件化从入门到放弃

蘑菇街Android组件与插件化

使用DroidPlugin的示例

mac下搭建react-native-android环境

react-native源码地址
react-native需要环境支持,首先来配置java sdk,android sdk

###下载java sdk
http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html
接受协议,并下载mac os版本

###安装brew
安装命令如下:curl -LsSf http://github.com/mxcl/homebrew/tarball/master | sudo tar xvz -C/usr/local –strip 1
当brew安装成功后,就可以随意安装自己想要的软件了
例如android-sdk,命令如下:sudo brew install android-sdk
卸载的话,命令如下:sudo brew uninstall android-sdk
查看安装软件的话,命令如下:sudo brew search /apache/注意/apache/是使用的正则表达式,用/分割。

我第一次安装的时候提示

error: unable to unlink old '.github/CONTRIBUTING.md' (Permission         denied)
error: unable to unlink old '.github/ISSUE_TEMPLATE.md' (Permission denied)
error: unable to unlink old '.github/PULL_REQUEST_TEMPLATE.md' (Permission denied)
error: unable to create symlink Library/ENV/3.2.6 (Permission denied)
error: unable to create symlink Library/ENV/4.2 (Permission denied)
fatal: cannot create directory at 'Library/ENV/4.3': Permission denied

然后执行

sudo chown -R $USER /usr/local;
brew update

安装成功

###安装android sdk
brew安装成功后,在Mac终端输入 brew install android-sdk
在 .bash_profile中 配置环境变量

export ANDROID_HOME=查找到到sdk路径export     
PATH=${PATH}:${ANDROID_HOME}/tools export   
PATH=${PATH}:${ANDROID_HOME}/platform-tools

命令行敲入以下,生效命令

$source ~/.bash_profile

在命令行中输入:$ adb 查看是否配置成功

###react native环境搭建
安装nvm, node.js, watchman, flow

安装nvm
nvm是node.js的版本管理器,可以用nvm来安装node.js

$brew install nvm

安装node.js

node.js 是一个基于 Chrome V8 引擎的 JavaScript 运行环境。Node.js 使用了一个事件驱动、非阻塞式 I/O 的模型,使其轻量又高效。Node.js 的包管理器 npm,是全球最大的开源库生态系统

直接在官网上下载nodejs安装包,node.js下载地址

安装watchman,flow
Watchman 是 facebook 的一个开源项目,它开源用来监视文件并且记录文件的改动情况,当文件变更它可以触发一些操作,例如执行一些命令等等
Flow:Facebook 的 JavaScript 静态类型检查器 点击了解flow更多

$brew install watchman 
$brew install flow

安装React-native-cli
React-native-cli是用来开发react native到命令行工具(比如后面用到的react-native命令)

$sudo npm install -g react-native-cli

npm是安装成功node.js后到包管理器

环境搭建成功,接下里就要开始第一个react native的例子了

###创建项目
到你的工作目录下,敲入以下命令

$react-native init MyFirstReactNativeDemo

挂着vpn我执行了好几次,前几次都执行了超过半个小时还没动劲,切换了一个更好的网络,关闭了vpn,一下子就执行成功了

进入到项目目录下

$cd MyFirstReactNativeDemo
$react-native run-android

一开始用as的模拟器打开运行后报错
com.android.ddmlib.InstallException: Failed to establish session,

换成真机,又报错
InstallException: Unable to upload some APKs

http://www.hacksparrow.com/react-native-android-unable-to-upload-some-apks.html

说是gradle版本有bug,我把project-gradle的版本改为2.1.2

dependencies {    classpath 'com.android.tools.build:gradle:2.1.2' }

并且把
/myReactNativeDemo/android/gradle/wrapper/gradle-wrapper.properties
里面的distributionUrl 改为最新的配置地址后,又出现了新的错误- -|||

com.android.ddmlib.InstallException: Failed to install all

发现是安装包无法安装,有两个解决办法:
1.我试着从myReactNativeDemo/android/app/build/outputs/apk 把包拷到手机上,可以安装运行
2.将gradle中的classpath ‘com.android.tools.build:gradle:2.1.2’改为1.2.3。不过在华为6plus android4.4上可以安装,在华为p8 android 5.0上不能安装,具体原因还没找到,挺奇怪的

could not connect to development server android

如果是真机调试,确保usb调试打开,在终端输入命令

adb reverse tcp:8081 tcp:8081

运行成功。电脑自动打开server网页,设备上显示Welcome toReact Native。
第一个例子就运行成功了

摇一摇摇出菜单,选择enable live reload,在android.index.js中的改动,都是在app上进行刷新

找资料的时候发现
http://reactnative.cn/docs/0.28/running-on-device-android.html#content
这个网站里面的中文资料比较全

Espresso 自动化测试框架介绍

Espress介绍
google Espresso官方地址

github相关源码排行

谷歌13年的时候开源了espress,谷歌的思路是,等到它足够成熟和稳定以后,将其迁移到Android SDK中,以此可见对他的重视。Google使用Espresso测试了他们自己的超过30个应用程序,包括G+、Maps和Drive。下面对Espresso做一个基本介绍

Espresso测试是非常容易实现的,它由三部分组成:

ViewMachers:寻找用来测试的View。

ViewActions:发送交互事件。

ViewAssertions:检验测试结果。

用法
先直接放用法,以下用例的执行效果是直接打开MainActivity,执行showMain中的用例

onView(ViewMachers)

.preform(ViewActions)

.check(ViewAssertions)

@Rule

public IntentsTestRule mainActivityIntentsTestRule = new IntentsTestRule(MainActivity.class);

@Test

public void showMain(){

//是否选中

onView(withId(R.id.tv_username)).check(matches(isDisplayed()));

//测试点击

onView(withId(R.id.tv_username)).perform(click());

//根据文本内容匹配

String username =”HELLO Brian123!”;

onView(withId(R.id.tv_username)).check(matches(withText(username)));

}
Espresso 备忘录
方便你在开发过程中快速查阅。它包含了大部分可用的​Matchers​,​ViewActions​, 和​ViewAssertions​。pdf 格式的离线版本在此:espersso-cheat-sheet-2.1.0.pdf

搭建步骤
项目目录/build.gradle中的ext版本号:

// Define versions in a single place

ext {

// Sdk and tools

minSdkVersion =15

targetSdkVersion =22

compileSdkVersion =23

buildToolsVersion =’23.0.3’

//app Code

versionCode =1

versionName =”1.0”

// App dependencies

supportLibraryVersion =’23.4.0’

junitVersion =’4.12’

mockitoVersion =’1.10.19’

powerMockito =’1.6.2’

hamcrestVersion =’1.3’

runnerVersion =’0.4.1’

rulesVersion =’0.4.1’

espressoVersion =’2.2.1’

}
然后在android studio的项目目录下/app/build.gradle中配置

testCompile”junit:junit:$rootProject.ext.junitVersion”

androidTestCompile”junit:junit:$rootProject.ext.junitVersion”
// Android Testing Support Library’s runner and rules

androidTestCompile”com.android.support.test:runner:$rootProject.ext.runnerVersion”

androidTestCompile”com.android.support.test:rules:$rootProject.ext.runnerVersion”

// Resolve conflicts between main and test APK:

androidTestCompile”com.android.support:support-annotations:$rootProject.supportLibraryVersion”

androidTestCompile”com.android.support:support-v4:$rootProject.supportLibraryVersion”

androidTestCompile”com.android.support:recyclerview-v7:$rootProject.supportLibraryVersion”
// Espresso UI Testing

androidTestCompile”com.android.support.test.espresso:espresso-core:$rootProject.espressoVersion”

androidTestCompile (“com.android.support.test.espresso:espresso-contrib:$rootProject.espressoVersion”)

androidTestCompile”com.android.support.test.espresso:espresso-intents:$rootProject.espressoVersion”

compile”com.android.support.test.espresso:espresso-idling-resource:$rootProject.espressoVersion”

// Android Testing Support Library’s runner and rules

androidTestCompile”com.android.support.test:runner:$rootProject.ext.runnerVersion”

androidTestCompile”com.android.support.test:rules:$rootProject.ext.runnerVersion”
Espresso和UIAutomator的区别
Espresso是个功能强大、效率很高的Android自动化测试框架,但是他有一个重要的局限,你只能在被测试app的Context中操作。

所以应用的推送、从另一个应用程序进入、处理通知栏消息等功能无法在Espresso中使用,需要混合UIAutomator使用

其他资料参考

http://www.jianshu.com/p/ef4ad5424784 官方文档翻译

http://www.w2bc.com/article/40324?from=extend

http://axiu.me/coding/espresso-introduction/

https://github.com/googlesamples/android-testing 谷歌自动化测试demo

android混淆文件说明

dump.txt
介绍了APK中所有的class文件的内部结构
**
mapping.txt
提供原文件对应混淆后的类、方法和字段名称。
seeds.txt
列出没有混淆的类和成员。
usage.txt**
列出从apk中删除的代码

华为、小米等国产android系统收不到推送消息的解决办法

因第三方系统对后台推送进程的限制,部分国产手机会出现收不到推送,推送延迟时间长,这种情况需要用户手动操作

#小米【MIUI】

自启动管理:需要把应用加到【自启动管理】列表,否则杀进程或重新开机后进程无法开启
通知栏设置:应用默认都是显示通知栏通知,如果关闭,则收到通知也不会提示
网络助手:可以手动禁止已安装的第三方程序访问2G/3G和WIFI的网络和设置以后新安装程序是否允许访问2G/3G和WIFI的网络
MIUI 7 神隐模式:允许应用进行自定义配置模式,应用在后台保持联网可用,否则应用进入后台时,应用无法正常接收消息。【设置】下电量和性能中【神隐模式】

#华为【Emotion】

自启动管理:需要把应用加到【自启动管理】列表,否则杀进程或重新开机后进程不会开启,只能手动开启应用
后台应用保护:需要手动把应用加到此列表,否则设备进入睡眠后会自动杀掉应用进程,只有手动开启应用才能恢复运行
通知管理:应用状态有三种:提示、允许、禁止。禁止应用则通知栏不会有任何提醒

#魅族【Flyme】

自启动管理:需要把应用加到【自启动管理】列表,否则杀进程或重新开机后进程无法开启
通知栏推送:关闭应用通知则收到消息不会有任何展示
省电管理:安全中心里设置省电模式,在【待机耗电管理】中允许应用待机时,保持允许,否则手机休眠或者应用闲置一段时间,无法正常接收消息。

#VIVO【Funtouch OS】

内存一键清理:需要将应用加入【白名单】列表,否则系统自带的“一键加速”,会杀掉进程
自启动管理:需要将应用加入“i管家”中的【自启动管理】列表,否则重启手机后进程不会自启。但强制手动杀进程,即使加了这个列表中,后续进程也无法自启动。

#OPPO【ColorOS】

冻结应用管理:需要将应用加入纯净后台,否则锁屏状态下无法及时收到消息
自启动管理:将应用加入【自启动管理】列表的同时,还需要到设置-应用程序-正在运行里锁定应用进程,否则杀进程或者开机后进程不会开启,只能手动开启应用

在android studio中使用lambda

Lambda表达式是在JDK 8中开始支持的一种函数式推导语言,能够大量减少匿名内部类那种冗余的代码。在Android中,可以大量使用在设置监听,设置异步回调等场景。

  Android Studio目前的版本还没有直接支持Lambda表达式的支持,需要插件支持,当然,JDK版本也必须使用JDK 8 或者以上(当然过些时间会有更高版本的JDK)。

  • 1.引入retrolambda插件:

  在Project的build.gradle中添加

apply plugin: ‘me.tatarka.retrolambda‘
  • 2.设置编译选项(可能也可以不写)

  在Project的build.gradle的android节点中添加如下代码

compileOptions {
    sourceCompatibility JavaVersion.VERSION_1_8
    targetCompatibility JavaVersion.VERSION_1_8
}
  • 3.引入retrolambda的类路径,在Module:app的build.gradle中的buildscript->dependencies节点中添加如下代码

    classpath ‘me.tatarka:gradle-retrolambda:3.2.0‘
    
  • 4.对build.gradle进行build

  • 5.编写测试代码,简单写法如下

    protected void onCreate(Bundle savedInstanceState) {

    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    textView = (TextView)findViewById(R.id.text);
    textView.setOnClickListener( v -> Toast.makeText(getApplicationContext(), "Lambda", Toast.LENGTH_LONG).show());
    }
    

      6.运行查看是否正常