Problemas comuns e soluções do OSS SDK for Android.
Antes de usar o código de exemplo, instale o HTTPDNS SDK for Android. Para mais informações, consulte Instale o HTTPDNS SDK for Android.
O OSS SDK for Android oferece suporte a pré-resolução de DNS e políticas de cache?
Sim. Use o HTTPDNS SDK com o OkHttp para implementar a pré-resolução de DNS e o cache.
-
Implemente a API de um serviço personalizado de resolução de 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 { // Use the asynchronous resolution API to get the IP address. String ip = httpdns.getIpByHostAsync(hostname); if(ip != null) { // IP address resolved — use it for the network request. List<InetAddress> inetAddresses = Arrays.asList(InetAddress.getAllByName(ip)); Log.e("OkHttpDns", "inetAddresses:" + inetAddresses); return inetAddresses; } // No IP address resolved — fall back to the system DNS service. return Dns.SYSTEM.lookup(hostname); } } -
Crie uma instância de OkHttpClient e configure a pré-resolução de DNS e o cache.
String endpoint = "http://oss-cn-hangzhou.aliyuncs.com"; ClientConfiguration conf = new ClientConfiguration(); conf.setConnectionTimeout(15 * 1000); // Connection timeout. Default: 15 seconds. conf.setSocketTimeout(15 * 1000); // Socket timeout. Default: 15 seconds. conf.setMaxConcurrentRequest(5); // Maximum concurrent requests. Default: 5. conf.setMaxErrorRetry(2); // Maximum retries. Default: 2. OkHttpClient.Builder builder = new OkHttpClient.Builder() .dns(OkHttpDns.getInstance(getApplicationContext())); // If you set a custom OkHttpClient, it overrides some ClientConfiguration settings. // Set those settings directly on the 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 and later support conf.setOkHttpClient(). conf.setOkHttpClient(builder.build()); OSS oss = new OSSClient(getApplicationContext(), endpoint, credentialProvider, conf);
Por que o callback de progresso retorna totalSize=-1 ao baixar objetos?
Causa: Ao baixar objetos de determinados tipos de conteúdo (text/cache-manifest, text/xml, text/plain, text/css, application/javascript, application/x-javascript, application/rss+xml, application/json, text/json) com tamanho igual ou superior a 1 KB, o OkHttp adiciona automaticamente o cabeçalho Accept-Encoding: gzip, mesmo sem definição explícita. Assim, o OSS retorna o objeto em formato gzip sem o cabeçalho Content-Length, fazendo o callback de progresso reportar totalSize=-1.
-
Solução: Defina um cabeçalho Range na solicitação. Isso impede que o OkHttp adicione o Accept-Encoding: gzip, permitindo que o OSS retorne o Content-Length e o callback de progresso informe o tamanho correto.
Map<String, String> header = new HashMap<>(); header.put("x-oss-range-behavior", "standard"); // Replace examplebucket with your bucket name and exampledir/exampleobject.txt with the full object path. 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) { // Process the downloaded data. } } catch (IOException e) { e.printStackTrace(); } } @Override public void onFailure(GetObjectRequest request, ClientException clientExcepion, ServiceException serviceException) { if (clientExcepion != null) { // Handle a local exception, such as a network error. clientExcepion.printStackTrace(); } if (serviceException != null) { // Handle a service exception. Log.e("ErrorCode", serviceException.getErrorCode()); Log.e("RequestId", serviceException.getRequestId()); Log.e("HostId", serviceException.getHostId()); Log.e("RawMessage", serviceException.getRawMessage()); } } });
Callback onFailure não acionado no Kotlin
Causa
Esse comportamento decorre da nulabilidade do Kotlin. O OSS Android SDK define o callback onFailure com sintaxe Java, na qual os parâmetros são não nulos por padrão:
@Override
public void onFailure(GetObjectRequest request, ClientException clientException, ServiceException serviceException){
if (clientException != null) {
clientException.printStackTrace();
}
if (serviceException != null) {
Log.e("ErrorCode", serviceException.getErrorCode());
Log.e("RequestId", serviceException.getRequestId());
Log.e("HostId", serviceException.getHostId());
Log.e("RawMessage", serviceException.getRawMessage());
}
}
O Kotlin trata esses parâmetros como não nulos, o que pode impedir o acionamento esperado do callback. Declare explicitamente os parâmetros como anuláveis na assinatura da função:
onFailure(request: ResumableUploadRequest, clientException: ClientException?, serviceException: ServiceException?)