푸시 알림에서 SQLite로 데이터 저장

소개



이 튜토리얼에서는 앱이 닫힌 경우에도 푸시 알림을 수신하고 처리하고 SQLite에 저장하기 위해 Kotlin을 사용하여 백그라운드 서비스를 만드는 방법을 배웁니다.

컨텍스트는 Flutter 앱이지만 어쨌든 네이티브 Android 코드를 작성해야 하므로 네이티브 개발에 적용할 수 있습니다.

Jump to the final code 또는 자세한 연습을 계속 읽으십시오.

문맥



최근 개인 프로젝트( Notificador LTV )에서 Flutter 앱에 푸시 알림 기능을 추가해야 했습니다. 나는 그것을 위해 OneSignal을 사용했고 통합은 정말 쉬웠다.

한 가지 요구 사항은 수신된 알림을 SQLite 데이터베이스에 저장하는 것이었습니다. Flutter에서 모든 것을 구현하는 것은 sqflite 을 사용하여 간단했지만, 또 다른 요구 사항은 사용자가 앱을 완전히 닫았을 때도 이 동작이 발생해야 한다는 것이었습니다. 이를 위해 Android에서 background service을 구현해야 했습니다.

OneSignal은 이러한 기능을 추가하기 위해 확장할 수 있는 NotificationExtenderService 클래스를 제공하므로 SQLite 코드만 처리하면 됩니다.

설정



푸시 알림 듣기



첫 번째 단계는 NotificationExtenderService 를 확장하는 클래스를 만드는 것입니다. CustomNotificationExtender.kt 와 함께 android/app/src/main/kotlin/your/package/name 폴더 아래에 MainActivity.kt 를 만듭니다.

이 파일에 다음 코드를 추가합니다.

package your.package.name

import com.onesignal.NotificationExtenderService
import com.onesignal.OSNotificationReceivedResult

class CustomNotificationExtender : NotificationExtenderService() {
  protected override fun onNotificationProcessing(receivedResult: OSNotificationReceivedResult): Boolean {
    return false
  }
}


이 클래스는 백그라운드 알림만 수신하고 false - 이에 대해 확인OneSignal's documentation하여 UI에 표시할 수 있도록 합니다.

매니페스트에서 클래스 선언


AndroidManifest.xml에 다음을 추가하십시오.

<service
   android:name=".CustomNotificationExtender" // change this if your class has another name
   android:permission="android.permission.BIND_JOB_SERVICE"
   android:exported="false">
   <intent-filter>
      <action android:name="com.onesignal.NotificationExtender" />
   </intent-filter>
</service>


데이터베이스 설명



SQLite와 상호 작용하려면 사용되는 데이터베이스와 테이블을 설명해야 합니다. 이를 위한 도우미 클래스를 만들고 예제를 위해 테이블에는 idtitle 두 개의 열만 있습니다.

생성한 동일한 파일 안에 다음 코드를 추가합니다.

package your.package.name

import android.database.sqlite.SQLiteDatabase
import android.database.sqlite.SQLiteOpenHelper
import com.onesignal.NotificationExtenderService
import com.onesignal.OSNotificationReceivedResult

object MyData {
  const val TABLE_NAME = "mytable"
  const val COLUMN_NAME_ID = "id"
  const val COLUMN_NAME_TITLE = "title"
}

class MyDbHelper(context: Context) : SQLiteOpenHelper(context, DATABASE_NAME, null, DATABASE_VERSION) {
  private val CREATE_MY_TABLE =
    "CREATE TABLE ${MyData.TABLE_NAME} (" +
      "${MyData.COLUMN_NAME_ID} INTEGER PRIMARY AUTOINCREMENT," +
      "${MyData.COLUMN_NAME_TITLE} TEXT)"

  override fun onCreate(db: SQLiteDatabase) {
    db.execSQL(CREATE_MY_TABLE)
  }

  override fun onUpgrade(db: SQLiteDatabase, oldVersion: Int, newVersion: Int) {
    onCreate(db)
  }

  companion object {
    const val DATABASE_VERSION = 1
    const val DATABASE_NAME = "my_database.db"
  }
}

class CustomNotificationExtender : NotificationExtenderService() {
  protected override fun onNotificationProcessing(receivedResult: OSNotificationReceivedResult): Boolean {
    return false
  }
}


이제 테이블 및 열 이름에 대한 일부 정적 값을 설명하는 개체(나중에 도움이 될 것입니다)와 데이터베이스에 연결할 때 수행해야 하는 작업을 설명하는 확장 개체SQLite's OpenHelper와 버전도 생성했습니다.

알림 데이터 유지



