How to create Handler & Runnable Object in Android Inherited Activity Class - android

I have an Inherited class which all of my Activities inherit. In that Base/Parent Activity class I would like to create a Handler and Runnable Object that runs every 5 seconds in all Child Activities without having to add code in each Child Activity. I have it working, where the Runnable Object is in the Inherited Activity but I can't seem to get around having to add code for the Handler in each Child Activity.
open class BaseActivity : AppCompatActivity(), NavigationView.OnNavigationItemSelectedListener {
val handlerBaseActivity = Handler()
val runnable = object : Runnable {
override fun run() {
Log.d("icepts","runnable from base...")
}
}
class MainActivity : BaseActivity () {
val handlerLocalActivity = Handler()
override fun onCreate(savedInstanceState: Bundle?) {
...
handlerLocalActivity.postDelayed(runnable, 100)
}
override fun onPause() {
super.onPause()
handlerBaseActivity.removeCallbacks(runnable)
}
Anyone have any ideas that they'd be interested in sharing? Thanks so much!

You can use Activity's lifecycle in the BaseActivity as well so that every child Activity does exactly the same work in their lifecycle callback. You need to post your Runable in the onResume() or onCreate() of the BaseActivity and remove it in onPause() or onDestroy(). Please see the example below,
BaseActivity
class BaseActivity : AppCompatActivity(), NavigationView.OnNavigationItemSelectedListener {
val handlerBaseActivity = Handler()
val runnable = object : Runnable {
override fun run() {
Log.d("icepts","runnable from base...")
// TODO perform your repetitive operation
// call the handler again after 5 sec to perform above operation again
handlerBaseActivity.postDelayed(this, 5000)
}
}
override fun onResume() {
super.onResume()
handlerBaseActivity.postDelayed(runnable, 100)
}
override fun onPause() {
super.onPause()
handlerBaseActivity.removeCallbacks(runnable)
}
}
After that, you do not have to call postDelayed() function manually in each Activity that inherits BaseActivity. So, to make MainActivity do the same operation that is written in run() function in BaseActivity you just simply inherit BaseActivity.
class MainActivity : BaseActivity () {
// no need to declare local handler
override fun onCreate(savedInstanceState: Bundle?) {
...
// No need to call postDelayed() of localHandler as it's already called in BaseActiviy
}
// No need to remove callbacks in onPause() as it's already defined in BaseActivity
}
Update
I want to clear your confusion of the following comment,
If I put the removeCallBacks in the onPause of the BaseActivity then
the runnable wouldn't run at all because the onPause of the
BaseActivity gets executed before the Child Activity starts.
If you also have BaseActivity registered in the AndroidMannifest and it runs as an independent Activity, removing the runnable from it will not prevent any child from executing their runnable at all. Basically, all the childs will have their own instance of runnables and handlers that will be tied to their own lifecycles.

You can use a Thread to perform this task. I am aware that in Kotlin you have Coroutines which are cheaper to instantiate and easier to manage, so you might want to go that path. Make sure you create your handler outside of this thread, that way when you perform UI changes you don't get errors, however, if you want to perform background tasks then don't use a handler at all.
You can keep a reference to this thread so that you can pause it, by setting running to false and interrupting it, then setting it to null, for good measure, when you completely stop using it.
fun loop() {
handler.post(runnable)
//or
runOnUIThread(runnable)
//background task
}
Thread(Runnable{
while(running){
loop()
try{
Thread.sleep(1000 * 5)
}catch(e: InterruptedException){
//some weird stuff happened
}
}
}).start()

Related

Different between Android onResume(), onStart() and lifecycleScope

I have viewmodel call TestViewModel and a method call fetchDataFromDataSource() to call fetch data from the server, I used to call load data on OnResume() until I bump into lifecycleScope
I have tried to read more but didn't really get which is better.
class TestViewModel: Viewmodel() {
fun fetchDataFromDataSource(){
....
}
}
class TestActivity : AppCompatActivity() {
private val viewModel: TestViewModel by viewModels()
override fun onCreate(savedInstanceState: Bundle?) {
...
lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.STARTED) {
// Is it best to call here
viewModel.fetchDataFromDataSource()
}
}
}
onResume(){
super.onResume()
// or is it best to call here
viewModel.fetchDataFromDataSource()
}
}
where is the best place to call fetchDataFromDataSource(), is it in onResume() or lifecycleScope and what is the advantage lifecycleScope has over onResume() or onStart()
I know the view has rendered at onResume() so what benefit does lifecycleScope has over android lifecycle (onResume onCreate onStart...)
repeatOnLifecycle is similar to calling methods on the respective lifecycle events every time the Activity hits that state but with a quick access to the lifecycleScope which can launch a coroutine.
Example:
override fun onResume(){
super.onResume()
viewModel.fetchDataFromDataSource()
}
is equivalent to -
class MainActivity : AppCompatActivity {
init {
lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.RESUMED) {
viewModel.fetchDataFromDataSource()
}
}
}
}
If you want to load the data from ViewModel every time the user comes to foreground from background, use onStart or repeatOnLifecycle(Lifecycle.State.STARTED).
If you need to load the data everytime the Activity resumes, then use onResume or the lifecycleScope equivalent as shown above but if this is just a one-time op, consider using onCreate.

