I have many activities ( say activity A, B, C) calling a single activity ( say activity D) for a result (startActivityForResult(..)).
But want to do something according to the caller activity in the callee activity ( activity D) before the callee sets result and finishes.
The task I want to do needs to use data variables of the calling activity, so now I am planning to pass a callback class similar to this:
public interface myCallBack implements Seriabizable{
public void myMethod();
}
from each caller to the callee on launching the activity;
intent.putExtra(CALL_BACK_OBJECT_KEY, new myCallBack(){
#Override
public void myMethod(){
//code that uses instance variables of caller class
}
});
of course the callback is seriabizable.
myCallback = getIntent().getSerializableExtra(CALL_BACK_OBJECT_KEY);
Now the callee activity ( activity D) will do the task like this:
myCallback.myMethod();
But is this the right way of doing it? Is it good to pass callback class across activities? What other options are available?
Or is there a way to call methods of the caller activity directly? In this case I will force the caller activities to implement MyCallBack interface.
NB:
The reason I dont want to pass the data needed from caller to callee is, the data needed by the task is completely different for each caller activity and also some time big. So I dont to mess the callee activity with those data variables.
What you suggest may work. However it looks complicated and not a good practice.
If you just want to pass data accross activity, why don't you use intent.putExtra with the data you want to pass accross activities ?
If the data you want to pass are too large, or too complex, to be pass like that, maybe you should implement your application another way. You could for instance use a Service. You wil have a three services (one for each activity, or use case). These services will implement a common interface or herit from the same class. Your calling actvity (A, B or C) will start the appropriate service and the service will store the datas. When you start the activity D, this activity will bind with the service. Then she will be able to fully access all datas or objects from the service.
Related
What is the best way to pass data from an Activity to fragment, but the fragment is hosted by another activity.
Elaborating:
Activity A hosts Fragment A (content in activity A) <== Activity B
I have already achieved this, but apparently, my way of doing it has caused some memory leak.
An example would be to refresh a RecyclerView contained in a fragment when an activity is closed, but I do not want to put it in the onResume.
interface contained in the activity(is not the host)
public class Activity extends AppCompatActivity{
public static OnlistenClose delegate;
public interface OnlistenClose {
void refreshList();
}
}
//fragment that implements the interface
public class FragmentB extends Fragment implements Activity.OnlistenClose{
Activity.delegate = this;
#Override
public void refreshList(){
//my code
}
}
Using square/leakcanary indicates there are leaks.
What is the best way to pass data from an Activity to fragment, but the fragment is hosted by another activity.
Ultimately, you are passing data from one activity to another. If the data is stored permanently in a database or file, then the receiving activity should just read the data after the first one has updated it.
On the other hand, if you are passing data that only needs to be available in memory, then you can pass the data with an Intent.
As you have seen, keeping a reference to one activity in another activity causes memory leaks. The callback method which you are attempting to use is only valid for the activity which owns the fragment, not for a second activity. Instead, you should honor the activity lifecycle and use Intents for communication as appropriate.
There is no "best practice" for a general question or even a specific circumstance.
Standard ways of passing data:
Intents
Storage
Shared Preferences
Internal Storage
External Storage
SQLite Databases
Network Connection
Static class (no link necessary)
Etc, etc
There are many ways to accomplish a task as there are ways to describe that task in a sentence.
I finished my work.
I completed my task.
It's published in the Play Store.
I'm done.
I have nothing to do.
I want to pass an interface from 1st activity to 2nd activity.
I want to initiate methods from the interface from the 2nd activity which will affect the 1st activity.
I'm well aware that it's very overkilling not using the onActivityResult mechanism, and that it might not be good programming, but roll with me please.
Here's the issue - my interface can't implement Serializable / Parcelable since interface can't implement another class.
This is my interface :
public interface ITest {
void onSuccess(String text);
}
But, i can't start my activity with this interface since it's not Parcelable.
intent.putExtra("testInterface", new ITest() {
#Override
void onSuccess(String text) {
}
}
Obviously, i receive a compilation error :
Cannot resolve method 'putExtra(java.lang.String, ITest)'
You cannot "pass an interface". An "interface" is an abstract thing. What you want to pass is a concrete implementation of that interface, ie: an object (that happens to implement your interface). When you instantiate your "interface" (in your example, like this:
intent.putExtra("testInterface", new ITest() {
#Override
void onSuccess(String text) {
}
}
you are actually creating an instance of an anonymous class that implements the interface ITest. To pass this in an Intent you would need to make this class also implement Parcelable or Serializable.
However, even if you did that, it would not solve your problem. What you need to understand is that you can't pass objects (instances) by putting them as "extras" in an Intent. When you do that, Android actually serializes and then deserializes the object so that you end up with 2 objects, one is a serialized/deserialized copy of the original.
If you want ActivityB to communicate with ActivityA, you will need to use another technique. Try one of these:
ActivityB sends a broadcast Intent, which ActivityA listens for
ActivityA starts ActivityB using startActivityForResult() and ActivityB sends data back to ActivityA using setResult()
Use public static (ie: global) variables to communicate
Store data in shared preferences, or a file, or a database
What you really need to understand is that, under certain conditions, the following can occur:
your app is running with ActivityA in the stack and ActivityB on the top of the stack
user presses HOME
Android moves your task to the background and eventually kills the hosting process
User returns to your app (by starting it again or selecting it from "recent task" list)
Android creates a new process for your app, and instantiates ActivityB, then calls onCreate(), onStart() and onResume() of ActivityB.
In this case, there is no instance of ActivityA anymore. That instance is dead. So ActivityB cannot communicate with ActivityA because it no longer exists.
A similar need sometimes comes up for me when there is no ActivityA around but I still need some code to be executed that ActivityB doesn't know about.
What I will usually do in this case is pass a Messenger in the intent. (A Messenger implements Parcable so it can be passed).
ActivityB will send defined messages to the messenger, And ActivityA will execute the corresponding interface calls when receiving the correct messages.
A simple solution for the problem would be making the interface Itest extend the Parcelable class. That way, the implementing class of the interface will have to implement Parcelable methods, and you can pass the interface reference in putParcelable method. Hope this helps.
Let's say you have an activity A that use an object mObject (e.g. here a client for an API).
This object constructor check whether or not the user is already identified, and if he's not, start a login form activity (B) that prompt user for credentials so that the API can just log him.
How can you do that?
I've thought just asking for the Context in the constructor but actually it doesn't implement startActivityForResult so I either have to cast it or to actually ask for a Activity object.
Even if I do this however, the result will be sent on the onActivityResult callback (of the activity A) and not in the object (while ideally, I would like to do it "synchronously" : when the activity is returned, store it in an object and continue the execution of the constructor).
What would be a good way to do this, then?
No, it's not reasonable to do that in a constructor.
It's also not a great idea to have a non-Activity class use the Activity's Context to start another activity.
The most straightforward way would be for Activity A to use mObject to determine whether login is required, and if so Activity A should start Activity B (startActivityForResult is fine if you need to pass data back to Activity A from Activity B).
How you communicate the login data to mObject is up to you, but you'll probably want mObject to be shared somehow between Activity A and Activity B.
There are a few ways to do that.
You could have mObject as a member of your Application class.
Or you could have mObject just be a class with static data and static methods.
Or you could use a dependency injection framework, and inject mObject into both activities.
I am using 3 classes A, B and C. In class A I created a method clickButton() and In class B I used onClick() for a button. while clicking on the button it has to call the method clickButton() in Class A, and inside the clickButton() i wrote intent for initiating the class C.
The problem is I couldn't able to call the Class A method in class B.
You generally don't want to touch another Activity directly. The Android design paradigm represents a largely separated viewpoint between different activities, and so instead of directly calling methods on a class A, you will send it an Intent or message or something. If you have a utility method, you should consider moving them into a shared class and making them static. If you have something that needs to be performed in the background or shared persistent store, you should consider moving to a service or content provider.
My launch activity starts up another activity whose launch is set to single instance. In this 2nd activity, I have a public method. I then start up a 3rd activity and that activity needs to access the public method in the 2nd activity. I don't want to use startActivity and pass it extras because I assume the onCreate will get called (or am I wrong?) and I need to avoid the 2nd activity from reinitializing itself.
When an activity is started using startActivity, is it possible to gain access to the underlying class instance itself and simply call the method?
I actually came up with a simple solution. As a matter of fact you can access the underlying class of an activity. First, you create a class that is used to hold a public static reference to activity 2. When activity 2 is created, in its onCreate method you store "this" in the static reference. Activity 2 implements an interface with the methods that you want available to any other activity or object. The static reference you hold would be of a data type of this interface. When another activity wants to call a method in this activity, it simply accesses the public static reference and calls the method. This is no hack but is intrinsic to how Java operates and is totally legitimate.
It is not a good idea.
As I can understand method from second activity is actually not connected to particular activity while you want to call it from another one. So carry the method out to other (non-activity) class (maybe static method) and use it from both activities.
It's not directly possible to gain access to activity object started using startActivity (without using some hacks). And frankly you shouldn't even trying to accomplish this.
One Activity component can cycle through several Activity java object while its alive. For example, when user rotates the screen, old object is discarded and new activity object is created. But this is still one Activity component.
From my experience, when you need to do things you described, there is something wrong with your architecture. You either should move part of activity's responsibilities to Service or to ContentProvider, or use Intents, etc. Its hard to recommend anything more specific without knowing more details.
No there is no way to pass a reference via startActivity() however you can use some sort of shared memory to keep reference to your Activity. This is probably a bad design. However passing an extra with your Intent will not cause onCreate, that is completely related to the lifecycle.