download_downloading - CSDN
精华内容
参与话题
  • 在H5中,为a标签新增了一个download属性,来直接文件的下载; 一 : 浏览器的支持 ①download属性暂时只支持Google Chrome 和 Mozilla Firefox,其他浏览器均不支持该属性; ②download是H5新增的属性,H5以前...


    在H5中,为a标签新增了一个download属性,来直接文件的下载;


    一 : 浏览器的支持

    ①download属性暂时只支持Google Chrome 和 Mozilla Firefox,其他浏览器均不支持该属性;
    ②download是H5新增的属性,H5以前没有该属性;

    这里写图片描述

    这里写图片描述



    二 : Chrome 浏览器测试

    // 引用本地文件   
    <a href="img/53ae64907656c.jpg">不加download属性</a>    
    <p></p>
    <a href="img/53ae64907656c.jpg"  download="xxx.jpg">加download属性</a>`

    ①在不加download属性的时候,a标签点击之后 浏览器是直接打开图片进行预览

    这里写图片描述

    ②添加了download属性之后,a标签点击之后,浏览器会强制进行文件下载,下载的文件名就是download所命名的文件名

    这里写图片描述

    //引用网络地址
    <a href="http://f.hiphotos.baidu.com/image/pic/item/3ac79f3df8dcd1008742b1cc788b4710b8122f0  4.jpg">不加download属性</a>
    <p></p>
    <a href="http://f.hiphotos.baidu.com/image/pic/item/3ac79f3df8dcd1008742b1cc788b4710b8122f04.jpg"  download="xxx.jpg">加download属性</a>

    ①在不加download属性的时候,浏览器会直接进行预览

    这里写图片描述

    ②添加了download属性的时候,浏览器强制进行文件下载;
    这里写图片描述



    二 : Firefox浏览器测试

    //引用本地文件
    <a href="img/53ae64907656c.jpg">不加download属性</a>
    <p></p>
    <a href="img/53ae64907656c.jpg"  download="xxx.jpg">加download属性</a>

    ①在不加download属性的时候,a标签点击之后 浏览器是直接打开图片进行预览
    这里写图片描述

    ②添加了download属性之后,a标签点击之后,浏览器会强制进行文件下载,下载的文件名就是download所命名的文件名
    这里写图片描述

    //引用网络地址
    <a href="http://f.hiphotos.baidu.com/image/pic/item/3ac79f3df8dcd1008742b1cc788b4710b8122f04.jpg">不加download属性</a>
    <p></p>
    <a href="http://f.hiphotos.baidu.com/image/pic/item/3ac79f3df8dcd1008742b1cc788b4710b8122f04.jpg"  download="xxx.jpg">加download属性</a>
    

    ①在不加download属性的时候,浏览器会直接进行预览

    这里写图片描述

    ②添加了download属性之后,火狐浏览器并没有对文件进行下载,这是因为火狐浏览器有同源限制,所以在引用网络地址的时候,不管是否添加download属性,浏览器都只会对进行预览
    这里写图片描述


    终结:
    ①a标签添加download属性之后会对文件进行强制下载,文件名就是download属性文件名;
    ②download只对火狐浏览器以及谷歌浏览器有效,其他浏览器无效;
    ③当href引用的地址是网络地址时,火狐浏览器不管是否添加download属性,都只会对图片进行预览,而不会下载;

    展开全文
  • 目录 一、5.0新特性 ...六、 一步步跟着案例进行版本升级踩坑——DownloadManager 1、适配6.0 动态权限 2、适配Android 7.0 解析包时出现问题 3、适配Android 8.0:未知来源的应用权限 本案例下载地址: ...

    目录

    一、5.0新特性

    二、6.0新特性

    三、7.0新特性

    四、8.0新特性

    五、9.0新特性

    六、 一步步跟着案例进行版本升级踩坑——DownloadManager

    1、适配6.0 动态权限

    2、适配Android 7.0 解析包时出现问题

    3、适配Android 8.0:未知来源的应用权限


    本案例下载地址:

    https://download.csdn.net/download/csdn_aiyang/10906816

    https://github.com/aiyangtianci/DownloadManagerDemo


    Android 最近几年发展非常快,自08年智能机时代开始,Android操作系统一路高歌,每年推出升级新的版本。然而,这一转眼都10年过去了,10年的智能机发展之路,使Android系统占全球市场份额近7成(IOS 近3成)。有趣的是谷歌给自己的这个‘亲儿子’起名字可谓绞尽了脑汁,Android1.1开始,就有过给起名Astro(小金刚)、Bender(机器人班亭)本是想从字母A到字母Z,每一代都用首字母排序,可是,由这样的名字太二了,于是从Android1.5的时候,聪明的谷歌就想到了以甜点给‘亲儿子’命名的文化传统了,就在17年8月份谷歌为提高续航和安全发布了Android 8.0,主题是“奥利奥饼干”。但是,由于版本系统推出过快及兼容过多旧版代码,Android 版本碎片化越来越严重了。

    谷歌从8.0后推出的一种新的技术框架“Project Treble”,可以改善安卓系统的碎片化问题。将操作系统框架代码与“特定供应商”硬件代码分开,允许手机厂商在无须芯片商参与的情况下推送更新,希望由此减少升级阻碍,加快新系统普及速度。在Android 9.0发布后,谷歌加入了强制性的rollback protection功能。简单来说,这项功能直接杜绝了用户的系统降级操作,虽然早在Android 8.0中就已经加入了这项功能,但是不同的是,原本这项功能是处于用户自选的状态,在更新Android 9之后就变更为强制性了。这也就寓意着,从Android 9开始,之后的安卓系统终于要正式致敬苹果的iOS,系统只提供升级,不允许降级。

    其实,谷歌一直以来都在想尽办法试图解决安卓系统碎片化的问题,但是由于覆盖广、适配设备过多等因素,始终没有收获什么成效。碎片化这一问题,也始终被用户所诟病,不过谷歌这次真的下狠手了,通过在系统内置Android Verified Boot 2.0,来阻止用户降级,一旦用户强制输入低于当前版本的安卓系统,被自动检测到后会出现无限重启、无法开机,不得不佩服谷歌的这招真的是做绝了。

     

    在正式开篇之前,本人有些心路历程想和大家分享一下,说白了,就是想吐吐这几天苦水,哈哈哈。从收集文章素材、参考文章、动手写demo、写文章等,用了近一个星期。我个人觉得自己写文章的效率很高,这篇文章中的案例在踩坑时出现过很多次奇怪的闪退,着实要费了些心思。例如,在一个手机上开发时遇见崩溃,换个手机就没问题了,我实在搞不懂崩溃原因时候,也会像小白一样重新创建新项目,把旧代码一行行重新复制粘贴过去,重新运行项目去解决问题。然而,就这样我尽然重新创建过四次。手动捂脸哭表情。说这些,是希望同学们能认真对待我的博文,谢谢,因为我很用心在分享技术!

     

    一、5.0新特性

    • Material Design
    • 支持多种设备
    • 全新通知中心
    • 支持 64 位 ART 虚拟机
    • 电池续航改进
    • 全新“最近应用程序”
    • 安全性改进
    • 不同数据独立保存
    • 改进搜索
    • 支持蓝牙 4.1、USB Audio、多人分享等

    重点注意: 加了很多新控件,如抽屉布局,菜单布局,卡片布局,列表布局新增RecyclerView等。努力改善应用界面吧!

    Android Material Design之CoordinatorLayout效果实现

    Android ToolBar 基础使用——进阶封装BaseActivity(附源码)

    Android 探究onCreateViewHolder和onBindViewHolder两者关系和调用次数

    Android 使用RecycleView实现吸附小标题的Demo(附源码)

    Android5.0Activity的转场动画、过渡动画、过场动画、跳转动画

    Android共享元素转场动画Fragment to Fragment

     

    二、6.0新特性

    • 动态权限管理
    • 系统层支持指纹识别
    • APP 关联
    • Android Pay
    • 电源管理
    • TF 卡默认存储

    重点注意:动态的权限申请,6.0以下的版本可以直接申请权限直接使用了,以上的版本需要一些敏感权限时需要动态申请。可参考我的文章。

    Android6.0版本以上危险权限动态申请及RxPermissions权限库使用

     

    三、7.0新特性

    • 分屏多任务
    • 下拉快捷开关
    • 新通知消息
    • 夜间模式
    • 流量保护模式
    • 全新设置样式
    • 改进 Doze 休眠机制
    • 系统级电话黑名单
    • 菜单键快速切换应用

    重点注意 7.0对于SDCard的文件URI的访问做了限制,获取文件uri的方式也变了,开发时需要注意。 下面案例会讲到。

    鸿洋的:Android 7.0 行为变更 通过FileProvider在应用间共享文件吧

     

    四、8.0新特性

    • 画中画
    • 通知标志
    • 自动填充框架
    • 系统优化
    • 后台限制
    • 应用快捷键
    • 语言区域和国际化

    重点注意:

    8.0限制了后台服务这些,启动后台服务需要设置通知栏,使服务变成前台服务。

    8.0对于安装位置来源的应用做了更严格的限制,在app更新安装时需要做些处理。 下面案例会讲到。

    Android 8.0 P适配详细指南

     

    五、9.0新特性

    • 刘海设计
    • 黑白模式切换
    • 加入长截图
    • 加入护眼模式
    • 通知栏的体验优化
    • Material Design功能更新等等

    重点注意:Android 9.0强制使用https,会阻塞http请求,如果app使用的第三方sdk有http,将全部被阻塞。

     

    六、10 Q 新特性

    Q的最大更新就是用户隐私权限变更。

    • 存储权限:判断当应用运行在Q平台上时,访问自己文件不需申请读写权限,访问音频需要申请新的媒体特定权限。
    • 后台定位权限:如果目标版本targetSDK <= P 请求了ACCESS_FINE_LOCATION 或 ACCESS_COARSE_LOCATION权限,Q设备会自动帮你申请ACCESS_BACKGROUND_LOCATION权限。
    • 后台启动 Activity:仅针对与用户毫无交互就启动一个Activity的情况,比如微信登陆授权。
    • minSDK警告:谷歌要求运行在Q设备上的应用targetSDK>=Android 6.0(API 级别 23),不然会向用户发出警告。
    • 设备标识符(DeviceId):TelephonyManager.getDeviceId()方法失效。新权限READ_PRIVILEGED_PHONE_STATE只提供给系统app使用。下面是一个通过硬件信息生产的UUID。设备ID的获取一个版本比一个版本艰难,如有好的方法请指出。
    public static String getUUID() {
    String serial = null;
     
    String m_szDevIDShort = "35" +
            Build.BOARD.length() % 10 + Build.BRAND.length() % 10 +
     
            Build.CPU_ABI.length() % 10 + Build.DEVICE.length() % 10 +
     
            Build.DISPLAY.length() % 10 + Build.HOST.length() % 10 +
     
            Build.ID.length() % 10 + Build.MANUFACTURER.length() % 10 +
     
            Build.MODEL.length() % 10 + Build.PRODUCT.length() % 10 +
     
            Build.TAGS.length() % 10 + Build.TYPE.length() % 10 +
     
            Build.USER.length() % 10; //13 位
     
    try {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            serial = android.os.Build.getSerial();
        } else {
            serial = Build.SERIAL;
        }
        //API>=9 使用serial号
        return new UUID(m_szDevIDShort.hashCode(), serial.hashCode()).toString();
    } catch (Exception exception) {
        //serial需要一个初始化
        serial = "serial"; // 随便一个初始化
    }
        //使用硬件信息拼凑出来的15位号码
        return new UUID(m_szDevIDShort.hashCode(), serial.hashCode()).toString();
    }
    

     

    最后,版本升级踩坑记——DownloadManager

    DownloadManager使用介绍参考:https://blog.csdn.net/csdn_aiyang/article/details/64126379

    MainActivity.class 代码

    public class MainActivity extends AppCompatActivity {
        private TextView down;
        private TextView progress;
        private ProgressBar pb_update;
        private DownloadManager downloadManager;
        private DownloadManager.Request request;
        public static String downloadUrl = "http://www.wanandroid.com/blogimgs/ecb4c318-42f3-454a-a6c4-615ad16f35bd.apk";
    
        private DownloadReceiver completeReceiver;
        private final Uri CONTENT_URI = Uri.parse("content://downloads/my_downloads");
        private DownloadChangeObserver observer;
        long id;
        Handler handler = new Handler() {
            @Override
            public void handleMessage(Message msg) {
                super.handleMessage(msg);
                Bundle bundle = msg.getData();
                int pro = bundle.getInt("pro");
                pb_update.setProgress(pro);
                progress.setText(String.valueOf(pro) + "%");
            }
        };
    
    
        class DownloadChangeObserver extends ContentObserver {
    
            public DownloadChangeObserver(Handler handler) {
                super(handler);
            }
    
            @Override
            public void onChange(boolean selfChange) {
                super.onChange(selfChange);
                updateView();
            }
        }
    
        class DownloadReceiver extends BroadcastReceiver {
    
            @Override
            public void onReceive(final Context context, final Intent intent) {
                Log.i("aaa", "广播监听");
            
                long completeDownLoadId = intent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, -1);
                Intent intentInstall = new Intent();
                Uri uri = null;
                if (completeDownLoadId == id) {
                    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) { // 兼容6.0以下
                       
                        uri = downloadManager.getUriForDownloadedFile(completeDownLoadId);
                        installPackge(context,intentInstall,uri);
                    } 
                }
            }
        }
    
        /**
         * 安装APK
         * @param context
         * @param intentInstall
         * @param uri
         */
        private void installPackge(Context context,Intent intentInstall,Uri uri){
            intentInstall.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
            intentInstall.setAction(Intent.ACTION_VIEW);
            // 安装应用
            Log.i("aaa", "app下载完成了,开始安装。。。"+uri);
            intentInstall.setDataAndType(uri, "application/vnd.android.package-archive");
            context.startActivity(intentInstall);
        }
    
    
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            down = (TextView) findViewById(R.id.down);
            progress = (TextView) findViewById(R.id.progress);
            pb_update = (ProgressBar) findViewById(R.id.pb_update);
            down.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                    LoadApp();
                    //requestPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE,Code_PERMISSION);
                }
            });
        }
    
        private void LoadApp() {
            //创建下载对象
            downloadManager = (DownloadManager) getSystemService(DOWNLOAD_SERVICE);
            request = new DownloadManager.Request(Uri.parse(downloadUrl));
            request.setTitle("app-release.apk");
            request.setAllowedNetworkTypes(DownloadManager.Request.NETWORK_WIFI);
            request.setAllowedOverRoaming(false);
            request.setMimeType("application/vnd.android.package-archive");
            request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
    
            //设置文件存放路径
            request.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, "app-release.apk");
        }
    
        private void updateView() {
            int[] bytesAndStatus = new int[]{0, 0, 0};
            DownloadManager.Query query = new DownloadManager.Query().setFilterById(id);
            Cursor c = null;
            try {
                c = downloadManager.query(query);
                if (c != null && c.moveToFirst()) {
                    //已经下载的字节数
                    bytesAndStatus[0] = c.getInt(c.getColumnIndexOrThrow(DownloadManager.COLUMN_BYTES_DOWNLOADED_SO_FAR));
                    //总需下载的字节数
                    bytesAndStatus[1] = c.getInt(c.getColumnIndexOrThrow(DownloadManager.COLUMN_TOTAL_SIZE_BYTES));
                }
            } finally {
                if (c != null) {
                    c.close();
                }
            }
            int pro = (bytesAndStatus[0] * 100) / bytesAndStatus[1];
            Message msg = Message.obtain();
            Bundle bundle = new Bundle();
            bundle.putInt("pro", pro);
            msg.setData(bundle);
            handler.sendMessage(msg);
            Log.i("aaa", "下载进度:" + bytesAndStatus[0] + "/" + bytesAndStatus[1]);
        }
    
        //开始下载
        private void onDownBegin() {
            try {
                id = downloadManager.enqueue(request);
    
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                //更新UI
                observer = new DownloadChangeObserver(handler);
                getContentResolver().registerContentObserver(CONTENT_URI, true, observer);
                //安装
                completeReceiver = new DownloadReceiver();
                registerReceiver(completeReceiver, new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE));
                down.setText("正在下载");
                down.setClickable(false);
            }
        }
    
        @Override
        protected void onDestroy() {
            super.onDestroy();
            if (observer != null) {
                getContentResolver().unregisterContentObserver(observer);
                unregisterReceiver(completeReceiver);
            }
    
        }
    }
    

    布局文件:

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:orientation="vertical" android:layout_width="match_parent"
        android:gravity="center"
        android:layout_height="match_parent">
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginBottom="20dp"
            android:text="下载进度"/>
    
        <ProgressBar
            android:id="@+id/pb_update"
            style="?android:attr/progressBarStyleHorizontal"
            android:layout_width="match_parent"
            android:layout_height="10dp"
            android:layout_marginLeft="10dp"
            android:layout_marginRight="10dp"
            android:layout_gravity="center_horizontal"
            android:layout_marginTop="10dp"
            android:max="100"
            android:progress="10"
            android:layout_marginBottom="20dp"
            />
    
        <TextView
            android:id="@+id/progress"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:gravity="center"
            android:layout_marginBottom="20dp"
            android:text="10%"/>
        <TextView
            android:id="@+id/down"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:gravity="center"
            android:paddingTop="10dp"
            android:paddingBottom="10dp"
            android:paddingLeft="30dp"
            android:paddingRight="30dp"
            android:background="@color/colorAccent"
            android:text="立即下载"/>
    </LinearLayout>
    

    开始运行,进行版本适配。

    1、适配6.0 动态权限

    targetSdkVersion 23

    此时,运行报错:

    java.lang.SecurityException: 
    No permission to write to /storage/emulated/0/Download/app-release.apk:
    Neither user 10484 nor current process has android.permission.WRITE_EXTERNAL_STORAGE.

    没有读写权限。下面进行添加申请权限代码:

    AndroidManifest.xml 中:

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

    MainActivity.class 中添加

      /**
         * 请求权限
         */
        int Code_PERMISSION = 0;
        /**
         * 权限申请
         * @param ManifestPermission
         * @param CODE
         * @return
         */
        private boolean requestPermission(final String ManifestPermission, final int CODE) {
            //1. 检查是否已经有该权限
            if (ContextCompat.checkSelfPermission(this,ManifestPermission) != PackageManager.PERMISSION_GRANTED) {
                if (ActivityCompat.shouldShowRequestPermissionRationale(this,ManifestPermission)) {
                    new AlertDialog.Builder(this)
                            .setTitle("权限申请")
                            .setMessage("亲,没有权限我会崩溃,请把权限赐予我吧!")
                            .setPositiveButton("赏给你", new DialogInterface.OnClickListener() {
                                @Override
                                public void onClick(DialogInterface dialog, int which) {
                                    dialog.cancel();
                                    // 用户同意 ,再次申请
                                    ActivityCompat.requestPermissions(MainActivity.this, new String[]{ManifestPermission}, CODE);
                                }
                            })
                            .setNegativeButton("就不给", new DialogInterface.OnClickListener() {
                                @Override
                                public void onClick(DialogInterface dialog, int which) {
                                    dialog.cancel();
                                    // 用户拒绝 ,如果APP必须有权限否则崩溃,那就继续重复询问弹框~~
                                }
                            }).show();
                } else {
                    //2. 权限没有开启,请求权限
                    ActivityCompat.requestPermissions(this,
                            new String[]{ManifestPermission}, CODE);
                }
    
            } else {
                //3. 权限已开,处理逻辑
                return true;
            }
            return false;
        }
    
        //4. 接收申请成功或者失败回调
        @Override
        public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
            super.onRequestPermissionsResult(requestCode, permissions, grantResults);
            if (requestCode == Code_PERMISSION) {
                if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                    //权限被用户同意,做相应的事情
                    onDownBegin();
                } else {
                    //权限被用户拒绝,做相应的事情
                    Toast.makeText(this,"拒绝了权限",Toast.LENGTH_SHORT);
                }
            }
            super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        }

    在点击下载时候进行权限申请。可以执行下载了,但是最后安装还是报错了:

    Caused by: 
    android.content.ActivityNotFoundException:
    No Activity found to handle Intent { 
    act=android.intent.action.VIEW typ=application/vnd.android.package-archive flg=0x10000000 } 

    原因是在Android6.0以下和Android6.0上,通过DownloadManager 获取到的Uri不一样。区别如下:

    • Android 6.0以下版本:getUriForDownloadedFile得到的为:file:///storage/emulated/0/Android/data/packgeName/files/Download/xxx.apk; 
    •  Android 6.0时:getUriForDownloadedFile得到的值为: content://downloads/my_downloads/10 。
     //通过downLoadId查询下载的apk,解决6.0以后安装的问题
        public static File queryDownloadedApk(Context context, long downloadId) {
            File targetApkFile = null;
            DownloadManager downloader = (DownloadManager) context.getSystemService(Context.DOWNLOAD_SERVICE);
    
            if (downloadId != -1) {
                DownloadManager.Query query = new DownloadManager.Query();
                query.setFilterById(downloadId);
                query.setFilterByStatus(DownloadManager.STATUS_SUCCESSFUL);
                Cursor cur = downloader.query(query);
                if (cur != null) {
                    if (cur.moveToFirst()) {
                        String uriString = cur.getString(cur.getColumnIndex(DownloadManager.COLUMN_LOCAL_URI));
                        if (!TextUtils.isEmpty(uriString)) {
                            targetApkFile = new File(Uri.parse(uriString).getPath());
                        }
                    }
                    cur.close();
                }
            }
            return targetApkFile;
        }
    

    在广播监听安装app处添加6.0-7.0代码:

      if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) { // 6.0以下
                  uri = downloadManager.getUriForDownloadedFile(completeDownLoadId);
                  installPackge(context,intentInstall,uri);
       } else if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) { // 6.0 - 7.0
                       
                  File apkFile = queryDownloadedApk(context, completeDownLoadId);
                  uri = Uri.fromFile(apkFile);
                  installPackge(context,intentInstall,uri);
       }

     

    2、适配Android 7.0 解析包时出现问题

    从Android 7.0开始,不再允许在app中把file:// Uri暴露给其他app,否则应用会抛出FileUriExposedException。异常如下:

    E/AndroidRuntime: FATAL EXCEPTION: Timer-0
                                                                                 
    Process: com.demo.aiyang.downloadmanger, PID: 7985
                                                                                  
    android.os.FileUriExposedException: file:///storage/emulated/0/Download/app-release.apk exposed beyond app through Intent.getData()

    原因在于,Google认为使用file:// Uri存在一定的风险。比如,文件是私有的,其他app无法访问该文件,或者其他app没有申请READ_EXTERNAL_STORAGE运行时权限。使用FileProvider生成content:// Uri来替代file:// Uri,如下图:


    使用FileProvider解决上述异常。

    (1)声明FileProvider
    首先在Manifest.xml文件中申明FileProvider。

             //记得替换成你项目包名
            <provider
                android:name="android.support.v4.content.FileProvider"
                android:authorities="你的包名.FileProvider"
                android:exported="false"
                android:grantUriPermissions="true">
                <meta-data
                    android:name="android.support.FILE_PROVIDER_PATHS"
                    android:resource="@xml/file_paths" />
            </provider>
     
    • android:name:固定写法;
    • android:authorities:可自定义,是用来标识该provider的唯一标识;
    • android:exported:必须设置成 false,否则运行时报错java.lang.SecurityException: Provider must not be exported ;
    • android:grantUriPermissions:用来控制共享文件的访问权限;
    • <meta-data>节点中的android:resource指定了共享文件的路径。

    (2) 添加file_paths.xml文件到res的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="name" path="path" />
         <external-cache-path name="name" path="path" />
    </paths>

    根元素<paths>是固定的,属性path表示子目录,内部元素节点如下:

    • <root-path/> 代表设备的根目录new File("/");
    • <files-path/> 代表context.getFilesDir()
    • <cache-path/> 代表context.getCacheDir()
    • <external-path/> 代表Environment.getExternalStorageDirectory()
    • <external-files-path>代表context.getExternalFilesDirs()
    • <external-cache-path>代表getExternalCacheDirs()

    此处,我们apk下载存放在Sdk外部存储区目录下  :

    <?xml version="1.0" encoding="utf-8"?>
    <paths xmlns:android="http://schemas.android.com/apk/res/android">
        <!-- 该方式提供在应用的外部存储区根目录的下的文件。
       它对应Context#getExternalFilesDir(String) Context.getExternalFilesDir(null)返回的路径。
       eg:”/storage/emulated/0/Android/data/com.jph.simple/files”
        -->
    
        <external-path name="download" path="" />
    
    </paths>
    

    上述代码中path="",是有特殊意义的,它代码根目录。如果你将path设为path="pictures",那么它代表着根目录下的pictures目录(eg:/storage/emulated/0/pictures),如果你向其它应用分享pictures目录范围之外的文件是不行的。

    (3)在广播监听APP安装代码中添加兼容>7.0

    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) { // 兼容6.0以下
         uri = downloadManager.getUriForDownloadedFile(completeDownLoadId);
        installPackge(context,intentInstall,uri);
    } else if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) { // 兼容6.0-70
        File apkFile = queryDownloadedApk(context, completeDownLoadId);
        uri = Uri.fromFile(apkFile);
        installPackge(context,intentInstall,uri);
    } else {//兼容7.0
       intentInstall.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); // 给目标应用一个临时授权
       File file= new File(Environment.getExternalStoragePublicDirectory( Environment.DIRECTORY_DOWNLOADS),"app-release.apk");
        
       Uri uri = FileProvider.getUriForFile(context, getPackageName() + ".fileProvider",file);
       installPackge(this,intentInstall,uri);
    }

    其中:

    file路径:/storage/emulated/0/Download/app-release.apk

    使用FileProvide得到uri为:content://com.demo.aiyang.demo.fileProvider/download/Download/app-release.apk  

    这里,讲一个坑。

    在下载成功后,安装时出错,如下图所示

    错误获取File路径代码如下: 

    //改正前写法
     File file = new File(getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS), "app-release.apk");
    //file路径:/storage/emulated/0/Android/data/com.demo.aiyang.demo/files/Download/app-release.apk
    
    
    //改正后写法
    File file= new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS),
    "app-release.apk");
    //file路径:/storage/emulated/0/Download/app-release.apk

    因为FileProvider不支持sdcard目录下的文件共享。Android 7.0 DownloadManager与FileProvider的坑

    (一把辛酸泪!这个问题解决了很久,试了很多方法,比较坑的是将文件下载后再写入到私有目录!)

     

    3、适配Android 8.0:未知来源的应用权限

    Android 8.0的手机会出现在线更新不了新版本,华为荣耀V10手机测试apk下载完成后直接白屏提示“解析包时出现问题”。原因是Android8.0以上未知来源的应用是不可以通过代码来执行安装的(允许手动安装)。Google这么做是为了防止不合法APK安装侵犯了用户权益。适配如下:

    (1) 在清单文件中申明权限

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


    (2)监听apk下载状态的广播中添加代码:

    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) { // 兼容6.0以下
                    
                        uri = downloadManager.getUriForDownloadedFile(completeDownLoadId);
                        installPackge(context,intentInstall,uri);
    } else if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) { // 兼容6.0-70
           
                        File apkFile = queryDownloadedApk(context, completeDownLoadId);
                        uri = Uri.fromFile(apkFile);
                        installPackge(context,intentInstall,uri);
    } else {
                        Log.i("aaa", ">7.0");
                        InstallPackgeAPI28(context);// 兼容Android 8.0
    }

    兼容Android 8.0 是否有安装权限 

       /**
         *  兼容 8.0 未知来源应用安装
         */
        int Code_INSTALLPACKAGES = 1;
        @RequiresApi(api = Build.VERSION_CODES.O)
        private void startInstallPermissionSettingActivity() {
            Uri packageURI = Uri.parse("package:" + getPackageName());
            Intent intent = new Intent(Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES, packageURI);
            startActivityForResult(intent, Code_INSTALLPACKAGES);
        }
    
        @Override
        protected void onActivityResult(int requestCode, int resultCode, Intent data) {
            super.onActivityResult(requestCode, resultCode, data);
            if (requestCode == Code_INSTALLPACKAGES){
                InstallPackgeAPI28(this);
            }
        }
    
     private void InstallPackgeAPI28(Context context){
            Intent intentInstall = new Intent();
            intentInstall.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); // 给目标应用一个临时授权
            File file= new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS),"app-release.apk");
            Uri uri = FileProvider.getUriForFile(context, getPackageName() + ".fileProvider", file);
    
            boolean isInstallPermission = false;//是否有8.0安装权限
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                isInstallPermission = getPackageManager().canRequestPackageInstalls();
                if (isInstallPermission) {
                    installPackge(this,intentInstall,uri);
                } else {
                    new AlertDialog.Builder(this)
                            .setTitle("权限申请")
                            .setMessage("亲,没有权限我会崩溃,请把权限赐予我吧!")
                            .setPositiveButton("赏给你", new DialogInterface.OnClickListener() {
                                @Override
                                public void onClick(DialogInterface dialog, int which) {
                                    dialog.cancel();
                                    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                                        startInstallPermissionSettingActivity();
                                    }
                                }
                            }).setNegativeButton("取消",null ).show();
                }
            }else{
                installPackge(this,intentInstall,uri);
            }
        }

    运行结果:

    4、适配Android 9.0:Https网络请求

    前面代码基本上都已经算非常完整了,可以在大部分手机上都可以正常安装和使用。但是,我发现依然有很多网友留言说让我兼容9.0。于是乎,我自己也用9.0的测试手机运行了一下,可以下载安装啊!没问题啊!

    呵呵。。。终究是我太大意了。 demo 里的  targetSdkVersion  并没有大于  API 28 Android 9.0 。因此,也就不会出现什么问题。审视一下一边代码就明白了,无非就是无法下载apk的问题。

    Android P 9.0 的系统上面默认所有Http的请求都被阻止了,就没办法访问了网络了。解决这个问题最好当然是把Http换成Https了。然鹅,并不是每个公司都做到了。还有一个办法,就是通过在AnroidManifest.xml中的application标签下设置如下属性即可。

    android:usesCleartextTraffic="true"
    

    完。2019年12月24日16:55:03


    欢迎订阅公众号:数据结构、算法、面试经验、每日新闻、闲聊趣文等。欢迎一起学习!

    欢迎加入Android QQ交流学习群:

     

    参考链接:

    https://www.jianshu.com/p/c58d17073e65

    https://blog.csdn.net/yq6073025/article/details/52934326

    https://www.jianshu.com/p/121bbb07cb07

    展开全文
  • Html a标签 download属性失效原因

    万次阅读 2018-09-27 09:19:21
    在 HTML5 中,download 属性是 &lt;a&gt; 标签的新属性。 因此,download 是属于HTML5的新属性,失效原因分析如下:   1、没有声明html5,需要再html文件头声明&lt;!DOCTYPE html&gt; &lt;...

    HTML 4.01 与 HTML 5 之间的差异

    在 HTML5 中,download 属性是 <a> 标签的新属性。

    因此,download 是属于HTML5的新属性,失效原因分析如下:

     

    1、没有声明html5,需要再html文件头声明<!DOCTYPE html>

    <!DOCTYPE> 声明必须是 HTML 文档的第一行,位于 <html> 标签之前。

    <!DOCTYPE> 声明不是 HTML 标签;它是指示 web 浏览器关于页面使用哪个 HTML 版本进行编写的指令。

    在 HTML 4.01 中,<!DOCTYPE> 声明引用 DTD,因为 HTML 4.01 基于 SGML。DTD 规定了标记语言的规则,这样浏览器才能正确地呈现内容。

    HTML5 不基于 SGML,所以不需要引用 DTD。

    提示:请始终向 HTML 文档添加 <!DOCTYPE> 声明,这样浏览器才能获知文档类型。

    2、存在跨域问题,通过download 属性下载文件,必须为同一域名下

     

    参考链接:

    https://segmentfault.com/q/1010000014555598?utm_source=index-hottest

    http://www.w3school.com.cn/tags/tag_doctype.asp

    http://www.w3school.com.cn/tags/att_a_download.asp

    展开全文
  • 在前端想要生成txt或者其他格式文件来保存一些数据,或者在数据请求服务器成功后想要将数据生成文件并下载,这些需求还是比较多的,这里使用到的download.js。既可以满足这些需求。 第一步:下载 1.下载可以在...

    知识点引用来自于

    http://danml.com/download.html

    在前端想要生成txt或者其他格式文件来保存一些数据,或者在数据请求服务器成功后想要将数据生成文件并下载,这些需求还是比较多的,这里使用到的download.js。既可以满足这些需求。

    第一步:下载
    1.下载可以在官网上进行下载 原网址:download2.js
    2.也可以使用我上传的资源(下载与上述官网) 免费哦! 下载

    第二步:引用
    1.将下载的文件放在项目中,并使用 <script src="./download2.js"></script> 进行引用 (速度快)
    2.使用网络引用 <script src="http://danml.com/js/download2.js"></script>

    第三步:开始使用

    //生成一个txt的文件,讲字符串变量 str 的内容放在里面,并完成下载
    var str = "hello world可以中文吗?答案:可以的";
    download(str, "Text.txt", "text/plain");
    //也将生成一个txt文件,和上面的结果是一摸一样
    str = "data:text/plain,hello%20world";
    download(str, "dlDataUrlText.txt", "text/plain");
    //将数组或者json对象写入到文件中
    var debug = {hello : "world"};
    var blob = new Blob([JSON.stringify(debug, null, 2)]);
    download(blob, "dlTextBlob.txt", "text/plain");
    
    var str= "hello world", arr= new Uint8Array(str.length);
    str.split("").forEach(function(a,b){
        arr[b]=a.charCodeAt();
    });
    download( arr, "textUInt8Array.txt", "text/plain" );
    //将html页面中的一部分生成单独的 html文档
    download(document.body.outerHTML, "dlHTML.html", "text/html");
    //给字符串添加标签,或者html片段直接给方法,将会生成对应内容的文件
    download(new Blob(["hello world".bold()]), "dlHtmlBlob.html", "text/html");
     //将ajax查询回来的结果,赋值给文件,并下载
    $.ajax({
        url: "/download.html",
        success: download.bind(true, "text/html", "dlAjaxCallback.html")
    });
    //将图片的base64装入到图片中并下载。
    download("data:image/gif;base64,R0lGODlhRgAVAIcAAOfn5+/v7/f39yH5BAAAAP8ALAAAAABGABUAAAj/AAEIHAgggMGDCAkSRMgwgEKBDRM+LBjRoEKDAjJq1GhxIMaNGzt6DAAypMORJTmeLKhxgMuXKiGSzPgSZsaVMwXUdBmTYsudKjHuBCoAIc2hMBnqRMqz6MGjTJ0KZcrz5EyqA276xJrVKlSkWqdGLQpxKVWyW8+iJcl1LVu1XttafTs2Lla3ZqNavAo37dm9X4eGFQtWKt+6T+8aDkxUqWKjeQUvfvw0MtHJcCtTJiwZsmLMiD9uplvY82jLNW9qzsy58WrWpDu/Lp0YNmPXrVMvRm3T6GneSX3bBt5VeOjDemfLFv1XOW7kncvKdZi7t/S7e2M3LkscLcvH3LF7HwSuVeZtjuPPe2d+GefPrD1RpnS6MGdJkebn4/+oMSAAOw==", "dlDataUrlBin.gif", "image/gif");
    //使用xhr获取本域名下文件,并封装到文件进行下载。这里的前提是xhr访问的文件必须和该程序执行在同一域名下,
    //否则会报跨域的问题。比如此程序执行在localhost下面,而这里的访问地址使用的是http://danml.com,就会访问不成功。
        var x=new XMLHttpRequest();
        x.open("GET", "http://danml.com/wave2.gif", true);
        x.responseType = 'blob';
        x.onload=function(e){
            download(x.response, "dlBinAjax.gif", "image/gif" );
        };
        x.send();

    扩展:还可以使用文件上传再下载进行测试。

    <input id="file" type="file" onchange="download(this.files[0], this.files[0].name, this.files[0].type)">
    展开全文
  • pip download 命令的使用方法

    万次阅读 2018-09-04 17:04:41
    比如下载 django 1.8.11版本和simplejson 3.14.0版本的包 那么就将所需的包写入 requirement.txt   那么我的requirement.txt内容就是: django==1.8.11 simplejson==3.14.0 ...如果还需要其他的包可以依次写,...
  • HTML5 中download属性

    2019-03-05 17:54:56
    ...lt;...标签新增download属性,为&lt;a&gt;标签添加该属性可实现下载功能。 语法: &lt;a href="下载地址" download[=名字]&gt;下载&lt;/a&gt; //备注:do...
  • 这里写自定义目录标题欢迎使用Markdown编辑器新的改变功能快捷键合理的创建标题,有助于目录的生成如何改变文本的样式插入链接与图片如何插入一段漂亮的代码片生成一个适合你的列表创建一个表格设定内容居中、居左、...
  • PanDownload 满速下载的方法

    万次阅读 2019-09-07 16:53:36
    目前还是满速在跑。 下载里面,最大连接数设置为 64。 登陆百度账号。 粘贴下载文件的链接和提取码。...使用 PanDownload 下载。...相信大家都有pandownload,如果没有,或者需要最新版本,可以私信我。 ...
  •  用过百度文库、豆丁的朋友,都知道,我们只要将文档上传到这些网站上,就可以实现分享。无论你上传的是pdf、ppt,还是doc、txt、pptx文件,都会在网页上显示给用户看。从技术上简单地说,可以分成三步:
  • Internet Download Manager用假的序列号注册,IDM将退出

    万次阅读 多人点赞 2019-01-30 09:25:50
    下载下来直接双击绿化按钮即可.  软件链接 : https://pan.baidu.com/s/1agK3cLtjJzXcGEgsuv5mVQ 提取码: ckm7    
  • 大量oreilly电子书免费下载

    万次阅读 2005-12-21 02:49:00
    NetworkOReilly - Network Troubleshooting Tools.pdfDownloadOReilly - Building Wireless Community Networks.pdfDownloadOReilly - Network Security with OpenSSL Ebook.pdfDownloaddephiOReilly - Delphi In A
  • 50个极好的bootstrap 后台框架主题下载

    万次阅读 多人点赞 2016-07-06 18:53:16
    50个极好的bootstrap 后台框架主题下载  ... ...越来越多的设计师和前端工程师开始用bootstrap来作为网页项目的框架,在这一次的免费设计资源中,我们收集了一些bootstrap的主题希望大家会喜欢!...
  • PanDownload 完美解决百度网盘限速

    万次阅读 2019-05-04 09:02:50
    http://pandownload.com/ 小心!!!有被百度封ID限速的风险!!! 运行exe文件,登录网盘账户即可随心所欲下载。 和百度网盘的对比: 然后下载完成以后,你会发现再次使用网速就变慢了。 ...
  • IDEA can not download source问题

    万次阅读 2017-11-24 13:35:54
    IDEA can not download source问题有时候用IDEA无法下载源码,可以在命令行项目根目录下,执行如下命令下载:mvn dependency:resolve -Dclassifier=sources
  • IDEA设置Maven下载source、document

    万次阅读 2018-06-15 08:17:15
    1、打开Maven设置2、在import设置中勾选source和document3、对原项目进行重新下载,打开右侧Maven projects,选中所有项目模块之后,点击download sources and/or documentation,即可重新下载依赖jar的源码及文档4...
  • HTML5 a标签的download属性

    万次阅读 2016-09-05 18:03:36
    download属性能让我们指定浏览器下载时采用新的文件名称,也就是在客户端重命名下载文件。而不是链接上原始的文件名称。 添加download属性后,无论a标签的href属性链接到一个网页、图片、文本等,download属性都会...
  • 1.eclipse:集成Maven后在Window-...勾选上 download Artifact Sources 和 download Artifact JavaDoc 下次下载更新时候,就可以下载Sources 和JavaDoc了。 2.Myeclipse:集成Maven后在Window-->Preference-->Maven4My
  • Pandownload复活教程

    万次阅读 2020-04-20 12:04:48
    众所周知,因为某些愿意pandownload作者已被抓捕,pandownload无法再次登录。但是,经过热心网友的努力,发现被端掉的只是pandownload的服务器,其余功能皆可正常使用。故在此根据网友总结,再次验证了pandownload...
  • PanDownload下载变慢的一个解决办法

    万次阅读 2019-10-31 22:52:41
    自己今天下载一个比较大的压缩包,发现用PanDownload下载速度变慢了很多,基本在300k-400k左右,然后经过自己不断的努力总算是提高到3M左右。下面介绍一下我的做法(这是我个人的成功,你们可以试一试,但不一定确保...
  • 系统:ubuntu 16.04 + ROS kinetic google chrome 与终端都可以科学上网 1. 问题 按照 ros/installation在ubuntu 16.04上安装ros kinetic,在执行以下命令时 ...ERROR: cannot download default sources ...
1 2 3 4 5 ... 20
收藏数 1,057,292
精华内容 422,916
关键字:

download