基于Small及Weex的定制化APP方案
前言
small是android与iOS平台比较出名的轻巧的跨平台插件化框架,也正是被这一点吸引,决定将small应用到集团内部的应用引擎模块化方案中,本篇博文主要讲述本人基于small在android平台实现的定制化APP方案(运营自由配置、自由组合、自动打包)~
框架解决问题
- 由于公司业务的发展,导致更多的超级app诞生于世,导致app 项目太大、耦合严重,且相关开发人员相互耦合,导致效率低下
- app 热修复问题在很多场景是急需的
- 对于开发效率方面,采用了weex模块进行快速迭代
在介绍框架之前首先熟悉一下small的原理~
原理介绍
small 插件化方案分为两个步骤
- gradle 打包插件机制
- 运行期加载机制
打包插件机制
官方说明
将多个app与lib工程编译成so文件
运行期加载机制
Dynamic load classes
官方说明
DexClassLoader不支持”.so”后缀,为了让应用启动时能自动复制插件包到应用存储目录,需要支持”.so”后缀。做法就是模拟 压缩包加载代码块,创建一个dex元素,再反射添加到宿主class loader里的dexPathList。
Dynamic load resources
官方说明
Dynamic register activities
activity 启动过程:
注: Android activities受Instrumentation监控
- 由Activity的startActivityForResult方法启动,通过instrumentation的execStartActivity方法激活生命周期。
- 在ActivityThread的performLaunchActivity方法中通过instrumentation的newActivity方法实例化。
small 实现方案:
1. 首先在宿主manifest中注册一个命名特殊的占坑activity
<!-- Stub Activities -->
<activity android:name=".A.0" android:launchMode="standard"/>
2. 封装一个instrumentation,替换掉宿主的
(1)、欺骗startActivityForResult(启动过程1)以获得生命周期
(2)、欺骗performLaunchActivity(启动过程2)来创建插件activity实例
ActivityThread thread = currentActivityThread();
Instrumentation base = thread.@mInstrumentation;
Instrumentation wrapper = new InstrumentationWrapper(base);
thread.@mInstrumentation = wrapper;class InstrumentationWrapper extends Instrumentation {// 欺骗startActivityForResult获得生命周期public ActivityResult execStartActivity(..., Intent intent, ...) {fakeToStub(intent);base.execStartActivity(args);}// 欺骗performLaunchActivity创建实例@Overridepublic Activity newActivity(ClassLoader cl, String className, Intent intent) {className = restoreToReal(intent, className);return base.newActivity(cl, className, intent);}
}
项目构建
按照官方的教程,初始化成功之后,项目结构如下:
框架获取
- git clone https://github.com/osmartian/odyssey.git
- cd odyssey
- npm install
项目结构
small-frame
├── app (宿主app)
│ ├── LaunchActivity
│ │
│ └── SmallApp
│
├── app.top (topbar框架APP)
│ │
│ └── MainActivity
│
├── app.bottom (bottombar框架app)
│ │
│ └── MainActivity
│
├── app.home (首页模块app)
│ └── MainFragment
│
├── app.weex (weex模块app)
│ │
│ ├── MainActivity
│ │
│ └── MainFragment
│
├── app.detail (详情模块app)
│ ├── MainActivity
│ │
│ └── SubActivity
│
├── lib.weex (weex lib 库)
│
├── lib.martian (公共工具库)
│
└── lib.style (公共样式库)└── res├── colors.xml├── dimens.xml└── styles.xml
config驱动文件打包APK流程
打包配置说明
{// app基本信息配置baseInfo: {applicationId: 'com.syswin.toon.bottom',versionCode: 2,appIcon: 'bottom',appName: 'BOTTOM框架',versionName: '1.0.1'},// 框架末班配置frame: {// 框架模块uriuri: 'bottom',// 可选tags: []},// 配置small打包配置modules: {// small 版本version: '1.0.0',// 需要打包的app及libbundles: [{uri: 'bottom',pkg: 'com.osmartian.small.app.bottom'},... {uri: 'lib.style',pkg: 'com.osmartian.small.lib.style'}]}
}
APK生成过程
- 动态修改APP基本信息
采用动态修改gradle配置方式,修改AppID、appName、appIcon…
在项目根build.gradle中有如下配置,只需动态修改此配置即可:
ext {compileSdkVersion = 25buildToolsVersion = '25.0.2'applicationId = "com.syswin.toon.walid"appName = "Walid APK"appIcon = "top"minSdkVersion = 15targetSdkVersion = 25versionCode = 10versionName = "1.0.1"
}
- 设置框架frame模块
动态设置框架模块uri,用于宿主模块调起
见宿主app模块下com.osmartian.small中的config.java文件:
package com.osmartian.small;/*** @Author : walid* @Data : 2017-03-14 22:36* @Describe : INDEX URL配置*/
public class Config {public static final String INDEX_URI = "bottom?tags=%5B%7B%22name%22%3A%22%E9%A6%96%E9%A1%B5%22%2C%22uri%22%3A%22home%22%7D%2C%7B%22name%22%3A%22%E6%88%91%E7%9A%84%22%2C%22uri%22%3A%22weex%3Furl%3Dhttp%253A%252F%252F172.31.243.44%253A12580%252Fdist%252Fweex%252Fviews%252Fmine%252Fapp.js%22%7D%2C%7B%22name%22%3A%22%E4%B8%AA%E4%BA%BA%E8%B5%84%E6%96%99%22%2C%22uri%22%3A%22weex%3Furl%3Dhttp%253A%252F%252F172.31.243.44%253A12580%252Fdist%252Fweex%252Fviews%252Fuserinfo%252Fapp.js%22%7D%5D";
}
- 生成bundle.json small打包需要文件
动态生成bundle.json small打包文件,且copy到android、ios项目
(1)、生成bundle.json文件
const bundlePath = path.join(__dirname, '../../build/output', 'bundle.json')// 框架模块安装
function packModules(modules) {console.log(modules)return new Promise((resolve, reject) => {fs.writeFile(bundlePath, JSON.stringify(modules), (err) => {err ? reject(err) : resolve()})})
}
(2)、copy 至 android 、 ios项目
// cp -vf build/output/bundle.json android/app/src/main/assets/bundle.json
npm run copy:bundle
- 执行small打包 -> so 文件
将需要打包的模块打包成so文件
npm run build:small
- 编译生成apk文件
执行npm指令进行apk生成
npm run dev:android
// 或
npm run build:android
small 模块跳转操作
1、 跳转h5
Small.openUri("https://github.com/osmartian/small-frame", getContext());
2、 跳转app module 传值
Small.openUri("detail?params=我是参数,从首页传送过来的~", getContext());
3、 跳转app module 二级界面
Small.openUri("detail/sub", getContext());
项目打包APK示例
打包topbar框架APK
- config 文件配置
{baseInfo: {applicationId: 'com.syswin.toon.top',versionCode: 2,appIcon: 'top',appName: 'TOP框架',versionName: '1.0.1'},frame: {uri: 'top',tags: [{name: '首页',uri: 'home'},{name: '发起筹款',uri: `weex?url=${encodeURIComponent(`http://${ipAddress}:12580/dist/weex/views/launch/app.js`)}`},{name: '我的',uri: `weex?url=${encodeURIComponent(`http://${ipAddress}:12580/dist/weex/views/mine/app.js`)}`}]},modules: {version: '1.0.0',bundles: [{uri: 'top',pkg: 'com.osmartian.small.app.top'},{uri: 'home',pkg: 'com.osmartian.small.app.home'},{uri: 'weex',pkg: 'com.osmartian.small.app.weex'},{uri: 'lib.weex',pkg: 'com.osmartian.small.lib.weex'},{uri: 'lib.martian',pkg: 'com.osmartian.small.lib.martian'},{uri: 'lib.style',pkg: 'com.osmartian.small.lib.style'}]}
}
示例图片
打包bottombar框架APK
- config 文件配置
{baseInfo: {applicationId: 'com.syswin.toon.bottom',versionCode: 2,appIcon: 'bottom',appName: 'BOTTOM框架',versionName: '1.0.1'},frame: {uri: 'bottom',tags: [{name: 'Weex首页',uri: `weex?url=${encodeURIComponent(`http://${ipAddress}:12580/dist/weex/views/home/app.js`)}`},{name: '原生首页',uri: `home`},{name: '我的',uri: `weex?url=${encodeURIComponent(`http://${ipAddress}:12580/dist/weex/views/mine/app.js`)}`}]},modules: {version: '1.0.0',bundles: [{uri: 'bottom',pkg: 'com.osmartian.small.app.bottom'},{uri: 'home',pkg: 'com.osmartian.small.app.home'},{uri: 'weex',pkg: 'com.osmartian.small.app.weex'},{uri: 'detail',pkg: 'com.osmartian.small.app.detail',rules: {sub: 'Sub'}},{uri: 'lib.weex',pkg: 'com.osmartian.small.lib.weex'},{uri: 'lib.martian',pkg: 'com.osmartian.small.lib.martian'},{uri: 'lib.style',pkg: 'com.osmartian.small.lib.style'}]}
}
示例图片
结语
至此,基于small的定制化APP方案介绍完毕了,此方案还是雏形阶段,也希望业界朋友多多点评、多多吐槽,也希望大家前去start及提一些各自的建议。
项目地址:https://github.com/osmartian/odyssey.git
基于Small及Weex的定制化APP方案相关推荐
- 基于 RocketMQ Prometheus Exporter 打造定制化 DevOps 平台
作者 | 陈厚道 冯庆 来源 | 阿里巴巴云原生公众号 导读:本文将对 RocketMQ-Exporter 的设计实现做一个简单的介绍,读者可通过本文了解到 RocketMQ-Exporter 的实 ...
- 精品基于Uniapp+SSM实现的定制旅游APP
<[含文档+PPT+源码等]精品基于Uniapp+SSM实现的定制旅游APP[包运行成功]>该项目含有源码.文档.PPT.配套开发软件.软件安装教程.项目发布教程等 软件开发环境及开发工具 ...
- 高度可定制化的方案_如何开发高度可定制的产品
高度可定制化的方案 您是否听说过:"我们非常喜欢您的产品--除了一些小细节."? 然后,CIO推出了一系列其他"必备"要求的清单,其中有数百个要添加到您的惊人产 ...
- .NET Core开发实战(第14课:自定义配置数据源:低成本实现定制化配置方案)--学习笔记...
14 | 自定义配置数据源:低成本实现定制化配置方案 这一节讲解如何定义自己的数据源,来扩展配置框架 扩展步骤 1.实现 IConfigurationSource 2.实现 IConfiguratio ...
- 平安保险基于 SPI 机制的 RocketMQ 定制化应用
作者:孙园园|平安人寿资深开发 为什么选用 RocketMQ 首先跟大家聊聊我们为什么会选用 RocketMQ,在做技术选型的过程中,应用场景应该是最先考虑清楚的,只有确定好了应用场景在做技术选型的过 ...
- android studio项目实例基于Uniapp+SSM实现的定制旅游APP
- 计算机毕业设计安卓App毕设项目之ssm定制旅游APP
目录 一.项目介绍 二.文档截图 三.运行截图 一.项目介绍 含文档+PPT+源码等]精品基于Uniapp+SSM实现的定制旅游APP[包运行成功]>该项目含有源码.文档.PPT.配套开发软件. ...
- 服务器定制化—服务器行业发展的新驱动力
虽说在人工智能.边缘计算.云计算等为代表的新数据时代的大背景下,企业的数字化进程离不开服务器算力的支撑,但由于服务器市场已经相对成熟,格局基本形成. 因此在X86服务器行业中,鲜有大事发生,各服务器品 ...
- android 圆角边框边框渐变,Android深度定制化TabLayout:圆角,渐变色,背景边框,圆角渐变下划线,基于Android原生TabLayout...
Android深度定制化TabLayout:圆角,渐变色,背景边框,圆角渐变下划线,基于Android原生TabLayout 在附录1的基础上丰富自定义的TabLayout,这次增加两个内容: 1,当 ...
最新文章
- Python数据挖掘:数据探索,数据清洗,异常值处理
- ubuntu12.04 alternate win7 双系统安装
- 怎么讲d 盘里的软件弄到桌面_教大家电脑怎么把e盘文件移到d位置
- [trustzone]-ARMV8的aarch64和aarch32环境下ELx级别的理解
- jzoj6276-[Noip提高组模拟1]树【线段树,扫描线,倍增】
- 街篮混服服务器信息,街篮手游闻鸡起舞服务器火爆开启
- WordPress博客网站搬家和换域名方法
- selenium常用方法
- 我的博客面貌焕然一新
- 二分法01:查找一个数
- 并发编程 - io模型 - 总结
- 我不曾忘记的初心-大厂小厂
- If,for,range混合使用笔记-(VBA视频教程2:使用IF进行逻辑判断)
- App Store审核标准
- 【技能】快递管家无需开发集成金蝶云星辰示例
- form表单Get方式提交时,action中带参数传递不了
- matlab 变量的定义变量名称,matlab定义变量名
- web多媒体技术在视频编辑场景的应用
- 速途网范锋:重要合作可能决定网络企业生死
- Freemarker导出word图片不显示可能的原因