bytesculptor
bytesculptor

Reputation: 603

How to read a json file from resources without application context

I'm trying to read a json file from res/raw folder, but I'm not in an Activity, it's in a Kotlin object. I can't manage to read it without context or applicationContext which I don't have there.

I tried to create a class where I inject the context with Hilt, but that failed too because I can't inject that class in my object. I'm stuck, what am I missing? It can't be that difficult?

The json file is currently only

{
   "spacer": 8
}

Perhaps in the future some more parameters will be added. I want to save this value in an object (which contains other values not from json) so I don't have to read it every time it's needed. The parameter is used in a Compose theme.

Upvotes: 2

Views: 1591

Answers (2)

cesonha
cesonha

Reputation: 1195

Unfortunately, as Thales said in this comment, you can create an EntryPoint to inject anything that Hilt is providing but you'll still need the applicationContext to start with, so you will still have to workaround the fact that you don't have it in your object. You could have your Application class inject its context in its onCreate method, using a 'initializer' function for your object that will both save the applicationContext for the use case that you need strings or other resources and also will enable things like injecting any object provided by Hilt in your application. I will give you an example on how you would need to change your application class and how your object could look like.

import android.app.Application
import dagger.hilt.android.HiltAndroidApp

@HiltAndroidApp
class MyApplication : Application() {

    override fun onCreate() {
        super.onCreate()
        MyObject.initializeMyObject(applicationContext)
    }
}

and your object could be something like this:

import android.annotation.SuppressLint
import android.content.Context
import dagger.hilt.EntryPoint
import dagger.hilt.InstallIn
import dagger.hilt.android.EntryPointAccessors
import dagger.hilt.components.SingletonComponent

// MyExampleInterface should be properly configured to be provided by Hilt
@EntryPoint
@InstallIn(SingletonComponent::class)
interface MyObjectEntryPoint {
    fun myExampleInterface(): MyExampleInterface
}

@SuppressLint("StaticFieldLeak")
object MyObject {

    private var myContext: Context? = null
    private var injectedDependency: MyExampleInterface? = null

    // make sure the context passed is applicationContext to avoid memory leaks
    fun initializeMyObject(applicationContext: Context) {
        myContext = applicationContext
        myContext?.let {
            val hiltEntryPoint =
                EntryPointAccessors.fromApplication(it, MyObjectEntryPoint::class.java)
            injectedDependency = hiltEntryPoint.myExampleInterface()
        }
    }

    fun getResourceFromObject(): String {
        return myContext?.getString(R.string.app_name) ?: "Context not injected"
    }

    fun usingInjectedDependency(): String {
        return injectedDependency?.myExampleFunction() ?: "Dependency not injected"
    }
}

I am not a super expert on memory leaks, but if you make sure the context passed to initializeMyObject is always the applicationContext from its onCreate method, you won't have any problems with leaks since the applicationContext is not released until the application restarts or it is killed anyways.

if you were using Koin you could do something like this:

import android.content.Context
import com.your.app.R
import org.koin.java.KoinJavaComponent.get

object YourObject {

  val context: Context = get(Context::class.java)
  val yourString = context.getString(R.string.your_string)

  // do other stuff with context here

}

you should just take care not to store this context object because it could cause memory leaks, you could just use it to get your resource without assigning it to any variables like

val yourString = get(Context::class.java).getString(R.string.your_string)

Upvotes: 0

cosminaP
cosminaP

Reputation: 21

You could try to use a context extension function to read your json file from /assets folder:

fun Context.getJsonFromAssets(fileName: String): String? {
    val jsonString: String
    try {
        val inputStream = assets.open(fileName)

        val size = inputStream.available()
        val buffer = ByteArray(size)
        inputStream.read(buffer)
        inputStream.close()

        jsonString = String(buffer, Charset.defaultCharset())
    } catch (e: IOException) {
        e.printStackTrace()
        return null
    }

    return jsonString
}

Upvotes: 2

Related Questions