美文网首页Flutter圈子FlutterFlutter中文社区
【Android开发学Flutter】APP检查更新

【Android开发学Flutter】APP检查更新

作者: JairusTse | 来源:发表于2019-12-10 18:44 被阅读0次

APP检查更新基本上是所有APP必备的功能,除了应用市场的提示更新之外,我们还希望APP有应用内检查更新功能,方便第一时间通知用户。

Android的应用内检查更新可以使用第三方平台的检查更新服务,比如友盟和bugly,可以减轻自己服务器的压力;当然也可以把安装包放在自己的服务器上,然后通过接口检查更新;iOS跳转到App Store下载更新,如果是企业版,需要通过plist文件下载更新包。

下面就说一下Flutter下,安装包放在自己服务器上,APP检查更新的具体实现。

【Android】

  • APP通过接口检查更新
  • 下载安装包
  • 安装

【iOS】

  • APP通过接口检查更新
  • 打开App Store或者打开下载plist网页
  • 用户手动下载安装

APP检查更新

首先,android更新包apk,iOS(企业版)更新包ipa和配置文件plist都要上传到我们自己的服务器中,并且提供检查更新的接口。

plist文件:plist文件的下载地址必须是HTTPS协议,文件内容如下,ipa下载地址包名版本号APP名称需要改成自己的:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>items</key>
    <array>
        <dict>
            <key>assets</key>
            <array>
                <dict>
                    <key>kind</key>
                    <string>software-package</string>
                    <key>url</key>
                    <string>自己的ipa下载地址</string>
                </dict>
            </array>
            <key>metadata</key>
            <dict>
                <key>bundle-identifier</key>
                <string>包名</string>
                <key>bundle-version</key>
                <string>版本号</string>
                <key>kind</key>
                <string>software</string>
                <key>title</key>
                <string>APP名称</string>
            </dict>
        </dict>
    </array>
</dict>
</plist>

plist文件的下载网页(itms-services:///?action=download-manifest&url=是固定写法):

<!DOCTYPE HTML>
<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
        <title>Install</title>
    </head>
    <body>
        <p align=center>
          <font size="10">
            <a style="color:#69DEDA" href="itms-services:///?action=download-manifest&url=https://自己的plist文件下载地址">点击安装</a>
          </font>
        </p>
    </body>
</html>

接口的设计如下(可自行设计):
APP上传参数:当前版本号手机系统

    /// 版本号
    PackageInfo.fromPlatform().then((PackageInfo packageInfo) {
      buildNumber = packageInfo.buildNumber;
    });
    
    /// 手机系统
    if (Platform.isIOS) {
      /// iOS
    } else {
      /// Android
    }

接口返回参数:是否有新版本是否强制更新下载地址更新提示内容

class UpdateData {
    String isUpload; ///是否有新版本
    String url; ///下载地址
    String remark; ///更新提示内容
    String isForceUpdate; ///是否强制更新
}

下载地址根据实际情况,返回apk下载地址App Store下载地址plist文件下载网页 其中一个
强制更新非必须,一般的处理就是更新Dialog只有确定按钮,并且点击屏幕其他位置不可取消。

下载安装包

  • Android直接下载apk,可以使用Dio的下载文件或者用第三方的下载文件库,我这里是直接用Dio下载;
  /// 下载文件
  Future<bool> downloadFile({
    @required String url,
    @required String path,
    @required onProgress(int count, int total),
    @required onError(int code, String message)
  }) async {
    try {
      // 必须加上 否则download 报 can not open file
      File file = new File(path);
      file.create(recursive: true);

      print("--------- 开始下载 ---------");
      await dio.download(url, path, onReceiveProgress: (int count, int total) {
        //进度
        print("$count $total");
        onProgress(count, total);
      });
      print('--------- 下载成功 ---------');
      return true;
    } catch (e) {
      onError(Constants.codeNetError, e.toString());
      return false;
    }
  }
  • 企业版iOS用url_launcher打开plist文件的下载网页,用户手动点击下载;
  _launchURL(String url) async {
    if (await canLaunch(url)) {
      await launch(url);
    } else {
      throw 'Could not launch $url';
    }
  }
  • 非企业版iOS用url_launcher打开App Store
if (Platform.isIOS) {
  final url = "https://itunes.apple.com/cn/app/id1380512641"; // id 后面的数字换成自己的应用 id 就行了
  if (await canLaunch(url)) {
    await launch(url, forceSafariVC: false);
  } else {
    throw 'Could not launch $url';
  }
}

安装

这一步只有Android需要,下载成功后Flutter调用Android原生方法安装apk。

flutter代码(Channel名可自定义,不过要保持一致):
  // 调用android代码安装apk
  void _installApk(String path) {
    const platform = MethodChannel("包名/update");
    try {
      // 调用app地址
      platform.invokeMethod('install', {"bundleId": packageName, "path": path});
    } on PlatformException catch (_) {}
  }
Android MainActivity.java
public class MainActivity extends FlutterActivity {

    //渠道名
    private static final String CHANNEL = "包名/update";//渠道名

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        GeneratedPluginRegistrant.registerWith(this);
        MethodChannel channel = new MethodChannel(getFlutterView(), CHANNEL);//获取渠道
        channel.setMethodCallHandler(this::handleMethod);//设置方法监听
    }

    /**
     * 处理方法回调监听
     *
     * @param methodCall 方法的参数相关
     * @param result     方法的返回值相关
     */
    private void handleMethod(MethodCall methodCall, MethodChannel.Result result) {
        switch (methodCall.method) {//根据方法名进行处理
            case "install":
                String path = methodCall.argument("path");
                String bundleId = methodCall.argument("bundleId");
                installApk(path, bundleId);//具体处理
                break;
            default:
                result.notImplemented();
        }
    }

    /**
     * 安装apk
     */
    private void installApk(String filePath, String bundleId) {
        File file = new File(filePath);
        Uri fileUri = FileProvider.getUriForFile(MainActivity.this,
                bundleId + ".my.fileprovider", file);
        Intent it = new Intent();
        it.setAction(Intent.ACTION_VIEW);
        it.setDataAndType(fileUri, "application/vnd.android.package-archive");
        it.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
        it.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);// 防止打不开应用
        MainActivity.this.startActivity(it);
    }
}
AndroidManifest.xml
    <uses-permission android:name="android.permission.INTERNET"/>
    <uses-permission android:name="android.permission.CAMERA" />
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.RECORD_AUDIO" />
    <uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />

    <application
        android:name=".MyApplication"
        ...>
        
        <provider
            android:name=".MyFileProvider"
            android:authorities="${applicationId}.my.fileprovider"
            android:exported="false"
            android:grantUriPermissions="true">
            <meta-data
                android:name="android.support.FILE_PROVIDER_PATHS"
                android:resource="@xml/file_path"/>
        </provider>

    </application>

