I have a DialogFragment that allows users to filter and search in the same Fragment. The filter works by passing back data to the hosting activity/fragment using a callback interface. This seemed to work great until I added a SearchView to the DialogFragment as when I enter text and click search it works but then throws the following expception and crashes:
Parcelable encountered IOException writing serializable object (name = com.braden.android.fragments.ListItemFragment$6)
...
Caused by: java.io.NotSerializableException: com.braden.android.fragments.ListItemFragment
To do the callback I used a fairly standard callback interface pattern. The interface extends Serializable. Here's the code for my callback:
private void displayFilter() {
FilterCategoryDialogFragment filterCategoryDialogFragment = new FilterCategoryDialogFragment();
Bundle bundle = new Bundle();
mOnFilterClickListener = new OnFilterClickListener() {
#Override
public void onCategoryClickListener(String filterName) {
updateVenues(mFilter);
}
};
bundle.putSerializable("listenerFilter",
mOnFilterClickListener);
filterCategoryDialogFragment.setArguments(bundle);
filterCategoryDialogFragment.show(getFragmentManager(), DIALOG_CATEGORY_FILTER);
}
This seems to have something to do with using an anonymous inner class that implements serializable so I'm wondering:
1) Why is it that I'm only receiving this exception when I use SearchView and not when I perform an action to send back data via callback or simply click out of the dialog.
2) Is there a workaround here or is this just a bad pattern for me to use.
I found the answer to this question here: Callback to a Fragment from a DialogFragment
They key is the "setTargetFragment" method which allows you to tell a fragment which fragment to send its result to. This allows you to avoid having to serialize an interface reference for the callback.
All fields of class must be Serialized, otherwise you should get NotSerializableException.
if you check Exception stack you will be able to find that object which not serialized.
Related
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'm using Backendless as a backend service, it offers a class called BackendlessUser for saving and retrieving users. I'm trying to pass a User between two activities on Android by passing it as Serializable extra:
Intent intent = new Intent(PeopleActivity.this, ConversationActivity.class);
intent.putExtra("withUser", contacts.get(position));
startActivity(intent);
Since the class BackendlessUser implements Serializable. However when I run it, it gives me this error:
java.lang.RuntimeException: Parcelable encountered IOException writing serializable object (name = com.backendless.BackendlessUser)
at android.os.Parcel.writeSerializable(Parcel.java:1468)
at android.os.Parcel.writeValue(Parcel.java:1416)
....
Caused by: java.io.NotSerializableException: java.lang.Object
....
Due to, I think, this variable:
public final class BackendlessUser implements Serializable {
private final Map<String, Object> properties = new HashMap<String, Object>();
...
How can I solve this considering that I cannot modify the BackendlessUser class?
In Android you should use Parcelable which offers better performances compared to Serializable. For an explanation about how to implement it take a look at this answer
Also, if you need to use Parcelable on your map object, see this answer
Edit: since Object is not Parcelable though you might want to follow Alexander's answer, or, even better, use a database for data persistence
Instead of passing the object, you can save the reference to it in a singleton so that it's available between the activities.
You can extend the Application class and save there. The Application class exists all the time while your app is running and is a singleton.
public class MyApp extends Application {
public BackendUser currentUser;
}
Then:
((MyApp)getApplication()).currentUser
You should be using Parcelable to pass objects between activities. Parcelable is Android's version of Serializable, so you use that. You can find more information on Parcelable here.
If you can't modify the backend user, your best bet would be to use Alexanders suggestion and create a Singleton instance of User. This would allow you to create/update you user from any activity.
I am having trouble with volley (again). I want to run a volley request in a class, so more than one activity can feed off its results and update their UI's accordingly. I have got it return data and call the request from the UI but now im struggling to update the UI with the new data. I have looked at answers but I'm trying to understand the structure and I am at a loss, can some please advise/ talk me through it
assuming I understand what you mean as being:
A Volley request returns, updates some data set through some activity
In this case, assuming the calling activity contains everything, and reminding that this is a very general example, what you should usually do (usually, since there are exceptions to the case), is just insert the data into the data set contained in your UI holder (e.g. your recycler adapter) and update it, an example would be your adapter holding a method similar to this:
public void updateDataSet(List<Item> items)
{
//mItemList is the adapters member list
if (null != mItemList)
{
mItemList.clear();
mItemList.addAll(items);
}
else
mItemList = items;
notifyDataSetChanged();
}
you call this inside the request callback you fired earlier, just make sure to initialize everything BEFORE you fire the request, e.g.
#Override
public void onResponse(JSONObject response)
{
Log.d(TAG + ": ", "somePostRequest Response : " + response.toString());
// here you need to parse to JSON to a list and then call...
List<Item> items = parseResponse(response);
myAdapter.updateDataSet(items);
}
Now, if what you meant was
A Volley request returns in some Activity, I want it to update stuff in another place
there are a couple of options:
As someone said in the comments - you could go for EventBus.
You could hold a DataManager class, which would be a global singleton, in which case you can either hold the data and update it there, and then every activity (in it's onResume or other relevant lifecycle method) knows to pull that data.
You could do the same as option 2, with the exception of that DataManager holding a reference to other UI parts (e.g. Fragments), and triggering member methods in them that pass the data and trigger the updates.
Personally I find option 3 cumbersome and somewhat bad practice, but if all else fails, (and it shouldn't, but if it does) then you can try.
There are more options out there, it depends and varies according to the data, your app architecture, coding style and other stuff you apply.
Hope this helps!
You can use EventBus. To use EventBus you need to register class where you will receive update and when you publish event for those event all classes will receive it.
Here is an example using greenrobot's EventBus :
Event Model :
public class MessageEvent {
/* Additional fields if needed */
}
Subscribe :
EventBus.getDefault().register(this); // In Activity onCreate method
#subscribe
public void onMessageEvent(MessageEvent event){
// this is the method to receive event
}
Publish event :
EventBus.getDefault().post(new MessageEvent());
Now every class subscribed for this event model will be updated.
Note : subscribed classes have to alive, If anyone destroyed they won't receive update.
How I understand, the Goal of the Adapter pattern is to call some class methods using some interface (which opened to clients). To make adapter pattern we need to implement some interface (which uses by client), and also we need to extend some class, which methods client need to call when calling interface methods.
class Adapter extends NeedClass implements PublicInterface{}
But what if we haven't interface, but have only 2 classes? For example we have some class(not interface!) which methods uses clients. Now we need to call methods of other class by making adapter class, but we cant to do this, because we cant make multiple Inheritance on the adapter class.
class Adapter extends NeedClass, PublicInterface
above code doesnt work.
What we can do in this case?
You can has an instance of NeedClass in Adapter and call it, when you need. So you extend only from PublicInterface.
public class Adapter extends PublicInterface {
private NeedClass needClass;
#Override
public void doSomething() {
needClass.doSomethingElse("someParameter");
}
}
You can use a composition instead of inheritance. Add a field to Adapter class of type NeedClass:
public class Adapter extends PublicInterface {
private NeedClass needClass;
}
Then inside Adapter methods delegate execution to needClass field.
From what i have understood the Adapter Pattern.
it is helpful when dealing with the third part codes such as API which is/ are subject to changes any time and my likely to break your code if implemented direct.
For example : Using Paypal in your site for payment online.let's assume the Paypal uses the method payMoney() for payment. and after sometime they decide to change the method to something else let's say sendMoney(). This is likely to break your code if implemented directly, with the use of Adapter Design pattern this can be solves as follow
the third part code => Paypal
class Paypal {
public function __construct(){
// their codes
}
public function payMoney($amount){
// the logic of validating
// the $amount variables and do the payment
}
}
so implement it directly in the code as below will break the code
$pay = new Paypal();
$pay->payMoney(200);
using adapter will save numbers of hours and a complex work of updating the code from payMoney() to sendMoney() in every where that the API scripts has been implemented. Adapter enable update in one place and that's it.
Let see it.
class paypalAdapter {
private $paypal;
// Paypal object into construct and check if it's pa
// Paypal object via type hint
public function __construct(PayPal $paypal) {
$this->paypal = $paypal;
}
// call the Paypal method in your own
//custom method that is to be
// implemented directly into your code
public function pay($amount) {
$this->paypal->payMoney($amount);
}
}
so it is like that and there you can go and use the PaypalAdater directly into the code as follow;
$pay = new PaypalAdapter(new Paypal);
$pay->pay(200);
So in future when the Vendor(Paypal) decide to use sendMoney instead of payMoney what to be done is to open the PaypalAdapter class and do the following in the pay($amount) method:
// SEE THIS METHOD ABOVE TO OBSERVE CHANGES
// FROM $this->paypal->payMoney($amount);
// TO $this->paypal->senMoney($amount);
public function pay($amount) {
$this->paypal->sendMoney($amount);
}
After this minor change in one place, everything works well as before.
Sorry for bugging you again, but I still can't find a way to make a callback from my activity to a service...
Found a similar question - How to Define Callbacks in Android?
// 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 callback() {
// callback code goes here
}
}
worker.callback = new Callback();
yet not sure how to integrate that sample into my project.
Any suggestions or links to clear tutorials would be great!
That kind of callbacks (Observer pattern) that you are showing in your example won't work between a service and an activity. Use observer patter when, from class A, you created the instance of class B and want to send callbacks from B to A.
With regards to the services and activities, things are completely different. AFAICT, if you want to callback your Activity from a Service, the best method to achieve this is to use ResultReceiver. There are a lot of interesting things about ResultReceiver:
Its constructor receives a Handler (that you must create inside the activity), which will allow you to change UI from the service.
It implements Parcelable thus you can put a reference of your ResultReceiver in the Intent extras that you used to start the service.
Its onReceive method has a result code integer which allows you to generate different kind of callbacks (this is like if your callback interface had many methods). Also, it receives a Bundle which you can use to put all result data.
On the other hand, if you want to do a callback (not sure if that is correct term in this case), from your Activity to your Service, I guess you will have to send a Broadcast message or something like that.