이제 수신된 데이터에 액세스하고 이를 유지해야 합니다. 몇 단계로 나누어 보겠습니다.
  • 가져오기 업데이트
  • 수신된 데이터에 액세스
  • 데이터를 유지해야 하는지 확인하십시오.
  • 데이터 유지
  • 오류 처리

  • 가져오기 업데이트




    import android.content.ContentValues
    import android.content.Context
    import android.database.sqlite.SQLiteDatabase
    import android.database.sqlite.SQLiteOpenHelper
    import android.util.Log
    import com.onesignal.NotificationExtenderService
    import com.onesignal.OSNotificationReceivedResult
    


    수신된 데이터에 액세스


    receivedResult 변수에는 알림 메시지, 추가 데이터 등을 가져오는 데 사용할 수 있는 payload 유형의 속성OSNotificationPayload이 있습니다. 또한 앱의 상태를 제공하는 restoringisAppInFocus가 있습니다.

    이 예에서는 레코드가 삽입될 때 ID가 자동 생성되므로 제목만 원합니다.

    val title = receivedResult.payload.title
    


    데이터를 유지해야 하는지 확인



    Flutter 코드에서 알림을 처리하는 방법에 따라 네이티브 코드에서 아무 것도 하고 싶지 않을 수 있습니다. 이 경우 다음과 같이 할 수 있습니다.

    if (receivedResult.isAppInFocus || receivedResult.restoring) {
      Log.i("ExtenderIgnored", "App is in focus or restoring")
      // `false` allows the notification to be displayed
      return false
    }
    


    데이터를 유지해야 하는지 결정하기 전에 query on SQLite을 실행할 수 있습니다. 이 예에서 제목이 같은 두 개의 알림을 방지합니다.

    val context = getBaseContext()
    val dbHelper = MyDbHelper(context)
    val db = dbHelper.getWritableDatabase()
    val cursor = db.query(MyData.TABLE_NAME, null, "title = ?", arrayOf(title), null, null, null, null)
    
    // Don't persist data if another record has the same title
    if (cursor.count > 0) {
      // `false` allows the notification to be displayed
      return false
    }
    


    데이터 유지



    데이터를 유지하려면 ContentValues 개체가 필요합니다.

    val values = ContentValues()
    values.put(MyData.COLUMN_NAME_TITLE, title)
    
    db.insert(MyData.TABLE_NAME, null, values)
    
    // `false` allows the notification to be displayed
    return false
    


    오류 처리



    위의 코드 중 일부는 실패할 수 있으므로 try/catch 로 래핑해야 합니다.

    최종 코드

    package your.package.name
    
    import android.content.ContentValues
    import android.content.Context
    import android.database.sqlite.SQLiteDatabase
    import android.database.sqlite.SQLiteOpenHelper
    import android.util.Log
    import com.onesignal.NotificationExtenderService
    import com.onesignal.OSNotificationReceivedResult
    
    object MyData {
      const val TABLE_NAME = "mytable"
      const val COLUMN_NAME_ID = "id"
      const val COLUMN_NAME_TITLE = "title"
    }
    
    class MyDbHelper(context: Context) : SQLiteOpenHelper(context, DATABASE_NAME, null, DATABASE_VERSION) {
      private val CREATE_MY_TABLE =
        "CREATE TABLE ${MyData.TABLE_NAME} (" +
          "${MyData.COLUMN_NAME_ID} INTEGER PRIMARY AUTOINCREMENT," +
          "${MyData.COLUMN_NAME_TITLE} TEXT)"
    
      override fun onCreate(db: SQLiteDatabase) {
        db.execSQL(CREATE_MY_TABLE)
      }
    
      override fun onUpgrade(db: SQLiteDatabase, oldVersion: Int, newVersion: Int) {
        onCreate(db)
      }
    
      companion object {
        const val DATABASE_VERSION = 1
        const val DATABASE_NAME = "my_database.db"
      }
    }
    
    class CustomNotificationExtender : NotificationExtenderService() {
      protected override fun onNotificationProcessing(receivedResult: OSNotificationReceivedResult): Boolean {
        try {
          if (receivedResult.isAppInFocus || receivedResult.restoring) {
            Log.i("ExtenderIgnored", "App is in focus or restoring")
            return false
          }
    
          val title = receivedResult.payload.title
    
          val context = getBaseContext()
          val dbHelper = MyDbHelper(context)
          val db = dbHelper.getWritableDatabase()
          val cursor = db.query(MyData.TABLE_NAME, null, "title = ?", arrayOf(title), null, null, null, null)
    
          // Don't persist data if another record has the same title
          if (cursor.count > 0) {
            return false
          }
    
          val values = ContentValues()
          values.put(MyData.COLUMN_NAME_TITLE, title)
    
          db.insert(MyData.TABLE_NAME, null, values)
    
          return false
        } catch (e: Exception) {
          // Report it to an external service if you wish
          Log.e("NotificationExtenderServiceError", e.toString())
          return false
        }
      }
    }
    

    메모



    자세한 내용은 OneSignal's documentation을 확인하십시오.

    다른 솔루션을 사용 중이거나 이 예제를 개선하기 위한 제안 사항이 있으면 의견에 공유하세요.

    네이티브 Android 코드를 작성하는 것은 이번이 처음이므로 잘못된 작업을 수행했거나 권장하지 않는 사항이 있으면 알려주세요.

    iOS에서 동일한 동작을 구현하지 않았으므로 이 주제에 대해 링크할 리소스가 있으면 댓글로 공유하세요.


    이 게시물과 follow me 모든 플랫폼에서 더 많은 정보를 얻으셨기를 바랍니다.

    좋은 웹페이지 즐겨찾기