提交 fe22e068 authored 作者: vipcxj's avatar vipcxj

add: 增加更新api

improve:完善文档
上级 f7c1a392
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="EntryPointsManager">
<list size="1">
<item index="0" class="java.lang.String" itemvalue="com.facebook.react.bridge.ReactMethod" />
</list>
</component>
<component name="NullableNotNullManager">
<option name="myDefaultNullable" value="android.support.annotation.Nullable" />
<option name="myDefaultNotNull" value="android.support.annotation.NonNull" />
......@@ -24,7 +29,7 @@
</value>
</option>
</component>
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_7" project-jdk-name="1.8" project-jdk-type="JavaSDK">
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" project-jdk-name="1.8" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/build/classes" />
</component>
<component name="ProjectType">
......
......@@ -8,11 +8,11 @@
</facet>
<facet type="android" name="Android">
<configuration>
<option name="SELECTED_BUILD_VARIANT" value="devDebug" />
<option name="ASSEMBLE_TASK_NAME" value="assembleDevDebug" />
<option name="COMPILE_JAVA_TASK_NAME" value="compileDevDebugSources" />
<option name="SELECTED_BUILD_VARIANT" value="debug" />
<option name="ASSEMBLE_TASK_NAME" value="assembleDebug" />
<option name="COMPILE_JAVA_TASK_NAME" value="compileDebugSources" />
<afterSyncTasks>
<task>generateDevDebugSources</task>
<task>generateDebugSources</task>
</afterSyncTasks>
<option name="ALLOW_USER_CONFIGURATION" value="false" />
<option name="MANIFEST_FILE_RELATIVE_PATH" value="/src/main/AndroidManifest.xml" />
......@@ -23,59 +23,24 @@
</facet>
</component>
<component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_1_7">
<output url="file://$MODULE_DIR$/build/intermediates/classes/dev/debug" />
<output-test url="file://$MODULE_DIR$/build/intermediates/classes/test/dev/debug" />
<output url="file://$MODULE_DIR$/build/intermediates/classes/debug" />
<output-test url="file://$MODULE_DIR$/build/intermediates/classes/test/debug" />
<exclude-output />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/r/dev/debug" isTestSource="false" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/aidl/dev/debug" isTestSource="false" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/buildConfig/dev/debug" isTestSource="false" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/rs/dev/debug" isTestSource="false" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/apt/dev/debug" isTestSource="false" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/res/rs/dev/debug" type="java-resource" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/res/resValues/dev/debug" type="java-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/devDebug/res" type="java-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/devDebug/resources" type="java-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/devDebug/assets" type="java-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/devDebug/aidl" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/devDebug/java" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/devDebug/rs" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/devDebug/shaders" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/r/androidTest/dev/debug" isTestSource="true" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/aidl/androidTest/dev/debug" isTestSource="true" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/buildConfig/androidTest/dev/debug" isTestSource="true" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/rs/androidTest/dev/debug" isTestSource="true" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/apt/androidTest/dev/debug" isTestSource="true" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/res/rs/androidTest/dev/debug" type="java-test-resource" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/res/resValues/androidTest/dev/debug" type="java-test-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/testDevDebug/res" type="java-test-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/testDevDebug/resources" type="java-test-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/testDevDebug/assets" type="java-test-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/testDevDebug/aidl" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/testDevDebug/java" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/testDevDebug/rs" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/testDevDebug/shaders" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/dev/res" type="java-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/dev/resources" type="java-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/dev/assets" type="java-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/dev/aidl" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/dev/java" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/dev/rs" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/dev/shaders" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/androidTestDev/res" type="java-test-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/androidTestDev/resources" type="java-test-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/androidTestDev/assets" type="java-test-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/androidTestDev/aidl" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/androidTestDev/java" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/androidTestDev/rs" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/androidTestDev/shaders" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/testDev/res" type="java-test-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/testDev/resources" type="java-test-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/testDev/assets" type="java-test-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/testDev/aidl" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/testDev/java" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/testDev/rs" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/testDev/shaders" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/r/debug" isTestSource="false" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/aidl/debug" isTestSource="false" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/buildConfig/debug" isTestSource="false" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/rs/debug" isTestSource="false" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/apt/debug" isTestSource="false" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/res/rs/debug" type="java-resource" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/res/resValues/debug" type="java-resource" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/r/androidTest/debug" isTestSource="true" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/aidl/androidTest/debug" isTestSource="true" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/buildConfig/androidTest/debug" isTestSource="true" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/rs/androidTest/debug" isTestSource="true" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/apt/androidTest/debug" isTestSource="true" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/res/rs/androidTest/debug" type="java-test-resource" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/res/resValues/androidTest/debug" type="java-test-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/debug/res" type="java-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/debug/resources" type="java-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/debug/assets" type="java-resource" />
......@@ -130,11 +95,12 @@
</content>
<orderEntry type="jdk" jdkName="Android API 25 Platform" jdkType="Android SDK" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" exported="" name="com.google.android.gms:play-services-tasks-license-11.6.0" level="project" />
<orderEntry type="library" exported="" name="com.android.support:multidex-1.0.1" level="project" />
<orderEntry type="library" exported="" name="com.google.android.gms:play-services-tasks-license-11.6.0" level="project" />
<orderEntry type="library" exported="" name="com.facebook.fresco:imagepipeline-okhttp3-1.3.0" level="project" />
<orderEntry type="library" exported="" name="__local_jars__:/Users/yaohx_169/Develop/Project/React-Native/AwesomeProject/android/app/libs/HSProtAPI.jar:unspecified@jar" level="project" />
<orderEntry type="library" exported="" name="com.google.android.gms:play-services-gcm-license-11.6.0" level="project" />
<orderEntry type="library" exported="" scope="TEST" name="com.android.support:multidex-instrumentation-1.0.1" level="project" />
<orderEntry type="library" exported="" name="__local_jars__:/Users/yaohx_169/Develop/Project/React-Native/AwesomeProject/android/app/libs/gson-2.2.4-sources.jar:unspecified@jar" level="project" />
<orderEntry type="library" exported="" name="com.google.android.gms:play-services-base-license-11.6.0" level="project" />
<orderEntry type="library" exported="" name="javax.inject:javax.inject:1@jar" level="project" />
......
package com.bolan.android.modules;
import com.bolan.android.modules.update.Updater;
import com.facebook.react.ReactPackage;
import com.facebook.react.bridge.NativeModule;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.uimanager.ViewManager;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class UpdaterReactPackage implements ReactPackage {
@Override
public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {
List<NativeModule> modules = new ArrayList<>();
modules.add(new Updater(reactContext));
return modules;
}
@Override
public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
return Collections.emptyList();
}
}
package com.bolan.android.modules;
import android.content.Context;
import android.os.Environment;
import java.io.File;
public class Utils {
public static File getDiskCacheDir(Context context) {
File cacheFile;
if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState()) || !Environment.isExternalStorageRemovable()) {
cacheFile = context.getExternalCacheDir();
} else {
cacheFile = context.getCacheDir();
}
return cacheFile;
}
}
package com.bolan.android.modules.update;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.util.Log;
import com.bolan.android.modules.Utils;
import com.facebook.react.bridge.Callback;
import com.facebook.react.bridge.Promise;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
@SuppressWarnings("unused")
public class Updater extends ReactContextBaseJavaModule {
private static volatile boolean updating = false;
private static volatile boolean cancel = false;
public Updater(ReactApplicationContext reactContext) {
super(reactContext);
}
@Override
public String getName() {
return "Updater";
}
@ReactMethod
public synchronized void update(final String sUrl, final Callback cb, final Promise promise) {
if (!updating) {
updating = true;
cancel = false;
new Thread(new Runnable() {
@Override
public void run() {
InputStream is = null;
FileOutputStream fos = null;
File cacheFile = Utils.getDiskCacheDir(getCurrentActivity());
File downloadFile = new File(cacheFile, "download");
File apkFile = new File(downloadFile, "bolan.apk");
try {
if (!downloadFile.exists()) {
if (!downloadFile.mkdir()) {
promise.reject("ERROR_UPDATE", "Unable to create the download directory.");
return;
}
}
File[] files = downloadFile.listFiles();
for (File file : files) {
if (!file.delete()) {
Log.w("Updater", "Unable to delete the old downloaded file: " + file.getAbsolutePath());
}
}
URL url = new URL(sUrl);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.connect();
int length = connection.getContentLength();
is = connection.getInputStream();
fos = new FileOutputStream(apkFile);
int count = 0;
byte buf[] = new byte[1024 * 64];
do {
int numRead = is.read(buf);
count += numRead;
int progress = (int) (((float) count / length) * 100);
cb.invoke("downloading", progress, count, length);
if (numRead <= 0) {
cb.invoke("downloaded", progress, count, length);
break;
}
fos.write(buf, 0, numRead);
} while (!cancel);
if (cancel) {
return;
}
Intent i = new Intent(Intent.ACTION_VIEW);
i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
i.setDataAndType(Uri.parse("file://" + apkFile.toString()), "application/vnd.android.package-archive");
Context context = getCurrentActivity();
if (context == null) {
promise.reject("ERROR_UPDATE", "Unable to get the context.");
return;
}
getCurrentActivity().startActivity(i);
android.os.Process.killProcess(android.os.Process.myPid());
promise.resolve(null);
} catch (MalformedURLException e) {
promise.reject("ERROR_UPDATE", "invalid url: " + sUrl);
} catch (Exception e) {
promise.reject(e);
} finally {
cancel = false;
updating = false;
if (fos != null) {
try {
fos.close();
} catch (IOException e) {
Log.e("Updater", "Unable to close file stream.", e);
}
}
if (is != null) {
try {
is.close();
} catch (IOException e) {
Log.e("Updater", "Unable to close url stream.", e);
}
}
}
}
});
} else {
promise.reject("ERROR_UPDATE", "There is another updater worker running already.");
}
}
public synchronized void cancel(final Promise promise) {
cancel = true;
promise.resolve(null);
}
}
......@@ -5,6 +5,7 @@ import android.content.Context;
import android.support.multidex.MultiDex;
import com.bolan.android.modules.IDCardReactPackage;
import com.bolan.android.modules.UpdaterReactPackage;
import com.facebook.react.ReactApplication;
import com.microsoft.codepush.react.CodePush;
import com.learnium.RNDeviceInfo.RNDeviceInfo;
......@@ -36,7 +37,8 @@ public class MainApplication extends Application implements ReactApplication {
new MainReactPackage(),
new CodePush(BuildConfig.CODEPUSH_KEY, getApplicationContext(), BuildConfig.DEBUG, "http://192.168.1.2:3000"),
new RNDeviceInfo(),
new IDCardReactPackage()
new IDCardReactPackage(),
new UpdaterReactPackage()
);
}
......
/** @module native/Updater */
import { NativeModules } from 'react-native';
const { Updater } = NativeModules;
/**
* @callback UpdateCallback
* @param {string} status 可能值为downloading(下载中)和downloaded(下载完成)
* @param {number} progress 下载进度,0到100的整数
* @param {number} count 已下载字节数
* @param {number} length 总字节数
*/
/**
* 下载更新包并自动安装
* @param {string} url 更新地址
* @param {UpdateCallback} cb 更新回调
* @returns {Promise.<null>}
*/
export const update = async (url, cb) => {
return Updater.update(url, cb);
};
/**
* 取消当前更新进程
* @returns {Promise.<void>}
*/
export const cancel = async () => {
return Updater.cancel();
};
/** @module services/update */
import DeviceInfo from 'react-native-device-info';
import post from '../utils/post';
import config from '../utils/config';
/**
* @typedef {Object} DeploymentInfo 部署信息
* @property {number} id 部署id
* @property {string} versionNumber 版本号
* @property {string} description 描述,一般为更新说明
* @property {Date} updateTime 部署发布时间
* @property {string} uri 资源内部uri,表示app安装包文件的内部定位地址
* @property {string} status 部署状态,可能值为release,development,broken。release表示公开版本,development表示内部测试版,broken表示有重大bug,短时间不能解决,需要回滚
*/
/**
* @typedef {Object} VersionCheck 升级检查结果
* @property {string} action 可能值分别为update(可更新),rollback(需回滚),upToDate(已经是最新版,无需更新)
* @property {DeploymentInfo} deploymentInfo 需更新或回滚的部署包信息
*/
/**
* 升级检查
* @returns {Promise.<RestResponse<VersionCheck>>}
*/
export const checkUpdate = async () => {
return post(`${config.apiContextPath}/api/app/user/apps/check`, { name: config.productId, version: DeviceInfo.getVersion() });
};
import _ from 'lodash';
import { Resolver } from 'fastjson_ref_resolver';
/**
* @global
* @typedef {Object} RestResponse
* @template T
* @property {number} errorCode 错误码,0表示成功
* @property {T} data 数据
*/
export function checkStatus(response) {
if (response.status >= 200 && response.status < 300) {
return response;
......
/** @module utils/post */
/* eslint-disable no-param-reassign */
import _ from 'lodash';
import { fetch } from './polyfill';
......@@ -13,7 +14,15 @@ const defaultOptions = {
},
};
/**
* post方法
* @param {string} url 地址
* @param {Object} data body中的数据
* @param {Object} params
* @param {Object} options
* @param {boolean} auth
* @returns {Promise.<*>}
*/
export default async function post(url, data, params = {}, options = {}, auth = true) {
if (!data) {
data = {};
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论