Skip to content

การจัดการเครือข่ายและการรองรับแบบออฟไลน์

ภาพรวม

SORI SDK ประมวลผลการรู้จำเสียงบนอุปกรณ์ อย่างไรก็ตาม จำเป็นต้องเชื่อมต่อ อินเทอร์เน็ตเพื่อรับแคมเปญที่เกี่ยวข้อง โหลดทรัพยากรรูปภาพที่เกี่ยวข้อง และส่งต่อ Webhook เมื่อการรู้จำเสียงสำเร็จแต่ไม่สามารถใช้งานเครือข่าย ระหว่างประมวลผล SDK มี methods หลายรายการสำหรับจัดการ สถานการณ์นี้

ฟีเจอร์

  • ตรวจจับเครือข่ายอัตโนมัติ: SDK ติดตามการเปลี่ยนแปลงการเชื่อมต่อเครือข่าย แบบเรียลไทม์
  • จัดคิวคำขอ: คำขอที่ล้มเหลวเนื่องจากปัญหาเครือข่ายจะถูกจัดคิวไว้ใน หน่วยความจำ
  • ลองใหม่อัตโนมัติ: คำขอในคิวจะถูกประมวลผลโดยอัตโนมัติเมื่อ เครือข่ายกลับมาใช้งานได้
  • กำหนดค่าพฤติกรรมได้: คุณสามารถปรับแต่งวิธีที่ SDK จัดการ ความล้มเหลวของเครือข่ายได้
  • หมดอายุตามเวลา: คำขอที่เก่ากว่า 10 นาทีจะถูก ลบทิ้งโดยอัตโนมัติ

การกำหนดค่า

คุณสามารถกำหนดค่าพฤติกรรมการจัดการเครือข่ายด้วยคลาส SORIConfig:

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

// Create a custom configuration
val config = SORIConfig(
    enableDelayedSending = true,     // Enable request queuing
    maxPendingTimeMillis = 600000L,   // 10 minutes (default)
    autoRetryOnNetworkRecovery = true // Auto-retry when network returns
)

// Apply configuration to the recognizer
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;

// Create a custom configuration
SORIConfig config = new SORIConfig(
    true,     // enableDelayedSending
    600000L,  // maxPendingTimeMillis (10 minutes)
    true      // autoRetryOnNetworkRecovery
);

// Apply configuration to the recognizer
SORIAudioRecognizer sori = new SORIAudioRecognizer(
    "YOUR_SORI_API_KEY",
    "YOUR_SORI_SECRET_KEY"
);
sori.setConfig(config);

จัดการ Events ของเครือข่าย

SDK มี callbacks สำหรับแจ้งแอปเกี่ยวกับ events ที่เกี่ยวข้องกับเครือข่าย ผ่าน interface ISORIListener:

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

class MyListener : ISORIListener {
    override fun onReady() {
        // SDK is ready
    }
    
    override fun onStateChanged(state: String) {
        // Service state changed
    }
    
    override fun onError(error: String) {
        // An error occurred
    }
    
    override fun onCampaignFound(campaign: SORICampaign) {
        // Campaign detected successfully
    }
    
    override fun onNetworkError(materialId: String, error: String) {
        // Network error occurred while posting activity
        Log.w("SORI", "Network error for material $materialId: $error")
        // You can show a user-friendly message here
    }
    
    override fun onRequestQueued(materialId: String, queueCount: Int) {
        // Request has been queued for later delivery
        Log.i("SORI", "Request queued for material $materialId. Queue size: $queueCount")
        // You can notify the user that the request will be sent later
    }
}

// Set the listener
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 is ready
    }
    
    @Override
    public void onStateChanged(String state) {
        // Service state changed
    }
    
    @Override
    public void onError(String error) {
        // An error occurred
    }
    
    @Override
    public void onCampaignFound(SORICampaign campaign) {
        // Campaign detected successfully
    }
    
    @Override
    public void onNetworkError(String materialId, String error) {
        // Network error occurred while posting activity
        Log.w("SORI", "Network error for material " + materialId + ": " + error);
        // You can show a user-friendly message here
    }
    
    @Override
    public void onRequestQueued(String materialId, int queueCount) {
        // Request has been queued for later delivery
        Log.i("SORI", "Request queued for material " + materialId + 
               ". Queue size: " + queueCount);
        // You can notify the user that the request will be sent later
    }
}

