OSS SDK for Android の一般的な問題と解決策について説明します。
サンプルコードを使用する前に、HTTPDNS SDK for Android をインストールする必要があります。詳細については、「HTTPDNS SDK for Android のインストール」をご参照ください。
OSS SDK for Android は DNS 事前解決とキャッシュポリシーをサポートしていますか
はい、サポートしています。HTTPDNS SDK と OkHttp を組み合わせて使用することで、DNS 事前解決とキャッシュを実装できます。
-
カスタム DNS 解決サービス用の API を実装します。
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 { // 非同期解決 API を使用して IP アドレスを取得します。 String ip = httpdns.getIpByHostAsync(hostname); if(ip != null) { // IP アドレスが解決された場合は、ネットワークリクエストに使用します。 List<InetAddress> inetAddresses = Arrays.asList(InetAddress.getAllByName(ip)); Log.e("OkHttpDns", "inetAddresses:" + inetAddresses); return inetAddresses; } // IP アドレスが解決されなかった場合は、システム DNS サービスにフォールバックします。 return Dns.SYSTEM.lookup(hostname); } } -
OkHttpClient インスタンスを作成し、DNS 事前解決とキャッシュを設定します。
String endpoint = "http://oss-cn-hangzhou.aliyuncs.com"; ClientConfiguration conf = new ClientConfiguration(); conf.setConnectionTimeout(15 * 1000); // 接続タイムアウト。デフォルト:15 秒。 conf.setSocketTimeout(15 * 1000); // ソケットタイムアウト。デフォルト: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 が返される理由
原因: OkHttp が特定のコンテンツタイプ (text/cache-manifest、text/xml、text/plain、text/css、application/javascript、application/x-javascript、application/rss+xml、application/json、text/json) で 1 KB 以上のオブジェクトをダウンロードする場合、ユーザーが設定していなくても、自動的に Accept-Encoding: gzip ヘッダーを追加します。その結果、OSS は Content-Length ヘッダーを含まない gzip 形式でオブジェクトを返すため、進捗コールバックで totalSize=-1 が報告されます。
-
解決策: リクエストに Range ヘッダーを設定します。これにより、OkHttp が Accept-Encoding: gzip を追加しなくなるため、OSS は Content-Length を返し、進捗コールバックで正しいサイズが報告されます。
Map<String, String> header = new HashMap<>(); header.put("x-oss-range-behavior", "standard"); // examplebucket をバケット名に、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 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()); } } });
Kotlin で onFailure コールバックがトリガーされない
原因
これは Kotlin の null 許容性の問題です。OSS Android SDK は Java 構文を使用して onFailure コールバックを定義しており、パラメータはデフォルトで非 null 許容となっています。
@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());
}
}
Kotlin はこれらのパラメータを非 null 許容として扱うため、コールバックが期待通りにトリガーされない場合があります。関数シグネチャでパラメータを明示的に null 許容として宣言してください。
onFailure(request: GetObjectRequest?, clientException: ClientException?, serviceException: ServiceException?)