很多项目可能是在中途才从纯原生转向Hybrid,那就涉及到将Ionic、React Native等开发的功能模块集成到已有的原生安卓项目中,那么非原生的部分当然就需要热更新了,否则每次都打包apk就失去这么做的意义了。
本人也是在网上查看了一些文章,看了一些相关书本,把项目集成好了决定整理一下写下本文,供大家参考,有不对的地方欢迎指出。
环境:
Android Studio 3.2 + JDK 1.8 + node.js 10.13.0 + ionic 4.3.1 + cordova 8.1.2 + code-push 2.1.9
npm命令搭建Ionic的环境咱就跳过吧(0_0)
参考:
Ionic创建项目以及集成code-push官方文档: https://ionicframework.com/docs/native/code-push/
Ionic集成到原生安卓项目:https://blog.csdn.net/qq_42618969/article/details/81173034
创建项目:
首先打开命令行,进入指定路径(项目路径根据个人情况自行修改),一波连续操作:
ionic start codepush-ionic-test tabs
创建tabs基础ionic项目(这里我并没有选择使用Ionic4,Ionic的资料有点少,所以不想用beta版本给自己找坑)
cd codepush-ionic-test
进入项目文件夹
ionic cordova plugin add cordova-plugin-code-push
添加code-push插件
npm install --save @ionic-native/code-push
node_modules添加code-push库
ionic cordova platform add android
Ionic添加安卓平台支持(不添加的话项目只是web,苹果平台将android改为ios即可)
npm install
安装code-push-cli(这个一定加上-g表明全局安装,以后就可以省略这步)
npm install -g code-push-cli
登录code-push(这一步会自动打开电脑浏览器,注册账号或者使用github账号登录都行,登录后会给一个token)
code-push login
在code-push中创建项目(苹果端替换android为ios)
code-push app add codepush-ionic-test-android android cordova
如果已经创建了项目忘记了项目的key可以通过这个命令查看
code-push deployment ls codepush-ionic-test-android -k
在项目根目录打开config.xml,将code-push的key添加进去
编辑src/app/app.module.ts代码如下(这个有点像安卓中的build.gradle,这里主要是设置CodePush库引用):
import { NgModule, ErrorHandler } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { IonicApp, IonicModule, IonicErrorHandler } from 'ionic-angular';
import { MyApp } from './app.component';
import { AboutPage } from '../pages/about/about';
import { ContactPage } from '../pages/contact/contact';
import { HomePage } from '../pages/home/home';
import { TabsPage } from '../pages/tabs/tabs';
import { StatusBar } from '@ionic-native/status-bar';
import { SplashScreen } from '@ionic-native/splash-screen';
import { CodePush } from '@ionic-native/code-push';
@NgModule({
declarations: [
MyApp,
AboutPage,
ContactPage,
HomePage,
TabsPage
],
imports: [
BrowserModule,
IonicModule.forRoot(MyApp)
],
bootstrap: [IonicApp],
entryComponents: [
MyApp,
AboutPage,
ContactPage,
HomePage,
TabsPage
],
providers: [
StatusBar,
SplashScreen,
CodePush,
{provide: ErrorHandler, useClass: IonicErrorHandler}
]
})
export class AppModule {}
编辑src/app.component.ts添加热更新的检查更新方法(这个有点类似application初始化):
import { Component } from '@angular/core';
import { Platform } from 'ionic-angular';
import { StatusBar } from '@ionic-native/status-bar';
import { SplashScreen } from '@ionic-native/splash-screen';
import { HomePage } from '../pages/home/home';
import { CodePush, InstallMode, SyncStatus } from '@ionic-native/code-push';
import { AlertController } from 'ionic-angular/components/alert/alert-controller';
@Component({
templateUrl: 'app.html'
})
export class MyApp {
rootPage:any = HomePage;
constructor(platform: Platform, statusBar: StatusBar, splashScreen: SplashScreen,
private codePush: CodePush, private alertCtrl: AlertController) {
platform.ready().then(() => {
// Okay, so the platform is ready and our plugins are available.
// Here you can do any higher level native things you might need.
statusBar.styleDefault();
splashScreen.hide();
this.checkCodePush(); //Use the plugin always after platform.ready()
});
}
checkCodePush() {
this.codePush.sync({
updateDialog: {
appendReleaseDescription: true,
descriptionPrefix: "\n\nChange log:\n"
},
installMode: InstallMode.IMMEDIATE
}).subscribe(
(data) => {
console.log('CODE PUSH SUCCESSFUL: ' + data);
},
(err) => {
console.log('CODE PUSH ERROR: ' + err);
}
);
}
}
接下来在电脑浏览器运行一下,继续命令行
ionic serve
到这里就已经将Ionic集成code-push了,最后编译安卓项目
ionic cordova build android
集成到原生安卓项目
Ionic项目中找到安卓项目,就在platforms文件夹里面(ios项目路径也在这),这已经是一个能直接用Android Studio打开的项目了。
1、将其中的CordovaLib整个文件夹拷贝到要集成的原生安卓项目中,在gradle配置一下作为module。
2、将项目中res/xml/config.xml文件拷贝到原生项目中,将Ionic项目中的“org文件夹、io文件夹”拷贝到原生项目的main/java下。
3、接下来是将Ionic中写的功能相关代码拷贝到原生项目中,也就是assets中的www文件夹,里面才是在Ionic中的代码,而热更新便是更新这里面的内容。
其实如果是集成Ionic到原生项目的话,到这里已经完成了,但是code-push还没放进去。
4、在安卓原生项目中添加依赖(这个库在Ionic安卓项目的cordova-plugin-code-push/starter-build-extras.gradle中添加了引用)
implementation 'com.nimbusds:nimbus-jose-jwt:5.1'
5、拷贝Ionic安卓项目中的src/main/java/com到原生安卓项目中,最后原生项目文件结构如图所示(马赛克是原生项目本来的代码):
到这里整个项目集成Ionic + code-push就基本完成了,随便找个地方从原生项目跳转进Ionic的功能模块里面(跳转activity就好,跳到io.ionic.starter.MainActivity,记得到AndroidManifest.xml中添加这个Activity,这里为了省事我把Ionic的路径直接一起传过去)
Bundle bundle = new Bundle();
bundle.putString("url", "file:///android_asset/www/index.html");
IntentUtils.startActivity(mActivity, MainActivity.class, bundle);
再稍微改一下io.ionic.starter.MainActivity
public class MainActivity extends CordovaActivity
{
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
// enable Cordova apps to be started in the background
Bundle extras = getIntent().getExtras();
if (extras != null && extras.getBoolean("cdvStartInBackground", false)) {
moveTaskToBack(true);
}
// Set by in config.xml
loadUrl(getIntent().getStringExtra("url"));
}
}
这时已经可以运行原生安卓项目了。当然,还有热更新没测试,再回到Ionic项目中,打开源码src文件夹随便修改点东西,然后命令行编译一下
ionic cordova prepare android
这时候再上传推送包到微软的code-push中,命令中的0.0.1是版本号,用原生安卓项目的versionName的值,--description后面跟的是热更新包的说明,-d后面跟的是推送分支,code-push创建项目后便会生成正式线和测试线两个分支,
code-push release codepush-ionic-test-android ./platforms/android/app/src/main/assets/www/ 0.0.1 --description "Your awesome change description" -d "Staging"
题外:
1、热更新检查可以不弹框提示采取静默安装,具体的就自行去修改Ionic那部分代码了,毕竟ios审核禁止热更新,还是不能光明正大的。配置在config.xml里面热更新使用的key也可以动态修改,在代码里控制测试线、正式线以及ios和android,自己去做判断就好,简单上几行代码:
//根据平台修改key
if (this.platform.is('android')) {
key = '';
} else if (this.platform.is('ios')) {
key = '';
}
//定义检查更新参数
let options = {
installMode:InstallMode.ON_NEXT_RESUME,
deploymentKey:key
}
//检查更新方法
checkCodePush() {
this.codePush.sync(options,(data) => {
}).subscribe(
(data) => {
console.log('CODE PUSH SUCCESSFUL: ' + data);
},
(err) => {
console.log('CODE PUSH ERROR: ' + err);
}
);
}
2、检查更新有几个模式,这里用的是启动Ionic的时候检查更新,其它的自己加代码吧,类似这样:
//从后台回到应用时检查更新
this.platform.resume.subscribe(() => {
this.checkCodePush();
});
3、另外说一下本人在集成过程中踩的一个坑,因为用fiddler抓包,手机设置了代理,将集成好的安卓项目打包release包运行时,发现code-push不检测更新,在代码里找了好几圈,看Android Studio中的Logcat发现日志出现了检测更新的请求,说网络连接有问题,才明白code-push对网络有做检查,release包运行一旦有网络代理就不会发起请求,这是https加密传输的SSL证书问题,具体的就不深究了。
4、这里使用的是微软code-push的服务,也可以自行搭建一个code-push服务端。
5、当然了,可以把www文件夹以及cdvasset.manifest文件放到自定义路径:
一是使用自己的方式更新文件,只需要修改跳转io.ionic.starter.MainActivity时传过去的页面路径即可修改ionic页面访问。
//正常情况下读取文件我们会这样写路径
String path = Environment.getExternalStorageDirectory().getAbsolutePath() + "www/index.html"
//在这里需要加个前缀,像原本assets路径类似
String path = "file:" + Environment.getExternalStorageDirectory().getAbsolutePath() + "www/index.html"
还有就是把codepush热更新的路径也改掉,需要修改三个地方:
// com.microsoft.cordova.CodePush中的WWW_ASSET_PATH_PREFIX常量
private static final String WWW_ASSET_PATH_PREFIX = "file:" + Environment.getExternalStorageDirectory().getAbsolutePath() + "/www/";
// org.apache.cordova.ConfigXmlParser中的setStartUrl方法,这里是设置开始页面
launchUrl = "file:" + Environment.getExternalStorageDirectory().getAbsolutePath()+"/www/index.html";
// org.apache.cordova.file.AssetFilesystem中的lazyInitCaches方法,注意这里无须添加“file:”,将原本打开assets路径文件改为普通的打开文件并转换成InputStream对象(FileInputStream是InputStream的子类)
ois = new ObjectInputStream(new FileInputStream(Environment.getExternalStorageDirectory().getAbsolutePath()+"/cdvasset.manifest"));
最后说一下,code-push一旦进行过热更新后,文件会保存到内部存储中,真正自定义还需要修改CodePush以及Cordova其它代码。
发包对应APP版本范围表达式
1.2.3——仅仅只有1.2.3的版本
*——所有版本
1.2.x——主要版本1,次要版本2的任何修补程序版本
1.2.3 - 1.2.7——1.2.3版本到1.2.7版本
>=1.2.3 <1.2.7——大于等于1.2.3版本小于1.2.7的版本
~1.2.3——大于等于1.2.3版本小于1.3.0的版本
^1.2.3——大于等于1.2.3版本小于2.0.0的版本