I am new with Kotlin and little bit stack with intentService. Manifest shows me an error that my service doesn't contain default constructor, but inside service it looks ok and there are no errors.
Here is my intentService:
class MyService : IntentService {
constructor(name:String?) : super(name) {
}
override fun onCreate() {
super.onCreate()
}
override fun onHandleIntent(intent: Intent?) {
}
}
I was also try another variant:
class MyService(name: String?) : IntentService(name) {
but when I try to run this service I still get an error:
java.lang.Class<com.test.test.MyService> has no zero argument constructor
Any ideas how to fix default constructor in Kotlin?
Thanks!
As explained here your service class needs to have parameterless consturctor. Change your implementation to in example:
class MyService : IntentService("MyService") {
override fun onCreate() {
super.onCreate()
}
override fun onHandleIntent(intent: Intent?) {
}
}
The Android documentation on IntentService states that this name is only used for debugging:
name String: Used to name the worker thread, important only for debugging.
While not stated explicitly, on the mentioned documentation page, the framework needs to be able to instantiate your service class and expects there will be a parameterless constructor.
Related
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 ^-^
I have a variable in my class that extends BroadcastReceiver and implements one additional method called isNetworkAvailable. When I call the method within the class it works but it results in "Unresolved reference" when called from outside. The preexisting methods of the class are also accessible.
Any ideas?
class MainActivity : AppCompatActivity() {
private var connectivityReceiver: BroadcastReceiver = object: BroadcastReceiver(){
override fun onReceive(context: Context, arg1: Intent) {
if ( isNetworkAvailable(this#MainActivity) ) { // Works just as it's supposed to.
// ...
}
}
fun isNetworkAvailable(context: Context?): Boolean {
// ...
}
}
override fun onCreate(savedInstanceState: Bundle?) {
// ...
connectivityReceiver.onReceive() // Accessible if arguments are provided
connectivityReceiver.isNetworkAvailable(this#MainActivity) // ERROR: Unresolved reference
}
}
#Fred answer is correct, it is because compiler don't know that the BroadcastReceiver has the function called isNetworkAvailable.
Kotlin is strong in type interference, you don't need to specify explicitly that the variable is of type BroadcastReceiver it tells the compiler that the object is of type BroadcastReceiver which does not have any function called isNetworkAvailable().
Just remove the explicit type declaration from the variable
private var connectivityReceiver = object: BroadcastReceiver() {...}
Kotlin will automatically assign the correct type using its inferred type of anonymous object.
You're variable connectivityReceiver is of type BroadcastReceiver, which has no method isNetworkAvailable. Unfortunately, it's not as simple as this because for Kotlin connectivityReceiver is nothing but a BroadcastReceiver.
To make your method available you can create a specific class and not an anonymous object.
class MyBroadcaseReceiver : BroadcastReceiver(){
override fun onReceive(context: Context, arg1: Intent) {
if ( isNetworkAvailable(this#MainActivity) ) { // Works just as it's supposed to.
// ...
}
}
fun isNetworkAvailable(context: Context?): Boolean {
// ...
}
}
Then in the activity just use
private var connectivityReceiver: MyBroadcaseReceiver = MyBroadcaseReceiver()
Note that if you do something like
private var connectivityReceiver: BroadcaseReceiver = MyBroadcaseReceiver()
you'll end up in the same issue since connectivityReceiver will be a BroadcastReceiver and not the class where isNetworkAvailable is defined.
There's also the possibility of just removing the explicit type:
private var connectivityReceiver = object: BroadcastReceiver(){
override fun onReceive(context: Context, arg1: Intent) {
if ( isNetworkAvailable(this#MainActivity) ) { // Works just as it's supposed to.
// ...
}
}
fun isNetworkAvailable(context: Context?): Boolean {
// ...
}
}
Kotlin's inference should be able to pick up the method then. The reason why I think this is not a great approach is because it only works if you want to keep this object expression private - see object expressions
Note that anonymous objects can be used as types only in local and private declarations. If you use an anonymous object as a return type of a public function or the type of a public property, the actual type of that function or property will be the declared supertype of the anonymous object, or Any if you didn't declare any supertype. Members added in the anonymous object will not be accessible.
Basically means in your case if you remove private the member isNetworkAvailable won't be accessible anymore. I believe code is meant to change a lot and especially a class like that eventually should go to its own place as it becomes more complex and makes it easier to test. This is of course just a personal opinion.
I'm looking for a way to combine different features in an Android activity, that should be reusable for different activity classes. Specifically the problem arises from overriding open methods where the super's implementation also has to be called.
open class FirstActivity : FragmentActicity() {
override fun onStart() {
super.onStart()
doSomething()
}
}
That's simple enough, but it is not reusable. I could e.g. want to have the same behavior with a different base activity class:
open class SecondActivity : AppCompatActivity() {
override fun onStart() {
super.onStart()
doSomething()
}
}
where I'd have to duplicate the code. If I have a very basic functionality like tracking the state of the activity, I would want this in more or less all of my activities which do have different base classes.
It get's even worse when I want to create some more features that can be combined:
open class ThirdActivity : FragmentActivity() {
override fun onResume() {
super.onResume()
doSomeResuming()
}
}
open Class FirstActivityAgain : ThirdActivity {
override fun onStart() {
super.onStart()
doSomething()
}
}
class MyFragmentActivity : FirstActivity() {
override fun onStop() {
doSomethingElse()
super.onStop()
}
}
class MyFragmentActivityWithResuming : FirstActivityAgain() {
override fun onStop() {
doSomethingElse()
super.onStop()
}
}
class MyTopBarActivity : SecondActivity() {
override fun onStop() {
doSomethingElse()
super.onStop()
}
}
In Scala I can use Traits to do this stackable modification, which allows for very flexible mixins of functionality. It's even possible to modify the same method over and over again, one just has to be careful with the linearization order.
None of this is possible in Kotlin because a Scala Trait is neither equivalent to a Kotlin abstract class nor to a Kotlin Interface.
It doesn't seem to be possible with Kotlin's delegates either. I also thought about using generics, which in my limited imagination could look like this:
open class FirstActivity<BaseActivity : Activity> : BaseActivity() {
...
}
which of course is also not possible.
Is there anything I've overlooked? Can it be done by using Dagger?
What you are referring to in Kotlin called interfaces in conjunction with some basic delegation.
interface Base {
fun printMessage()
fun printMessageLine()
}
class BaseImpl(val x: Int) : Base {
override fun printMessage() { print(x) }
override fun printMessageLine() { println(x) }
}
class Derived(b: Base) : Base by b {
override fun printMessage() { print("abc") }
}
fun main() {
val b = BaseImpl(10)
Derived(b).printMessage()
Derived(b).printMessageLine()
}
Though it won't save you from the super problem since it Android framework issue rather than Kotlin Language.
For your case I would do something like
interface BaseActivityContainer{
var activity: Activity
}
class MainActivity: BaseActivityContainer{
override var activity: Activity = this
}
interface BaseDoable: BaseActivityContainer{
fun doActivityStuff(){
activity.getString(...)
}
}
interface BaseDoableSecond: BaseActivityContainer{
fun doActivityStuff(){
activity.getDrawable(...)
}
}
class SomeActivity: MainActivity, BaseDoableSecond by this
Handle Lyfecycle events with the help of Android Lifecycle
This is not complete and barely functional but I hope it will clear some stuff for you.
I have a test class with RobolectricTestRunner which I use for getting application context and also I extend one class with KoinComponent. When I started my test it returned java.lang.IllegalStateException: KoinApplication has not been started and points to my class that extends KoinComponent. I tried to start Koin in setUp() method with loading modules and removed Robolectric but in this way it can't find application context. Is there a way to write unit test with Robolectric and Koin?
As you can read here, BroadcastReceivers declared in the AndroidManifest get created before your Application's onCreate. Therefore, Koin is not yet initialized. A workaround for that is to create a Helper for your Broadcast Receiver and initialize the Helper lazy:
class MyBroadcastReceiver : BroadcastReceiver() {
// Broadcast Receivers declared in the AndroidManifest get created before your Application's onCreate.
// The lazy initialization ensures that Koin is set up before the broadcast receiver is used
private val koinHelper: BroadcastReceiverHelper
by lazy { BroadcastReceiverHelper() }
override fun onReceive(context: Context, intent: Intent) {
koinHelper.onReceive(context, intent)
}
}
class BroadcastReceiverHelper : KoinComponent {
private val myClassToInject: MyClassToInject by inject()
fun onReceive(context: Context, intent: Intent) {
// do stuff here
}
}
Lets say my default activity is MainActivity and I start another activity DepositActivity without using finish() in MainActivity
Now how can I access the instance of MainActivity inside DepositActivity
Now as how can I access the instance of MainActivity inside DepositActivity
AFAIK That is not possible to access instance of one activity in other Activity
if you have this type of requirement than Try to manage using Fragments
If you want to retrieve some result from DepositActivity use startActivityForResult(..., DepositActivity::class.java) method. In MainActivity override onActivityResult method:
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
// retrieve data using 'data' variable
}
In DepositActivity you need to set data using method setResult() before finishing DepositActivity.
If you want to pass some data to DepositActivity use intent for that, for example:
val intent = Intent(this, DepositActivity::class.java)
intent.putExtra("Extra_Name", /*Some Data*/)
startActivity(intent)
Not Recommended: Use static reference to MainActivity (don't forget to delete it in onDestroy() method):
class MainActivity : AppCompatActivity() {
companion object {
#SuppressLint("StaticFieldLeak")
#JvmStatic
var instance: MainActivity? = null
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
instance = this
}
override fun onDestroy() {
instance = null
super.onDestroy()
}
}
In DepositActivity you can access it like this:
MainActivity.instance?./* call some method or property */
But you should not rely on onDestroy() being called, cause there are situations where the system will simply kill the activity's hosting process without calling this method (or any others) in it... So you can have memory leak
You need to declare as companion object variable and method in MainActivity. Static type of variables and methods are declared as companion object in Kotlin.
Look at below example,
Declare variables and methods in MainActivity,
val value : String = "hello from Main"
companion object {
lateinit var instance : MainActivity
fun getInstancem() : MainActivity {
return instance
}
}
Use this instance and print value in DepositActivity like,
Log.d("log_in_second_activity", "message " + MainActivity.getInstancem().value)
You can see log message.
Hope this will give you hint.