下载文件

首先实现代码

效果,从必应的链接下载一个图片,地址如下

"http://cn.bing.com/az/hprichbg/rb/MoriBuilding_EN-US5143587469_1920x1080.jpg"
private void startDownload(final String downloadUrl) {

        permissionCheck();
        Observable.create(new ObservableOnSubscribe<Integer>() {
            @Override
            public void subscribe(ObservableEmitter<Integer> emitter) throws Exception {
                InputStream in = null;
                RandomAccessFile saveFile = null;
                File file = null;
                long downloadLength = 0;
                String fileName = downloadUrl.substring(downloadUrl.lastIndexOf("/"));
                String directory = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).getPath();
                Log.i(TAG, "subscribe: directory="+directory);
                file = new File(directory + fileName);
                if (file.exists()) {
                    downloadLength = file.length();
                }
                long contetnLength;
                Request request = new Request.Builder().get().url(downloadUrl).build();
                Call call = OkHttpUtils.getInstance().newCall(request);
                try {
                    Response response = call.execute();
                    contetnLength = response.body().contentLength();
                    if (contetnLength == 0) {
                        Log.i(TAG, "startDownload: content null");
                    } else if (contetnLength == downloadLength) {
                        Log.i(TAG, "startDownload: downloaded");
                    }
                    in = response.body().byteStream();
                    saveFile = new RandomAccessFile(file, "rw");
                    saveFile.seek(downloadLength);

                    byte[] b = new byte[1024];
                    int total = 0;
                    int len;
                    while ((len = in.read(b)) != -1) {
                        total += len;
                        saveFile.write(b, 0, len);
                        int progress = (int) ((total + downloadLength) * 100 / contetnLength);
                        Log.i(TAG, "subscribe: progress="+progress);
                        emitter.onNext(progress);
                    }
                    response.body().close();
                } catch (IOException e) {
                    e.printStackTrace();
                } finally {

                    try {
                        if (in != null) {
                            in.close();
                        }
                        if (saveFile != null) {
                            saveFile.close();
                        }

                    } catch (IOException e) {
                        e.printStackTrace();
                    }

                }
                emitter.onComplete();
            }
        })
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Observer<Integer>() {
                    @Override
                    public void onSubscribe(Disposable d) {
                        progressBar.setVisibility(View.VISIBLE);
                    }

                    @Override
                    public void onNext(Integer integer) {
                        progressBar.setProgress(integer);
                    }

                    @Override
                    public void onError(Throwable e) {

                    }

                    @Override
                    public void onComplete() {
                        progressBar.setVisibility(View.GONE);
                    }
                });
    }

 

控制文件的下载位置

有两种,一个是应用关联缓存目录  /storage/emulated/0/Android/data/com.example.*****/cache

这个目录可以跳过权限的申请

getExternalCacheDir().getPath();
getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS).getPath()

一个是SD卡目录  路径是/storage/emulated/0 

Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).getPath()

 

在4.4以前,写入SD卡需要在Manifest文件中增加权限

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

在6.0以后需要进行运行时权限申请

  if (ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE)!= PackageManager.PERMISSION_GRANTED){
            ActivityCompat.requestPermissions(MainActivity.this,new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},1);
        }

 

 

下载apk并安装

https://blog.csdn.net/cfy137000/article/details/70257912

https://blog.csdn.net/xqkillua/article/details/79114385

 

填坑:

下载完成以后,在打开安装包的时候,提示报错

Caused by: java.lang.NullPointerException: Attempt to get length of null array
        at com.android.packageinstaller.InstallStart.declaresAppOpPermission(InstallStart.java:134)
        at com.android.packageinstaller.InstallStart.onCreate(InstallStart.java:81)

原来,在8.0系统以后,系统设置当中的”允许未知来源”安装应用程序的选项被删除,每次安装第三方应用必须手动开启

https://blog.csdn.net/feibendexiaoma/article/details/80093354

目前增加一条权限即可解决

<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES"/>

问题2   解析安装包出错

有两种原因:

原因1,权限问题,在安卓7.0之后(版本号24),应用目录内的私有文件访问,必须通过内容提供器包装后生成新的URI来访问,不再允许外部程序访问.

安装程序是通过系统应用installer来打开apk文件,因此,除了通过内容提供器包装URI,还应该给installer添加访问权限

参考https://blog.csdn.net/qq_32452623/article/details/78924113

两种做法

方法1,给intent添加临时权限.注意,addFlag在setFlag之后,值允许出现1次setFlag

install.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
install.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
//查询所有符合 intent 跳转目标应用类型的应用
install.setDataAndType(apkUri, "application/vnd.android.package-archive");

方法2,查找打开apk的活动,然后添加访问权限

 install.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
//            install.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
//查询所有符合 intent 跳转目标应用类型的应用
install.setDataAndType(apkUri, "application/vnd.android.package-archive");
List<ResolveInfo> resInfoList = mActivity.getPackageManager().queryIntentActivities(install, PackageManager.MATCH_DEFAULT_ONLY);
//然后全部授权
for (ResolveInfo resolveInfo : resInfoList) {
     String packageName = resolveInfo.activityInfo.packageName;
     mActivity.grantUriPermission(packageName, apkUri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION | Intent.FLAG_GRANT_READ_URI_PERMISSION);
}

方法2的好处在于引用:

"区别在于 context.grantUriPermission()的临时访问权限不会在数据接收页面关闭时自动取消,只有手动调用 revokeUriPermission() 或者整个应用重新启动,才会失效。"

通过intent临时权限:"这个是 Intent 的一个 Flag 值,会临时授予目标引用对所传递 Uri 的临时访问权限。 
这个临时访问权限在setResult()数据接收方 Activity 还在栈中时,都是有效的,如果弹出了,临时访问权限自动取消。"

原因2:

有可能文件还没有下载完毕,就启动了installer打开apk,自然失败.

建议通过延时1-3s再启动安装包比较稳妥

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值