So, we have this existing App with a MainActivity, with whole bunch of interface implementations. I want to implement IBranchSessionInterface. I have followed the documentation as given here: https://docs.branch.io/pages/apps/xamarin/
Have BranchActivity and BranchErrorActivity
One thing I can't seem to understand in the following sequence:
ApplicationClass calls:
BranchAndroid.GetAutoInstance(this.ApplicationContext);
MainActivity that Implements IBranchSessionInterface has this code in OnCreate()
BranchAndroid.Init(this, Resources.GetString(Resource.String.branch_key), this);
When the Branchsession is successfully initiated, it calls the IBranchSessionInterface, InitSessionComplete callback.
Here is the full method impl:
public void InitSessionComplete(Dictionary<string, object> data)
{
var intent = new Intent(this, typeof(BranchActivity));
intent.PutExtra("BranchData", JsonConvert.SerializeObject(data));
StartActivity(intent);
}
After this, what needs to happen? Because after Starting branch activity I see nothing but empty screen. How is it suppose to come back to MainActivity?
The Branch activity is simply an example of how you can use Branch link data. You don't actually need that activity, or the intent to redirect to it.
You may simply delete all of that code and replace it with the following to view the data upon app open:
public void InitSessionComplete(Dictionary<string, object> data)
{
Console.WriteLine("Branch Link Data: " + JsonConvert.SerializeObject(data));
}
Related
I have implemented a bus for my app for communication between fragments and activities. I add a subscriber by adding an instance of either a Fragment or an Activity to a list. and I iterate through that list invoking a method to notify each of the subscribers of what is going on. Now I need to keep the list clean, I don't want to add multiple instances of of the same class in the list. I can use equals() for an Activity but I cant for a Fragment because its final so I cant override it.
WHAT I HAVE TRIED
I have tried to keep a Class object of each subscriber in the list which works fine until I go to invoke the method. You cant invoke a method without an instance to invoke it from. So that doesnt work.
I could also keep a separate list, one to hold Class objects and one to hold the actual instance. But I want to avoid adding another dependency if at all possible.
I could also manually do a instanceof check for each Fragment, but I dont want to do that because I already have 5 fragments, and if I add or remove any then I have to come back here and update this method.
So my question is, other than adding another List to hold the Class objects or manual instanceof checks, are there any other ways I can make sure I dont add multiple instances to the subscribers List?
Here is the relevant code if you need it:
public void subscribe(Object object) {
if (!mSubscribers.contains(object)) {
mSubscribers.add(object);
}
}
public void notifySubscribers(BusEvent event) throws InvocationTargetException, IllegalAccessException {
for (Object o : mSubscribers) {
Method methodToCall = getMethodToCall(o);
if (methodToCall != null) {
methodToCall.invoke(o, event);
}
}
}
Ok I have found a suitable answer to my problem. I want to share it here in hopes that it will help someone else out. Android has a class called LocalBroadcastManager. It is available in the v4 support library. In your activity you call 'LocalBroadcastManager.getInstance().registerReceiver()'. You pass into that method a class that extends BroadcastReceiver and an 'IntentFilter' to tell the receiver what to listen for. Then in any class including Fragments you call LocalBroadcastManager.getInstance().sendBroadcast() and pass in an Intent that matches the IntentFilter you used when registering. Here is the code I used to get it to work:
private void registerLocalBroadcastReceiver() {
// call this method in your activity (or any class you want to listen for broadcasts)
LocalBroadcastManager manager = LocalBroadcastManager.getInstance(this);
manager.registerReceiver(new OpenMenuBroadcastReceiver(), new IntentFilter("open-html"));
}
private void sendMessageToActivity(int position) {
// use this in a fragment (or any other class) to send a message
LocalBroadcastManager broadcast = LocalBroadcastManager.getInstance(getActivity());
Intent message = new Intent("open-html");
String name = (String) getListAdapter().getItem(position);
message.putExtra("name", name);
broadcast.sendBroadcast(message);
}
class OpenMenuBroadcastReceiver extends BroadcastReceiver {
// this is an inner class to my activity, when you send the message this method
// will be called to handle the message
#Override
public void onReceive(Context context, Intent intent) {
String name = intent.getStringExtra("name");
if (name != null && name.equalsIgnoreCase("home")) {
replaceFragment(Tag.HOME_FRAGMENT.getTag(), new HomeFragment(), R.id.main_frame);
mDrawerLayout.closeDrawer(Gravity.START);
return;
}
openMenuItemsFragment(name);
}
}
The good thing about this is that it is completely local to your app. External apps cant receive your broadcasts so its secure. You can find out more on how to use it on the Android developer site.
This question is mostly to solicit opinions on the best way to handle my app. I have three fragments being handled by one activity. Fragment A has one clickable element the photo and Fragment B has 4 clickable elements the buttons. The other fragment just displays details when the photo is clicked. I am using ActionBarSherlock.
The forward and back buttons need to change the photo to the next or previous poses, respectively. I could keep the photo and the buttons in the same fragment, but wanted to keep them separate in case I wanted to rearrange them in a tablet.
I need some advice - should I combine Fragments A and B? If not, I will need to figure out how to implement an interface for 3 clickable items.
I considered using Roboguice, but I am already extending using SherlockFragmentActivity so that's a no go. I saw mention of Otto, but I didn't see good tutorials on how to include in a project. What do you think best design practice should be?
I also need help figuring out how to communicate between a fragment and an activity. I'd like to keep some data "global" in the application, like the pose id. Is there some example code I can see besides the stock android developer's information? That is not all that helpful.
BTW, I'm already storing all the information about each pose in a SQLite database. That's the easy part.
The easiest way to communicate between your activity and fragments is using interfaces. The idea is basically to define an interface inside a given fragment A and let the activity implement that interface.
Once it has implemented that interface, you could do anything you want in the method it overrides.
The other important part of the interface is that you have to call the abstract method from your fragment and remember to cast it to your activity. It should catch a ClassCastException if not done correctly.
There is a good tutorial on Simple Developer Blog on how to do exactly this kind of thing.
I hope this was helpful to you!
The suggested method for communicating between fragments is to use callbacks\listeners that are managed by your main Activity.
I think the code on this page is pretty clear:
http://developer.android.com/training/basics/fragments/communicating.html
You can also reference the IO 2012 Schedule app, which is designed to be a de-facto reference app. It can be found here:
http://code.google.com/p/iosched/
Also, here is a SO question with good info:
How to pass data between fragments
It is implemented by a Callback interface:
First of all, we have to make an interface:
public interface UpdateFrag {
void updatefrag();
}
In the Activity do the following code:
UpdateFrag updatfrag ;
public void updateApi(UpdateFrag listener) {
updatfrag = listener;
}
from the event from where the callback has to fire in the Activity:
updatfrag.updatefrag();
In the Fragment implement the interface in CreateView do the
following code:
((Home)getActivity()).updateApi(new UpdateFrag() {
#Override
public void updatefrag() {
.....your stuff......
}
});
To communicate between an Activity and Fragments, there are several options, but after lots of reading and many experiences, I found out that it could be resumed this way:
Activity wants to communicate with child Fragment => Simply write public methods in your Fragment class, and let the Activity call them
Fragment wants to communicate with the parent Activity => This requires a bit more of work, as the official Android link https://developer.android.com/training/basics/fragments/communicating suggests, it would be a great idea to define an interface that will be implemented by the Activity, and which will establish a contract for any Activity that wants to communicate with that Fragment. For example, if you have FragmentA, which wants to communicate with any activity that includes it, then define the FragmentAInterface which will define what method can the FragmentA call for the activities that decide to use it.
A Fragment wants to communicate with other Fragment => This is the case where you get the most 'complicated' situation. Since you could potentially need to pass data from FragmentA to FragmentB and viceversa, that could lead us to defining 2 interfaces, FragmentAInterface which will be implemented by FragmentB and FragmentAInterface which will be implemented by FragmentA. That will start making things messy. And imagine if you have a few more Fragments on place, and even the parent activity wants to communicate with them. Well, this case is a perfect moment to establish a shared ViewModel for the activity and it's fragments. More info here https://developer.android.com/topic/libraries/architecture/viewmodel . Basically, you need to define a SharedViewModel class, that has all the data you want to share between the activity and the fragments that will be in need of communicating data among them.
The ViewModel case, makes things pretty simpler at the end, since you don't have to add extra logic that makes things dirty in the code and messy. Plus it will allow you to separate the gathering (through calls to an SQLite Database or an API) of data from the Controller (activities and fragments).
I made a annotation library that can do the cast for you. check this out.
https://github.com/zeroarst/callbackfragment/
#CallbackFragment
public class MyFragment extends Fragment {
#Callback
interface FragmentCallback {
void onClickButton(MyFragment fragment);
}
private FragmentCallback mCallback;
#Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.bt1
mCallback.onClickButton(this);
break;
case R.id.bt2
// Because we give mandatory = false so this might be null if not implemented by the host.
if (mCallbackNotForce != null)
mCallbackNotForce.onClickButton(this);
break;
}
}
}
It then generates a subclass of your fragment. And just add it to FragmentManager.
public class MainActivity extends AppCompatActivity implements MyFragment.FragmentCallback {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
getSupportFragmentManager().beginTransaction()
.add(R.id.lo_fragm_container, MyFragmentCallbackable.create(), "MY_FRAGM")
.commit();
}
Toast mToast;
#Override
public void onClickButton(MyFragment fragment) {
if (mToast != null)
mToast.cancel();
mToast = Toast.makeText(this, "Callback from " + fragment.getTag(), Toast.LENGTH_SHORT);
mToast.show();
}
}
Google Recommended Method
If you take a look at this page you can see that Google suggests you use the ViewModel to share data between Fragment and Activity.
Add this dependency:
implementation "androidx.activity:activity-ktx:$activity_version"
First, define the ViewModel you are going to use to pass data.
class ItemViewModel : ViewModel() {
private val mutableSelectedItem = MutableLiveData<Item>()
val selectedItem: LiveData<Item> get() = mutableSelectedItem
fun selectItem(item: Item) {
mutableSelectedItem.value = item
}
}
Second, instantiate the ViewModel inside the Activity.
class MainActivity : AppCompatActivity() {
// Using the viewModels() Kotlin property delegate from the activity-ktx
// artifact to retrieve the ViewModel in the activity scope
private val viewModel: ItemViewModel by viewModels()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
viewModel.selectedItem.observe(this, Observer { item ->
// Perform an action with the latest item data
})
}
}
Third, instantiate the ViewModel inside the Fragment.
class ListFragment : Fragment() {
// Using the activityViewModels() Kotlin property delegate from the
// fragment-ktx artifact to retrieve the ViewModel in the activity scope
private val viewModel: ItemViewModel by activityViewModels()
// Called when the item is clicked
fun onItemClicked(item: Item) {
// Set a new item
viewModel.selectItem(item)
}
}
You can now edit this code creating new observers or settings methods.
There are severals ways to communicate between activities, fragments, services etc. The obvious one is to communicate using interfaces. However, it is not a productive way to communicate. You have to implement the listeners etc.
My suggestion is to use an event bus. Event bus is a publish/subscribe pattern implementation.
You can subscribe to events in your activity and then you can post that events in your fragments etc.
Here on my blog post you can find more detail about this pattern and also an example project to show the usage.
I'm not sure I really understood what you want to do, but the suggested way to communicate between fragments is to use callbacks with the Activity, never directly between fragments. See here http://developer.android.com/training/basics/fragments/communicating.html
You can create declare a public interface with a function declaration in the fragment and implement the interface in the activity. Then you can call the function from the fragment.
I am using Intents to communicate actions back to the main activity. The main activity is listening to these by overriding onNewIntent(Intent intent). The main activity translates these actions to the corresponding fragments for example.
So you can do something like this:
public class MainActivity extends Activity {
public static final String INTENT_ACTION_SHOW_FOO = "show_foo";
public static final String INTENT_ACTION_SHOW_BAR = "show_bar";
#Override
protected void onNewIntent(Intent intent) {
routeIntent(intent);
}
private void routeIntent(Intent intent) {
String action = intent.getAction();
if (action != null) {
switch (action) {
case INTENT_ACTION_SHOW_FOO:
// for example show the corresponding fragment
loadFragment(FooFragment);
break;
case INTENT_ACTION_SHOW_BAR:
loadFragment(BarFragment);
break;
}
}
}
Then inside any fragment to show the foo fragment:
Intent intent = new Intent(context, MainActivity.class);
intent.setAction(INTENT_ACTION_SHOW_FOO);
// Prevent activity to be re-instantiated if it is already running.
// Instead, the onNewEvent() is triggered
intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP | Intent.FLAG_ACTIVITY_CLEAR_TOP);
getContext().startActivity(intent);
There is the latest techniques to communicate fragment to activity without any interface follow the steps
Step 1- Add the dependency in gradle
implementation 'androidx.fragment:fragment:1.3.0-rc01'
I am using the 'Dungeon' example as a basis for my code, so it is the standard examplecode for now.
I can call the Market place and successfully place an order. Within BillingReciever the event
public void onReceive is called twice, as I would expect and I can put a breakpoint there and see the result which is fine.
checkResponseCode gets called too and starts
Intent intent = new Intent(Consts.ACTION_RESPONSE_CODE);
intent.setClass(context, BillingService.class);
intent.putExtra(Consts.INAPP_REQUEST_ID, requestId);
intent.putExtra(Consts.INAPP_RESPONSE_CODE, responseCodeIndex);
context.startService(intent);
And there the execution stops, and my app returns to a run state.
I have an extended PurchaseObserver class which is supposed to respond to billing changes, which I have tried starting two different ways
mPurchaseObserver = new iPurchaseObserver(this,mHandler);
private class iPurchaseObserver extends PurchaseObserver {
public iPurchaseObserver(UpgradeActivity upgradeActivity, Handler handler) {
super(upgradeActivity, handler);
}
And
mPurchaseObserver = new iPurchaseObserver(mHandler);
private class iPurchaseObserver extends PurchaseObserver {
public iPurchaseObserver( Handler handler) {
super(upgradeActivity.this, handler);
}
None of the events/methods within the PurchaseObserver are fired, i.e onPurchaseStateChange.
I am suspecting it is the Activity Context I am passing or using when constructing my PurchaseObserver, but I am not sure, and advise would be appreciated!
Thanks
Found it, after loads and loads of wasted time. I had put the classes in a folder, when I moved them back to the root, they worked.
I am attempting to create an enhanced Intent class (I call it DataIntent) by giving it the ability to hold a "payload" of type Object (versus using it's built-in facility for Uri's). DataIntent extends Android's Intent class.
My Activity creates the extended object without any problems and the invocation of the startActivityForResult() goes off without any problems also. But, in my responding Activity when I call the getIntent() method, and attempt to cast it to my DataIntent, I'll throw the ClassCastException.
I realize this is probably a very dumb question - a 1,000 appologies in advance - but does anyone know why I cannot cast it to the DataIntent since that's what was used to start the new Activity, and DataIntent is a child of Intent?
DataIntent dataIntent = (DataIntent)getIntent();
// invoked inside the responding Activity instance - throws a ClassCastException
You can't do that, sorry. You need to place your data inside of the Intent. The Intent object is moved across processes and thus the one you get back is not the same instance as the one you created.
I did the same thing CirrusFlyer. I also looked for the final keyword before I started to implement it. Google should mark Intent class as final.
You can and should extend an Intent, but you must understand the purpose of an Intent.
#1 an intent must be parcelable to support persisting the intent-data across an app restart (collapsed due to memory limitations, trimMemory etc).
#2 Understand that the Intent constructed by the caller, is not the Intent provided to the activity. This is due to item #1. So any object references would be lost and are a bad idea -- it needs to be parcelable remember.
#3 Intents should only contain data-context for the activity (or whatever). So you should not place pages of data into an intent, rather, it should contain ids or keys, or whatever contextual data is necessary to re-obtain the data for the activity (or whatever).
Now... Why should you extend an Intent? For good contracts!
Intents by themselves are terrible contracts, way too loose. Some people create static method helpers but there is a better way.
If an ABCActivity requires "A" "B" and "C" to perform properly. A generic intent cannot describe that, we'd rely on documentation that no one will read.
Instead we can create a ABCIntent whose constructor demands A,B & C. This creates a clear contract on what is required to load the activity. We can do that with a static method, but a an ABCIntent can also provide getters for A B C making it a clean packaged contract for describing requirements to load the activity and how to obtain the data.
There is one caveat, we need a private constructor to construct our ABCIntent from a generic intent to inherit the extras.
public ABCActivity extends Activity {
private ABCIntent intent;
public static ABCIntent extends Intent {
private ABCIntent(Intent intent) {
super(intent);
}
public ABCIntent(A a, B b, C c) {
putExtra("EXTRA_A", A.serialize(a));
putExtra("EXTRA_B", B.serialize(b));
putExtra("EXTRA_C", C.serialize(c));
}
public A getA() { return A.deserialize(getExtra("EXTRA_A")); }
public B getB() { return B.deserialize(getExtra("EXTRB_B")); }
public C getC() { return C.deserialize(getExtra("EXTRC_C")); }
}
#Override
protected ABCIntent getIntent() {
return intent == null ? (intent = new ABCIntent(super.getIntent())) : intent;
}
#Override
protected void onCreate( ... ) {
A a = getIntent().getA();
B b = getIntent().getB();
C c = getIntent().getC();
// TODO: re-obtain activity state based on A, B, C then render
}
}
Notice that we construct ABCIntent from intent.
The ABCIntent inherits the intent extras.
Now you have a nicely packaged class who's job it is to define the contract for the activity and to provide the contractual data to the activity.
If you're a new engineer on the project, there is no way for you to misunderstand how to use this. No docs to read.
Despite similar question was asked, I have differnet situation:
My app consists mostly of a background Service. I want to start external activities and get results back.
I see several options:
Create dummy Activity and keep reference to it for using its startActivityForResult. This consumes quite a lot of memory, as we know.
Use Broadcast Intents instead of Android's results infrastructure: ask client activities to broadcast their results before closing. This kind of breaks the idea and not so performance-efficient.
Use Instrumentation directly - try to copy code from startActivityForResult into my Service.
Use Service interfaces - serialize and add AIDL connection to the Intent for starting an Activity. In this case Activity should call Service directly instead of providing result.
The third approach feels closer to Android for me, but I'm not sure if it's possible to do - Service does not have its Instrumentation, and default implementation seems to always return null.
Maybe you have any other ideas?
I’ve been thinking about this recently when implementing account authenticators with three-legged authorisation flows. Sending a result back to the service for processing performs better than processing it in the activity. It also provides a better separation of concerns.
It’s not that clearly documented, but Android provides an easy way to send and receive results anywhere (including services) with ResultReceiver.
I’ve found it to be a lot cleaner than passing activities around, since that always comes with the risk of leaking those activities. Additionally, calling concrete methods is less flexible.
To use ResultReceiver in a service, you’ll need to subclass it and provide a way to process the received result, usually in an inner class:
public class SomeService extends Service {
/**
* Code for a successful result, mirrors {#link Activity.RESULT_OK}.
*/
public static final int RESULT_OK = -1;
/**
* Key used in the intent extras for the result receiver.
*/
public static final String KEY_RECEIVER = "KEY_RECEIVER";
/**
* Key used in the result bundle for the message.
*/
public static final String KEY_MESSAGE = "KEY_MESSAGE";
// ...
/**
* Used by an activity to send a result back to our service.
*/
class MessageReceiver extends ResultReceiver {
public MessageReceiver() {
// Pass in a handler or null if you don't care about the thread
// on which your code is executed.
super(null);
}
/**
* Called when there's a result available.
*/
#Override
protected void onReceiveResult(int resultCode, Bundle resultData) {
// Define and handle your own result codes
if (resultCode != RESULT_OK) {
return;
}
// Let's assume that a successful result includes a message.
String message = resultData.getString(KEY_MESSAGE);
// Now you can do something with it.
}
}
}
When you start an activity in the service, create a result receiver and pack it into the intent extras:
/**
* Starts an activity for retrieving a message.
*/
private void startMessageActivity() {
Intent intent = new Intent(this, MessageActivity.class);
// Pack the parcelable receiver into the intent extras so the
// activity can access it.
intent.putExtra(KEY_RECEIVER, new MessageReceiver());
startActivity(intent);
}
And finally, in the activity, unpack the receiver and use ResultReceiver#send(int, Bundle) to send a result back.
You can send a result at any time, but here I've chosen to do it before finishing:
public class MessageActivity extends Activity {
// ...
#Override
public void finish() {
// Unpack the receiver.
ResultReceiver receiver =
getIntent().getParcelableExtra(SomeService.KEY_RECEIVER);
Bundle resultData = new Bundle();
resultData.putString(SomeService.KEY_MESSAGE, "Hello world!");
receiver.send(SomeService.RESULT_OK, resultData);
super.finish();
}
}
I think option 2 is the most idiomatic way on android. Using startActivityForResult from an Activity is a synchronous/blocking call, i.e., the parent activity waits and does not do anything until the child is done. When working from a Service and interacting with activities your primarily doing asynchronous/non-blocking calls, i.e., the service calls out for some work to be done and then waits for a signal to tell it that it can continue.
If you are using the android local service pattern then you can have your activities acquire a reference of the Service and then call a specific function after it has performed its work. Attempting your option 3 would be counter to what the framework provides for you.