Create class extend AsyncTask without leaking context object in Android Kotlin - android

I defined a base class extends AsyncTask and have some custom method. I want to create some child classes which extend this, and override some methods. But by this way, its field will leaks the Context object.
abstract class ParentTask(context: Context) : AsyncTask<Void, Int, String>()
{
...
override fun doInBackground(vararg params: Void): String? {
...
}
...
}
Please tell me why and show me how to implement it in Android Kotlin? I'm very appreciated if there's an example about this.
p/s : I'm searched for this but can't understand. Why using Singleton, or other design pattern

You can't avoid this warning this way, but you can handle that your context is application context and it won't leak:
abstract class ParentTask(context: Context) : AsyncTask<Void, Int, String>() {
private val context: Context
init {
this.context = context.applicationContext
}
override fun doInBackground(vararg voids: Void): String? {
//...
}
}

Related

java.lang.reflect.InvocationTargetException (no error message) for provide adapters

I use my own custom adapter classes, but when I want to provide them, I have a problem and I do not understand why!
#Module
abstract class AppointmentListModule {
companion object {
#Provides
#PerChildFragment
fun adapter(appExecutors: AppExecutors): SingleDataAdapter<AppointmentDigestData> = SingleDataAdapter(
appExecutors,
R.layout.item_appointment_patient,
SimpleDiffCallback(AppointmentDigestData::id),
BR.item
)
}
}
This is the error I receive:
error: #Provides methods can only be present within a #Module or #ProducerModule
public final ir.logicfan.core.ui.recyclerview.adapter.SingleDataAdapter<com.ennings.data.entity.patient.AppointmentDigestData> adapter(#org.jetbrains.annotations.NotNull()
SingleDataAdapter constructor:
open class SingleDataAdapter<T>(
appExecutors: AppExecutors,
#LayoutRes protected val itemLayout: Int,
diffCallback: DiffUtil.ItemCallback<T>,
private val bindingItemVariableId: Int,
private val positionBindingVariableId: Int? = null
) : ListAdapter<T, DataBindingViewHolder<T>>(
AsyncDifferConfig.Builder<T>(diffCallback)
.setBackgroundThreadExecutor(appExecutors.diskIO())
.build()
) {....}
Anyone have an idea?
I cannot check it right now, but as it stated here try to add #JvmStatic to the fun adapter(appExecutors: AppExecutors) function. And if you can, please show the SingleDataAdapter constructor

Why I can't use a Class type as a generic parameter in Kotlin?

I am working in an Android application using Kotlin, but when I try to use a concrete class as a fourth parameter I got an error, my question is, what I am doing wrong?
This is the base adapter of the RecyclerView
abstract class BaseAdapter<K, T: DbEntity<K>, VDB: ViewDataBinding, VH: BaseViewHolder<K,DbEntity<K>, VDB>>: RecyclerView.Adapter<VH>(){
val items: MutableList<T> = ArrayList()
fun addNewItems(newItems: List<T>){
}
}
This is the class that I use for specify a generic parameter and I get an error
class CaseByCountryViewHolder(mDataBinding: ItemCaseByCountryBinding): BaseViewHolder<Int, CaseByCountry, ItemCaseByCountryBinding>(mDataBinding) {
override fun bind(item: CaseByCountry) {
}
}
This is the Base ViewHolder class:
abstract class BaseViewHolder<K, T: DbEntity<K>, VDB: ViewDataBinding>(mDataBinding: ViewDataBinding)
:RecyclerView.ViewHolder(mDataBinding.root){
protected val context: Context = mDataBinding.root.context
protected val layoutInflater: LayoutInflater = LayoutInflater.from(context)
abstract fun bind(item: T)
}
And finally, this is the error i get:
Can you help me please? I don't know what I am doing wrong, thanks in advance.
It was just an argument passing error.
abstract class BaseAdapter<K, t : A<K>,
VDB: ViewDataBinding, VH: BaseViewHolder<K,t, VDB>>{ }
class CaseByCountryAdapter()
: BaseAdapter<Int, CaseByCountry, ViewDataBinding, CaseByCountryViewHolder>()
abstract class BaseViewHolder<K, t : A<K>, VDB: ViewDataBinding> {}
class CaseByCountryViewHolder(mDataBinding: ItemCaseByCountryBinding)
: BaseViewHolder<Int, CaseByCountry, ViewDataBinding>() {}

Not able to access parent class variable in child class in kotlin

In kotlin I am trying to use parent class variables in child class but I am not able to use them ,as I am new to kotlin I don't understand how to do it simply
I am trying to access sharedPerfernces and getting but its giving me null
class webViewActivity : AppCompatActivity{
internal var shared_preferences: SharedPreferences? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
shared_preferences = this.getPreferences(Context.MODE_PRIVATE)
mContext = this
}
class JavaScriptInterface(private val mContext: Context) {
#JavascriptInterface
fun exampleGet(path: String): String {
return webViewActivity().shared_preferences!!.getString(path, "")
//here shared_perferences is null
}
}
}
why I am not able to access parent class variables without constructor of parent class in child class . give me some suggestion ,little help will be appreciated
Add inner before JavaScriptInterface class.
Just like that:
inner class JavaScriptInterface(private val mContext: Context) {
#JavascriptInterface
fun exampleGet(path: String): String {
return shared_preferences!!.getString(path, "")
}
}

