How to Use Interface to Communicate Between Two Activities

How to use interface to communicate between two activities

Have you considered using LocalBroadcastManager?

In Act1's onCreate:

act2InitReceiver= new BroadcastReceiver()
{

@Override
public void onReceive(Context context, Intent intent)
{
// do your listener event stuff
}
};
LocalBroadcastManager.getInstance(this).registerReceiver(act2InitReceiver, new IntentFilter("activity-2-initialized"));

In Act1's onDestroy:

LocalBroadcastManager.getInstance(this).unregisterReceiver(act2InitReceiver);

In Act2's onCreate:

LocalBroadcastManager.getInstance(this).sendBroadcast(new Intent("activity-2-initialized"));

Give me a comment if the code doesn't compile, I'm writing it by hand.

Communication between two activities using interface not working as expected

Your problem is that you are calling onReturnedToActivity() on a new instance of MainActivity, not the one you started from or the one that you will return to.

There are several ways to share data between activities, one is to use startActivityForResult().

I think your problem however is that Activity1 will be stopped, so you can't really send it data from Activity2. You can however start Activity2 with startActivityForResult and return a result.
Here is the example from the documentation.

Starting the activity.

const val PICK_CONTACT_REQUEST = 1  // The request code
...
private fun pickContact() {
Intent(Intent.ACTION_PICK, Uri.parse("content://contacts")).also { pickContactIntent ->
pickContactIntent.type = Phone.CONTENT_TYPE // Show user only contacts w/ phone numbers
startActivityForResult(pickContactIntent, PICK_CONTACT_REQUEST)
}
}

Getting the returned data.

override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent) {
// Check which request we're responding to
if (requestCode == PICK_CONTACT_REQUEST) {
// Make sure the request was successful
if (resultCode == Activity.RESULT_OK) {
// The user picked a contact.
// The Intent's data Uri identifies which contact was selected.

// Do something with the contact here (bigger example below)
}
}
}

Other schemes include a Global Singleton, launching activities with Intents, or using SharedPreferences writting data in one activity and then reading it back in another.
If you do a search on android share data between activities You will see there are various options on the best way to accomplish this.

Communication between Class and Activity via Interface

In the constructor of other class: ClassB, accept interface as argument and pass reference of Activity, hold that object in your Activity.

like so:

public class MainActivity extends AppCompatActivity implements MyInterface()
{
private ClassB ref; // Hold reference of ClassB directly in your activity or use another interface(would be a bit of an overkill)

@Override
public void onCreate (Bundle savedInstanceState) {
// call to super and other stuff....
ref = new ClassB(this); // pass in your activity reference to the ClassB constructor!
}

@Override
public void doAction () {
// any code action here
}
}

public class ClassB
{
private MyInterface myinterface;

public ClassB(MyInterface interface)
{
myinterface = interface ;
}

// Ideally, your interface should be declared inside ClassB.
public interface MyInterface
{
// interface methods
}
}

FYI, this is also how View and Presenter classes interact in MVP design pattern.

How to use interface to communicate between activities after process death?

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.

How to use interface to communicate between fragment and activity?

You need to create the interface for it like below

public interface FilterValuePassInterface {

public void onSelectedFilterValue(String name);
}

Fragment class should look like below

class MyFragment extends Fragment implements FilterValuePassInterface {

@Override
public void onAttach(@NonNull Context context) {
super.onAttach(context);
try {
((YOUR_ACTIVITY) getActivity()).setOnDataListener(this);
} catch (Exception ex) {
ex.printStackTrace();
}
}

@Override
public void onSelectedFilterValue(String name) {

}
}

And inside the Activity class , you need to create the method setOnDataListener and initialise the fragment like below

 MyFragment myFragment;
public void setOnDataListener(MyFragment myFragment) {

this.myFragment = myFragment;

}

Again inside the activity you can send the data from any click or event, you just need to call this method from the activity to transfer the data in fragment like below

    YOUR_CLICK.setOnClickListener(new OnClickListener() {

public void onClick(View v) {
// TODO Auto-generated method stub
myFragment.onSelectedFilterValue("YOUR_MSG");

}
});

Communication between two Activities?

You can use Intent with extra parameters.
For example, on ToursActivity.java

Intent intent = new Intent(ToursActivity.this, MapActivity.class);
intent.putExtra("lat", latitude);
intent.putExtra("long", longitude);
startActivity(intent);

Then you can get get these parameters on onCreate() method of MapActivity.java:

Intent intent = getIntent();
long latitude = intent.getLongExtra("lat", 0);
long longitutde = intent.getLongExtra("long", 0);

Android: How to send interface from one activity to another

First and foremost, this is very bad:

this.inter = (MyInterface) this.context;

If you pass a context into the constructor which does not implement your interface your application will crash and it's easy to make such a mistake. So you see, this is very error prone, instead implement it like this:

public class MyObject {

private Context context;
private MyInterface inter;

public MyObject(Context context, MyInterface inter) {
this.context = context;
this.inter = inter;
inter.aMethod();
}
}

This way it's much safer, and cleaner.


To send the Object to another Activity make sure it implements Serializable like this:

public interface MyInterface extends Serializable {
public void aMethod();
}

And now you can just add the interface as an extra to the Intent which starts the other Activity:

