การจัดการเครือข่ายและการรองรับแบบออฟไลน์
ภาพรวม
SORI SDK ประมวลผลการรู้จำเสียงบนอุปกรณ์ อย่างไรก็ตาม จำเป็นต้องเชื่อมต่อ อินเทอร์เน็ตเพื่อรับแคมเปญที่เกี่ยวข้อง โหลดทรัพยากรรูปภาพที่เกี่ยวข้อง และส่งต่อ Webhook เมื่อการรู้จำเสียงสำเร็จแต่ไม่สามารถใช้งานเครือข่าย ระหว่างประมวลผล SDK มี methods หลายรายการสำหรับจัดการ สถานการณ์นี้
ฟีเจอร์
- ตรวจจับเครือข่ายอัตโนมัติ: SDK ติดตามการเปลี่ยนแปลงการเชื่อมต่อเครือข่าย แบบเรียลไทม์
- จัดคิวคำขอ: คำขอที่ล้มเหลวเนื่องจากปัญหาเครือข่ายจะถูกจัดคิวไว้ใน หน่วยความจำ
- ลองใหม่อัตโนมัติ: คำขอในคิวจะถูกประมวลผลโดยอัตโนมัติเมื่อ เครือข่ายกลับมาใช้งานได้
- กำหนดค่าพฤติกรรมได้: คุณสามารถปรับแต่งวิธีที่ SDK จัดการ ความล้มเหลวของเครือข่ายได้
- หมดอายุตามเวลา: คำขอที่เก่ากว่า 10 นาทีจะถูก ลบทิ้งโดยอัตโนมัติ
การกำหนดค่า
คุณสามารถกำหนดค่าพฤติกรรมการจัดการเครือข่ายด้วยคลาส SORIConfig:
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)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:
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())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
| Parameter | Type | ค่าเริ่มต้น | คำอธิบาย |
|---|---|---|---|
enableDelayedSending | Boolean | true | เปิดหรือปิดการจัดคิวคำขอเมื่อเครือข่ายใช้ไม่ได้ เมื่อเป็น false คำขอที่ล้มเหลวจะถูกลบทิ้งทันที |
maxPendingTimeMillis | Long | 600000 (10 นาที) | ระยะเวลาสูงสุดที่จะเก็บคำขอที่รอดำเนินการไว้ก่อนลบทิ้ง |
autoRetryOnNetworkRecovery | Boolean | true | ลองส่งคำขอในคิวใหม่โดยอัตโนมัติเมื่อเครือข่ายกลับมาใช้งานได้ เมื่อเป็น false คำขอจะยังอยู่ในหน่วยความจำ แต่ต้องประมวลผลด้วยตนเอง มีผลเฉพาะเมื่อ enableDelayedSending เป็น true |
ทำความเข้าใจตัวเลือกการกำหนดค่า
ตัวเลือก boolean ทั้งสองมีวัตถุประสงค์ต่างกัน:
enableDelayedSending: ควบคุมว่าจะบันทึกคำขอที่ล้มเหลวลงใน คิวหรือไม่ เมื่อเครือข่ายใช้ไม่ได้true: บันทึกคำขอที่ล้มเหลวไว้ส่งภายหลังเมื่อเครือข่ายใช้ไม่ได้false: ลบทิ้งคำขอที่ล้มเหลวทันทีเมื่อเครือข่ายใช้ไม่ได้- หมายเหตุ: เมื่อเครือข่ายใช้ได้ คำขอจะถูกส่งทันทีเสมอ โดยไม่ขึ้นกับการตั้งค่านี้
autoRetryOnNetworkRecovery: ควบคุมสิ่งที่จะเกิดขึ้นเมื่อเครือข่าย กลับมาใช้งานได้true: ประมวลผลคำขอในคิวโดยอัตโนมัติเมื่อเครือข่ายกลับมาfalse: เก็บคำขอไว้ในคิวแต่ไม่ประมวลผลอัตโนมัติ (ต้อง ดำเนินการด้วยตนเอง)- หมายเหตุ: การตั้งค่านี้มีผลเฉพาะเมื่อ
enableDelayedSendingเป็นtrue
สรุปพฤติกรรม
| สถานะเครือข่าย | enableDelayedSending | พฤติกรรม |
|---|---|---|
| ใช้งานได้ | true หรือ false | ส่งคำขอทันที |
| ใช้งานไม่ได้ | true | จัดคิวคำขอไว้ส่งภายหลัง |
| ใช้งานไม่ได้ | false | ลบทิ้งคำขอ |
แอปพลิเคชันส่วนใหญ่จะตั้งค่าตัวเลือกทั้งสองเป็นค่าเดียวกัน (true สำหรับ พฤติกรรมที่ทนทานต่อความล้มเหลว และ false สำหรับจัดการความล้มเหลวทันที)
แนวทางปฏิบัติที่ดี
1. ข้อความตอบกลับสำหรับผู้ใช้
แจ้งผู้ใช้เสมอเมื่อเกิดปัญหาเครือข่าย:
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 ต้องส่งทันที คุณสามารถปิดการจัดคิวได้:
val config = SORIConfig(
enableDelayedSending = false // Disable queuing
)3. ติดตามสถานะคิว
ติดตามคำขอที่รอดำเนินการเพื่อแจ้งผู้ใช้:
override fun onRequestQueued(materialId: String, queueCount: Int) {
if (queueCount > 5) {
// Many requests are pending, consider notifying the user
showPendingRequestsNotification(queueCount)
}
}วิธีการทำงาน
- ขั้นตอนการตรวจจับ: เมื่อตรวจพบเสียง SDK จะพยายามส่ง activity ไปยังเซิร์ฟเวอร์
- ตรวจสอบเครือข่าย: ก่อนส่ง SDK จะตรวจสอบว่าเครือข่ายใช้งานได้หรือไม่
- จัดคิวเมื่อล้มเหลว: หากเครือข่ายใช้ไม่ได้ คำขอจะถูกเพิ่มลงใน คิวในหน่วยความจำ
- ติดตามเครือข่าย: SDK ติดตามการเปลี่ยนแปลงสถานะเครือข่ายผ่าน
ConnectivityManagerของ Android - ลองใหม่อัตโนมัติ: เมื่อเครือข่ายกลับมาใช้งานได้ คำขอในคิวจะถูก ประมวลผลโดยอัตโนมัติ
- ตรวจสอบการหมดอายุ: คำขอที่เก่ากว่า timeout ที่กำหนดจะถูกลบทิ้ง
สิทธิ์ที่จำเป็น
SDK ต้องใช้ permissions ต่อไปนี้สำหรับการจัดการเครือข่าย:
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />permissions เหล่านี้รวมอยู่ใน manifest ของ SDK แล้ว และจะถูก ผสานรวมเข้ากับแอปโดยอัตโนมัติ
การแก้ไขปัญหา
คำขอไม่ถูกจัดคิว
ตรวจสอบว่าเปิดใช้งานการส่งแบบหน่วงเวลาแล้ว:
val config = SORIConfig(enableDelayedSending = true)
sori.setConfig(config)คำขอในคิวไม่ถูกส่ง
- ตรวจสอบว่าเปิดใช้งาน
autoRetryOnNetworkRecoveryแล้ว - ตรวจสอบว่าแอปมี permission
ACCESS_NETWORK_STATE - ตรวจสอบว่าคำขอยังไม่หมดอายุ (ค่าเริ่มต้นคือเก่ากว่า 10 นาที)
ข้อควรพิจารณาเกี่ยวกับหน่วยความจำ
คิวคำขอจะเก็บไว้ในหน่วยความจำ หากระบบปิดแอป คำขอในคิวจะสูญหาย สำหรับการดำเนินการที่สำคัญ ควรนำ กลไก persistent storage ของคุณเองไปใช้
ตัวอย่าง: การนำไปใช้ฉบับสมบูรณ์
ตัวอย่างฉบับสมบูรณ์ต่อไปนี้แสดงวิธีนำการจัดการเครือข่ายไปใช้:
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
}
}
}
}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);
}
});
}
}