Access application context in companion object in kotlin

How can we access application context inside companion object in Android kotlin?
I have a companion object inside an abstract class and I want to access context to read Shared Preferences, but I'm not able to get the context.
UPDATE: I'm working with this stuff in an Android library and also the class that I'm working in is abstract
please see this go to link
class MainApplication : Application() {
init {
instance = this
}
companion object {
private var instance: MainApplication? = null
fun applicationContext() : Context {
return instance!!.applicationContext
}
}
override fun onCreate() {
super.onCreate()
// initialize for any
// Use ApplicationContext.
// example: SharedPreferences etc...
val context: Context = MainApplication.applicationContext()
}
}
Extends Application class like this
import android.app.Application
import android.content.Context
class MyApplication : Application() {
override fun onCreate() {
super.onCreate()
MyApplication.appContext = applicationContext
}
companion object {
lateinit var appContext: Context
}
}
then get context like this
val context = MyApplication.appContext
Actually I'm working inside an Android library and the class is abstract, so can't go with the already suggested solutions. However, I found way to do that.
Creat a lateinit Context field inside companion object.
abstract class MyClass {
companion object {
private lateinit var context: Context
fun setContext(con: Context) {
context=con
}
}
}
And then set it after the app has started
public class WelcomeActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_welcome);
MyClass.Companion.setContext(this);
}
}
There is a super cool article from the guys from Firebase explaining how their SDK gets hold of the context.
Basically my contentprovider looks like this:
/**
* This content provider is only responsible to inject the application context into the common module.
*/
class ContextProvider : ContentProvider() {
companion object {
private val TAG = ContextProvider::class.java.simpleName
}
override fun onCreate(): Boolean {
context?.let {
Common.setContext(it)
return true
}
Logger.e(TAG, "Context injection to common failed. Context is null! Check ContextProvider registration in the Manifest!")
return false
}
override fun query(uri: Uri, projection: Array<String>?, selection: String?, selectionArgs: Array<String>?, sortOrder: String?): Cursor? = null
override fun getType(uri: Uri): String? = null
override fun insert(uri: Uri, values: ContentValues?): Uri? = null
override fun delete(uri: Uri, selection: String?, selectionArgs: Array<String>?): Int = 0
override fun update(uri: Uri, values: ContentValues?, selection: String?, selectionArgs: Array<String>?): Int = 0
}
And the Common object, which I treat like an sibling of any Application class looks like this:
/**
* Partially working like an Application class by holding the appContext which makes it accessible inside this module.
*/
#SuppressLint("StaticFieldLeak")
object Common {
/**
* App appContext
*/
#Volatile
lateinit var appContext: Context
var isStoreVersion: Boolean = false
fun setContext(context: Context) {
appContext = context
}
}
As you can see I also enriched the Common object with a flag to store if the current build is a store version or not. Mainly because the BuildConfig of the app module is also not available in a module or library.
Don't forget to add the ContentProvider to the AndroidManifest of your library within the <application> tag
<provider android:name=".util.ContextProvider"
android:authorities="${applicationId}.common.util.contextprovider"
android:exported="false" />
You can save the instance directly inside a companion object and accessing it outside without problems, I think this approach is the simplest.
IMPORTANT: change the visibility of the instance property to private to ensure no one but Application has write access.
class App : Application() {
override fun onCreate() {
super.onCreate()
instance = this
}
companion object {
lateinit var instance: App
private set
}
}
class Test {
companion object {
lateinit var sharedPreferences: SharedPreferences
fun init(context: Context) {
// to prevent multiple initialization
if (!Companion::sharedPreferences.isInitialized) {
sharedPreferences = context.getSharedPreferences("preference_name", Context.MODE_PRIVATE)
}
}
}
}

How to modify instance variable from separate AsyncTask

let's say i have an activity with instance variable loadedMovie and a method that executes AsyncTask which is in another file
class MainActivity:AppCompatActivity(), NavigationView.OnNavigationItemSelectedListener {
var loadedMovie: Movie? = null
....
fun loadMovie() {
val task = LoadMovieTask(this)
task.execute()
}
}
separate AsyncTask
class LoadMovieTask(val ctx: Activity) : AsyncTask<Void, Void, Void>() {
var movie: Movie? = null
override fun onPreExecute() {
....
}
// loading information from network
override fun doInBackground(vararg params: Void?): Void? {
movie = load()
return null
}
// here i modify views with help of kotlin android extensions
override fun onPostExecute(result: Void?) {
....
}
}
problem is: somehow i can't modify loadedMovie neither from doInBackground (which is ok, because it runs on separate thread) and onPostExecute (which is not ok)
i just type ctx.loadedMovie in onPostExecute and it's not there.. maybe i don't understand something? or maybe there is another way to do it that i'm not aware of
Use this
class LoadMovieTask(val ctx: MainActivity)
instead of
class LoadMovieTask(val ctx: Activity)
MainActivity has the method and not the Android's Activity class itself. So even though you need the context, since you are trying to access the method specific to MainActivity, it is required to pass that

Categories

Resources