新增MyApplication.java 文件:
public class MyApplication extends FlutterApplication implements PluginRegistry.PluginRegistrantCallback {
    @Override
    public void registerWith(PluginRegistry registry) {
        //
        // Integration note:
        //
        // In Flutter, in order to work in background isolate, plugins need to register with
        // a special instance of `FlutterEngine` that serves for background execution only.
        // Hence, all (and only) plugins that require background execution feature need to
        // call `registerWith` in this method.
        //
        // The default `GeneratedPluginRegistrant` will call `registerWith` of all plugins
        // integrated in your application. Hence, if you are using `FlutterDownloaderPlugin`
        // along with other plugins that need UI manipulation, you should register
        // `FlutterDownloaderPlugin` and any 'background' plugins explicitly like this:
        //
        // FlutterDownloaderPlugin.registerWith(registry.registrarFor("vn.hunghd.flutterdownloader.FlutterDownloaderPlugin"));
        //
        GeneratedPluginRegistrant.registerWith(registry);
    }
}
res/xml目录下新增file_path.xml 文件:
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
    <root-path
        name="root"
        path="" />

    <files-path
        name="files"
        path="" />

    <cache-path
        name="cache"
        path="" />

    <external-path
        name="external"
        path="" />

    <external-files-path
        name="external_file_path"
        path="" />

    <external-cache-path
        name="external_cache_path"
        path="" />

</paths>

最后GitHub传送门:FlutterDemo

相关文章

  • 【Android开发学Flutter】APP检查更新

    APP检查更新基本上是所有APP必备的功能,除了应用市场的提示更新之外,我们还希望APP有应用内检查更新功能,方便...

  • flutter app检查更新

    处理逻辑在首页通过接口获取最新版本号和应用本地版本号比对,不一样则弹框提示是否更新( 强制更新和非强制更新), 苹...

  • Flutter开发实现APP检查更新功能

    一、前言 在Flutter项目开发过程中,需要实现App检查更新功能,因为iOS端只需要判断APP版本,然后跳转至...

  • Android开发 学习Flutter 入门

    Android开发 学习Flutter 入门 一、前言 最近项目准备用Flutter混合开发,写写学习的笔记吧,学...

  • 搭建Flutter开发环境

    公司项目需要,要用 Flutter 开发 APP,原来的开发环境是Mac + Android Studio,下面记...

  • AndroidGo

    AndroidGo github 地址 简介: Android、Flutter 开发者帮助 APP。包含事件分发、...

  • OAuth 同意屏幕

    flutter或者Android app开发,google登录OAuth 同意屏幕; 主页参考下面格式https:...

  • 使用 Flutter 开发 macOS App

    使用 Flutter 开发 macOS App 使用 Flutter 开发 macOS App

  • Android自动更新的实现

    Android自动更新的实现 需求: 在我们开发App的过程中,总会遇到这样的需求。在App运行时检查是否有新版本...

  • Android开发者的Flutter入门(二)

    前言 上篇文章Android开发者的Flutter入门(一)讲解了用Flutter开发一个简单的新闻app的大体流...

网友评论

    本文标题:【Android开发学Flutter】APP检查更新

    本文链接:https://www.haomeiwen.com/subject/cguggctx.html