本文介紹使用OSS Android SDK的常見問題及解決方案。
說明
使用樣本前需要進行HTTPDNS Android SDK接入,具體請參見Android SDK接入。
Android SDK是否支援DNS預解析和緩衝策略?
以下提供Android結合HTTPDNS SDK以及OkHttp實現DNS預解析和緩衝策略的樣本。
自訂DNS介面
public class OkHttpDns implements Dns { private static final Dns SYSTEM = Dns.SYSTEM; HttpDnsService httpdns; private static OkHttpDns instance = null; private OkHttpDns(Context context) { this.httpdns = HttpDns.getService(context, "account id"); } public static OkHttpDns getInstance(Context context) { if(instance == null) { instance = new OkHttpDns(context); } return instance; } @Override public List<InetAddress> lookup(String hostname) throws UnknownHostException { //通過非同步解析介面擷取IP地址。 String ip = httpdns.getIpByHostAsync(hostname); if(ip != null) { // 如果返回的IP地址不為null,則直接使用該IP地址進行網路請求。 List<InetAddress> inetAddresses = Arrays.asList(InetAddress.getAllByName(ip)); Log.e("OkHttpDns", "inetAddresses:" + inetAddresses); return inetAddresses; } // 如果返回的IP地址為null,則通過系統DNS服務解析網域名稱。 return Dns.SYSTEM.lookup(hostname); } }
產生okHttpClient執行個體,並配置到OSS。
String endpoint = "http://oss-cn-hangzhou.aliyuncs.com"; ClientConfiguration conf = new ClientConfiguration(); conf.setConnectionTimeout(15 * 1000); // 連線逾時,預設15秒。 conf.setSocketTimeout(15 * 1000); // socket逾時,預設15秒。 conf.setMaxConcurrentRequest(5); // 最大並發請求書,預設5個。 conf.setMaxErrorRetry(2); // 失敗後最大重試次數,預設2次。 OkHttpClient.Builder builder = new OkHttpClient.Builder() .dns(OkHttpDns.getInstance(getApplicationContext())); // 如果設定了自訂的okHttpClient,ClientConfiguration的部分設定將會失效。您需要將其手動設定到builder上。 if (conf != null) { Dispatcher dispatcher = new Dispatcher(); dispatcher.setMaxRequests(conf.getMaxConcurrentRequest()); builder.connectTimeout(conf.getConnectionTimeout(), TimeUnit.MILLISECONDS) .readTimeout(conf.getSocketTimeout(), TimeUnit.MILLISECONDS) .writeTimeout(conf.getSocketTimeout(), TimeUnit.MILLISECONDS) .followRedirects(conf.isFollowRedirectsEnable()) .followSslRedirects(conf.isFollowRedirectsEnable()) .dispatcher(dispatcher); if (conf.getProxyHost() != null && conf.getProxyPort() != 0) { builder.proxy(new Proxy(Proxy.Type.HTTP, new InetSocketAddress(conf.getProxyHost(), conf.getProxyPort()))); } } // 僅Android SDK 2.9.12或以上版本支援使用conf.setOkHttpClient();方法。 conf.setOkHttpClient(builder.build()); OSS oss = new OSSClient(getApplicationContext(), endpoint, credentialProvider, conf);
部分檔案下載時,進度回調顯示totalSize=-1
問題原因
當檔案Content-Type為text/cache-manifest、text/xml、text/plain、text/css、application/javascript、application/x-javascript、application/rss+xml、application/json或者text/json,且檔案大於或者等於1 KB時,如果Header中顯式設定了Accept-Encoding:gzip,則OSS會返回經過Gzip壓縮的資料。下載檔案時,即使未設定Accept-Encoding,okhttp也會自動填滿為Accept-Encoding:gzip。而經過Gzip壓縮後無法得知檔案大小,即不會返回Content-Length,因此進度回調顯示totalSize=-1。
解決方案
通過設定Range的方式阻斷okhttp自動填滿Accept-Encoding:gzip,此時將返回Content-Length,並正常顯示進度回調。
Map<String, String> header = new HashMap<>(); header.put("x-oss-range-behavior", "standard"); // 依次填寫Bucket名稱(例如examplebucket)和Object完整路徑(例如exampledir/exampleobject.txt)。 GetObjectRequest get = new GetObjectRequest("examplebucket", "exampledir/exampleobject.txt"); get.setRange(new Range(0, -1)); get.setRequestHeaders(header); OSSAsyncTask task = oss.asyncGetObject(get, new OSSCompletedCallback<GetObjectRequest, GetObjectResult>() { @Override public void onSuccess(GetObjectRequest request, GetObjectResult result) { // 請求成功。 InputStream inputStream = result.getObjectContent(); byte[] buffer = new byte[2048]; int len; try { while ((len = inputStream.read(buffer)) != -1) { // 處理下載的資料。 } } catch (IOException e) { e.printStackTrace(); } } @Override public void onFailure(GetObjectRequest request, ClientException clientExcepion, ServiceException serviceException) { // 請求異常。 if (clientExcepion != null) { // 本地異常如網路異常等。 clientExcepion.printStackTrace(); } if (serviceException != null) { // 服務異常。 Log.e("ErrorCode", serviceException.getErrorCode()); Log.e("RequestId", serviceException.getRequestId()); Log.e("HostId", serviceException.getHostId()); Log.e("RawMessage", serviceException.getRawMessage()); } } });