Does android PhoneStateListener run on background?

I'm currently working on an android application to monitor the incoming and outgoing calls from a phone and register the call info into a file, and from what I've read PhoneStateListener seems to do what I need.
The thing is I need the application to run on background and I was thinking of using a service for that, but every example I've found that uses the listener declares it in the main activity, so I'm not sure if I need to create a service for it to run on background.
For a little more context, I have specific instructions that I can't create an application to "replace" the default calling app, so there's not much use in creating a GUI (I know the app needs a main activity, but it's only functionality should be starting the monitor).
The idea I have at the moment looks something like:
class CallMonitorService : Service() {
private lateinit var serviceLooper: Looper
private lateinit var serviceHandler: ServiceHandler
private lateinit var monitor: StateMonitor
private inner class ServiceHandler(looper: Looper) : Handler(looper) {
override fun handleMessage(msg: Message) {...}
}
override fun onCreate() {
Log.d(serviceName, "Created")
HandlerThread("ServiceStartArguments", Process.THREAD_PRIORITY_BACKGROUND).apply {
start()
// Get the HandlerThread's Looper and use it for our Handler
serviceLooper = looper
serviceHandler = ServiceHandler(looper)
val manager = getSystemService(TELEPHONY_SERVICE) as TelephonyManager
manager.listen(StateMonitor(), PhoneStateListener.LISTEN_CALL_STATE | ...)
}
}
...
}
class StateMonitor : PhoneStateListener() {
// Handler methods
}
So, as a summary, I need the PhoneStateListener to be running at all moments, without the need of an app to be running on foreground.
Should I create a service to run the listener on background or the listener runs on background by itself?
You need to move your listener into Service that will be running standalone. The service is already in "background", so you don't need to create extra thread. Moreover, from what you have posted there is no code that is blocking code, all your events will be sent in callback manner.
Ok, so I've solved the issue.
The short answer is no, the PhoneStateListener doesn't run in background.
By default the listener runs only when the application is in foreground for what I've seen.
Maybe there's a way to run it in a service but I couldn't get it to work.
Instead I solved the problem with a BroadcastReceiver.
So, for the solution
The CallStateMonitor stayed the same as it was before, I just moved it from where it was.
For the service, as I said, I replaced it for a broadcast receiver:
class CallBroadcastReceiver : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
val telephonyManager = context?.getSystemService(TELEPHONY_SERVICE) as TelephonyManager
val monitor = StateMonitor()
telephonyManager.listen(monitor, LISTEN_CALL_STATE)
}
private val tag = "STATE_MONITOR"
private inner class StateMonitor : PhoneStateListener() {
override fun onCallStateChanged(state: Int, phoneNumber: String?) {
when (state) {
TelephonyManager.CALL_STATE_IDLE -> Log.d(tag, "IDLE")
TelephonyManager.CALL_STATE_OFFHOOK -> Log.d(tag, "OFF-HOOK")
TelephonyManager.CALL_STATE_RINGING -> Log.d(tag, "RINGING")
}
}
}
}
The rest is just starting the receiver from the main activity:
class CallMonitorActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
registerReceiver(CallBroadcastReceiver(), IntentFilter(ACTION_CONFIGURATION_CHANGED))
Log.d(ACTIVITY_TAG, "Registered call receiver.")
}
}
And that's it, hope it can help someone else ^-^

