Using Retrofit to check values in database for a login operation. I don't want to move forward without checking this thus want this to occur synchronically.
I am using the execute method instead of enqueue but still getting the result asynchronously. I understand if I do it all in the same class, it might work but I am trying to keep things separately to prevent code duplicate for other future calls. Please advice what I am doing wrong or how I could go about addressing this. Thanks.
Following method occurs when I click a submit button. This method is at my Activity.
Note that everything in this method works. Problem is after startService, it doesn't wait to get information from my rest service and instead jumps straight to the Toast. After that it carries on to perform the execute method which is useless by then.
private void sendLoginRequest(User user){
String token = verificationHelper.createToken(properties.API_KEY, user, 60000);
Retrofit retrofit = retrofitHelper.getRetrofit(properties.BASE_URL);
UserRepository userRepository = retrofit.create(UserRepository.class);
Call<User> call = userRepository.login(token);
if(call != null){
syncService = new SyncService(call);
Intent i = new Intent(this, SyncService.class);
startService(i);
if(verificationHelper.isValidLogin()){
goToMainActivity();
}
}
Toast.makeText(this, "Invalid Login", Toast.LENGTH_SHORT).show();
}
This is my service which gets into the onHandleIntent method after the Toast is called. (Expecting this to be called first and validated before ever going to the Toast).
public class SyncService extends IntentService {
private VerificationHelper verificationHelper = new VerificationHelper();
private PropertiesUtil properties = new PropertiesUtil();
private RetrofitHelper retrofitHelper = new RetrofitHelper(verificationHelper, properties);
private static Call<User> staticCall;
public SyncService(Call<User> call) {
super("SyncService");
staticCall = call;
}
public SyncService(){
super("SyncService");
}
#Override
protected void onHandleIntent(Intent intent) {
retrofitHelper.performCallBackSync(staticCall);
}
}
This is over at my RetrofitHelper for reference.
public class RetrofitHelper {
private VerificationHelper verificationHelper;
private PropertiesUtil properties;
public RetrofitHelper(VerificationHelper verificationHelper, PropertiesUtil properties) {
this.verificationHelper = verificationHelper;
this.properties = properties;
}
public void performCallBackSync(Call<User> call){
try {
// some logic that handles and verifies token values.
verificationHelper.setValidLogin(true);
} catch (IOException e) {
//some error handling
}
}
}
Related
I am building an SDK and need to implement callbacks between activities, without actually finish an activity. I previously used onActivityResult to provide results back to caller activity. However, this closes activity and I need to deliver callback, without finishing activity from SDK. My current implementation:
fun initializeSDK(){
SDK.getInstance().initialize(resultsCallbackImpl)
}
val resultsCallbackImpl:ResultsCallback = object : ResultsCallback {
override fun response1() {
}
override fun response2() {
}
};
For example, the client calls initializeSDK() from his activity after the button click. Then the client passes interface as parameter, which is set as a property in SDK singleton. Then I use that interface to return results.
The problem occurs after process death. The interface becomes null, because it is not serialized and I can't return callback to client anymore. How should I edit my code to tackle this issue? Is it even possible?
I know that client can initialize SDK in the application class, then it will be re-set after process death. However, such an approach will result in difficulty for the client to communicate results back to activity from application class.
Update:
Do a right click on the project tree and add a new AIDL file called IMyAidlInterface.aidl:
package com.test.aidlsample;
import com.test.aidlsample.MyData;
interface IMyAidlInterface {
List<MyData> getData(long id);
}
If you need to return objects to your client you need to declare and define them as parcelable and import them in aidl file too, here is the MyData.aidl that should be beside the other aidl file:
package com.test.aidlsample;
// Declare MyData so AIDL can find it and knows that it implements
// the parcelable protocol.
parcelable MyData;
and this is MyData.java in the java folder:
public class MyData implements Parcelable {
private long productId;
private String productName;
private long productValue;
public MyData(long productId, String productName, long productValue) {
this.productId = productId;
this.productName = productName;
this.productValue = productValue;
}
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeLong(this.productId);
dest.writeString(this.productName);
dest.writeLong(this.productValue);
}
protected MyData(Parcel in) {
this.productId = in.readLong();
this.productName = in.readString();
this.productValue = in.readLong();
}
public static final Parcelable.Creator<MyData> CREATOR = new Parcelable.Creator<MyData>() {
#Override
public MyData createFromParcel(Parcel source) {
return new MyData(source);
}
#Override
public MyData[] newArray(int size) {
return new MyData[size];
}
};
}
Now build the project so Stub class gets built. After a successful build continue with the service:
public class SdkService extends Service {
private IMyAidlInterface.Stub binder = new IMyAidlInterface.Stub() {
#Override
public List<MyData> getData(long id) throws RemoteException {
//TODO: get data from db by id;
List<MyData> data = new ArrayList<>();
MyData aData = new MyData(1L, "productName", 100L);
data.add(aData);
return data;
}
};
#Nullable
#Override
public IBinder onBind(Intent intent) {
return binder;
}
}
and add the service to the sdk manifest. If you are adding sdk as a dependency to the client like: implementation project(':sdk') you don't need to add AIDL files to client. If not, you have to add them and build the client application. Now, only remains to implement the client activity:
public class MainActivity extends AppCompatActivity {
IMyAidlInterface mService;
/**
* Class for interacting with the main interface of the service.
*/
private ServiceConnection mConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className,
IBinder service) {
// This is called when the connection with the service has been
// established, giving us the service object we can use to
// interact with the service. We are communicating with our
// service through an IDL interface, so get a client-side
// representation of that from the raw service object.
mService = IMyAidlInterface.Stub.asInterface(service);
try {
List<MyData> data = mService.getData(1L);
updateUi(data);
} catch (RemoteException e) {
// In this case the service has crashed before we could even
// do anything with it; we can count on soon being
// disconnected (and then reconnected if it can be restarted)
// so there is no need to do anything here.
}
}
public void onServiceDisconnected(ComponentName className) {
// This is called when the connection with the service has been
// unexpectedly disconnected -- that is, its process crashed.
mService = null;
}
};
private void updateUi(List<MyData> data) {
//TODO: Update UI here
}
#Override
protected void onResume() {
if (mService == null) {
Intent serviceIntent = new Intent();
//CAREFUL: serviceIntent.setComponent(new ComponentName("your.client.package", "your.sdk.service.path"));
serviceIntent.setComponent(new ComponentName("com.test.sampleclient", "com.test.aidlsample.SdkService"));
bindService(serviceIntent, mConnection, Context.BIND_AUTO_CREATE);
} else {
try {
updateUi(mService.getData(1L));
} catch (RemoteException e) {
e.printStackTrace();
}
}
super.onResume();
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
}
every time your client activity gets visibility, it gets data from sdk service. Just build your logic over this template. In sdk activity save data to a database and in service query them from database. I've used simple parameters in this sample.
I assumed your sdk is a library in the client app. If not, you need to do some small modifications maybe. And as I mentioned before you can find more details here: Android Interface Definition Language (AIDL). There are lots of samples and even more Q/A here in the SO on the subject. Good luck.
Original: You need to get callbacks from an activity that is currently invisible since your SDK activity is in front, right? To do that you can create a database for your SDK, persist data to your database and get data via an AIDL in the starting activity:
SdkService sdkService;
CallbackData callbackData
private ServiceConnection mConnection = new ServiceConnection() {
// Called when the connection with the service is established
public void onServiceConnected(ComponentName className, IBinder service) {
sdkService = SdkService.Stub.asInterface(service);
}
// Called when the connection with the service disconnects unexpectedly
public void onServiceDisconnected(ComponentName className) {
Log.e(TAG, "Service has unexpectedly disconnected");
sdkService = null;
}
};
in onCreate:
Intent i = new Intent()
i.setClassName("your.sdk.packageName", "your.sdk.service.path.and.name");
bindService(i, mConnection, Context.BIND_AUTO_CREATE);
and in whenever needed:
if(sdkService != null){
callbackData = sdkService.getCallbacks();
updateUI();
}
Just be careful getting a binder is an async job so if you call bindService and right after call sdkService.getCallbackData you get a NullPointerException. So you might want to move getCallbacks and updateUI inside the onServiceConnected and call bindService in onResume so every time activity becomes visible you would check if there is CallbackData so you can update your UI or whatever.
You cannot use interfaces directly to communicate between activities.
As soon as you start a new activity and new activity becomes visible android OS can kill 1st activity anytime (you can try this with a flag inside developer option "Don't keep activities"). So user of your SDK will complain about certain random "null pointer exception".
So, Now if you want to share data between current and previous screen, you might have to rethought your solution using Fragments.
Exposing your UI using a fragment and communicating back your result to activity which then would update proper fragment which needs the data.
I faced similar issue in one existing app which I was asked to fix. I switched entire app to fragments and single activity, first to release a hot fix.
The problem occurs after process death. The interface becomes null, because it is not serialised and I can't return callback to client anymore. How should I edit my code to tackle this issue? Is it even possible?
This is not possible. If the client process dies, all of its executing code - including your SDK - gets wiped away.
I know that client can initialise SDK in the application class, then it will be re-set after process death. However, such approach will result in difficulty for client to communicate results back to activity from application class.
So what? If the client Activity is restarted, it should call the SDK again to set a new callback instance which you can use from that point forward.
You can use a sharedviewmodel that is bound to both activities; have a mutablelivedata variable that you can observe from the two activities.
ideally on the first activity you can just put the value inside the mutablelivedata variable. Then on the second activity get the activity.
Follow the following link to give you a guideline.
ViewModel Overview
I use google's volley library and I have been battling memory leaks in my apps for weaks now. I have done soo much research and tried soo much already but now I just do not know what to do. This is a sample code:
SplashActivity.java
public class SplashActivity extends AppCompatActivity {
Context mContext;
AuthRequest mAuthRequest;
GetTokenOnSuccessListener mGetTokenOnSuccessListener;
GetTokenOnErrorListener mGetTokenOnErrorListener;
private ConfigTable mConfigTable;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
initialiseViewsAndComponents();
}
#Override
protected void onStart() {
super.onStart();
getAuthToken();
}
private void initialiseViewsAndComponents() {
mContext = SplashActivity.this;
mAuthRequest = new AuthRequest(mContext);
mGetTokenOnSuccessListener = new GetTokenOnSuccessListener(mContext);
mGetTokenOnErrorListener = new GetTokenOnErrorListener(mContext);
mConfigTable = new ConfigTable(mContext);
}
private void getAuthToken() {
if (!mConfigTable.get("INITIALISED").equals("")) {
mAuthRequest.guest(mGetTokenOnSuccessListener, mGetTokenOnErrorListener);
} else {
Intent mainIntent = new Intent(mContext, MainActivity.class);
startActivity(mainIntent);
}
}
}
GetTokenOnSuccessListener.java
public class GetTokenOnSuccessListener implements Response.Listener<JSONObject> {
//private Activity mActivity;
private Context mContext;
private ConfigTable mConfigTable;
private int mSuccess = 0;
private String mMessage = "";
public GetTokenOnSuccessListener(Context context) {
//this.mActivity = context;
this.mContext = context;
this.mConfigTable = new ConfigTable(this.mContext);
}
#Override
public void onResponse(JSONObject response) {
try {
mSuccess = Integer.parseInt(response.get("success").toString());
mMessage = response.get("message").toString();
if (mSuccess == 1) {
mConfigTable.setAuthToken(response.get("message").toString());
Intent mainIntent = new Intent(mContext, MainActivity.class);
mContext.startActivity(mainIntent);
((SplashActivity) mContext).finish();
} else {
Toast.makeText(mContext, "Lol access denied, could not retrieve token from server.", Toast.LENGTH_SHORT).show();
}
} catch (JSONException e) {
e.printStackTrace();
Toast.makeText(mContext, "Lol access denied, could not retrieve token from server.", Toast.LENGTH_SHORT).show();
}
}
}
GetTokenOnErrorListener.java
public class GetTokenOnErrorListener implements Response.ErrorListener {
private Context mContext;
public GetTokenOnErrorListener(Context context) {
this.mContext = context;
}
#Override
public void onErrorResponse(VolleyError error) {
Utils.showNetworkResponse(mContext, error);
}
}
Okay now I moved the response listeners to their own separate classes based on something I read online thinking it will resolve the leak but no it did not. I added code to cancel all pending requests onDestroy() based on the request's tag but still I had memory leaks.
This is just my splash activity and the leaks here are small, I have a feeling it's because I call finish() but I don't get that because I call it after the request has been completed successfully. All my other activities have similar codes but leak more memory as much as 11mb.
So my question is has anyone worked with the volley library? How do I use it and avoid memory leaks?
Using this version:
compile 'com.android.volley:volley:1.0.0'
It's not enough just to "Move response listeners to their own separate classes".
Your listeners have strong references to the Activity (mContext), introducing a leak during the request. It means that your Activity can't be garbage collected, while the request is ongoing.
It's not really a Volley's fault, but rather a natural way of things.
You have couple of options in your case:
1) Pass a WeakReference<Context> to your listeners, instead a strong reference to Context. This way you won't introduce a leak and will have to check if this referenced Context isn't yet null, when you try to access it. But I'd rather go for the 2nd option.
2) Set mContext to null in listeners, when Activity's onDestroy() is called. And perform null check as well, when you are trying to do something with Context in listener. So as soon as Activity will be destroyed, you'll remove other strong references to it, allowing GC to collect it normally.
Please update to latest volley version they have fixed memory leaks.
'com.android.volley:volley:1.1.0-rc1'
https://github.com/google/volley/releases/tag/1.1.0-rc1
You are always creating a new activity. Please try this instead. Read more about it here: https://developer.android.com/reference/android/content/Intent.html#FLAG_ACTIVITY_CLEAR_TOP
Intent mainIntent = new Intent(mContext, MainActivity.class);
mainIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
mContext.startActivity(mainIntent);
I am testing with Mockito. I Have a callback interface:
interface Callback {
void onMessageRetrieved(String message);
void onRetrievalFailed(String error);
}
Then, I have a method that accepts Runnable object that calls that Callback method:
Firstly, interactor.run() calls this method that calls postMessage:
#Override
public void run() {
final String message = mMessageRepository.getWelcomeMessage();
if (message == null || message.length() == 0) {
notifyError();
return;
}
postMessage(message);
}
private void postMessage(final String msg) {
mMainThread.post(new Runnable() {
#Override
public void run() {
mCallback.onMessageRetrieved(msg);
}
});
}
But line mCallback.onMessageRetrieved(msg); is never executed and so I get:
Wanted but not invoked:
callback.onMessageRetrieved(
"Welcome, friend!"
);
Why is run() not executed?
I am working with the project from this articles. This is my Test class:
public class ExampleUnitTest {
private MessageRepository mMessageRepository;
private Executor mExecutor;
private MainThreadImpl mMainThread;
private WelcomingInteractor.Callback mMockedCallback;
#Before
public void setUp() {
mMessageRepository = mock(MessageRepository.class);
mExecutor = mock(Executor.class);
mMainThread = mock(MainThreadImpl.class);
mMockedCallback = mock(WelcomingInteractor.Callback.class);
}
#Test
public void testWelcomeMessageFound() throws Exception {
String msg = "Welcome, friend!";
when(mMessageRepository.getWelcomeMessage()).thenReturn(msg);
WelcomingInteractorImpl interactor = new WelcomingInteractorImpl(
mExecutor,
mMainThread,
mMockedCallback,
mMessageRepository);
interactor.run();
Mockito.verify(mMessageRepository).getWelcomeMessage();
Mockito.verifyNoMoreInteractions(mMessageRepository);
Mockito.verify(mMockedCallback).onMessageRetrieved(msg);
}
}
Here:
mMainThread.post(new Runnable()
But in your unit test, you have:
mExecutor = mock(Executor.class);
mMainThread = mock(MainThreadImpl.class);
That post() call receives a Runnable object. And I assume that your impl class runs Runnable.run() in some thread.
But: within your unit test, you are mocking all those elements.
You have to understand: mMainThread = mock(MainThreadImpl.class); doesn't create a real MainThreadImpl object. It returns something that looks like an object of that class; but this is a mock. It is completely decoupled from your actual implementation. When you do mMainThread.poll() ... nothing will happen. That run method is never executed. Because the mock object does nothing about that parameter when you invoke poll() on it.
In other words: you have to configure all your mocks. For example you can use an ArgumentCaptor and capture the Runnable object given to the post() method.
But of course, as that will be just some instance of an anonymous inner class, this will not help too much.
Long story short: you should step back; and do some more reading how/why you use mock objects...
I am trying to create a handler to make my api calls using retrofit.
My call work and I get a response, but I would like to know what would be a best practice to return the response.
Here is a simplified version of my code:
public class ApiHandler{
private static String username = "myUsername";
private static String sessionID = "mySessionID";
public static ObjectResponse getObjects(String id){
ClientApi.getApiClient().getObjects(new ObjectRequest(username, sessionID, id), new Callback<ObjectResponse>() {
#Override
public void success(ObjectResponse ObjectResponse, Response response) {
//Request succes
/*I would like to pass the ObjectResponse from the success method to the getObjects method to be able to return them*/
}
#Override
public void failure(RetrofitError error) {
//Request failure
///TODO
System.out.println(error.getResponse().getStatus());
}
});
//I would like to return the ObjectResponse here
return null;
}
}
Then I just call from my fragment: myObjectResponse = ApiHandler.getObjects(id);
My problem is that when the success() method is called in the callback, the getObject() method already ended and I'm not able to return the response.
I saw that I could use an eventbus (otto) to achieve that but I'm not sure how that works or if it even is the best way to do that.
So how could I achieve that?
I finally found my answer.
Using otto I just need to send the objectResponse to the bus as followed:
public class ApiHandler{
private static String username = myUsername;
private static String sessionID = mySessionID;
public static ObjectResponse getObjects(String id){
ClientApi.getApiClient().getObjects(new ObjectRequest(username, sessionID, id), new Callback<ObjectResponse>() {
#Override
public void success(ObjectResponse objectResponse, Response response) {
//Request success
BusProvider.getInstance().post(objectResponse);
/*I would like to pass the ObjectResponse from the success method to the getObjects method to be able to return them*/
}
#Override
public void failure(RetrofitError error) {
//Request failure
///TODO
System.out.println(error.getResponse().getStatus());
}
});
//I would like to return the ObjectResponse here
return null;
}
}
And then in my fragment, register as a receiver:
#Override
public void onPause(){
super.onPause();
//Unregister the fragment from the bus provider
BusProvider.getInstance().unregister(this);
}
#Override
public void onResume(){
super.onResume();
//Register the fragment to be able to receive events through the bus provider
BusProvider.getInstance().register(this);
}
#Subscribe
public void onObjectResponse(ObjectResponse objectResponse){
System.out.println("We did it!!!");
//Do what you want with your object!
}
Before EventBus, an interface was required as a private member of the ApiHandler class (in your case). Now on success, you call your unimplemented method of your interface.
You implement this method somewhere in your UI, and you call getObject() to run in the background. Once the method is successful, it will trigger your interface method which you have implemented in your UI along with the objectResponse to play around with.
EventBus has quite similar concept except that it is a central (or singular) and works in a channel/subscribe manner. Try to use Eventbus + Retrofit + any dependency injector to ease your life.
During the most recent Google IO, there was a presentation about implementing restful client applications. Unfortunately, it was only a high level discussion with no source code of the implementation.
In this diagram, on the return path there are various different callbacks to other methods.
How do I declare what these methods are?
I understand the idea of a callback - a piece of code that gets called after a certain event has happened, but I don't know how to implement it. The only way I've implemented callbacks so far have been overriding various methods (onActivityResult for example).
I feel like I have a basic understanding of the design pattern, but I keep on getting tripped up on how to handle the return path.
In many cases, you have an interface and pass along an object that implements it. Dialogs for example have the OnClickListener.
Just as a random example:
// The callback interface
interface MyCallback {
void callbackCall();
}
// The class that takes the callback
class Worker {
MyCallback callback;
void onEvent() {
callback.callbackCall();
}
}
// Option 1:
class Callback implements MyCallback {
void callbackCall() {
// callback code goes here
}
}
worker.callback = new Callback();
// Option 2:
worker.callback = new MyCallback() {
void callbackCall() {
// callback code goes here
}
};
I probably messed up the syntax in option 2. It's early.
When something happens in my view I fire off an event that my activity is listening for:
// DECLARED IN (CUSTOM) VIEW
private OnScoreSavedListener onScoreSavedListener;
public interface OnScoreSavedListener {
public void onScoreSaved();
}
// ALLOWS YOU TO SET LISTENER && INVOKE THE OVERIDING METHOD
// FROM WITHIN ACTIVITY
public void setOnScoreSavedListener(OnScoreSavedListener listener) {
onScoreSavedListener = listener;
}
// DECLARED IN ACTIVITY
MyCustomView slider = (MyCustomView) view.findViewById(R.id.slider)
slider.setOnScoreSavedListener(new OnScoreSavedListener() {
#Override
public void onScoreSaved() {
Log.v("","EVENT FIRED");
}
});
If you want to know more about communication (callbacks) between fragments see here:
http://developer.android.com/guide/components/fragments.html#CommunicatingWithActivity
No need to define a new interface when you can use an existing one: android.os.Handler.Callback. Pass an object of type Callback, and invoke callback's handleMessage(Message msg).
Example to implement callback method using interface.
Define the interface, NewInterface.java.
package javaapplication1;
public interface NewInterface {
void callback();
}
Create a new class, NewClass.java. It will call the callback method in main class.
package javaapplication1;
public class NewClass {
private NewInterface mainClass;
public NewClass(NewInterface mClass){
mainClass = mClass;
}
public void calledFromMain(){
//Do somthing...
//call back main
mainClass.callback();
}
}
The main class, JavaApplication1.java, to implement the interface NewInterface - callback() method. It will create and call NewClass object. Then, the NewClass object will callback it's callback() method in turn.
package javaapplication1;
public class JavaApplication1 implements NewInterface{
NewClass newClass;
public static void main(String[] args) {
System.out.println("test...");
JavaApplication1 myApplication = new JavaApplication1();
myApplication.doSomething();
}
private void doSomething(){
newClass = new NewClass(this);
newClass.calledFromMain();
}
#Override
public void callback() {
System.out.println("callback");
}
}
to clarify a bit on dragon's answer (since it took me a while to figure out what to do with Handler.Callback):
Handler can be used to execute callbacks in the current or another thread, by passing it Messages. the Message holds data to be used from the callback. a Handler.Callback can be passed to the constructor of Handler in order to avoid extending Handler directly. thus, to execute some code via callback from the current thread:
Message message = new Message();
<set data to be passed to callback - eg message.obj, message.arg1 etc - here>
Callback callback = new Callback() {
public boolean handleMessage(Message msg) {
<code to be executed during callback>
}
};
Handler handler = new Handler(callback);
handler.sendMessage(message);
EDIT: just realized there's a better way to get the same result (minus control of exactly when to execute the callback):
post(new Runnable() {
#Override
public void run() {
<code to be executed during callback>
}
});
You can also use LocalBroadcast for this purpose. Here is a quick guide
Create a broadcast receiver:
LocalBroadcastManager.getInstance(this).registerReceiver(
mMessageReceiver, new IntentFilter("speedExceeded"));
private BroadcastReceiver mMessageReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
Double currentSpeed = intent.getDoubleExtra("currentSpeed", 20);
Double currentLatitude = intent.getDoubleExtra("latitude", 0);
Double currentLongitude = intent.getDoubleExtra("longitude", 0);
// ... react to local broadcast message
}
This is how you can trigger it
Intent intent = new Intent("speedExceeded");
intent.putExtra("currentSpeed", currentSpeed);
intent.putExtra("latitude", latitude);
intent.putExtra("longitude", longitude);
LocalBroadcastManager.getInstance(this).sendBroadcast(intent);
unRegister receiver in onPause:
protected void onPause() {
super.onPause();
LocalBroadcastManager.getInstance(this).unregisterReceiver(mMessageReceiver);
}
asume the main function is the activity that is triggering the event:
fun main() {
val worker = Worker()
worker.setOnCallListener(
object: OnCallListener {
override fun onCall() {
// here we define what should happen
// when the event accures
print("event happend")
}
}
)
// most events will be called from Android system itself
// but in our case we have to call it manually
worker.listener.onCall()
}
the Worker class has an instance of Type OnCallListener interface
and a method to set its value:
class Worker() {
lateinit var listener: OnCallListener
fun setOnCallListener(listener: OnCallListener) {
this.listener = listener
}
}
and the OnCallListener interface looks like this:
interface OnCallListener {
fun onCall()
}