Flutter—Android混合开发之下载安装的实现

前言

该功能已加入Bedrock快速开发框架,连接:
复制代码

github.com/bladeofgod/…java

框架介绍:
复制代码

Bedrock——基于MVVM+Provider的Flutter快速开发框架android

后语

通常应用的更新方式:
    IOS端是直接跳转app store
    Android端跳转应用市场,或者从服务器下载自行安装。
咱们这里实现一下Android的下载安装功能(其实我不会IOS)
复制代码

简介

如今flutter也有一些下载和安装的插件:ios

flutter_downloader

install_plugin
复制代码

可是我了解了一下,不是与第三方插件冲突,要么就是好久不更新了,再适配修复的话还不如本身写一个,并且也更灵活。git

Flutter端

咱们须要的插件有:github

path_provider用来获取存储路径
dio用来下载你的安装包。
permission_handler 用来请求权限
复制代码

权限

首先咱们要申请权限(安卓端须要进行声明,这个在 Android端 说):bash

checkPermission()async{
    Map<Permission,PermissionStatus> permissions =
    await [
      Permission.storage,
    ].request();
    if(permissions.values.first.isGranted){
      downloadAPK();

    }else{
      showToast('permissions denied');
    }

  }
复制代码

下载

dio的download功能很是好用,具体使用方法以下:服务器

downloadAPK()async{
    try{
      await dio.download(url, getSavePath(),
          cancelToken:cancelToken,
          onReceiveProgress: (receive,total){
            //debugPrint('apk info $receive $total');
            setProgress((receive/total *100).toStringAsFixed(1));
          } ).then((response){
            if(response.statusCode == 200){
              installAPK();
            }
      });
    }catch(e,s){
      ///若是经过cancelToken取消任务,那么会抛出一个dio exception . cancel
      ///你能够对它进行捕捉并操做,也能够啥都不作
    }
  }
复制代码

参数简介:app

url是下载的路径框架

cancelToken可选参数,能够用来取消下载任务async

onReceiveProgress 是一个回调,receive是当前下载的大小,total是整个资源的大小

getSavePath() 提供一个保存路径,代码以下:

String getSavePath(){
    String path = StorageManager.externalDirectory.path + '/test/bedrock.apk';
    return path;
  }
复制代码

当咱们下载完成后,即可以进行安装步骤了,这部分须要原生端配合来实现,咱们先把拉起原生端的Channel实现。

channel 有三个,这里作一下介绍,有兴趣的能够去百度:

 BasicMessageChannel:用于传递字符串和半结构化的信息,这个用的比较少

 MethodChannel:用于传递方法调用(method invocation)一般用来调用native中某个方法

 EventChannel: 用于数据流(event streams)的通讯。有监听功能,好比电量变化以后直接推送数据给flutter端。
复制代码

这里咱们要用到method_channel用以和原生端进行通行:

先造一个channel:

static const MethodChannel _channel = const MethodChannel('com.lijiaqi.bedrock');
构造函数传一个名字进去,具体随意起,但要保证与原生端一致。
复制代码

再定义一个方法名:

static final String methodInstall = 'install_apk'; //这个一样要与原生端保持一致
复制代码

以后咱们就能够拉起原生端对应的方法了:

//path是apk的安装路径
  void installApk(String path)async{
    ///ios建议直接取应用市场
    if(Platform.isAndroid){
      await _channel.invokeMethod(methodInstall,
          {"path":path});
    }
  }
复制代码

下面实现安卓端

Android端

用Android studio打开项目下的android,依次打开:
app-src-main-java/kotlin 以下图:
复制代码

我没学kotlin,因此以java来写,二者差很少。
复制代码

咱们在java文件夹下新建一个包,名字你随意,我这里叫 com.lijiaqi.bedrock

再依次新建:

Mainactivity    由于项目默认是kotlin,因此我得新建一个这个

plugin/BedrockPlugin 用于处理flutter的请求
复制代码

MainActivity

import com.lijiaqi.bedrock.plugin.BedrockPlugin;

import io.flutter.embedding.android.FlutterActivity;
import io.flutter.embedding.engine.FlutterEngine;
import io.flutter.plugins.GeneratedPluginRegistrant;

public class MainActivity extends FlutterActivity {
    @Override
    public void configureFlutterEngine(@NonNull FlutterEngine flutterEngine) {
        GeneratedPluginRegistrant.registerWith(flutterEngine);
        flutterEngine.getPlugins().add(new BedrockPlugin(this));
    }
}
复制代码