Create a service that runs while activity is open

I want to create a Service that makes a network operation but i want it to run as long as an activity is open. So i want to bind it in the activity's lifecycle. If the user navigates to another activity and back i want it to restart. If the screen goes off and the user reopens it i want it to start again if its not possible to keep it
class PushService: Service() {
override fun onBind(intent: Intent?): IBinder? {
return null
}
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
// ToDo Create the request that i want
}
}
So i have to start and stop the service in the onResume and onStop of the Activity?
override fun onResume() {
super.onResume()
Intent(this, PushService::class.java).also { intent ->
startService(intent)
}
}
override fun onStop() {
super.onStop()
stopService(Intent(this, PushService::class.java))
}
Im not sure how to do that. Does anybody know the correct way?
Perhaps it would be a good idea to just create the proccess that i want inside the ViewModel instead of start a Service for it?
You are mostly doing it correctly, except you should either be using onResume/onPause or onStart/onStop, not mixing the two pairs. onStart and onStop are only called when your activity is going out of view entirely. So in your example, if a dialog from another app appeared in front of yours, onStop would not get called, but onResume would get called so your already started service will get multiple onStartCommand calls.
However, the whole point of Services is to run operations that continue when your app is not visible. If you're not doing that, it would be simpler to write your own class (maybe that implements LifecycleObserver or borrows lifecycleScope from the Activity) to handle the background work. Then you wouldn't have to deal with registering it in the manifest and handling intents.
Example of a LifecycleObserver:
// lifecycle is a property of AppCompatActivity. You can instantiate this class
// from your activity.onCreate()
class MyNeworkTaskManager(lifecycle: Lifecycle): LifecycleObserver, CoroutineScope by lifecycle.coroutineScope {
init {
lifecycle.addObserver(this)
}
#OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
private fun onResume() {
startMyRequest()
}
#OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
private fun onPause() {
pauseOrCancelMyRequest()
}
// Alternatively, if you want to expose suspend functions so your activity can request
// and respond to data in a coroutine without callbacks:
suspend fun getMyData(args: String): MyData {
val results = someNetworkRequestSuspendFunction(args)
return MyData(results)
}
// Or if you want to use coroutines for your network request, but still want
// your activity to use callbacks so it doesn't have to use coroutines to call
// these functions:
fun getMyDataAsync(args: String, callback: (MyData) -> Unit) = launch {
val results = someNetworkRequestSuspendFunction(args)
callback(MyData(results))
}
}
I don't do much with networking myself. But whatever library you're using, you can usually convert callbacks to coroutines using suspendCancellableCoroutine. There are tutorials for that you can look up.

How can I decrease a method count in my activity in MVP pattern?

