I'm learning Android studio with Kotlin, I have setup one listview in Oncreate, it list out user data, then I have another function onOptionItemSelected which add/delete user data item, the problem is : after I add/delete from another function, the data on listview cannot be updated:
Here is my code:
class MainActivity : AppCompatActivity() {
private lateinit var listView : ListView
override fun onCreate(savedInstanceState: Bundle?) {
.....
listView = findViewById(R.id.listview_main)
val adapter2 = listViews(this, array_firstname, array_lastname,array_age)
listView.adapter = adapter2
....
override fun onOptionsItemSelected(item: MenuItem): Boolean {
........
GlobalScope.launch {
db1.UsersDao().insertAll(User_tb(0,firstName, lastName, userAge))
(listView.adapter as listViews).notifyDataSetChanged()
}
With this, I got error "Only the original thread that created a view hierarchy can touch its views."
I searched internet and found I need to use runOnUiThread, then I below part under "listView.adapter = adapter2", but it still does not work:
Thread(Runnable {
this#MainActivity.runOnUiThread(java.lang.Runnable {
(listView.adapter as listViews).notifyDataSetChanged()
})
}).start()
I guess I did not understand runOnUiThread correctly but cannot figure out how, could somebody help?
Thanks!
You don't need to mess with Threads directly since you're using coroutines.
Replace this:
GlobalScope.launch {
db1.UsersDao().insertAll(User_tb(0,firstName, lastName, userAge))
(listView.adapter as listViews).notifyDataSetChanged()
}
with this:
lifecycleScope.launch {
withContext(Dispatchers.IO) {
db1.UsersDao().insertAll(User_tb(0,firstName, lastName, userAge))
}
listView.adapter.notifyDataSetChanged() // your cast was unnecessary
}
lifecycleScope runs on the UI thread, except where you wrap the code using withContext to run in the background. So after the withContext block is done, it automatically returns to the main UI thread to run your last line.
This scope also gives you leak protection. If the Activity is closed before the job is done, the coroutine is automatically cancelled and the view elements can be freed to the GC. It won't try to run the last line that updates the UI.
Also, a tip: Your class names should start with a capital letter so they are clearly distinguishable from variable/property names. And they should be more descriptive. For example, I would change the listviews class name to something like UserListAdapter.
Related
I have a project with 2 fragments. I am looking to pass an iterable from the first fragment to the second. Using navArgs is not an option, since it makes the program crash. Bundle seems to only work with primary data types. Is there a way to go about it, without using some super hacky solution, like passing a string of all the data separated by commas as a string?
The modern way to do this is with a ViewModel (here and here or with the FragmentResult API (last link). Otherwise you're looking at doing it manually through the parent Activity - call a function on the Activity that passes your data to the other Fragment, that kind of thing.
If these Fragments are in separate Activities then you're looking at making your data Parcelable so it can go in a Bundle, or serialisation (e.g. the Kotlin Serialization library) so you can put it in a Bundle as a String, or persist it on disk so you can load it from the next Activity. Serialisation libraries are a robust way of turning objects and data into a stream of text (and other formats if you like) but there's nothing wrong with a String and some separator character if it's all you need, e.g. storing a list of indices or IDs
You can use a shared view model.
In your first fragment:
<code>class FirstFragment : Fragment() {
private lateinit var viewModel: SharedViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
viewModel = ViewModelProviders.of(requireActivity()).get(SharedViewModel::class.java)
viewModel.setData(yourIterable)
}
}
</code>
In your second fragment:
<code>class SecondFragment : Fragment() {
private lateinit var viewModel: SharedViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
viewModel = ViewModelProviders.of(requireActivity()).get(SharedViewModel::class.java)
val data = viewModel.getData()
}
}
</code>
And your <code>SharedViewModel</code>:
<code>class SharedViewModel : ViewModel() {
private val _data
I was using fragment and now I attached ViewModel to it and was transferring code to ViewModel and activity?.packageManager?.getPackageInfo(uri, PackageManager.GET_ACTIVITIES) this line shows an error. How can I access package manager in ViewModel?
On way is to extend AndroidViewModel instead of ViewModel as:
class MyFragmentViewModel(application: Application) : AndroidViewModel(application) {
...
Now you can call:
application.packageManager?.getPackageInfo(uri, PackageManager.GET_ACTIVITIES)
Theoretically if you are implementing MVVM pattern (I see you implementing ViewModel), android.* layer should be handled in the View, Activities/Contexts shouldn't be managed in the ViewModel to avoid Memory Leaks. Even though, depending on the project context, of course this rule doesn't apply to every single project context, I think the best approach would be (if not Dependency Injection is been used) to have an Application Provider.
Create an object:
object ApplicationProvider {
#Volatile
lateinit var application: Application
fun initialize(_application: Application) {
if (!::application.isInitialized) {
synchronized(this) {
application = _application
}
}
}
}
In your MainActivity, initialise the ApplicationProvider as the following:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
ApplicationProvider.initialize(this.application)
}
Now you can access the ApplicationContext in your whole project where is needed with:
ApplicationProvider.application.applicationContext
Remember to not assign ApplicationContext to static fields (as a val e.g.) to avoid Memory Leaks.
PD: I'm not very fan of AndroidViewModel, but I guess it is a good solution as well, as a colleague mentioned before :D
When I implemented the member in the class for the on click listener as shown below:
class QuizQuestionsActivity : AppCompatActivity(), View.OnClickListener {
I was given the option of implementing it as:
override fun onClick(p0: View?) {
I need it to be
override fun onClick(v: View?) {
can someone explain the difference and why I am not getting the option of v: View
Kotlin provide default parameter with alphabets and digits you can simply change it with your variable name like --
override fun onClick(v: View?) { }
override fun onClick(view: View?) { }
override fun onClick(myView: View?) { }
It's quit good and it's meaningful variable name that remember as long time. where P0 and P1 named variable is not like good to remember.
Hope You can understand what I mean to say.
Both of those functions are the same, it's just a different name for the View variable. Kotlin parameters are listed with the parameter name first, then the class name.
If you want p0 to be v instead, just change the parameter name to be v.
i have 2 classes here:
#1 AlarmReceiver
class AlarmReceiver: BroadcastReceiver() {
...
fun setRepeatingAlarm(context: Context) {} //the method that i want to call
}
#2 SettingFragment (which is attached to SettingActivity)
SettingFragment(): PreferencefragmentCompat() {
private lateinit var reminder: String
private lateinit var language: String
private lateinit var alarmReceiver: AlarmReceiver
private lateinit var langPreference: Preference
private lateinit var reminderPreference: SwitchPreference
override fun onCreatePreferences(savedInstanceState: Bundle?, s: String?) {
addPreferencesFromResource(R.xml.settings)
init()
}
private fun init() {
reminder = resources.getString(R.string.key_reminder)
language = resources.getString(R.string.key_language)
alarmReceiver = AlarmReceiver()
langPreference = findPreference<Preference> (language) as Preference
reminderPreference = findPreference<SwitchPreference> (reminder) as SwitchPreference
}
private fun reminderSetting() {
// the method where i place the that method
}
i confused on how to define the context param from that method i called.
i tried to code this:
alarmReceiver.setRepeatingAlarm(activity.applicationContext)
but i got error "Only safe (?.) or non-null asserted (!!.) calls are allowed on a nullable receiver of type FragmentActivity?"
how to solve this problem? thank you :)
The activity property is actually calling the Fragment's getActivity() method, which can return null - it's possible for the fragment to be in a state where it's created, but isn't actually attached to an Activity yet, so in that case this method will return null.
You're chaining another call onto the result, so because it could be null, you're either calling Activity.applicationContext (good!) or null.applicationContext (very bad!). So the warning is telling you you're doing this, and you either need to handle that potential null safely (with the ?. call) or explicitly say "this is definitely not going to be null when I call it" (with the !!. call)
You should read up on Kotlin's null safety handling because it's important, and it's there to make your life a whole lot easier in the end. But in this case (and most cases) you shouldn't use !! because there are some situations where it won't be true, the property will be null, and then your app crashes
Here's the safe way to handle it:
activity?.let { alarmReceiver.setRepeatingAlarm(it.applicationContext) }
there's a whole bunch of ways you could write that, but that's the simplest to understand - if activity is not null, it runs the let block, passing in the non-null activity as a variable called it. Then you can just run your code the same way because now you know it's not null! If activity is null, that let block won't run at all. Your alarm won't be set, but it won't be set if your app crashes either - this way you can handle it, or if this is a rare case where it shouldn't ever happen, you can just do nothing - it's definitely not going to crash with a NullPointerException at least!
I am learning Scala multi-thread programming, and write a simple program through referring a tutorial:
object ThreadSleep extends App {
def thread(body: =>Unit): Thread = {
val t = new Thread {
override def run() = body
}
t.start()
t
}
val t = thread{println("New Therad")}
t.join
}
I can't understand why use {} in new Thread {} statement. I think it should be new Thread or new Thread(). How can I understand this syntax?
This question is not completely duplicated to this one, because the point of my question is about the syntax of "new {}".
This is a shortcut for
new Thread() { ... }
This is called anonymous class and it works just like in JAVA:
You are here creating a new thread, with an overriden run method. This is useful because you don't have to create a special class if you only use it once.
Needs confirmation but you can override, add, redefine every method or attribute you want.
See here for more details: https://docs.oracle.com/javase/tutorial/java/javaOO/anonymousclasses.html
By writing new Thread{} your creating an anonymous subclass of Thread where you're overriding the run method. Normally, you'd prefer to create a subclass of Runnable and create a thread with it instead of subclassing Thread
val r = new Runnable{ override def run { body } }
new Thread(r).start
This is usually sematincally more correct, since you'd want to subclass Thread only if you were specializing the Thread class more, for example with an AbortableThread. If you just want to run a task on a thread, the Runnable approach is more adequate.