Intent intent = new Intent(context, OtherActivity.class);
intent.putExtra("interface", inter);
startActivity(intent);

And in the OtherActivity you can get the Object from the Intent like this:

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

Intent intent = getIntent();
MyInterface inter = (MyInterface) intent.getSerializableExtra("interface");

...
}

EDIT:

When you do something like this in your Activity you are creating an anonymous class:

OnCreateListener inter = new OnCreateListener() {

@Override
public void onObjCreate() {
Log.d("pltk", "dfgbfdgh");
}
};

The thing about such an anonymous class is that they are not static, meaning you can still access methods and variables from the class in which this listener is nested. Internally the reason for this is that this new class keeps a reference to the instance of the class which created it, in your case the enclosing Activity. This is a great thing and we use it all the time for OnClickListener etc. But in your case it is a problem because you want to send this Object to another Activity. The internal reference to the old Activity keeps it from being serialised and that is a good thing. If you could just send an Object like that you would create memory leaks like crazy because of all the old references which the garbage collector cannot collect.

The solution is pretty simple, you can either define the class in its own file or you can make the class static. Static in this context means that the class is essentially treated like it were in it's own separate file and therefore cannot access the instance of the enclosing class.

So to summarise what you have to do is either keep the class nested and define it static like this:

public class YourActivity extends Activity {

private static class OnCreateListenerImpl implements OnCreateListener {

@Override
public void onObjCreate() {
Log.d("pltk", "dfgbfdgh");
}
}

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_palatok);

OnCreateListener inter = new OnCreateListenerImpl();
Intent in = new Intent(Palatok.this, SecondActivity.class);
in.putExtra("ob", inter);
startActivity(in);
}
}

Or you can move the implementation in its own separate file:

public class OnCreateListenerImpl implements OnCreateListener {

@Override
public void onObjCreate() {
Log.d("pltk", "dfgbfdgh");
}
}

Although the reason why you would want to send an OnCreateListener to another Activity still eludes me, this should solve your problem.

I hope I could help and if you have any further questions feel free to ask!

Communication Between Two Android Activities via Reference to Caller or Callee

You are using the wrong approach. The solution requires a bit more work than you would think. The correct approach is to:

First, realize that these activities Activity A and Activity B (and any other activities) are activities that are specific to your application and you want to establish direct communication between them.

Second, realize that you are trying to get the current (or a previous) activity's context. The context will help serve the reference.

Third, you can create your own Activity and Application classes by extending the desired classes. The Application class is a low-level class used for the activities.

From here, you will be able to make use of the getApplicationContext() which will return your custom Application class.

Design: It is inside of your CustomApplication class that you must track the references to the activities that you want. From there all that you have to do is cast the getApplicationContext() to your CustomApplication class and then call your methods that access the Activity(ies). You must of course cast your Activities if you want to access certain instances of a specific activity that you created to its "type." For example:

MapsActivity mact = (MapsActivity)(((MyApplication)(this.getApplicationContext())).getCurrentActivity())

You must of course note that this activity must be already created (the onCreate method was already called) for this to return the current activity. The same of course goes for the other life-cycle methods for the activity as you will make a baseActivity which will deal with these as well as you will also have an Application life-cycle that will help deal with this too.

To answer the question: "How to get the current foreground activity context in android?" I turned to StackOverflow and found user: gezdy 's answer to be exactly what I needed at: How to get current foreground activity context in android?.

(BEGIN QUOTATION FROM: GEZDY)

You should manage activities references. Add the name of the
application in the manifest file :

 <application
android:name=".MyApp"
....
</application>

Your application class :

public class MyApp extends Application {
public void onCreate() {
super.onCreate();
}

private Activity mCurrentActivity = null;
public Activity getCurrentActivity(){
return mCurrentActivity;
}
public void setCurrentActivity(Activity mCurrentActivity){
this.mCurrentActivity = mCurrentActivity;
}
}

Create a new Activity :

public class MyBaseActivity extends Activity {
protected MyApp mMyApp;

public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mMyApp = (MyApp)this.getApplicationContext();
}
protected void onResume() {
super.onResume();
mMyApp.setCurrentActivity(this);
}
protected void onPause() {
clearReferences();
super.onPause();
}
protected void onDestroy() {
clearReferences();
super.onDestroy();
}
private void clearReferences(){
Activity currActivity = mMyApp.getCurrentActivity();
if (this.equals(currActivity))
mMyApp.setCurrentActivity(null);
}
}

So, now instead of extending Activity class for your activities, just
extend MyBaseActivity. Now, you can get your current activity from
application or Activity context like that :

Activity currentActivity = ((MyApp)context.getApplicationContext()).getCurrentActivity();

(END OF QUOTATION FROM: GEZDY)

Note: All code is written in java for this answer.

How can I use an interface to create a simple bridge between two activities?

I just specify what you do wrong.

myMain2 = new SecondActivity();
myMain2.setMyInterface(this);
mainIntent = new Intent(MainActivity.this, SecondActivity.class);
startActivity(mainIntent);

You set interface for myMain2 but when show activity from a button Android does not show your myMain2, it will be create a new instance of SecondActivity base on your Intent you pass. It means when SecondActivity call onCreate it has no instance of myInterface.

Basically communication between 2 activity should be using startActivityForResult or some special case using Broadcast or event bus pattern



Related Topics



Leave a reply



Submit