aws-amplify / aws-sdk-android

AWS SDK for Android. For more information, see our web site:
https://docs.amplify.aws
Other
1.03k stars 549 forks source link

Moving from Amplify to TransferUtility causes a crash in the first time. #3232

Closed UrielFrankel closed 1 year ago

UrielFrankel commented 1 year ago

Describe the bug Use Amplify 2.5.0 in the app, and then move to use aws-android-sdk-s3 2.65.0. The app crashes. This is the StackTrace:

FATAL EXCEPTION: DefaultDispatcher-worker-3 Process: riverside.fm, PID: 13864 android.database.sqlite.SQLiteException: table awstransfer already exists (code 1 SQLITE_ERROR[1]): , while compiling: create table awstransfer(_id integer primary key autoincrement, main_upload_id integer, type text not null, state text not null, bucket_name text not null, key text not null, version_id text, bytes_total bigint, bytes_current bigint, speed bigint, is_requester_pays integer, is_encrypted integer, file text not null, file_offset bigint, is_multipart int, part_num int not null, is_last_part integer, multipart_id text, etag text, range_start bigint, range_last bigint, header_content_type text, header_content_language text, header_content_disposition text, header_content_encoding text, header_cache_control text, header_expire text); at android.database.sqlite.SQLiteConnection.nativePrepareStatement(Native Method) at android.database.sqlite.SQLiteConnection.acquirePreparedStatement(SQLiteConnection.java:1521) at android.database.sqlite.SQLiteConnection.prepare(SQLiteConnection.java:957) at android.database.sqlite.SQLiteSession.prepare(SQLiteSession.java:590) at android.database.sqlite.SQLiteProgram.<init>(SQLiteProgram.java:63) at android.database.sqlite.SQLiteStatement.<init>(SQLiteStatement.java:34) at android.database.sqlite.SQLiteDatabase.executeSql(SQLiteDatabase.java:2628) at android.database.sqlite.SQLiteDatabase.execSQL(SQLiteDatabase.java:2550) at com.amazonaws.mobileconnectors.s3.transferutility.TransferTable.onCreate(TransferTable.java:250) at com.amazonaws.mobileconnectors.s3.transferutility.TransferDatabaseHelper.onCreate(TransferDatabaseHelper.java:45) at com.amazonaws.mobileconnectors.s3.transferutility.TransferDatabaseHelper.onDowngrade(TransferDatabaseHelper.java:56) at android.database.sqlite.SQLiteOpenHelper.getDatabaseLocked(SQLiteOpenHelper.java:512) at android.database.sqlite.SQLiteOpenHelper.getWritableDatabase(SQLiteOpenHelper.java:413) at com.amazonaws.mobileconnectors.s3.transferutility.TransferDBBase.<init>(TransferDBBase.java:58) at com.amazonaws.mobileconnectors.s3.transferutility.TransferDBUtil.<init>(TransferDBUtil.java:67) at com.amazonaws.mobileconnectors.s3.transferutility.TransferUtility.<init>(TransferUtility.java:330) at com.amazonaws.mobileconnectors.s3.transferutility.TransferUtility.<init>(TransferUtility.java:108) at com.amazonaws.mobileconnectors.s3.transferutility.TransferUtility$Builder.build(TransferUtility.java:304) at riverside.fm.managers.S3Manager$transferUtility$2.invoke(S3Manager.kt:21) at riverside.fm.managers.S3Manager$transferUtility$2.invoke(S3Manager.kt:19) at kotlin.SynchronizedLazyImpl.getValue(LazyJVM.kt:74) at riverside.fm.managers.S3Manager.getTransferUtility(S3Manager.kt:19) at riverside.fm.utils.Logger$Companion.uploadLogFileToServer(Logger.kt:80) at riverside.fm.android.UploadService.cleanup(UploadService.kt:150) at riverside.fm.android.UploadService.access$cleanup(UploadService.kt:49) at riverside.fm.android.UploadService$onStartCommand$1.invokeSuspend(UploadService.kt:85) at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33) at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:106) at kotlinx.coroutines.internal.LimitedDispatcher.run(LimitedDispatcher.kt:42) at kotlinx.coroutines.scheduling.TaskImpl.run(Tasks.kt:95) at kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(CoroutineScheduler.kt:570) at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.executeTask(CoroutineScheduler.kt:750) at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.runWorker(CoroutineScheduler.kt:677) at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(CoroutineScheduler.kt:664) Suppressed: kotlinx.coroutines.DiagnosticCoroutineContextException: [StandaloneCoroutine{Cancelling}@bde3ac6, Dispatchers.IO]

Environment Information (please complete the following information):