I build my app in MVP architecture and I have a trouble with many functions in my activity and presenter. How Can I decrease a method count?
I have already heard about some solutions:
Split a big presenter into smaller ones but then I would have to create another methods in my activity for presenters connection.
Create a new class and create it instance in my activity which would implement the View interface and will require all of the views needed to manage the presenters. But I am not convinced to this solution. I think it may add another mess to my architecture.
Do you have other ideas or advantages/disadvantages about one described above?
There is more than a way to reduce methods from your Activity/Fragment
One is called inheritance, where you can extend abstract methods into your main Activity/Fragment class and manage the lifecycle from there.
For example, using BaseActivity or BaseFragment you can have more than one method inside of it and just extend that into your main Activity or Fragment
BaseActivity.kt
abstract class BaseActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
requestWindowFeature(Window.FEATURE_NO_TITLE)
window.setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN)
super.onCreate(savedInstanceState)
setContentView(getLayout())
}
#LayoutRes
abstract fun getLayout(): Int
fun Context.toast(message: String?, toastDuration: Int = Toast.LENGTH_SHORT) {
Toast.makeText(this, message, toastDuration).show()
}
override fun onDestroy() {
super.onDestroy()
//Do here what you want
}
override fun onStop() {
super.onStop()
//Do here what you want
}
override fun onStart(){
super.onStart()
//Do here what you want
}
override fun onPause() {
super.onPause()
//Do here what you want
}
override fun onRestart() {
super.onRestart()
//Do here what you want
}
}
This BaseActivity extends AppCompatActivity(), that means that you can manage the lifecycle of your Activity in this class, and then, just extend it in your main Activity, when you do this, all the functionality inside your BaseActivity will be applied to your MainActivity class, if you want to change or override something, just call the methods from that abstract class.
MainActivity.kt
class MainActivity : BaseActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
//You dont need setContentView since we do all the configuration in the BaseActivity
toast("This is a message with a toast since we implemented thi into the BaseActivity we do not need to do toasts all over again")
}
override fun getLayout(): Int {
return R.layout.activity_login_view
}
//For example, if you want to override the functionallity from a method inside your BaseActivity you can implement it like always
override fun onRestart() {
super.onRestart()
//Replace what BaseActivity onRestart() does
}
Doing this, you can have more than 1 method of your Activity inside your BaseActivity, this will reduce the methods inside your class that inherits from BaseActivity, also, if you need this to work with Fragments, just extend Fragment instead of AppCompatActivity an make a class called BaseFragment
Also, adding an interface for view operations and presenter operations is a great way to organize our apps, you can take a look at an example I'm making for login on Github

Reacting to activity lifecycle in ViewModel

