# NpHotfixExample
**Repository Path**: incachina_0/NpHotfixExample
## Basic Information
- **Project Name**: NpHotfixExample
- **Description**: 印加时代热更SDK示例项目
- **Primary Language**: Java
- **License**: Not specified
- **Default Branch**: download
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 0
- **Forks**: 0
- **Created**: 2024-09-23
- **Last Updated**: 2025-06-17
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
## NpHotfix
- 1. java 版本需要使用java17或以上
- 2. Android minSdk 设置为 23
## 示例项目运行
`main`分支的代码是在`/sdcard/`中加载升级补丁。`download`分支的代码是通过调用云api下载升级文件。
### 项目配置文件
#### `buildScripts/nphotfix.properties`
- 1. sevenZip:7zip执行文件的绝对路径。修改为在自己系统的路径。
- 2. isBaseBuild:第一次生成基础包编译的时候修改为1,其他版本的时候修改为0。
- 3. baseApkName:基础包名称。当云服务端上传大版本的时候,此处需要设置与其名称相同。
- 4. versionCode:版本号。每次发版的时候需要修改,建议修改值为累加1。
- 5. versionName:版本名称。每次发版的时候需要修改,建议修改值为累加0.1。
#### `buildScripts/ext.gradle`
- 1. nphotfixVersion:NpHotfix SDK版本号设置。
- 2. CHANNEL:渠道ID设置,找印加客服申请。
- 3. SPEED_UP: 设置快速更新不进行文件验证工作,请务必保证更新包的正确。(如果设置非1的数值会执行更新前的文件验证工作,验证工作需要耗时更新时间为1-2分钟。)
#### `buildScripts/appguard.gradle`
- 1. IS_ENABLE_APPGUARD:设置为1表示启用appguard,设置为0表示禁用appguard。
### `linux/macos`开发环境可以通过运行脚本快速构建程序并运行。
```bash
# 运行run_host.sh生成基础包。
# -r 参数表示 构建类型为release.
# -b 参数表示 生成基础包.
# -g 参数表示 启用appguard.
# 此脚本中自动修改了nphotfix.properties的baseApkName, isBaseBuild, versionCode,versionName
./run_host.sh -r -b
# 生成补丁包,并自动推送到/sdcard/
# 此脚本中自动修改了nphotfix.properties的isBaseBuild, versionCode, versionName
./host_patch.sh
```
### `windows`开发环境
修改 `buildScripts/nphotfix.properties`(注意需要添加转义字符):
__sevenZip=E\\:\\\7z安装路径\\\7z.exe__
```bat
# 生成基础包
.\gradlew.bat clean
.\gradlew.bat assembleRelease
# 生成补丁包
.\gradlew.bat tinkerPatchRelease
```
点击示例程序界面的 “Start Upgrade”,开始下载补丁app,当补丁安装完成后点击"Exit"并再次打开应用可查看界面的变化。
点击"Start Upgrade"升级补丁程序时,请查看logcat的日志,会有详细的一些进度输出。
## SDK接入使用
- 1. 将示例代码中的`buildScripts`文件复制到项目根目录中.
- 2. 将示例代码中的**proguard-rules.pro**, **tinker_multidexkeep.pro**文件复制到`app`项目目录下。
请注意在 `tinker_multidexkeep.pro`文件中关于**dex.loader** 的**com.inca.ota.example ExampleApplication**包名需要替换成自己项目的包名如:**com.mfyndk.testhotfix.MyApplication**
- 3. 在 `setting.gradle` 中添加以下内容:
```gradle
//分别在 pluginManagement 和 dependencyResolutionManagement的repositories标签中添加阿里云仓库。
repositories {
maven { url 'https://maven.aliyun.com/repository/public' }
maven {
credentials {
username = '66b5bdba2f2dfc5fa22a09ca'
password = 'jEpfL=_VG[[C'
}
url 'https://packages.aliyun.com/maven/repository/2486498-snapshot-3nBXCQ'
}
}
```
- 4. 在项目根目录中的 `build.gradle` 文件中添加以下内容:
```gradle
//添加 ext.gradle和classpath
buildscript {
apply from: 'buildScripts/ext.gradle'
dependencies {
classpath ("com.inca.ota:inca-ota-gradle:$nphotfixVersion")
}
}
```
- 5. 在 `app/build.gradle`文件中添加以下内容:
```gradle
//添加inca-ota-gradle插件
plugins {
//在application下面添加
id 'com.inca.ota.host'
}
android {
defaultConfig {
//为支持tinker 需要设置23,不能大于24
minSdk 23
//读取nphotfix.properties中的versionCode,versionName
versionCode project.ext.versionCode
versionName "${project.ext.versionName}"
//设置 multidex和proguard
multiDexEnabled true
multiDexKeepProguard file("tinker_multidexkeep.pro")
//设置AndroidManifest.xml的动态环境变量
manifestPlaceholders = [cleartextTrafficPermitted:"true",
channel:"${rootProject.ext.CHANNEL}",
tinkerId:"${project.android.defaultConfig.versionName}_${project.android.defaultConfig.versionCode}",
speedUp:"${rootProject.ext.SPEED_UP}",
apiHost:"${rootProject.ext.SERVER_API_URL}"]
}
//设置自己的签名,这个签名要与热更后台设置的签名sha1值相同。
//在项目根目录下自定义文件夹keystore,将签名放到此文件夹下并进行设置以下签名信息。
signingConfigs {
release {
try {
//替换为你的签名私钥口令
storeFile file("$rootDir/keystore/debug.keystore")
storePassword "xxxxx"
keyAlias "xxxxx"
keyPassword "xxxx"
} catch (ex) {
throw new InvalidUserDataException(ex.toString())
}
}
debug {
//替换为你的签名私钥口令
storeFile file("$rootDir/keystore/debug.keystore")
storePassword "xxxxx"
keyAlias "xxxxx"
keyPassword "xxxx"
}
}
buildFeatures {
//生成BuildConfig.java
buildConfig true
}
buildTypes {
release {
//启用proguard
minifyEnabled true
//指定proguard文件
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
//启用签名
signingConfig signingConfigs.release
}
}
}
//添加依赖组件
dependencies {
compileOnly("com.inca.ota:inca-ota-gradle:$nphotfixVersion")
implementation("com.inca.ota:nphotfix:$nphotfixVersion"){ changing = true }
implementation "androidx.multidex:multidex:2.0.1"
}
//添加构建appguard的脚本
apply from: "$rootDir/buildScripts/appguard.gradle"
```
- 6. 创建热更时需要的Application,注意注入标签`DefaultLifeCycle`的application属性的字符串值修改,并且其字符串值与`AndroidManifest.xml`中Application name值保持一致。
示例中是`ExampleApplicationLike.java`,请注意务必创建的这个Application末尾需要添加`Like`,但在`DefaultLifeCycle`和`AndroidManifest.xml`中配置时不需要添加`Like`。
```java
package com.inca.ota.example;
import android.annotation.TargetApi;
import android.app.Application;
import android.content.Context;
import android.content.Intent;
import android.os.Build;
import androidx.multidex.MultiDex;
import com.inca.ota.tinker.HostOta;
import com.tencent.tinker.anno.DefaultLifeCycle;
import com.tencent.tinker.entry.DefaultApplicationLike;
import com.tencent.tinker.loader.shareutil.ShareConstants;
@SuppressWarnings("unused")
@DefaultLifeCycle(application = "com.inca.ota.example.ExampleApplication",
//这个理需要替换成项目中自己的包名+Application name,这个Application name不能添加Like
flags = ShareConstants.TINKER_ENABLE_ALL,
loadVerifyFlag = false)
public class ExampleApplicationLike extends DefaultApplicationLike {
public ExampleApplicationLike(Application application, int tinkerFlags, boolean tinkerLoadVerifyFlag, long applicationStartElapsedTime, long applicationStartMillisTime, Intent tinkerResultIntent) {
super(application, tinkerFlags, tinkerLoadVerifyFlag, applicationStartElapsedTime, applicationStartMillisTime, tinkerResultIntent);
}
@TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
@Override
public void onBaseContextAttached(Context base) {
super.onBaseContextAttached(base);
MultiDex.install(base);
HostOta.get().init(getApplication(), this);
}
@TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
public void registerActivityLifecycleCallbacks(Application.ActivityLifecycleCallbacks callback) {
getApplication().registerActivityLifecycleCallbacks(callback);
}
@Override
public void onCreate() {
super.onCreate();
System.loadLibrary("testhotfix");
}
}
```
- 7. 在 `AndroidManifest.xml`中进行如下设置:
```xml
```
- 8. 创建 file_paths.xml `app/res/xml/*.xml`
```xml
```
- 9. 在启动页的activity中添加热更启动初始化代码,参考示例中的`MainActivity.java`文件
```java
//main分支的示例代码:在/sdcard中加载补丁包.
TinkerInstaller.onReceiveUpgradePatch(getApplicationContext(), Environment.getExternalStorageDirectory().getAbsolutePath() + "/app-"+com.inca.ota.example.host.BuildConfig.BUILD_TYPE+"-patch_signed_7zip.apk");
//download分支的代码:通过调用云api下载升级文件.
HostUpgrade.get().getNew(HostUpgrade.VERSION_TYPE_APP, MainActivity.this, new UpgradeCallback() {
@Override
public void onFinished() {
MainActivity.this.runOnUiThread(new Runnable() {
@Override
public void run() {
mBtnStartUpgrade.setEnabled(true);
}
});
}
public void onError() {
MainActivity.this.runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(MainActivity.this,"download error ...", Toast.LENGTH_LONG).show();
mBtnStartUpgrade.setEnabled(true);
}
});
}
});
//请求存储读写权限,Android11以上和旧版本有些不同,具体参考示例代码。
if(!hasRequiredPermissions()) {
requestForStoragePermissions();
}
```
- 10. 升级下载安装的进度状态,参考`MainActivity`中的实现。
```java
//升级文件下载时的进度状态显示函数
@Override
public void onDownloadProgress(int versionType ,long currentOffset, long totalLength) {
}
//热补丁安装的进度状态显示函数
@Subscribe(threadMode = ThreadMode.MAIN)
public void onPatchReslutEvent(PatchResultEvent event){
Log.i(TAG,"onPatchReslutEvent ...");
if (mProgressInstallPatch.isShowing()) {
mProgressInstallPatch.dismiss();
}
}
@Override
protected void onStart() {
super.onStart();
EventBus.getDefault().register(this);
}
@Override
protected void onStop() {
super.onStop();
EventBus.getDefault().unregister(this);
}
```