DialogFragment (support v4) crashes the app (NPE) - java

I already checked a lot of same questions on StackOverflow, but I didn't find any solution to my issue.
In a DialogFragment, I call an AsyncTask method and when the result has been received from the server, I launched another DialogFragment.
Here is the code I use to launch the DialogFragment :
public class RequesterConfirmRent extends DialogFragment {
// Called from onPostExecute() in AsyncTask class.
public void onPostComputeAmountToPay(JSONArray array){
double amountToPay = 0.0;
String ownerName = "";
try{
if(!array.getJSONObject(0).getBoolean("success"))
Log.e("Error", "Error with JSON received");
else {
amountToPay = array.getJSONObject(1).getDouble("amountToPay");
ownerName = array.getJSONObject(2).getString("ownerName");
}
}catch(JSONException e){
Log.e(e.getClass().getName(), "JSONException", e);
}
// Create and show the DialogFragment
Paiement p = new Paiement();
Bundle bdl = new Bundle();
bdl.putString("ownerName", ownerName);
bdl.putDouble("amountToPay", amountToPay);
p.setArguments(bdl);
// Buggy line (NPE)
p.show(getFragmentManager(), "4554");
}
}
And here is the code of the DialogFragment I try to display:
public class Paiement extends DialogFragment {
#NonNull
#Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
if(getDialog() == null)
super.setShowsDialog(false);
Dialog dialog = super.onCreateDialog(savedInstanceState);
dialog.setTitle("Synthesis of your rent");
return dialog;
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_paiement, container, false);
init(rootView);
return rootView;
}
private void init(View v){// Bla bla ...}
}
And I alway got a NullPointerException when I call the .show() method?
What did I do wrong?
Many thanks for your help!
EDIT 1 : As requested, here is the LogCat
05-11 09:58:34.470 31384-31384/com.example.celien.drivemycar
E/AndroidRuntime﹕ FATAL EXCEPTION: main
java.lang.NullPointerException
at android.support.v4.app.DialogFragment.show(DialogFragment.java:136)
at com.example.celien.drivemycar.fragment.RequesterConfirmRent.onPostComputeAmountToPay(RequesterConfirmRent.java:148)
EDIT 2 I modified the code like this, and it appears that getFragmentManager() is null. Why?
Paiement p = new Paiement();
Bundle bdl = new Bundle();
bdl.putString("ownerName", ownerName);
bdl.putDouble("amountToPay", amountToPay);
p.setArguments(bdl);
// BUGGY LINE
android.support.v4.app.FragmentManager f = getFragmentManager();
if(p == null)
Log.d("Exception ", "p is null");
if(f == null)
Log.d("Exception ", "f is null");
try {
p.show(f, "4554");
}catch(NullPointerException e){
Log.d("Exception ", e.toString());
}
EDIT 3:
Got some fresh infos! To avoid the creation of this Dialog, I display data in a Toast: Toast.makeText(this.getActivity(), "You have to pay "+amountToPay+"e to " +ownerName, Toast.LENGTH_LONG).show(); and also get a NPE!
BUT if I use the Log system, everything's fine :
Log.d("Rcvd ", String.valueOf(amountToPay));
Log.d("Rcvd ", ownerName);
So, why is my activity null?

If you get a NPE when calling p.show() but not p.setArguments(), it could be that p is ok but something inside the show call isn't?
On possible point to solve is that you're using a support version of FragmentManager, with the getFragmentManager() call. Try the getSupportFragmentManager() instead. It will fall back to the proper one when needed.
On the other hand, you're calling android.support.v4.app.FragmentManager. If you manually added the package, it's weird, so chances are your IDE did it for you. You could try to remove the package behind FragmentManager, and hope for the code be compliant to the non-support standard framework. Could be that only this reference to the support library is done, so removing the package part would solve the issue.
My advice: In an app, always stick to either the standard framework or the support library when defining activities and fragments. Because of that, make sure that every Activity and Fragment you create extends a proper support (or standard framework) version. Mixing them will end up with unexpected crashes.
Also, as mentioned in one of my comments, AsyncTask runs freely even after your fragment was detached, so no activity is properly referenced by this fragment anymore. This answer tells you to check if Fragment was detached by looking at isDetached(). Check for his answer. He's talking about using Loaders instead of AsyncTasks or move the AsyncTask up to the activity, so the activity is always available. Looking at the future, Loader is the best option (since it's the natural evolution of AsyncTask), looking at the present, try to move the AsyncTask up to the common Activity.