I'm trying to create an app which will use MVVM architecture and there's one thing I quite don't understand.
Official Android docs say that's not a good idea to reference activity context in ViewModel's (as ViewModel may outlive activity) so I've started to wonder about usecase when I want to execute some action when my activity is resumed.
I know ViewModel's shouldn't do business logic themselves but even if I use some service class (let's say GPSService which has to start and pauseeach time activity is resumed on paused), and inside this service I react to activity onResume (using Lifecycle observer) I will still reference this activity from ViewModel as I'm referencing service which holds reference to activity being observed, this may cause activity leak (correct me if I'm wrong).
So my question is, how to react to activity or fragment lifecycle in MVVM architecture?
If you need to have a ViewModel be lifecycle aware, then you can have it implement LifeCycleObserver and override life cycle events as necessary. Example,
public class MyModel extends ViewModel implements
LifecycleObserver {
#OnLifecycleEvent(Lifecycle.Event.ON_STOP)
protected void onLifeCycleStop() {
// do something
}
}
In the activity or fragment then you can add the view model to the activity life cycle owner.
public class MyActivity extends AppCompatActivity {
protected MyModel mMyModel;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mMyModel = ViewModelProviders
.of(this)
.get(MyModel.class);
getLifecycle().addObserver(mMyModel);
}
}
I know ViewModel's shouldn't do business logic themselves
Yes, you're right. ViewModel should not contain business logic but
it should contain UI related logic. So basically, API calls or Some
location related stuffs should be avoided in ViewModel logic.
So what if you wanna make some scenario which can react to any activity lifecycle? I'll suggest you to use LifecycleObserver.
Why?, Because LifecycleObserver will provide you callbacks once it's LifecycleOwner will change it's state.
What is LifecycleOwner here? In our case it may be Activity/Fragment.
So, how you can achieve this?
Let's say you want to make location requests during resume & pause period of any activity.
So, for that you can create a class called LocationUpdates as LifecycleObserver like below:
class LocationUpdates : LifecycleObserver {
constructor(){
// some basic location related initialization here
}
#OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
fun connectListener() {
// this method will respond to resume event of our Lifecycle owner (activity/fragment in our case)
// So let's get location here and provide callback
}
#OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
fun disconnectListener() {
// this method will respond to pause event of our Lifecycle owner (activity/fragment in our case)
// So let's stop receiveing location updates here and remove callback
}
#OnLifecycleEvent(Lifecycle.Event.ON_DESTROY) // Optional if you want to cleanup references
fun cleanUp() {
// this method will respond to destroy event of our Lifecycle owner (activity/fragment in our case)
// Clean up code here
}
}
Now from your activity, you can directly make your LocationUpdates, and receive callback.
class MyActivity : AppCompatActivity() {
private lateinit var mLocationUpdates: LocationUpdates
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
//Initialize your LifecycleObserver here & assign it to this activity's lifecycle
lifecycle.addObserver(mLocationUpdates)
}
}
You can refer to how to handle Lifecycle & Codelabs example.
Edit:
If you want to have ViewModel for that job, consider this:
class MyViewModel : ViewModel {
private lateinit var mLocationUpdates: LocationUpdates
constructor() : super() {
// initialize LocationUpdates here
}
// Assign our LifecyclerObserver to LifecycleOwner
fun addLocationUpdates(lifecycle: Lifecycle){
lifecycle.addObserver(mLocationUpdates)
}
//Optional, we really don't need this.
fun removeLocationUpdates(lifecycle: Lifecycle){
lifecycle.removeObserver(mLocationUpdates)
}
}
If your LocationUpdates depends upon Context, consider using AndroidViewModel.
We can now observe our location updates # any activity/fragment using LiveData, and assign our LifecycleObserver like below:
class MyActivity : AppCompatActivity() {
private val viewModel: MyViewModel by lazy {
return#lazy ViewModelProviders.of(this#MyActivity).get(MyViewModel::class.java)
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
viewModel.addLocationUpdates(lifecycle)
}
}
Please note: there's still lot to cover but making this answer as short as possible. So, if you're still confused about something related then please feel free to ask me in comment. I will edit my answer.
with java 8 LifecycleObserver has been deprecated. According to the [docs][1] it is not recommended to use this class as it uses reflection.
Rather the docs recommend using DefaultLifecycleObserver. To do that, extend your ViewModel class with DefaultLifecycleObserver like:
class MyViewModel : ViewModel(), DefaultLifecycleObserver {//implement default lifecycle observer
override fun onCreate(owner: LifecycleOwner) {//override lifecycle events
super.onCreate(owner)
}
override fun onStart(owner: LifecycleOwner) {
super.onStart(owner)
}
override fun onResume(owner: LifecycleOwner) {
super.onResume(owner)
}
override fun onPause(owner: LifecycleOwner) {
super.onPause(owner)
}
override fun onStop(owner: LifecycleOwner) {
super.onStop(owner)
}
override fun onDestroy(owner: LifecycleOwner) {
super.onDestroy(owner)
}
}
and get all the lifecycle event callbacks in your viewmodel by registering your viewmodel as lifecycle event observer in your view class (e.g. Activity class) like:
class MyActivity : AppCompatActivity() {
private val myViewModel: MyViewModel by viewModels()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
...
lifecycle.addObserver(splashViewModel)//registering observer
...
}
}
its just and update to the answer by #farid_z with kotlin and new sdk.
[1]: https://developer.android.com/reference/androidx/lifecycle/LifecycleObserver

Categories

Resources