对象存储

  • 对象存储 > SDK 下载 > Android SDK

    Android SDK

    最近更新时间: 2023-12-14 18:55:15

    Android SDK 只包含了最终用户使用场景中的必要功能。相比服务端 SDK 而言,客户端 SDK 不会包含对云存储服务的管理和配置功能。

    安装

    直接安装

    将 sdk jar文件 复制到项目中去, jar包下载地址 , 下载对应的 jar 包,以及搜索下载对应的依赖库

    happy-dns下载地址

    通过 maven

    • Android Studio中添加 dependencies 或者在项目中添加 maven 依赖
    // 1 直接导入
    implementation 'com.qiniu:qiniu-android-sdk:8.7.+'
    
    // 2 如果要修改okhttp依赖的版本,可采用以下方式(强烈建议使用七牛库依赖的okhttp版本)
    implementation ('com.qiniu:qiniu-android-sdk:8.7.+'){
        exclude (group: 'com.squareup.okhttp3', module: 'okhttp')
    }
    implementation 'com.squareup.okhttp3:okhttp:4.2.2'
    
    • 如果是 eclipse, 也可以直接添加依赖来处理。

    相关链接

    如果需要下载历史 jar 包,也可以在 maven 上直接下载,请参考 这里

    安全机制

    该 SDK 未包含凭证生成相关的功能。开发者对安全性的控制应遵循 安全机制 中建议的做法,即客户端应向业务服务器每隔一段时间请求 上传凭证 ,而不是直接在客户端使用 AccessKey / SecretKey 生成对应的凭证。在客户端使用 SecretKey 会导致严重的安全隐患。

    开发者可以在生成上传凭证前通过配置 上传策略 来控制上传的后续动作,例如在上传完成后通过回调机制通知业务服务器。该工作在业务服务器端进行,因此非本 SDK 的功能范畴。

    存储的服务端 SDK 提供了上传凭证的生成功能,请参考各个服务端语言的 SDK 文档。

    初始化

    一般情况下可以直接使用默认设置,不用单独配置。可以配置超时时长、分片上传阀值等。

    
    // AutoZone:自动根据 bucket 去查询相应 Zone,Zone 信息会被缓存
    Zone zone = new AutoZone();
    
    // 根据区域 ID 创建 Zone,无需查询,强烈推荐使用 AutoZone
    zone = FixedZone.createWithRegionId("z0");
    
    Configuration config = new Configuration.Builder()
                        .connectTimeout(90)              // 链接超时。默认90秒
                        .useHttps(true)                  // 是否使用https上传域名
                        .useConcurrentResumeUpload(true) // 使用并发上传,使用并发上传时,除最后一块大小不定外,其余每个块大小固定为4M,
                        .resumeUploadVersion(Configuration.RESUME_UPLOAD_VERSION_V2) // 使用新版分片上传
                        .concurrentTaskCount(3)          // 并发上传线程数量为3
                        .responseTimeout(90)             // 服务器响应超时。默认90秒
                        .recorder(recorder)              // recorder分片上传时,已上传片记录器。默认null
                        .recorder(recorder, keyGen)      // keyGen 分片上传时,生成标识符,用于片记录器区分是那个文件的上传记录
                        .zone(zone)                      // 设置区域,不指定会默认使用 AutoZone;指定不同区域的上传域名、备用域名、备用IP。
                        .build();
    
    // 重用uploadManager。一般地,只需要创建一个uploadManager对象
    UploadManager uploadManager = new UploadManager(config);
    
    

    支持使用 HTTP/3 协议发起请求

    导入 HTTP/3 client 插件,http3 client 插件依赖于 Android SDK v8.5.0及以上版本

    安装导入

    // 移除 qiniu-android-sdk 依赖 : implementation 'com.qiniu:qiniu-android-sdk:x.x.+' 
    implementation 'com.qiniu:qiniu-android-curl-plugin:1.0.0'
    

    使用 CurlClient

    import com.qiniu.client.curl.CurlClient;
    import com.qiniu.android.storage.GlobalConfiguration;
    import com.qiniu.android.storage.Configuration;
    import com.qiniu.android.storage.UploadManager;
    
    // 开启 HTTP/3
    GlobalConfiguration.getInstance().enableHttp3 = true;
    
    // @param caPath: SSL 证书本地路径;如果想自定义 CA 可设置此选项,此处为 CA 文件的本地路径。
    // 				  如果未定义(caPath 配置 null)则使用 SDK 内部提供的 CA 证书,证书来源:https://curl.se/ca/cacert.pem
    CurlClient client = new CurlClient(caPath);
    Configuration config = new Configuration.Builder()
                    .requestClient(client) // 指定 HTTP/3 Client
                    .build();
    UploadManager manager = new UploadManager(config);
    

    版本

    Qiniu SDK 版本 最低 Android版本 依赖库版本
    8.7.x Android 4.0+ okhttp 4+ (如果要支持 Android 4.0+,需调整 okhttp 至 v3.12.+)
    8.6.x Android 4.0+ okhttp 4+ (如果要支持 Android 4.0+,需调整 okhttp 至 v3.12.+)
    8.5.x Android 4.0+ okhttp 4+ (如果要支持 Android 4.0+,需调整 okhttp 至 v3.12.+)
    8.4.x Android 4.0+ okhttp 4+ (如果要支持 Android 4.0+,需调整 okhttp 至 v3.12.+)
    8.3.1+ Android 4.0+ okhttp 4+ (如果要支持 Android 4.0+,需调整 okhttp 至 v3.12.+)
    8.3.0 Android 5.0+ okhttp 4+
    8.2.x Android 5.0+ okhttp 4+
    8.1.x Android 5.0+ okhttp 4+
    8.0.x Android 5.0+ okhttp 4+
    7.6.x Android 5.0+ okhttp 4+
    7.5.x Android 5.0+ okhttp 4+
    7.4.6 Android 4.0+ okhttp 3.12.6
    7.3.x Android 2.3+ okhttp 3.11.0
    7.2.x Android 2.3+ okhttp 3+
    7.1.x Android 2.3+ okhttp 2.6+
    7.0.8,7.0.9 Android 2.2+ android-async-http 1.4.9
    7.0.7 Android 2.2+ android-async-http 1.4.8

    注意

    • 从 7.4.0 开始增加了 DNS 预解析和缓存策略,减少 DNS 解析错误。
    • 从 8.0.0 开始增加了每 2 分钟(进程非挂起状态)自动刷新缓存策略。同时增加了 DNS 预解析 开关控制,默认开启,可以通过 GlobalConfiguration.getInstance().isDnsOpen = false 关闭 DNS 预解析。
    • 从 8.3.2 开始对 DNS 预解析缓存增加缓存有效期,默认 10 分钟,支持手动修改有效期 GlobalConfiguration.getInstance().dnsCacheMaxTTL 时长,单位为 s。
    • SDK 内置服务系统 DNS 解析和 HTTP DNS 解析(从 8.4.0 开始),如果这两个 DNS 解析不能满足您的需求,那么可以选择自定义 DNS 解析。如果您自定义了 DNS 解析,那么在 DNS 预取时会优先使用您自定义的 DNS 解析。
    • 如果可以明确区域的话,最好指定固定区域,这样可以少一步网络请求,少一步出错的可能。
    • 如果使用 Android 4.x ,对应 okhttp 版本请调整至 3.12.+

    下方为 8.4.0 以上版本使用外部 DNS 组件 HappyDns 的示例代码(实例仅为使用方式演示,请根据您的需求自行处理 DNS 解析细节),如果找不到 Dns 类,可在工程的 Gradle 配置中依赖 HappyDns

    implementation 'com.qiniu:happy-dns:2.0.1'
    

    具体实例代码如下:

    import com.qiniu.android.dns.DnsManager;
    import com.qiniu.android.dns.Domain;
    import com.qiniu.android.dns.IResolver;
    import com.qiniu.android.dns.NetworkInfo;
    import com.qiniu.android.dns.Record;
    import com.qiniu.android.dns.dns.DnsUdpResolver;
    import com.qiniu.android.dns.dns.DohResolver;
    import com.qiniu.android.http.dns.Dns;
    import com.qiniu.android.http.dns.IDnsNetworkAddress;
    
    import java.io.IOException;
    import java.net.UnknownHostException;
    import java.util.ArrayList;
    import java.util.List;
    
    class CustomDns {
    
        public static Dns buildDefaultDns() {
    //         可添加修改 多个 IResolver
    //         适当调整不同 IResolver 的加入顺序,比如:223.5.5.5,119.29.29.29,114.114.114.114,8.8.8.8 等
            IResolver[] resolvers = new IResolver[2];
            // 自定义 DNS 服务器地址: UDP
            resolvers[0] = new DnsUdpResolver("8.8.8.8");
            // 自定义 DNS 服务器地址:Doh
            resolvers[1] = new DohResolver("https://dns.alidns.com/dns-query");
    
            final DnsManager dnsManager = new DnsManager(NetworkInfo.normal, resolvers);
            Dns dns = new Dns() {
                // 若抛出异常 Exception ,sdk 会使用 okhttp 组件默认 dns 解析结果
                @Override
                public List<IDnsNetworkAddress> lookup(String hostname) throws UnknownHostException {
                    Domain domain = new Domain(hostname);
                    List<IDnsNetworkAddress> addressList = null;
                    try {
                        Record[] records = dnsManager.queryRecords(domain);
                        if (records != null && records.length > 0) {
                            addressList = new ArrayList<>();
                            for (Record record : records) {
                                String source = "customized";
                                DemoDnsNetworkAddress address = new DemoDnsNetworkAddress(hostname, record.value, (long) record.ttl, source, record.timeStamp);
                                addressList.add(address);
                            }
                        }
                    } catch (IOException ignored) {
                    }
                    return addressList;
                }
            };
            return dns;
        }
    
        public static class DemoDnsNetworkAddress implements IDnsNetworkAddress {
            private final String hostValue;
            private final String ipValue;
            private final Long ttlValue;
            private final String sourceValue;
            private final Long timestampValue;
    
            DemoDnsNetworkAddress(String hostValue,
                                  String ipValue,
                                  Long ttlValue,
                                  String sourceValue,
                                  Long timestampValue) {
                this.hostValue = hostValue;
                this.ipValue = ipValue;
                this.ttlValue = ttlValue;
                this.sourceValue = sourceValue;
                this.timestampValue = timestampValue;
            }
    
            @Override
            public String getHostValue() {
                return hostValue;
            }
    
            @Override
            public String getIpValue() {
                return ipValue;
            }
    
            @Override
            public Long getTtlValue() {
                return ttlValue;
            }
    
            @Override
            public String getSourceValue() {
                return sourceValue;
            }
    
            @Override
            public Long getTimestampValue() {
                return timestampValue;
            }
        }
    }
    
    // 在 UploadManager 初始化之前配置    
    GlobalConfiguration.getInstance().dns = CustomDns.buildDefaultDns();
    uploadManager = new UploadManager();
    

    上传文件

    SDK 内置两种上传方式:表单上传和分片上传,并根据具体情况,内部做了自动切换。表单上传使用一个 HTTP POST 请求完成文件的上传,因此比较适合较小的文件和较好的网络环境。相比而言,分片上传更能适应不稳定的网络环境,也比较适合上传比较大的文件(例如数百 MB 或更大)。

    若需深入了解上传方式之间的区别,请参阅上传类型中 表单上传分片上传 v1 版分片上传 v2 版 接口说明。

    UploadManager.put 参数说明:

    参数 类型 说明
    data byte[]/String/File 数据,可以是 byte 数组、文件路径、文件、数据流和 Uri 资源
    key String 保存在服务器上的资源唯一标识,请参阅 键值对
    token String 服务器分配的 token
    completionHandler UpCompletionHandler 上传回调函数,必填
    options UploadOptions 如果需要进度通知、中途取消、指定 mimeType,则需要填写相应字段,详见下面的 UploadOptions 参数说明

    UploadOptions 参数说明:

    参数 类型 说明
    params Map<String, String> 自定义变量,key 必须以 x: 开始
    mimeType String 指定文件的 mimeType
    progressHandler UpProgressHandler 上传进度回调
    cancellationSignal UpCancellationSignal 取消上传,当 isCancelled() 返回 true 时,不再执行更多上传

    简单上传

    可以参考 简单上传demo

    //指定 zone 的具体区域 
    //FixedZone.zone0   华东机房
    //FixedZone.zone1   华北机房
    //FixedZone.zone2   华南机房
    //FixedZone.zoneNa0 北美机房
    
    //自动识别上传区域 
    //AutoZone.autoZone
    
    
    //Configuration config = new Configuration.Builder()
    //.zone(Zone.autoZone)
    //.zone(FixedZone.zone0)
    //.resumeUploadVersion(Configuration.RESUME_UPLOAD_VERSION_V2)
    //.build();
    //UploadManager uploadManager = new UploadManager(config);
    data = <File 对象 或 文件路径 或 字节数组 或 数据流 或 Uri 资源>
    String key = <指定七牛服务上的文件名,或 null>;
    String token = <从服务端 SDK 获取>;
    uploadManager.put(data, key, token,
        new UpCompletionHandler() {
            @Override
            public void complete(String key, ResponseInfo info, JSONObject res) {
                //res 包含 hash、key 等信息,具体字段取决于上传策略的设置
                 if(info.isOK()) {
                    Log.i("qiniu", "Upload Success");
                 } else {
                    Log.i("qiniu", "Upload Fail");
                    //如果失败,这里可以把 info 信息上报自己的服务器,便于后面分析上传错误原因
                 }
                 Log.i("qiniu", key + ",\r\n " + info + ",\r\n " + res);
                }
            }, null);
    
    

    回调参数说明:

    参数 说明
    key uploadManager.put(file, key, …) 方法指定的 key
    info http 请求的状态信息等,可记入日志,isOK() 返回 true 表示上传成功
    response 存储服务反馈的信息。可从中解析保存在存储服务的 key 等信息,具体字段取决上传策略的设置

    注意: 可能会抛出文件不存在,没有权限访问等异常。

    上传进度

    uploadManager.put(data, key, token,handler,
        new UploadOptions(null, null, false,
            new UpProgressHandler(){
                public void progress(String key, double percent){
                    Log.i("qiniu", key + ": " + percent);
                }
            }, null));
                    
    

    注意: progress(key, percent) 中的 key 即 uploadManager.put(file, key, …) 方法指定的 key。

    取消上传

    内部代码会检测 UpCancellationSignal##isCancelled() 的返回值,当其返回 true 时,将停止上传。可外部维护一个变量 isCancelled,当点击取消按钮时,设置 isCancelled = true; 。例如:

    // 初始化、执行上传
    private volatile boolean isCancelled = false;
    
    uploadManager.put(data, key, token,handler,
        new UploadOptions(null, null, false, progressHandler,
            new UpCancellationSignal(){
                public boolean isCancelled(){
                    return isCancelled;
                }
            }));
    
    
    // 点击取消按钮,让 UpCancellationSignal##isCancelled() 方法返回 true,以停止上传
    private void cancell() {
        isCancelled = true;
    }
    
    

    记录断点

    分片上传中,可将各个已上传的块记录下来,再次上传时,已上传的部分不用再次上传。断点记录类需实现 com.qiniu.android.storage.Recorder 接口。已提供保存到文件的 FileRecorder 实现。

    String dirPath = <断点记录文件保存的文件夹位置>
    Recorder recorder = null;
    try {
            recorder = new FileRecorder(dirPath);
    }catch (Exception e){
    
    }
    
    //默认使用 key 的 url_safe_base64 编码字符串作为断点记录文件的文件名
    //避免记录文件冲突(特别是 key 指定为 null 时),也可自定义文件名(下方为默认实现):
    KeyGenerator keyGen = new KeyGenerator(){
        public String gen(String key, File file){
            // 不必使用url_safe_base64转换,uploadManager内部会处理
            // 该返回值可替换为基于key、文件内容、上下文的其它信息生成的文件名
            return key + "_._" + new StringBuffer(file.getAbsolutePath()).reverse();
        }
    };
    
    // 重用 uploadManager。一般地,只需要创建一个 uploadManager 对象
    //UploadManager uploadManager = new UploadManager(recorder);  // 1
    //UploadManager uploadManager = new UploadManager(recorder, keyGen); // 2
    // 或在初始化时指定:
    Configuration config = new Configuration.Builder()
                        .resumeUploadVersion(Configuration.RESUME_UPLOAD_VERSION_V2)
                        // recorder分片上传时,已上传片记录器
                        // keyGen分片上传时,生成标识符,用于片记录器区分是哪个文件的上传记录
                        .recorder(recorder, keyGen)  
                        .build();
    UploadManager uploadManager = new UploadManager(config);
    
    uploadManager.put(data, key, ...)
    
    

    分片上传

    可以选择分片上传版本,推荐 Configuration.RESUME_UPLOAD_VERSION_V2,表示 分片上传 v2 版,默认 Configuration.RESUME_UPLOAD_VERSION_V1,表示 分片上传 v1 版,兼容历史情况。

    Configuration config = new Configuration.Builder()
                        .resumeUploadVersion(Configuration.RESUME_UPLOAD_VERSION_V2) // 选择分片上传版本
                        .useConcurrentResumeUpload(true) // 开启并发上传,默认为 NO
                        .concurrentTaskCount(3)  // 默认并行线程数为 3
                        .build();
    UploadManager uploadManager = new UploadManager(config);
    
    uploadManager.put(data, key, ...)
    

    下载文件

    该 SDK 并未提供下载文件相关的功能接口,因为文件下载是一个标准的 HTTP GET 过程。开发者只需理解资源 URI 的组成格式即可非常方便的构建资源 URI,并在必要的时候加上 下载凭证 ,即可使用 HTTP GET 请求获取相应资源。

    从安全性和代码可维护性的角度考虑,我们建议下载 URL 的拼接过程也在业务服务器进行,让客户端从业务服务器请求。

    线程安全性

    Android 一般情况下会使用一个主线程来控制 UI,非主线程无法控制 UI,在 Android4.0+ 之后不能在主线程完成网络请求,该 SDK 是根据以上的使用场景设计,所有的网络操作均使用独立的线程异步运行,UpCompletionHandler##complete、UpProgressHandler##progress 是在主线程调用的,在回调函数内可以直接操作 UI 控件。

    API 参考

    常见问题

    1. 混淆处理

    七牛 的 SDK 不需要做特殊混淆处理,如果有混淆,请将 七牛 相关的包都排除。

    • 在 Android Studio 中,混淆配置在 proguard-rules.pro 里面加上下面几行混淆代码就行:
    -keep class com.qiniu.**{*;}
    -keep class com.qiniu.**{public <init>();}
    -ignorewarnings
    

    注意:-ignorewarnings 这个也是必须加的,如果不加这个,编译的时候可能可以通过,但是 release 的时候还是会出现错误。

    • 在 eclipse 中,混淆配置在 proguard-project.txt 里面加上下面几行混淆代码就行:
    -keep class com.qiniu.**{*;}
    -keep class com.qiniu.**{public <init>();}
    -ignorewarnings
    

    2. 为什么进度会在 95% 停很久?

    因为上传进度是用 sdk 写入 socket 的字节数/总字节数作为进度,但写入 socket 不等于服务器收到并且处理完成,中间还有一段时间,如果只是用字节数就会出现更怪异的情况,在 100% 停留很久,所以综合考虑就使用了 95% 这个值。

    如果您发现上传卡在 95% 很长的时间,请检查是否使用了上传回调业务服务器的功能,请确保业务服务器接受到上传回调后可以返回正确的状态码和回复内容。

    3. 如何才能得到下载的 url ?

    上传没有域名概念,只有 bucket ,一个 bucket 可以绑定多个域名。下载的 url 可以用 bucket 里的域名加 key 拼接而成,私有 url 还要加上 token。

    4. 如何跳过 https 证书验证?

    在部分测试环境发生 Trust anchor for certification path not found 异常,可以设置跳过 https 证书验证:在 Configuration 配置 useHttps( false ) 开启。

    相关资源

    如果您有任何关于我们文档或产品的建议和想法,欢迎您通过以下方式与我们互动讨论:

    • 技术论坛 - 在这里您可以和其他开发者愉快的讨论如何更好的使用七牛云服务
    • 提交工单 - 如果您的问题不适合在论坛讨论或希望及时解决,您也可以提交一个工单,我们的技术支持人员会第一时间回复您
    • 博客 - 这里会持续更新发布市场活动和技术分享文章
    • 微博
    • 常见问题

    贡献代码

    1. Fork

    2. 创建您的特性分支 git checkout -b my-new-feature

    3. 提交您的改动 git commit -am ‘Added some feature’

    4. 将您的修改记录提交到远程 git 仓库 git push origin my-new-feature

    5. 然后到 github 网站的该 git 远程仓库的 my-new-feature 分支下发起 Pull Request

    许可证

    Copyright © 2021 qiniu.com

    基于 MIT 协议发布:

    以上内容是否对您有帮助?
  • Qvm free helper
    Close