经过:

flutterEngine.getPlugins().add(new BedrockPlugin(this));
复制代码

咱们能够对咱们的BedrockPlugin进行注册,其代码以下

BedrockPlugin,代码比较多,我把注解写在代码里,方便对应

public class BedrockPlugin implements FlutterPlugin, ActivityAware, MethodChannel.MethodCallHandler {
    
    ///这里要与flutter端的一致
    private static final String PLUGIN_NAME = "com.lijiaqi.bedrock";
    
    ///channel
    private MethodChannel mMethodChannel;
    
    private Application mApplication;
    private WeakReference<Activity> mActivity;

    public BedrockPlugin(Activity mActivity) {
        this.mActivity = new WeakReference<Activity>(mActivity);
    }

    @Override
    public void onAttachedToEngine(@NonNull FlutterPluginBinding binding) {
        ///这里咱们进行channel的初始化
        mMethodChannel = new MethodChannel(binding.getBinaryMessenger(),PLUGIN_NAME);
        mApplication = (Application) binding.getApplicationContext();
        mMethodChannel.setMethodCallHandler(this);

    }

    @Override
    public void onDetachedFromEngine(@NonNull FlutterPluginBinding binding) {
        ///对channel进行释放
        mMethodChannel.setMethodCallHandler(null);
        mMethodChannel = null;

    }

    ///这里是处理flutter请求的地方了
    
    @Override
    public void onMethodCall(@NonNull MethodCall call, @NonNull MethodChannel.Result result) {
        log("call");
        switch (call.method){
        ///经过call.method能够得到flutter端调用的那个方法的名字,必定要两端一致
        ///这里咱们知道flutter 调用的名字是:install_apk
            case "install_apk":
            ///经过call.argument按键path 就能够取到对应的值
            ///开始调用安装
                invokeInstall(call.argument("path"));
                break;
            default:
                break;
        }

    }
    
    ///下面单独说一下
    private static final String provider = "com.lijiaqi.flutter_bedrock.fileProvider";

    //安装
    //安卓7.0之前是能够直接经过路径建立File进行安装的,
    //可是以后的则须要fileProvider来协助安装(这个东西也能够应用之间共享文件,跨进程的)
    private void invokeInstall(String apkPath){
        if(apkPath != null && ! apkPath.isEmpty()){
            log(apkPath);
            Intent intent = new Intent(Intent.ACTION_VIEW);
            intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
            ///< 判断是不是AndroidN以及更高的版本
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
                // 不能再用setFlags了, setflags会重置以前的设置, 要么 setflags 多个|拼接,要么addflag
                intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
                Uri contentUri = FileProvider.getUriForFile(mActivity.get(), provider, new File(apkPath));
                intent.setDataAndType(contentUri, "application/vnd.android.package-archive");
            } else {
                intent.setDataAndType(Uri.fromFile(new File(apkPath)), "application/vnd.android.package-archive");
            }
            mActivity.get().startActivity(intent);

        }
    }

    private void log(String msg){
        Log.i("native method",msg);
    }
    
    ///这如下的暂时不用管

    @Override
    public void onAttachedToActivity(@NonNull ActivityPluginBinding binding) {

    }

    @Override
    public void onDetachedFromActivityForConfigChanges() {

    }

    @Override
    public void onReattachedToActivityForConfigChanges(@NonNull ActivityPluginBinding binding) {

    }

    @Override
    public void onDetachedFromActivity() {

    }

}
复制代码

以上方法就能够进行安装页面的拉起,可是在运行以前咱们还须要进行相应的权限配置,在上面的目录结构图中找到AndroidManifest.xml,打开添加以下权限:

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
复制代码

若是你跟我同样也是用java,且项目默认是kotlin的话,还须要更改<activity 标签里的name属性为我们刚才本身建立的那个。

以后,咱们在<application 标签内的底部添加provider,以下:

authorities,这个属性你能够填写包名+任意字符,确保和以前的代码中的 provider一致便可。

以后咱们在res文件夹下新建一个xml文件夹,再建立一个xml文件,名字随意,我这里就叫filepaths 内容以下:

<?xml version="1.0" encoding="utf-8"?>
<resources>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
    <files-path name="my_images"
        path="images"/>

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

    <cache-path
        name="cache_place"
        path="."/>
</paths>
复制代码

至此整个功能就实现了。

DEMO

DEMO地址

能够直接运行,在“综合demo演示”里,有一个“更新”按钮,点一下就能抵达。

相关文章
相关标签/搜索