Skip to content

ネットワーク処理とオフラインサポート

概要

SORI SDKはオーディオ認識をデバイス内で処理します。ただし、関連するキャンペーンの受信、関連画像リソースの読み込み、およびWebhookリレーのためにインターネット接続が必要です。オーディオ認識が成功し、その処理時にネットワークが利用できない場合、これを処理するためのいくつかの方法を提供します。

主な機能

  • 自動ネットワーク検出: SDKはネットワーク接続の変更をリアルタイムで監視します
  • リクエストキューイング: ネットワークの問題で失敗したリクエストはメモリ内のキューに保存されます
  • 自動リトライ: ネットワークが利用可能になると、キューに入っているリクエストが自動的に処理されます
  • 設定可能な動作: SDKがネットワーク障害を処理する方法をカスタマイズできます
  • 時間ベースの有効期限: 10分以上経過したリクエストは自動的に破棄されます

設定

SORIConfigクラスを使用してネットワーク処理の動作を設定できます:

kotlin
import com.iplateia.sorisdk.SORIConfig
import com.iplateia.sorisdk.SORIAudioRecognizer

// カスタム設定を作成
val config = SORIConfig(
    enableDelayedSending = true,     // リクエストキューイングを有効化
    maxPendingTimeMillis = 600000L,   // 10分(デフォルト)
    autoRetryOnNetworkRecovery = true // ネットワーク復旧時に自動リトライ
)

// 認識器に設定を適用
val sori = SORIAudioRecognizer(
    "YOUR_SORI_API_KEY",
    "YOUR_SORI_SECRET_KEY"
)
sori.setConfig(config)
java
import com.iplateia.sorisdk.SORIConfig;
import com.iplateia.sorisdk.SORIAudioRecognizer;

// カスタム設定を作成
SORIConfig config = new SORIConfig(
    true,     // enableDelayedSending
    600000L,  // maxPendingTimeMillis(10分)
    true      // autoRetryOnNetworkRecovery
);

// 認識器に設定を適用
SORIAudioRecognizer sori = new SORIAudioRecognizer(
    "YOUR_SORI_API_KEY",
    "YOUR_SORI_SECRET_KEY"
);
sori.setConfig(config);

ネットワークイベントの処理

SDKはISORIListenerインターフェースを通じて、ネットワーク関連のイベントに対するコールバックを提供します:

kotlin
import com.iplateia.sorisdk.ISORIListener
import com.iplateia.sorisdk.SORICampaign

class MyListener : ISORIListener {
    override fun onReady() {
        // SDKの準備完了
    }
    
    override fun onStateChanged(state: String) {
        // サービスの状態が変更された
    }
    
    override fun onError(error: String) {
        // エラーが発生した
    }
    
    override fun onCampaignFound(campaign: SORICampaign) {
        // キャンペーンが正常に検出された
    }
    
    override fun onNetworkError(materialId: String, error: String) {
        // アクティビティ送信中にネットワークエラーが発生
        Log.w("SORI", "素材 $materialId のネットワークエラー: $error")
        // ここでユーザーフレンドリーなメッセージを表示できます
    }
    
    override fun onRequestQueued(materialId: String, queueCount: Int) {
        // リクエストが後で配信するためにキューに追加された
        Log.i("SORI", "素材 $materialId のリクエストがキューに追加されました。キューサイズ: $queueCount")
        // リクエストが後で送信されることをユーザーに通知できます
    }
}

// リスナーを設定
sori.setListener(context, MyListener())
java
import com.iplateia.sorisdk.ISORIListener;
import com.iplateia.sorisdk.SORICampaign;
import android.util.Log;

public class MyListener implements ISORIListener {
    @Override
    public void onReady() {
        // SDKの準備完了
    }
    
    @Override
    public void onStateChanged(String state) {
        // サービスの状態が変更された
    }
    
    @Override
    public void onError(String error) {
        // エラーが発生した
    }
    
    @Override
    public void onCampaignFound(SORICampaign campaign) {
        // キャンペーンが正常に検出された
    }
    
    @Override
    public void onNetworkError(String materialId, String error) {
        // アクティビティ送信中にネットワークエラーが発生
        Log.w("SORI", "素材 " + materialId + " のネットワークエラー: " + error);
        // ここでユーザーフレンドリーなメッセージを表示できます
    }
    
    @Override
    public void onRequestQueued(String materialId, int queueCount) {
        // リクエストが後で配信するためにキューに追加された
        Log.i("SORI", "素材 " + materialId + 
               " のリクエストがキューに追加されました。キューサイズ: " + queueCount);
        // リクエストが後で送信されることをユーザーに通知できます
    }
}

// リスナーを設定
sori.setListener(context, new MyListener());

設定オプション

SORIConfigパラメータ