// Set the listener
sori.setListener(context, new MyListener());

ตัวเลือกการกำหนดค่า

พารามิเตอร์ของ SORIConfig

ParameterTypeค่าเริ่มต้นคำอธิบาย
enableDelayedSendingBooleantrueเปิดหรือปิดการจัดคิวคำขอเมื่อเครือข่ายใช้ไม่ได้ เมื่อเป็น false คำขอที่ล้มเหลวจะถูกลบทิ้งทันที
maxPendingTimeMillisLong600000 (10 นาที)ระยะเวลาสูงสุดที่จะเก็บคำขอที่รอดำเนินการไว้ก่อนลบทิ้ง
autoRetryOnNetworkRecoveryBooleantrueลองส่งคำขอในคิวใหม่โดยอัตโนมัติเมื่อเครือข่ายกลับมาใช้งานได้ เมื่อเป็น false คำขอจะยังอยู่ในหน่วยความจำ แต่ต้องประมวลผลด้วยตนเอง มีผลเฉพาะเมื่อ enableDelayedSending เป็น true

ทำความเข้าใจตัวเลือกการกำหนดค่า

ตัวเลือก boolean ทั้งสองมีวัตถุประสงค์ต่างกัน:

  • enableDelayedSending: ควบคุมว่าจะบันทึกคำขอที่ล้มเหลวลงใน คิวหรือไม่ เมื่อเครือข่ายใช้ไม่ได้

    • true: บันทึกคำขอที่ล้มเหลวไว้ส่งภายหลังเมื่อเครือข่ายใช้ไม่ได้
    • false: ลบทิ้งคำขอที่ล้มเหลวทันทีเมื่อเครือข่ายใช้ไม่ได้
    • หมายเหตุ: เมื่อเครือข่ายใช้ได้ คำขอจะถูกส่งทันทีเสมอ โดยไม่ขึ้นกับการตั้งค่านี้
  • autoRetryOnNetworkRecovery: ควบคุมสิ่งที่จะเกิดขึ้นเมื่อเครือข่าย กลับมาใช้งานได้

    • true: ประมวลผลคำขอในคิวโดยอัตโนมัติเมื่อเครือข่ายกลับมา
    • false: เก็บคำขอไว้ในคิวแต่ไม่ประมวลผลอัตโนมัติ (ต้อง ดำเนินการด้วยตนเอง)
    • หมายเหตุ: การตั้งค่านี้มีผลเฉพาะเมื่อ enableDelayedSending เป็น true

สรุปพฤติกรรม

สถานะเครือข่ายenableDelayedSendingพฤติกรรม
ใช้งานได้true หรือ falseส่งคำขอทันที
ใช้งานไม่ได้trueจัดคิวคำขอไว้ส่งภายหลัง
ใช้งานไม่ได้falseลบทิ้งคำขอ

แอปพลิเคชันส่วนใหญ่จะตั้งค่าตัวเลือกทั้งสองเป็นค่าเดียวกัน (true สำหรับ พฤติกรรมที่ทนทานต่อความล้มเหลว และ false สำหรับจัดการความล้มเหลวทันที)

แนวทางปฏิบัติที่ดี

1. ข้อความตอบกลับสำหรับผู้ใช้

แจ้งผู้ใช้เสมอเมื่อเกิดปัญหาเครือข่าย:

kotlin
override fun onNetworkError(materialId: String, error: String) {
    runOnUiThread {
        Toast.makeText(
            context,
            "Network unavailable. Will retry when connection is restored.",
            Toast.LENGTH_SHORT
        ).show()
    }
}

2. ปิดการจัดคิวสำหรับการดำเนินการที่ไวต่อเวลา

