So, this is probably a very basic design question, but Im just not sure how to go about it.
Normally, when I use event listeners, I define it in whatever class, then I override the necessary methods in my Activity and instantiate the class and the listener as needed.
However, in this case, I have my MainActivity class (that also implements the listener), a class (called testClass) that implements the listener, and a broadcast receiver class. The broadcast receiver class instantiates the calls the testClass. Now, what I am trying to do is to update a TextView in MainActivity when a given function is called in the testClass.
Not sure how to go about this.
Hope this wobbly issue description makes sense.
This is a problem I've tackled in the past when using a background Service to update data that is displayed on screen. The general pattern I use is to add a member variable to your processing class (in this case, I think it's your TestClass) that is a Map (named something like mCallbackMap) with android.os.Handler as the key and your listener object as the value (normally this will be an interface that you define). The Handler, which is created in the Activity and thus associated with the main thread, is needed because you can't change the UI of an Activity from outside the main Thread; you'll use the Handler to post a runnable to the main thread instead of manipulating it directly.
When your activity gets going, probably in onCreate, onStart, or onResume, you'll register it as a callback with your TestClass by using the mCallbackMap's put() method. Simply instantiate a Handler, which you'll also store as a member variable of your activity, and use it as the key and your Activity as the value. You'll need to remove the callback in onPause or onStop so you don't leak the activity after it's out of view.
Then, once TestClass finishes handling whatever the broadcast gives it, you'll iterate through your mCallbackMap (maybe you have more than one callback, maybe you don't) and call Handler.post(Runnable). In the Runnable's run() method, you call the callback's methods as appropriate.
Related
I have 2 activities: MainActivity and MySecondActivity.
I start MySecondActivity, but while I'm working in it, I receive a callback from an external library in MainActivity. This callback should update some information in MySecondActivity.
Question: Can I keep a reference of MySecondActivity in MainActivity in order to update it later ?
Basically it would be like this:
val mySecondIntent = Intent(applicationContext, MySecondActivity::class.java)
startActivity(mySecondIntent)
(...)
fun MyCallBackFunction(newInfo: Integer)
mySecondIntent.updateMyInfo(newInfo) <-- here I cannot access "MySecondIntent"
I tried to store the reference of mySecondIntent in the companion object but I could not make it work.
Do you have any suggestion ?
Thank you !
Here is my suggestion. Don't let any activity keeps the other activity reference, it's not the best practice.
Suggestion
If the callback you receive has impact on multiple activities, then make it global. I usually create a singleton manager class for handling each global event's logic that may have impact on many places in the app. Anywhere in your app need to listen for the event, register the event to the manager.
For example, my AppLocationManager is a singleton class, responsible for making gps refresh and other location stuff. When location change, fire events to all registered listeners. In your case, both MainActivity and SecondActivity knows the changes and update itself, remember to unregister the listener in activity onDestroy, or you will have a memory leak.
Workaround for quick implementation
If you still want to keep the activity reference for quick modification, which is not recommended, use WeakReference
Store a WeakReference of MySecondActivity as a static global variable:
public static WeakReference<Activity> mTmpAtivity
Unlike default StrongReference, this variable will release the instance if the activity is GC so it's memory safe.
I have an Activity called MainActivity that starts a Service called MainService. It is also binds the Service, so MainActivity can access methods and public variables within MainService. Is it possible to do it the other way round, i.e. the Service is able to access the Activity's methods?
I wish to implement it this way because I have a variable in MainActivity that is set upon onResume(), and on first startup the service has not yet started by the time onResume() runs, so at that point in time the service is still null.
This answer assumes that the Service in question runs in a different process:
Yes, it is possible. The general idea is that not only your Activity binds the remote Service through some AIDL defined interface, but it also implements additional AIDL interface which the Service is aware of, and sets itself as a callback target of the remote Service.
You'll have to have 2 AIDL files: the first one describes the interface of the Service, and the second one describes the interface of the Activity.
The implementation of such a scheme is very similar to "remote Service callbacks" described in this answer, though "callback" method would no longer be void, but return the value you're interested in.
Design considerations:
The above scheme will allow you to get values from Activity, but I don't think you should take this path. From the description of your use case, it looks that you only want to pass some value to the Service when Activity gets resumed. Since your Service is bound anyway, you can simply add a method setSomeValue(int value) to its AIDL definition and call this method from onServiceConnected() callback.
Yes it's possible.
You have to prepare method in your service to return back your activity just after service is bound:
public void bindActivity(MyActivity activity){...}
Then after service is bound to activity just call this method with MyActivity.this as parameter.
However...
You probably should not do it. Much more clear solution is using LocalBroadcastManager to pass events and data or use some more efficient solutions like Otto to do this same, but still - without direct access to one component's fields / methods from another.
I'm extending the Application class for my additional custom need. And I'm calling a method inside that. As the expected behaviour, it is getting invoked for type of Android components(Activity, Service, Broadcast receiver, etc.,) But I want that too be invoked only on Activity. Is that any other way to overcome this problem ?
public class MyApplication extends Application {
#Override
public void onCreate() {
super.onCreate();
// the below method needs to invoked only for service
// but now called for all application components
myCustomMethod();
}
....
}
But I want that too be invoked only on Activity.
Can't be done. The Application instance will run if one component of your Application is open.
You need to do the customized stuff in another class and open it just when the instance of your desire component is open.
just add your code to onCreate method of your entry-point activity. If you want it to be called once per session - add two int keys to your shared preferences - app_launch_count and method_invoke_count. Increment first on App's onCreate and check the second in your Activity's onCreate if first greater then invoke the method :)
Move myCustomMethod() into the activity. An Application has no way of knowing what triggered the creation of its process.
Or, use registerActivityLifecycleCallbacks() on Application to register an ActivityLifecycleCallbacks object, and put your myCustomMethod() logic in onActivityCreated(). This requires a minSdkVersion of 14 or higher. That will tell you when each activity is created after your process is instantiated — if you only care about the first one, you would have to unregister the callbacks in your onActivityCreated() implementation.
I'm wondering where I should place my AsyncTask in my android project. As of right now I'm implementing an AsyncTask as a private class of my activity its running under. What I am going to do is in each activity that has a network call I will implement its own private class of AsyncTask. I have a few questions though
In The preexecute method it says I can interact with the activity and place a spinner or progress bar. I do this by using My_Activity_Class_Name.this. So my question is does that line of code reference the activity the AsyncTask is called from? If so I believe that will be a static method. How do i actually pass in the instance of the class so I can interact with non static functions?
I want to place all my Async code into one class for its respective needs. My quesiotn though is if i need to return a type back to the class that calls the Async method how can I return a value? Also is this the best practice?
You should make your inner private AsyncTask class - static. This is because otherwise it will have implicit reference to your Activity - this means that if your Activity will be recreated - ie. due to config change - then your AsyncTask will still hold your activity reference causing reference leak. This means you should pass reference to your activity to AsyncTask in ie. constructor of AsyncTask, and keep it in WeakReference (WeakReference/AsyncTask pattern in android).
So my question is does that line of code reference the activity the ASYNC Task is called from?
yes, but only if your AsyncTask class is non static
If so I beleve that will be a static method. How do i actually pass in the instance of the class so i can interact with non static functions?
its not a static method, with My_Activity_Class_Name.this you can access non static methods of your Activity class.
My quesiotn though is if i need to return a type back to the class that calls the Async method how can I return a value? Also is this the best practice?
You can call a method on your Activity class, there is nothing wrong with that. Remember that you cannot access UI widgets from non UI thread. So update your Activity widgets from onPostExecute which is called on UI thread.
I am new to Android Development and have a question (will probably show my newbie status). I am calling an asynchtask from a custom activity. Once the asynch task is completed onPostExecute I would like to call back into my activity and set a pojo (Map()). I know that onPostExecute seems to run on the UI thread but I am not sure how to get visibility into the calling Activity.
The goal is to be able to have some variables set in my activity and ideally the webservice call will already be completed.
Thanks,
Craig
If the AsyncTask is a subclass of the activity, it has access to all public, protected, and private variables of the superclass. If its not, you need to write a public function in the Activity class to set the variables, and call it from the AsyncTask. That will probably require you to pass the activity to the AsyncTask via the constructor and save it in a member variable of the task.