パラメータデフォルト説明
enableDelayedSendingBooleantrueネットワークが利用できない時のリクエストキューイングを有効/無効化。falseの場合、失敗したリクエストは即座に破棄されます。
maxPendingTimeMillisLong600000(10分)保留中のリクエストを破棄するまでの最大保持時間
autoRetryOnNetworkRecoveryBooleantrueネットワークが利用可能になった時に保留中のリクエストを自動的に再試行。falseの場合、リクエストはキューに残りますが手動処理が必要です。enableDelayedSendingtrueの場合のみ有効です。

設定オプションの理解

2つのbooleanオプションは異なる目的を持っています:

  • enableDelayedSendingネットワークが利用できない時に失敗したリクエストをキューに保存するかどうかを制御

    • true:ネットワークが利用できない時に失敗したリクエストを後の配信のために保存
    • false:ネットワークが利用できない時に失敗したリクエストを即座に破棄
    • 注意:ネットワークが利用可能な場合、この設定値に関係なくリクエストは常に即座に送信されます
  • autoRetryOnNetworkRecovery:ネットワークが復旧した時の動作を制御

    • true:ネットワーク復旧時にキュー内のリクエストを自動的に処理
    • false:リクエストをキューに保持するが自動処理しない(手動介入が必要)
    • 注意:この設定はenableDelayedSendingtrueの場合のみ有効です

動作の概要

ネットワーク状態enableDelayedSending動作
利用可能true または false即座に送信
利用不可true後で配信するためにキューに保存
利用不可falseリクエスト破棄

ほとんどのアプリケーションは両方の設定を同じ値で使用します(復元力のある動作のためにtrue、即座の失敗処理のためにfalse)。

ベストプラクティス

1. ユーザーフィードバック

ネットワークの問題が発生した場合は、常にユーザーにフィードバックを提供してください:

kotlin
override fun onNetworkError(materialId: String, error: String) {
    runOnUiThread {
        Toast.makeText(
            context,
            "ネットワークが利用できません。接続が復元されたら再試行します。",
            Toast.LENGTH_SHORT
        ).show()
    }
}

2. 時間的制約のある操作でのキューイング無効化

即座の配信が必要な場合は、キューイングを無効にできます:

kotlin
val config = SORIConfig(
    enableDelayedSending = false  // キューイングを無効化
)

3. キューステータスの監視

ユーザーに通知するために保留中のリクエストを追跡します:

kotlin
override fun onRequestQueued(materialId: String, queueCount: Int) {
    if (queueCount > 5) {
        // 多くのリクエストが保留中なので、ユーザーに通知を検討
        showPendingRequestsNotification(queueCount)
    }
}

動作の仕組み

  1. 検出フェーズ: オーディオが認識されると、SDKはサーバーにアクティビティを送信しようとします
  2. ネットワークチェック: 送信前に、SDKはネットワークの可用性を確認します
  3. 失敗時のキューイング: ネットワークが利用できない場合、リクエストはメモリ内のキューに追加されます
  4. ネットワーク監視: SDKはAndroidのConnectivityManagerを介してネットワーク状態の変更を監視します
  5. 自動リトライ: ネットワークが利用可能になると、キューに入っているリクエストが自動的に処理されます
  6. 有効期限チェック: 設定されたタイムアウトより古いリクエストは破棄されます

必要な権限

SDKはネットワーク処理のために以下の権限が必要です:

xml
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />

これらの権限はすでにSDKのマニフェストに含まれており、アプリに自動的にマージされます。

トラブルシューティング

リクエストがキューに追加されない

遅延送信が有効になっていることを確認してください:

kotlin
val config = SORIConfig(enableDelayedSending = true)
sori.setConfig(config)

キューに入っているリクエストが送信されない

  1. autoRetryOnNetworkRecoveryが有効になっていることを確認
  2. アプリにACCESS_NETWORK_STATE権限があることを確認
  3. リクエストが期限切れになっていないことを確認(デフォルト:10分以上経過)

メモリに関する考慮事項

リクエストキューはメモリに保持されます。システムによってアプリが終了された場合、キューに入っているリクエストは失われます。重要な操作については、独自の永続ストレージメカニズムの実装を検討してください。

例:完全な実装

ネットワーク処理を実装する完全な例を示します:

kotlin
class MainActivity : AppCompatActivity() {
    private lateinit var sori: SORIAudioRecognizer
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        
        // カスタム設定でSORIを初期化
        initializeSori()
    }
    
    private fun initializeSori() {
        // 設定を作成
        val config = SORIConfig(
            enableDelayedSending = true,
            maxPendingTimeMillis = 600000L, // 10分
            autoRetryOnNetworkRecovery = true
        )
        
        // 認識器を作成
        sori = SORIAudioRecognizer(
            BuildConfig.SORI_API_KEY,
            BuildConfig.SORI_SECRET_KEY
        )
        
        // 設定を適用
        sori.setConfig(config)
        
        // リスナーを設定
        sori.setListener(this, object : ISORIListener {
            override fun onReady() {
                Log.d("SORI", "SDKの準備完了")
            }
            
            override fun onStateChanged(state: String) {
                Log.d("SORI", "状態変更: $state")
            }
            
            override fun onError(error: String) {
                Log.e("SORI", "エラー: $error")
            }
            
            override fun onCampaignFound(campaign: SORICampaign) {
                Log.i("SORI", "キャンペーン検出: ${campaign.name}")
                // キャンペーンを処理
                handleCampaign(campaign)
            }
            
            override fun onNetworkError(materialId: String, error: String) {
                Log.w("SORI", "ネットワークエラー: $error")
                // ユーザーフレンドリーなメッセージを表示
                showNetworkErrorMessage()
            }
            
            override fun onRequestQueued(materialId: String, queueCount: Int) {
                Log.i("SORI", "リクエストキュー追加。保留中の総数: $queueCount")
                // 保留状態を表示するようUIを更新
                updatePendingRequestsUI(queueCount)
            }
        })
        
        // 認識を開始
        sori.startRecognition(this)
    }
    
    private fun handleCampaign(campaign: SORICampaign) {
        // キャンペーン処理ロジック
    }
    
    private fun showNetworkErrorMessage() {
        runOnUiThread {
            Toast.makeText(
                this,
                "ネットワークが利用できません。接続が復元されたらキャンペーンが処理されます。",
                Toast.LENGTH_LONG
            ).show()
        }
    }
    
    private fun updatePendingRequestsUI(count: Int) {
        runOnUiThread {
            // 保留中のリクエストを表示するようUIを更新
            val pendingIndicator = findViewById<TextView>(R.id.pendingIndicator)
            if (count > 0) {
                pendingIndicator.visibility = View.VISIBLE
                pendingIndicator.text = "$count 件保留中"
            } else {
                pendingIndicator.visibility = View.GONE
            }
        }
    }
}
java
public class MainActivity extends AppCompatActivity {
    private SORIAudioRecognizer sori;
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        
        // カスタム設定でSORIを初期化
        initializeSori();
    }
    
    private void initializeSori() {
        // 設定を作成
        SORIConfig config = new SORIConfig(
            true,     // enableDelayedSending
            600000L,  // maxPendingTimeMillis(10分)
            true      // autoRetryOnNetworkRecovery
        );
        
        // 認識器を作成
        sori = new SORIAudioRecognizer(
            BuildConfig.SORI_API_KEY,
            BuildConfig.SORI_SECRET_KEY
        );
        
        // 設定を適用
        sori.setConfig(config);
        
        // リスナーを設定
        sori.setListener(this, new ISORIListener() {
            @Override
            public void onReady() {
                Log.d("SORI", "SDKの準備完了");
            }
            
            @Override
            public void onStateChanged(String state) {
                Log.d("SORI", "状態変更: " + state);
            }
            
            @Override
            public void onError(String error) {
                Log.e("SORI", "エラー: " + error);
            }
            
            @Override
            public void onCampaignFound(SORICampaign campaign) {
                Log.i("SORI", "キャンペーン検出: " + campaign.getName());
                // キャンペーンを処理
                handleCampaign(campaign);
            }
            
            @Override
            public void onNetworkError(String materialId, String error) {
                Log.w("SORI", "ネットワークエラー: " + error);
                // ユーザーフレンドリーなメッセージを表示
                showNetworkErrorMessage();
            }
            
            @Override
            public void onRequestQueued(String materialId, int queueCount) {
                Log.i("SORI", "リクエストキュー追加。保留中の総数: " + queueCount);
                // 保留状態を表示するようUIを更新
                updatePendingRequestsUI(queueCount);
            }
        });
        
        // 認識を開始
        sori.startRecognition(this);
    }
    
    private void handleCampaign(SORICampaign campaign) {
        // キャンペーン処理ロジック
    }
    
    private void showNetworkErrorMessage() {
        runOnUiThread(() -> {
            Toast.makeText(
                this,
                "ネットワークが利用できません。接続が復元されたらキャンペーンが処理されます。",
                Toast.LENGTH_LONG
            ).show();
        });
    }
    
    private void updatePendingRequestsUI(int count) {
        runOnUiThread(() -> {
            // 保留中のリクエストを表示するようUIを更新
            TextView pendingIndicator = findViewById(R.id.pendingIndicator);
            if (count > 0) {
                pendingIndicator.setVisibility(View.VISIBLE);
                pendingIndicator.setText(count + " 件保留中");
            } else {
                pendingIndicator.setVisibility(View.GONE);
            }
        });
    }
}