Related

Android Studio "not attached to context" errors [duplicate]

In activity in Toolbar I got a button which need to call method from fragment and update list in that fragment. Now it is an error.
Calling in activity
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()){
case R.id.menu_sort:
ListFragment listFragment = new ListFragment();
listFragment.sortByPopularity();
break;
}
return super.onOptionsItemSelected(item);
}
Fragment code. I have found an error when Activity not attached. But nothing with context
public class ListFragment extends Fragment implements ListAdapter.ItemClickListener {
/**
* Needed
*/
RecyclerView recyclerView;
View view;
List<BasePojo.Result> list;
ListAdapter listAdapter;
public ListFragment() {
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
/**
* Main Initialization
*/
view = inflater.inflate(R.layout.fragment_list, container, false);
recyclerView = view.findViewById(R.id.recycler_list_detailed);
recyclerView.setLayoutManager(new GridLayoutManager(getActivity(), 2));
list = new ArrayList<>();
listAdapter = new ListAdapter(list, setOnItemClickCallback());
recyclerView.setAdapter(listAdapter);
RetrofitClient.getApiService().getPhotosList(getString(R.string.api_key)).enqueue(new Callback<BasePojo>() {
#Override
public void onResponse(Call<BasePojo> call, Response<BasePojo> response) {
BasePojo basePojo = response.body();
list.addAll(basePojo.getResults());
recyclerView.getAdapter().notifyDataSetChanged();
}
#Override
public void onFailure(Call<BasePojo> call, Throwable t) {
Log.d("tag", "Response failed" + t.toString());
}
});
return view;
}
#Override
public void onItemClick(View view, int position) {
Log.v("in on click", "value " + position);
}
private OnItemClickListener.OnItemClickCallback setOnItemClickCallback() {
OnItemClickListener.OnItemClickCallback onItemClickCallback = new OnItemClickListener.OnItemClickCallback() {
#Override
public void onItemClicked(View view, int position) {
BasePojo.Result itemClicked = list.get(position);
Bundle bundle = new Bundle();
bundle.putString("title", itemClicked.getOriginalTitle());
bundle.putString("overview", itemClicked.getOverview());
bundle.putString("release_date", itemClicked.getReleaseDate());
bundle.putString("vote_average", itemClicked.getVoteAverage().toString());
bundle.putString("poster_path", itemClicked.getPosterPath());
DetailedFragment detailedFragment = new DetailedFragment();
detailedFragment.setArguments(bundle);
FragmentManager manager = getActivity().getSupportFragmentManager();
FragmentTransaction transaction = manager.beginTransaction();
transaction.replace(R.id.main_frame_list, detailedFragment);
Log.d("tag", "title is 111 " + bundle.get("title"));
transaction.commit();
}
};
return onItemClickCallback;
}
#Override
public void onAttachFragment(Fragment childFragment) {
super.onAttachFragment(childFragment);
}
public void sortByPopularity() {
RetrofitClient.getApiService().getPopularList(getString(R.string.api_key)).enqueue(new Callback<BasePojo>() {
#Override
public void onResponse(Call<BasePojo> call, Response<BasePojo> response) {
BasePojo basePojo = response.body();
list.addAll(basePojo.getResults());
recyclerView.getAdapter().notifyDataSetChanged();
}
#Override
public void onFailure(Call<BasePojo> call, Throwable t) {
Log.d("tag", "Response failed" + t.toString());
}
}); }
}
And here is an error
05-09 12:48:26.915 5775-5775/com.borisruzanov.popularmovies E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.borisruzanov.popularmovies, PID: 5775
java.lang.IllegalStateException: Fragment ListFragment{6dbd6de} not attached to a context.
at android.support.v4.app.Fragment.requireContext(Fragment.java:614)
at android.support.v4.app.Fragment.getResources(Fragment.java:678)
at android.support.v4.app.Fragment.getString(Fragment.java:700)
at com.borisruzanov.popularmovies.ListFragment.sortByPopularity(ListFragment.java:110)
at com.borisruzanov.popularmovies.MainActivity.onOptionsItemSelected(MainActivity.java:47)
at android.app.Activity.onMenuItemSelected(Activity.java:3204)
at android.support.v4.app.FragmentActivity.onMenuItemSelected(FragmentActivity.java:407)
at android.support.v7.app.AppCompatActivity.onMenuItemSelected(AppCompatActivity.java:195)
at android.support.v7.view.WindowCallbackWrapper.onMenuItemSelected(WindowCallbackWrapper.java:108)
at android.support.v7.view.WindowCallbackWrapper.onMenuItemSelected(WindowCallbackWrapper.java:108)
at android.support.v7.app.ToolbarActionBar$2.onMenuItemClick(ToolbarActionBar.java:63)
at android.support.v7.widget.Toolbar$1.onMenuItemClick(Toolbar.java:203)
at android.support.v7.widget.ActionMenuView$MenuBuilderCallback.onMenuItemSelected(ActionMenuView.java:780)
at android.support.v7.view.menu.MenuBuilder.dispatchMenuItemSelected(MenuBuilder.java:822)
at android.support.v7.view.menu.MenuItemImpl.invoke(MenuItemImpl.java:171)
at android.support.v7.view.menu.MenuBuilder.performItemAction(MenuBuilder.java:973)
at android.support.v7.view.menu.MenuBuilder.performItemAction(MenuBuilder.java:963)
at android.support.v7.widget.ActionMenuView.invokeItem(ActionMenuView.java:624)
at android.support.v7.view.menu.ActionMenuItemView.onClick(ActionMenuItemView.java:150)
at android.view.View.performClick(View.java:5610)
at android.view.View$PerformClick.run(View.java:22265)
at android.os.Handler.handleCallback(Handler.java:751)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:154)
at android.app.ActivityThread.main(ActivityThread.java:6077)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:866)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:756)
Thank you very much for your time and help. If my question looks not well please make a note and I will teach how to ask questions better
In my case, this problem occurred when I was calling getString()
changing this calls to getActivity().getString() solved the problem.
Using commit() can not solve the problem, we should try to find the solution in the source code of Fragment.
So, consider from the error stack you provided, the requireContext() in Fragment was:
public final Context requireContext() {
Context context = getContext();
if (context == null) {
throw new IllegalStateException("Fragment " + this + " not attached to a context.");
}
return context;
}
This means the system will check the Context from getContext(), if it's null, the exception will be thrown.
So, to avoid this problem, we can check the result of getContext() before do our business.
Create a fragment instance is not enough. It needs to be attached to Activity through a transaction:
getFragmentManager()
.beginTransaction()
.replace(R.id.container_layout, fragment)
.commit();
After a successful commit, onAttach method in the fragment is called, the view is created and then you can interact with its views.
In your case, create the fragment instance and attach it in activity onCreate, then call sortByPopularity later in a click event.
Read more about fragment life cycle: https://developer.android.com/guide/components/fragments
Kotlin:
My problem happened with getString()
Changing it to context.getString() solved it
If you are using CountDownTimer, you may get that error cause of detaching the fragment before finishing the timer. If you are performing ui changes in onFinish callback, you should check the context that it is null or not like below;
timer = object : CountDownTimer(startTimeInMillis, 1000) {
override fun onTick(millisUntilFinished: Long) {
}
override fun onFinish() {
context?.let {
//perform ui changes here
}
}
}
timer?.start()
or you should cancel the timer before detaching fragment like below;
override fun onDestroy() {
super.onDestroy()
timer?.cancel()
}
If a fragment is not shown (not added) or is removed, it's context == null. In this case getting resources will lead to this exception. getString(R.string.some_string) requires context and crashes.
You can check whether the fragment exists so:
if (isAdded) {
// Print getString(R.string.some_string).
}
But you might need to print the string even when the fragment was released, for instance, in LogCat, analytics or send a request to a server. In this case you need an application context to obtain a string resource.
class MyApplication : Application() {
override fun onCreate() {
super.onCreate()
instance = this
}
companion object {
lateinit var instance: MyApplication private set
}
}
object Strings {
fun get(#StringRes stringRes: Int, vararg formatArgs: Any = emptyArray()): String {
return instance.getString(stringRes, *formatArgs)
}
}
Then set MyApplication in AndroidManifest and use: Strings.get(R.string.some_string).
For kotlin developers
lifecycleScope.launchWhenResumed {
// do your work here
}
Kotlin : Use Lazy Initialisation
override val contentMessage by lazy {
getString(R.string.message)
}
For everybody who still have this error:
private Context mContext;
#Override
public void onAttach(Context context) {
super.onAttach(activity);
mContext = context;
}
And do not add mContext = null in onDetach method 'cause you will still get this error.
I know this is an old post, but I just figured out what you could do. It is true that creating a fragment instance is not enough, It needs to be attached to Activity through a transaction. However, you can initially add both fragments and detach them from fragment manager. That way, they are both 'alive' in fragmentManager and you can call attach and detach on those fragments later as you wish.
i.e
.add(container, fragment1).detach(fragment1).add(container, fragment2).commit();
.
.
.
.
.
ft.detach(fragment2)
ft.attach(fragment1
This assertion can occur anywhere you have a requireContext() call to get access to the Android context from your Fragment. Review the call site carefully, before you use requireContext(). I only use requireContext() when I'm certain that the fragment is going to be attached to the Activity at the time or the use case is so essential that it is better to crash with this assertion than any other course of action.
If for any reason the fragment could happen to be unattached and you can handle it yourself at the call site by avoiding or early returning, then the better idea is to null check the return from getContext() and only then proceed forward.
Typical Kotlin code for the null check looks like this:
fun myFragmentFunction(){
val context = getContext() ?: return // early return using Elvis operator
context.whatever() // guaranteed non-null context at this point
}
As Tam Huynh said, this crash happens when we our fragment is not attached. I had the same problem that your (but with a bottomSheet) and now it works fine.
We can receive this kind of error from two reasons:
requireContext() can crash directly if the context is null
Calling getString(R.string.xxx_xxx_xxx) from fragment will crash if the fragment is detached (because we will need the context and the context is null).
For me, with that piece of code we can check if our fragment is attached or not, and with that I solve the crash.
fun checkIfFragmentAttached(operation: Context.() -> Unit) {
if (isAdded && context != null) {
operation(requireContext())
}
}
More info => https://weidianhuang.medium.com/android-fragment-not-attached-to-a-context-24d00fac4f3d
In this scenario validate if you don't have any class level properties which are dependent on context as the fragment is not committed it won't have the context and we might end up with this exception.

Keeping objects onPause()

I'm having a lot of trouble with Android Fragments, and I don't know why.
I want to keep an object in memory whenever I pause the app (onPause()).
I have a MapView with some layers. And I want to keep those layers, so the map doesn't have to reload from scratch and recreate them whenever I pause and resume the app.
The thing is, I can't save them with onSaveInstanceState(). They are objects, not primitives. They are also from a library, so I can't make them implement Serializable or Parcelable.
I want to find a solution without resorting to:
android:configChanges="orientation|screenSize">
to my Activity, since it is considered bad practice. Also, it doesn't work for when you pause and resume the app.
I also tried
this.setRetainInstance(true); // retain this fragment
on my fragment's onCreate(), but I would also like to avoid it. It doesn't work anyway.
After much trial, error and testing, I found out that my fragment's onSaveInstanceState() doesn't even run. I can't even keep a simple boolean whenever I pause and resume the app.
I read:
Saving Android Activity state using Save Instance State
https://developer.android.com/training/basics/activity-lifecycle/recreating
savedInstanceState is always null
Amog many other posts and, of course, the Android documentation.
Some of my code:
Creating the fragment:
navFragment = FragNavigation.newInstance(); // keeping the reference to the navFragment
// setting up the viewpager
ViewPager viewPager = findViewById(R.id.vpMainViewPager);
MainPagerAdapter mainAdapter = new MainPagerAdapter(getSupportFragmentManager(), this);
mainAdapter.addFragment(navFragment);
viewPager.setAdapter(mainAdapter);
Trying to save a simple boolean in the instance state (remember: this doesn't even run):
#Override
public void onSaveInstanceState(#NonNull Bundle outState) {
super.onSaveInstanceState(outState);
Log.e(TAG, "onSaveInstanceState: is map loaded? " + mapIsLoaded );
outState.putBoolean("mapIsLoaded", mapIsLoaded);
}
And trying to load it:
#Override
public void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
this.setRetainInstance(true); // retain this fragment
Log.e(TAG, "onCreate: null? " + (savedInstanceState == null) );
if (savedInstanceState != null)
mapIsLoaded = savedInstanceState.getBoolean("mapIsLoaded", false);
}
What am I missing? I'm clearly not understanding something. Why is it so hard to keep values when you pause the app?
I would suggest storing the MapView layers using a ViewModel as per Architecture Components. If that is not an option, you can manually store and retrieve objects using the Activities getLastNonConfigurationInstance.
#Override
public void onPause() {
/**
* store your data
*/
super.onPause();
}
#Override
public void onResume() {
/**
* retrieve your data
*/
super.onResume();
}

How to get the current fragment when using FragmentStatePagerAdapter

It is surprisingly difficult to get the current fragment when using either of the pager adapters. With the FragmentPagerAdapter, however, you can look for a fragment with the tag "android:switcher:" + viewId + ":" + id.
Unfortunately, there does not seem to be a standard tag for the FragmentStatePagerAdapter. A related question provided a couple answers which suggested manually keeping a cache of the fragments, which were noted as being inadequate when doing a rotation: the underlying adapter stores state in a bundle and restores it when it is created, causing any simple caching solution to fail.
I found a better solution. getCurrentFragment() cannot be implemented correctly from what I can tell.
My code was previously launching a dialog and then calling back to the Activity which was stored by the dialog at onAttach. The Activity then needed to find the correct Fragment, which was problematic.
The correct solution is to first call setTargetFragment() on the new dialog fragment:
SelectProblemDialogFragment f = SelectProblemDialogFragment.newInstance(args);
f.setTargetFragment(this, 0);
f.show(getFragmentManager(), "select_problem_dialog_fragment");
and then in onAttach(), simply use that as the listener.
#Override
public void onAttach(Activity activity)
{
super.onAttach(activity);
try
{
mListener = (SelectProblemDialogListener) getTargetFragment();
if (mListener == null)
{
mListener = (SelectProblemDialogListener) activity;
}
} catch (ClassCastException e)
{
throw new ClassCastException("Must implement SelectProblemDialogListener");
}
}

strange NullPointerException inside Activity

In my Android app, I have found an error that happens in about 0.5% of attempts. This error is generate from an NullPointerException but this is strange.
Below is summarized the code the generates that exception:
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Intent intent = getIntent();
mInfo = intent.getParcelableExtra(EXTRA_MESSAGE_INFO); // mInfo is a private member of Activity
if(mInfo == null)
finish();
setContentView(R.layout.activity_fermata_details);
first = savedInstanceState.getBoolean(IS_FIRST_LOAD, true); // also onSaveInstanceState is used for maintain that value across configuration changes.
if(first){
first = false;
// a new fragment is inflated in the FrameLayout of the Activity
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
DetailsFragment fragment = new DetailsFragment();
transaction.replace(R.id.content_fragment, fragment);
transaction.commit();
}
}
The Activity also has the following public method:
public int getIdInfo(){
return mInfo.getId(); // here occurs the NullPointerException.
}
The method getIdInfo() is called from the Fragment in this way:
(MyActivity)getActivity()).getIdInfo();
I think that is a strange behavious, since the NullPointerException tells that mInfo should be null. But the condition in the onCreate:
if(mInfo == null)
finish();
should ensure that if the mInfo is null the Activity is finished.
Where can be the error? Can be related to Garbage Collector behaviour?
NOTE: I'm not able to get much more infomation about the error (the error was detected 3 times over 600 attempts), since I have retrieved this exception form Google Analytics, that gives my just this row:
NullPointerException (#MyActivity:getIdInfo:122) {main}
I think you are getting not-fully created Activity: one that has detected null and called finish(). Please consider the fact that finish() does not cause the onCreate() method to stop.
I suggest:
mInfo = intent.getParcelableExtra(EXTRA_MESSAGE_INFO);
if(mInfo == null) {
finish();
return;
}
One could suspect that FragmentManager will not try to attach any fragments to activity that called finish() on itself but I'm not able to find any documentation of that behavior.

Refreshing a view inside a fragment

I have searched the numerous questions that look like this one, but haven't found my answer in any of them.
I have an activity that has 3 tabs accessible through the action bar. I achieved this by adding 3 fragments that inflate a custom view I made extending the view class.
At the moment the database changes, I try to refresh the view in my tab by calling invalidate()/postinvalidate(), but this does not work. The same is true for calling onCreateView of the fragment just as many other options I considered.
When I go to another tab and go back, however, the change has been made and my view is updated as it should be.
How can I simulate the same thing that happens when changing to another tab? What does happen. I tried to look at the Fragment lifecycle (tried to call onCreateView()) to figure it out but it just doesn't want to refresh/redraw as it should.
The data is loaded properly, as the data is changed when I change to another tab.
I deleted some of the code as it is no longer relevant. I implemented Cursorloaders instead of my own Observer pattern to notify a change. This is my main activity right now.
The question is what should I do now if I want to redraw the view inside these fragments. If I apply fragmentObject.getView().invalidate() it does not work. I'm having the same problem as before, but now my Observer to notify a change in the database is properly implemented with loaders.
public class ArchitectureActivity extends Activity implements LoaderManager.LoaderCallbacks<Cursor> {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
ActionBar actionbar = getActionBar();
actionbar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
ActionBar.Tab EditTab = actionbar.newTab().setText("Edit");
ActionBar.Tab VisualizeTab = actionbar.newTab().setText("Visualize");
ActionBar.Tab AnalyseTab = actionbar.newTab().setText("Analyse");
Fragment editFragment = new EditFragment();
Fragment visualizeFragment = new VisualizeFragment();
Fragment analyseFragment = new AnalyseFragment();
EditTab.setTabListener(new MyTabsListener(editFragment));
VisualizeTab.setTabListener(new MyTabsListener(visualizeFragment));
AnalyseTab.setTabListener(new MyTabsListener(analyseFragment));
actionbar.addTab(EditTab);
actionbar.addTab(VisualizeTab);
actionbar.addTab(AnalyseTab);
ArchitectureApplication architectureApplication = (ArchitectureApplication)getApplicationContext();
architectureApplication.initialize();
getLoaderManager().initLoader(0, null, this);
getLoaderManager().initLoader(1, null, this);
}
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
if (id == 0){
return new CursorLoader(this, GraphProvider.NODE_URI , null, null, null, null);
} else if (id == 1){
return new CursorLoader(this, GraphProvider.ARC_URI , null, null, null, null);
}
return null;
}
public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) {
// Reloading of data, actually happens because when switching to another tab the new data shows up fine
Log.e("Data", "loaded");
}
public void onLoaderReset(Loader<Cursor> loader) {
}
}
Don't try to call onCreateView() yourself... it's a lifecycle method and should be called only by the framework.
Fragments are re-usable UI components. They have their own lifecycle, display their own view, and define their own behavior. You usually don't need to have your Activity mess around with the internal workings of a Fragment, as the Fragment's behavior should be self-contained and independent of any particular Activity.
That said, I think the best solution is to have each of your Fragments implement the LoaderManager.LoaderCallbacks<D> interface. Each Fragment will initialize a Loader (i.e. a CursorLoader if you are using a ContentProvider backed by an SQLite database), and that Loader will be in charge of (1) loading the data on a background thread, and (2) listening for content changes that are made to the data source, and delivering new data to onLoadFinished() whenever a content change occurs.
This solution is better than your current solution because it is entirely event-driven. You only need to refresh the view when data is delivered to onLoadFinished() (as opposed to having to manually check to see if the data source has been changed each time you click on a new tab).
If you are lazy and just want a quick solution, you might be able to get away with refreshing the view in your Fragment's onResume() method too.
I had a similar (although not identical) problem that I could solve in the following way:
In the fragment I added
public void invalidate() {
myView.post(new Runnable() {
#Override
public void run() {
myView.invalidate();
}
});
}
and I called this method from the activity when I wanted to refresh the view myView of the fragment. The use of post() ensures that the view is only invalidated when it is ready to be drawn.
I've found a workaround to refresh the view inside my fragment. I recreated (new CustomView) the view every time the database has been updated. After that I call setContentView(CustomView view). It looks more like a hack, but it works and nothing else that I tried does.
Although my problem was not actually solved, I gave the reward to Alex Lockwood. His advice on Loaders made my application better and it caused me to keep looking for a solution that I eventually found.
I had the same issue.
My solution was detach fragment and attach it again.
FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
Fragment f = getFragment(action);
if(forceUpdate)
{
fragmentTransaction.detach(f);
fragmentTransaction.attach(f);
}
fragmentTransaction.replace(R.id.mainFragment, f);
fragmentTransaction.commit();
currentAction = action;
The fastest solution working for me:
#Override
public void onPause() {
super.onPause();
if (isRemoving() && fragmentView != null) {
((ViewGroup) fragmentView).removeAllViews();
}
}

Categories

Resources