หาก use case ต้องส่งทันที คุณสามารถปิดการจัดคิวได้:

kotlin
val config = SORIConfig(
    enableDelayedSending = false  // Disable queuing
)

3. ติดตามสถานะคิว

ติดตามคำขอที่รอดำเนินการเพื่อแจ้งผู้ใช้:

kotlin
override fun onRequestQueued(materialId: String, queueCount: Int) {
    if (queueCount > 5) {
        // Many requests are pending, consider notifying the user
        showPendingRequestsNotification(queueCount)
    }
}

วิธีการทำงาน

  1. ขั้นตอนการตรวจจับ: เมื่อตรวจพบเสียง SDK จะพยายามส่ง activity ไปยังเซิร์ฟเวอร์
  2. ตรวจสอบเครือข่าย: ก่อนส่ง SDK จะตรวจสอบว่าเครือข่ายใช้งานได้หรือไม่
  3. จัดคิวเมื่อล้มเหลว: หากเครือข่ายใช้ไม่ได้ คำขอจะถูกเพิ่มลงใน คิวในหน่วยความจำ
  4. ติดตามเครือข่าย: SDK ติดตามการเปลี่ยนแปลงสถานะเครือข่ายผ่าน ConnectivityManager ของ Android
  5. ลองใหม่อัตโนมัติ: เมื่อเครือข่ายกลับมาใช้งานได้ คำขอในคิวจะถูก ประมวลผลโดยอัตโนมัติ
  6. ตรวจสอบการหมดอายุ: คำขอที่เก่ากว่า timeout ที่กำหนดจะถูกลบทิ้ง

สิทธิ์ที่จำเป็น

SDK ต้องใช้ permissions ต่อไปนี้สำหรับการจัดการเครือข่าย:

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

permissions เหล่านี้รวมอยู่ใน manifest ของ SDK แล้ว และจะถูก ผสานรวมเข้ากับแอปโดยอัตโนมัติ

การแก้ไขปัญหา

คำขอไม่ถูกจัดคิว

ตรวจสอบว่าเปิดใช้งานการส่งแบบหน่วงเวลาแล้ว:

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

คำขอในคิวไม่ถูกส่ง

  1. ตรวจสอบว่าเปิดใช้งาน autoRetryOnNetworkRecovery แล้ว
  2. ตรวจสอบว่าแอปมี permission ACCESS_NETWORK_STATE
  3. ตรวจสอบว่าคำขอยังไม่หมดอายุ (ค่าเริ่มต้นคือเก่ากว่า 10 นาที)

ข้อควรพิจารณาเกี่ยวกับหน่วยความจำ

คิวคำขอจะเก็บไว้ในหน่วยความจำ หากระบบปิดแอป คำขอในคิวจะสูญหาย สำหรับการดำเนินการที่สำคัญ ควรนำ กลไก persistent storage ของคุณเองไปใช้

ตัวอย่าง: การนำไปใช้ฉบับสมบูรณ์

ตัวอย่างฉบับสมบูรณ์ต่อไปนี้แสดงวิธีนำการจัดการเครือข่ายไปใช้:

