0

Hi I am new in Android development and my app is completely database oriented. In my app I am using the method of copying a database file from the assets folder. It will increase the size of the apk.

I want to copy it from the internet the first time my app runs on the phone.

How do I download the database file to my app database folder.

benRollag
  • 1,219
  • 4
  • 16
  • 21
Puneet Joshi
  • 91
  • 1
  • 1
  • 8
  • 2
    People reading your question don't have any clue about what you tried, what failed, and what indications you received about its failure. You will need to provide more detail to receive useful responses. – mah Feb 28 '12 at 12:41
  • If your DB size is small then keep it in assets, for using existent DB see this.http://stackoverflow.com/a/9109728/265167 – Yaqub Ahmad Feb 28 '12 at 13:23
  • What is the question: how will you go and download big file from the internet? How will you use this file as database for your application? How will you trigger a operation the first time application starts? – Boris Strandjev Feb 28 '12 at 13:26
  • @YaqubAhmad my database size is about 250kb.... i already performed the concept what u said me to do – Puneet Joshi Feb 28 '12 at 13:26
  • 1
    So you'd rather have YOUR server deal with all the requests instead of putting it through whichever market service you are using? If you were app of the day on Amazon and got 200,000 downloads, yourserver would have to serve up 50GB of data in one day, which could take it down or could cost you a fortune. Then your app would get slated for not starting, and you'd struggle to make any more sales. – cjk Feb 28 '12 at 13:30
  • I think 250kb is not a problem, you can keep it in the assists. – Yaqub Ahmad Feb 28 '12 at 13:46
  • @YaqubAhmad- but it increases the size of the apk – Puneet Joshi Feb 29 '12 at 04:39

2 Answers2

0

This is how I did it:

I have an implementation of DownloadManager that deals with downloading the DB. In my case the DB is significantly big so DownloadManager is a good option for effectively dealing with large downloads.

One thing to note when implementing DownloadManager; It's recommended that you download files first as temporary files and then move them to the final location. This is to avoid weird security issues I faced when doing so. Also download notification visibility may affect what permissions are required, if you decide to have no notifications at all you need to add a DOWNLOAD_WITHOUT_NOTIFICATION.

Permissions required:

<uses-permission android:name="android.permission.DOWNLOAD_WITHOUT_NOTIFICATION" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE " />

DownloadManager implementation

/**
 * Manages all DB retrieval and file level validation.
 *
 */

class DataBaseInitializationRepository(private val app: Application, private val dbHelper: DatabaseFileHelper) {


fun initiateDB( callback: DownloadCompleteCallback){

    val dbUrl = dbHelper.getDbUrl()
    val tempDbFile = dbHelper.getTempDbFile()
    val permanentDbFile = dbHelper.getPermanentDbFile()

    if (!permanentDbFile.exists() && tempDbFile.length() <= 0) {

        val downloadManager = app.getSystemService(Context.DOWNLOAD_SERVICE) as DownloadManager
        val downLoadDBRequest = DownloadManager.Request(Uri.parse( dbUrl ))
            .setTitle( app.getString( R.string.download_db_title ) )
            .setDescription(app.getString( R.string.download_db_description ))
            .setDestinationInExternalFilesDir( app,
                    null,
                    tempDbFile.path
            )
            .setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED)

        val downloadId = downloadManager.enqueue( downLoadDBRequest )

        registerReceiver( callback, downloadId )

    }else{
        callback.onComplete(0L)
    }

}

private fun registerReceiver(callback: DownloadCompleteCallback, downloadId: Long){
    val receiver = object: BroadcastReceiver(){
        override fun onReceive(context: Context?, intent: Intent?) {
            val id = intent?.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, -1)
            if (id == downloadId){

                //Move index reads to reusable function

                val downloadManager = app.getSystemService(Context.DOWNLOAD_SERVICE) as DownloadManager
                val query = DownloadManager.Query()
                query.setFilterById( downloadId )

                val data = downloadManager.query( query )

                if(data.moveToFirst() && data.count > 0){
                    val statusIndex = data.getColumnIndex(DownloadManager.COLUMN_STATUS)
                    val status = data.getInt( statusIndex )
                    if(status == DownloadManager.STATUS_SUCCESSFUL){

                        val localUriIndex = data.getColumnIndex(DownloadManager.COLUMN_LOCAL_URI)
                        val localFile = File(
                            data.getString( localUriIndex )
                                .replace("file://","" )
                        )

                        if(localFile.exists()){
                            permanentlyStoreDb(localFile)
                            callback.onComplete(id)
                        }else{
                            callback.onFail("Initial Database Download Failed - File not found")
                        }
                    }else if(status == DownloadManager.STATUS_FAILED){

                        val reasonIndex = data.getColumnIndex(DownloadManager.COLUMN_REASON)
                        val reason = data.getInt( reasonIndex )
                        if(reason == DownloadManager.ERROR_FILE_ALREADY_EXISTS){
                            callback.onComplete(id)
                        }else{
                            callback.onFail("Initial Database Download Failed: $reason")
                        }
                    }

                }else{
                    callback.onFail("Initial Database Download Failed - Unable to read download metadata")
                }

            }
        }

    }

    app.registerReceiver( receiver, IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE) )
}


private fun permanentlyStoreDb(tempFile: File): File {
    val permanentDbFile = dbHelper.getPermanentDbFile()

    try {
        if(tempFile.exists()) {
            tempFile.copyTo(
                permanentDbFile,
                true,
                1024
            )

            tempFile.delete()

        }else{
            throw IOException("Temporal DB file doesn't exist")
        }

    }catch (ioex: IOException) {
        throw IOException("Unable to copy DB to permanent storage:", ioex)
    }

    return permanentDbFile
}

/**
 * Allows download completion to be notified back to the calling view model
 */
interface DownloadCompleteCallback{
    fun onComplete(downloadId: Long)
    fun onFail(message: String)
}

}

DatabaseFileHelper contains the logic to determine the temporary file, the permanent DB location and the DB URL where the download will happen. This is the logic I used for the temporary file:

fun getTempDbFile(): File {
    return File.createTempFile(<FILE-LOCATION>, null, app.cacheDir)
}

So in case you want to notify a running Activity/Fragment, you only need to pass a DownloadCompleteCallback implementation to this component to get it.

In case you are using Room, just make sure your implementation of RoomDatabase uses the following on your getInstance method

 .createFromFile( dbFileHelper.getPermanentDbFile() )
Chepech
  • 5,258
  • 4
  • 47
  • 70
-1

just copy it from web instead of copying it form assets. Hint

piotrpo
  • 12,398
  • 7
  • 42
  • 58