tylerjroach commented 1 year ago

Amplify v2 and AWS Android SDK are incompatible with each other, as Amplify v2 migrates data from the AWS Android SDK.

Amplify v1 used AWS Android SDK under the hood. Amplify v2, uses the AWS Kotlin SDK under the hood. You cannot mix Amplify v2 with Amplify v1 or the AWS Android SDK.

UrielFrankel commented 1 year ago

I need to use 2 different buckets in the same app. Amplify does not support this. So I have to start using the AWS Android SDK. My solution is to delete the database by myself. because the code context.deleteDatabase(DATABASE_NAME); at TransferDatabaseHelper.java not working.

eeatonaws commented 1 year ago

Amplify does not currently support using multiple buckets. However, when using Amplify Android v2, you can use the escape hatch to access the Kotlin SDK, which allows you to specify a bucket name for an operation. For example, to list objects in a given bucket:

val plugin = Amplify.Storage.getPlugin("awsS3StoragePlugin") as AWSS3StoragePlugin
plugin.escapehatch.listObjectsV2 {
    this.bucket = s3BucketName
    this.prefix = path
}
sdhuka commented 1 year ago

Closing this as duplicate, issue for multiple bucket support is tracked here.

ruthwikkk commented 2 months ago

I need to use 2 different buckets in the same app. Amplify does not support this. So I have to start using the AWS Android SDK. My solution is to delete the database by myself. because the code context.deleteDatabase(DATABASE_NAME); at TransferDatabaseHelper.java not working.

I'm also facing the same issue How you managed to do this ? Will this create any other side effects ? @UrielFrankel

UrielFrankel commented 2 months ago
package *******

import android.content.Context
import android.database.sqlite.SQLiteDatabase
import android.database.sqlite.SQLiteException
import com.amazonaws.auth.CognitoCachingCredentialsProvider
import com.amazonaws.mobileconnectors.s3.transferutility.TransferUtility
import com.amazonaws.regions.Region
import com.amazonaws.regions.Regions
import com.amazonaws.services.s3.AmazonS3Client
import com.amazonaws.services.s3.S3ClientOptions
import org.json.JSONException
import org.koin.java.KoinJavaComponent

import java.io.File

class S3Manager(val appContext: Context) {

    private val TAG = "S3Manager"
    val TEST_BUCKET_PRODUCTION = "****"
    val TEST_BUCKET_DEV = "*****"
    val ANDROID_BUCKET_PRODUCTION = "***"
    val ANDROID_BUCKET_DEV = "******"

    private var sCredProvider: CognitoCachingCredentialsProvider? = null
    private var sS3Client: AmazonS3Client? = null
    val transferUtility: TransferUtility by lazy {
        try {
            TransferUtility.builder().context(appContext)
                .s3Client(getS3Client()).build()
        } catch (e: SQLiteException) {
            Logger.e(TAG, "should never happen  - probably moving from Amplify to TransferUtility ", e)
            TransferUtility.builder().context(appContext)
                .s3Client(getS3Client()).build()
        }
    }

    fun init() {
        // we have a crash when upgrading from Amplify to aws
        val dbName = "awss3transfertable.db"
        val dbFile = appContext.getDatabasePath(dbName) as File
        if (dbFile.exists()) {
            val dbVersion = SQLiteDatabase.openDatabase(dbFile.path, null, SQLiteDatabase.OPEN_READWRITE).version
            if (dbVersion > 6) {
                Logger.d(TAG, "deleted old Amplify db")
                // if we upgrade aws-android-sdk-s3 we need to make sure the version is right at TransferDatabaseHelper.java DATABASE_VERSION
                appContext.deleteDatabase(dbName)
            }
        }
    }

    private fun getCredProvider(): CognitoCachingCredentialsProvider? {
        if (sCredProvider == null) {
            sCredProvider = CognitoCachingCredentialsProvider(
                appContext,
                "us-east-1:********,
                Regions.US_EAST_1
            )
        }
        return sCredProvider
    }

    /**
     * Gets an instance of a S3 client which is constructed using the given
     * Context.
     *
     * @param context Android context
     * @return A default S3 client.
     */
    private fun getS3Client(): AmazonS3Client? {
        if (sS3Client == null) {
            try {
                val region = Region.getRegion(Regions.US_EAST_1) // or whatever region you need
                sS3Client = AmazonS3Client(getCredProvider(), region)
                sS3Client?.setS3ClientOptions(S3ClientOptions.builder().setAccelerateModeEnabled(true).build())

            } catch (e: JSONException) {
                e.printStackTrace()
            }
        }
        return sS3Client
    }

}