陈桥驿站 陈桥驿站

ReactNative Android 版本升级

--> React Native 阅读 ( 247 ) 文章转载请注明来源!

前言

最近ReactNative开发遇见一个棘手的问题,就是App的更新比较频繁,刚开始想的很简单,直接WebView套下载链接,最后发现Android平台下的WebView不能下载文件,但是iOS可以用.plist指向.ipa进行更新。
所以这里提供一下我解决问题的方案。

方案一 Native WebBrowser

既然WebView不好使,那么这个地址直接放到浏览器里面能直接下载apk。为什么不直接让Android设备打开本地浏览器呢。
于是乎:

if(this.state.showDialog) {
  if(nowVersions!= no) {
    Alert.alert('请更新至最新版本', message, [
    {
      text: '立即更新', onPress: ()=>{
        if(Platform.OS == 'ios') {
          this.props.navigation.navigate('Web', {'url': url, 'title': '版本升级'})
        }
        else if(Platform.OS == 'android') {
          var apk = 'http://www.iPhone6s.cn:31416/app/QiukuiAppClient' + no + '.apk';
          Linking.canOpenURL(apk).then(supported => {
            if (!supported) {
              
            } else {
              return Linking.openURL(apk);
            }
          }).catch((error) => {
            
          });
        }
      }
    },
    {
      text: '下次再说', onPress: ()=>{
    }}
  ]);
  this.setState({
    showDialog: false
  })}
}

看着貌似没问题,刚开始模拟器运行的时候也挺好,有一个Toast显示正在下载,但是过了几秒钟,浏览器停止运行
看了看系统日志想起来了。
下载文件是一个耗时操作,如果原生的Android开发需要启一个新的线程,然后通知主线程。但是ReactNative里面没有线程的概念,有一个异步async鸡肋的不要不要的。因此采用方案二。

方案二 ReactNative install apk + ReactNative fs

既然不能直接通过浏览器下载,那么我先把apk下载到本地,然后安装行不行。

npm install react-native-install-apk

npm install react-native-install-apk --save
react-native link react-native-install-apk

配置

AndroidManifest.xml

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    package="com.qiukuiappclient">

    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.CAMERA" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.CALL_PHONE" />

    <application tools:replace="android:allowBackup"
      android:name=".MainApplication"
      android:label="@string/app_name"
      android:icon="@mipmap/ic_launcher"
      android:allowBackup="false"
      android:theme="@style/AppTheme">
      <activity
        android:name=".MainActivity"
        android:label="@string/app_name"
        android:configChanges="keyboard|keyboardHidden|orientation|screenSize"
        android:windowSoftInputMode="adjustResize">
        <intent-filter>
            <action android:name="android.intent.action.MAIN" />
            <category android:name="android.intent.category.LAUNCHER" />
        </intent-filter>
      </activity>
      <activity android:name="com.facebook.react.devsupport.DevSettingsActivity" />
    </application>

</manifest>

InstallApkPackager.java

package com.heyao216.react_native_installapk;

import com.facebook.react.ReactPackage;
import com.facebook.react.bridge.JavaScriptModule;
import com.facebook.react.bridge.NativeModule;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.uimanager.ViewManager;

import java.util.Arrays;
import java.util.Collections;
import java.util.List;

/**
 * Created by heyao on 2016/11/4.
 */
public class InstallApkPackager implements ReactPackage{
    @Override
    public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {
        return Arrays.<NativeModule>asList(new InstallApkModule(reactContext));
    }

    public List<Class<? extends JavaScriptModule>> createJSModules() {
        return Collections.emptyList();
    }

    @Override
    public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
        return Collections.emptyList();
    }
}

npm install react-native-fs@2.11.17

使用

import {NativeModules} from 'react-native';
import DialogProgress from 'react-native-dialog-progress';
import RNFS from 'react-native-fs';
if(this.state.showDialog) {
  if(nowVersions!= no) {
    Alert.alert('请更新至最新版本', message, [
    {
      text: '立即更新', onPress: ()=>{
        if(Platform.OS == 'ios') {
          this.props.navigation.navigate('Web', {'url': url, 'title': '版本升级'})
        }
        else if(Platform.OS == 'android') {
          var apk = 'http://www.iPhone6s.cn:31416/app/QiukuiAppClient' + no + '.apk';
          // Linking.canOpenURL(apk).then(supported => {
          //   if (!supported) {
              
          //   } else {
          //     return Linking.openURL(apk);
          //   }
          // }).catch((error) => {
            
          // });
          var filePath = RNFS.DocumentDirectoryPath + '/QiukuiAppClient' + no + '.apk';
          DialogProgress.show(options);
          var download = RNFS.downloadFile({
              fromUrl: apk,
              toFile: filePath,
              progress: res => {
                  if(res.bytesWritten == res.contentLength) {
                    DialogProgress.hide();
                  }
              },
              progressDivider: 1
          });
          download.promise.then(result => {
              if(result.statusCode == 200){
                  NativeModules.InstallApk.install(filePath);
              }
          });
        }
      }
    },
    {
      text: '下次再说', onPress: ()=>{
    }}
  ]);
  this.setState({
    showDialog: false
  })}
}

Android7.0以上兼容处理

package com.heyao216.react_native_installapk;

import android.content.Intent;
import android.net.Uri;
import android.os.Build;

import androidx.core.content.FileProvider;

import com.facebook.react.bridge.Callback;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;

import java.io.File;

/**
 * Created by heyao on 2016/11/4.
 */
public class InstallApkModule extends ReactContextBaseJavaModule {
    private ReactApplicationContext _context = null;

    public InstallApkModule(ReactApplicationContext reactContext) {
        super(reactContext);
        _context = reactContext;
    }

    @Override
    public String getName() {
        return "InstallApk";
    }

    private Uri file2Url(File file) {
        return Build.VERSION.SDK_INT >= Build.VERSION_CODES.N ? 
                FileProvider.getUriForFile(_context, "net.cctv3.ZhonglianNewspaper.FileProvider", file) 
                : Uri.fromFile(file);
    }

    @ReactMethod
    public void install(String path) {
        String cmd = "chmod 777 " + path;
        try {
            Runtime.getRuntime().exec(cmd);
        } catch (Exception e) {
            e.printStackTrace();
        }
        Intent intent = new Intent(Intent.ACTION_VIEW);
        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        Uri uri = file2Url(new File(path));
        intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
        intent.setDataAndType(uri, "application/vnd.android.package-archive");
        _context.startActivity(intent);
    }
}
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    package="com.qiukuiappclient">

    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.CAMERA" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.CALL_PHONE" />
    <uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />

    <application
        android:name=".MainApplication"
        android:allowBackup="false"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme"
        tools:replace="android:allowBackup">
        <provider
            android:name="android.support.v4.content.FileProvider"
            android:authorities="com.qiukuiappclient.fileprovider"
            android:exported="false"
            android:grantUriPermissions="true">
            <meta-data
                android:name="android.support.FILE_PROVIDER_PATHS"
                android:resource="@xml/file_paths" />
        </provider>

        <activity
            android:name=".MainActivity"
            android:configChanges="keyboard|keyboardHidden|orientation|screenSize"
            android:label="@string/app_name"
            android:windowSoftInputMode="adjustResize">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <activity android:name="com.facebook.react.devsupport.DevSettingsActivity" />
    </application>

</manifest>
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <files-path name="name" path="" />
    <!--</paths>-->
</resources>

本文基于《署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0)》许可协议授权
文章链接:http://www.cctv3.net/archives/rnAppUpdate.html (转载时请注明本文出处及文章链接)

React Native
发表新评论
雷姆
拉姆