kotlin
class MainActivity : AppCompatActivity() {
    private lateinit var sori: SORIAudioRecognizer
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        
        // Initialize SORI with custom configuration
        initializeSori()
    }
    
    private fun initializeSori() {
        // Create configuration
        val config = SORIConfig(
            enableDelayedSending = true,
            maxPendingTimeMillis = 600000L, // 10 minutes
            autoRetryOnNetworkRecovery = true
        )
        
        // Create recognizer
        sori = SORIAudioRecognizer(
            BuildConfig.SORI_API_KEY,
            BuildConfig.SORI_SECRET_KEY
        )
        
        // Apply configuration
        sori.setConfig(config)
        
        // Set listener
        sori.setListener(this, object : ISORIListener {
            override fun onReady() {
                Log.d("SORI", "SDK is ready")
            }
            
            override fun onStateChanged(state: String) {
                Log.d("SORI", "State changed: $state")
            }
            
            override fun onError(error: String) {
                Log.e("SORI", "Error: $error")
            }
            
            override fun onCampaignFound(campaign: SORICampaign) {
                Log.i("SORI", "Campaign found: ${campaign.name}")
                // Handle the campaign
                handleCampaign(campaign)
            }
            
            override fun onNetworkError(materialId: String, error: String) {
                Log.w("SORI", "Network error: $error")
                // Show user-friendly message
                showNetworkErrorMessage()
            }
            
            override fun onRequestQueued(materialId: String, queueCount: Int) {
                Log.i("SORI", "Request queued. Total pending: $queueCount")
                // Update UI to show pending status
                updatePendingRequestsUI(queueCount)
            }
        })
        
        // Start recognition
        sori.startRecognition(this)
    }
    
    private fun handleCampaign(campaign: SORICampaign) {
        // Your campaign handling logic
    }
    
    private fun showNetworkErrorMessage() {
        runOnUiThread {
            Toast.makeText(
                this,
                "Network unavailable. Campaign will be processed when connection is restored.",
                Toast.LENGTH_LONG
            ).show()
        }
    }
    
    private fun updatePendingRequestsUI(count: Int) {
        runOnUiThread {
            // Update your UI to show pending requests
            val pendingIndicator = findViewById<TextView>(R.id.pendingIndicator)
            if (count > 0) {
                pendingIndicator.visibility = View.VISIBLE
                pendingIndicator.text = "$count pending"
            } else {
                pendingIndicator.visibility = View.GONE
            }
        }
    }
}
java
public class MainActivity extends AppCompatActivity {
    private SORIAudioRecognizer sori;
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        
        // Initialize SORI with custom configuration
        initializeSori();
    }
    
    private void initializeSori() {
        // Create configuration
        SORIConfig config = new SORIConfig(
            true,     // enableDelayedSending
            600000L,  // maxPendingTimeMillis (10 minutes)
            true      // autoRetryOnNetworkRecovery
        );
        
        // Create recognizer
        sori = new SORIAudioRecognizer(
            BuildConfig.SORI_API_KEY,
            BuildConfig.SORI_SECRET_KEY
        );
        
        // Apply configuration
        sori.setConfig(config);
        
        // Set listener
        sori.setListener(this, new ISORIListener() {
            @Override
            public void onReady() {
                Log.d("SORI", "SDK is ready");
            }
            
            @Override
            public void onStateChanged(String state) {
                Log.d("SORI", "State changed: " + state);
            }
            
            @Override
            public void onError(String error) {
                Log.e("SORI", "Error: " + error);
            }
            
            @Override
            public void onCampaignFound(SORICampaign campaign) {
                Log.i("SORI", "Campaign found: " + campaign.getName());
                // Handle the campaign
                handleCampaign(campaign);
            }
            
            @Override
            public void onNetworkError(String materialId, String error) {
                Log.w("SORI", "Network error: " + error);
                // Show user-friendly message
                showNetworkErrorMessage();
            }
            
            @Override
            public void onRequestQueued(String materialId, int queueCount) {
                Log.i("SORI", "Request queued. Total pending: " + queueCount);
                // Update UI to show pending status
                updatePendingRequestsUI(queueCount);
            }
        });
        
        // Start recognition
        sori.startRecognition(this);
    }
    
    private void handleCampaign(SORICampaign campaign) {
        // Your campaign handling logic
    }
    
    private void showNetworkErrorMessage() {
        runOnUiThread(() -> {
            Toast.makeText(
                this,
                "Network unavailable. Campaign will be processed when connection is restored.",
                Toast.LENGTH_LONG
            ).show();
        });
    }
    
    private void updatePendingRequestsUI(int count) {
        runOnUiThread(() -> {
            // Update your UI to show pending requests
            TextView pendingIndicator = findViewById(R.id.pendingIndicator);
            if (count > 0) {
                pendingIndicator.setVisibility(View.VISIBLE);
                pendingIndicator.setText(count + " pending");
            } else {
                pendingIndicator.setVisibility(View.GONE);
            }
        });
    }
}