Quickly changing Bottom Navigation app crashes - android

I have Bottom Navigation with fragment and Retrofit for api call
I call loadFragment for switching the fragments as below
private boolean loadFragment(Fragment fragment) {
//switching fragment
if (fragment != null) {
getSupportFragmentManager()
.beginTransaction()
.replace(R.id.fragment_container, fragment)
.commit();
return true;
}
return false;
}
The problem is my app crashes if i start clicking randomly and change fragment quickly, when i check logcat it shows NPE while setting some data.
My Fragment consist of following methods
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
// Inflate the layout for this fragment
View view = inflater.inflate(R.layout.fragment_current, container, false);
unbinder = ButterKnife.bind(this, view);
context = getActivity();
callCategoryAPI();
return view;
}
callCategory() is api retrofit call in separate controller class and brings back response via interface and then set data.
So what i suspect is my API returns response (as its asynchronous) but views are not available as user has changes the fragment (quickly) so views are null.
I already tried setuserVisibleHint also tried block click for 1200ms and also checked is my fragment view created, How to stop this crash? and make retrofit call Lifecycle dependent?
Logcat
at com.example.CurrentFragment$1.onApiSuccess(CurrentFragment.java:82)
at com.example.services.current_statement.CurrentStatementController$2.onResponse(CurrentStatementController.java:91)
at retrofit2.ExecutorCallAdapterFactory$ExecutorCallbackCall$1$1.run(ExecutorCallAdapterFactory.java:70)
at android.os.Handler.handleCallback(Handler.java:790)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:171)
at android.app.ActivityThread.main(ActivityThread.java:6606)
at java.lang.reflect.Method.invoke(Method.java)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:518)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:823)
--
Fatal Exception: java.lang.NullPointerException: Attempt to invoke
virtual method 'void
android.widget.TextView.setText(java.lang.CharSequence)' on a null
object reference
at com.example.fragment.statements.CurrentFragment$1.onApiSuccess(CurrentFragment.java:82)
at com.example.services.current_statement.CurrentStatementController$2.onResponse(CurrentStatementController.java:91)
at retrofit2.ExecutorCallAdapterFactory$ExecutorCallbackCall$1$1.run(ExecutorCallAdapterFactory.java:70)
at android.os.Handler.handleCallback(Handler.java:790)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:171)
at android.app.ActivityThread.main(ActivityThread.java:6606)
at java.lang.reflect.Method.invoke(Method.java)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:518)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:823)

set in your fragment
if(activity != null && isAdded) {
// perform your task
}

Check in fragment
if(activity != null && isAdded){
Perform your operation
}

set in your fragment
if(getActivity() != null && isAdded) {
//do your operation
}

This is the problem:
Attempt to invoke virtual method 'void android.widget.TextView.setText(java.lang.CharSequence)' on a null object reference
You would like to set text to a textview which is null. You should check at first that your textview is not null like this:
if (textview != null){
textview.setText("mytext");
}

Use this library below to block the activity until the network operation is finished.
Spot Dialog
This is what I used for my app in the image which I showed you earliar.

I may be late, but i hope i can help other developer as well , for i have encountered this problem too. just use isAdded() in your condition at onResponse callback inside the if-else
ex: inside your onResponse callback :
if (isAdded()){
// success response
} else {
if(isAdded()){
// error response
}
}

code is for kotlin in android:
TextView?.text
here ? mean not null
use it like shows as above

If you change navigation drawer menu item quickly, current fragment will replace by new one too. Android can remove any ongoing calculation that is running in main thread. But if any heavy task like background network response result or handler delayed task that has a reference of fragment can find null because fragment has already detach or replace by new one.
So you just have to check that, your desire fragment is still added in activity or not before process response data that has come lately.
if(frag.isAdded()){
//lets process your delayed response data
}

Related

Kotlin Update TextView after Dialog dismiss

I have a dialog with a button. I want to change a TextView (txtTotalDist) in the parent fragment when the button is clicked and the dialog is closed.
I tried several methods without success:
rootView.btnDialogOK.setOnClickListener {
val frag: ParentFragment()
frag.txtTotalDist.text="A"
//Attempt to invoke virtual method 'void android.widget.TextView.setText(java.lang.CharSequence)' on a null object reference
(activity as ParentFragment).txtTotalDist.post { txtTotalDist.text = "A" }
//cannot be cast to ParentFragment
txtTotalDist.post { txtTotalDist.text = "A" }
//Attempt to invoke virtual method 'boolean android.widget.TextView.post(java.lang.Runnable)' on a null object reference
txtTotalDist.text = "A"
//Attempt to invoke virtual method 'void android.widget.TextView.setText(java.lang.CharSequence)' on a null object reference
dismiss()
}
I also tried to find a function in the parent fragment to override, e.g. onAttach, onResume, onStart, onViewStateRestored, but none of them start when I close the dialog. How can I do it properly?
EDIT / SOLUTION
build.gradle Module:
implementation 'androidx.fragment:fragment-ktx:1.4.0'
Host fragment:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setFragmentResultListener("requestKey") { key, bundle ->
val result = bundle.getString("action")
if (result=="del") refreshStatusBar()
}
}
Dialog fragment:
rootView.btnDialogRemove.setOnClickListener {
setFragmentResult("requestKey", bundleOf("action" to "del"))
dismiss()
}
If the second fragment is nested inside in first fragment, you can pass a callback to second fragment and invoke it after closing dialog, or you can return an observable from second fragment to observe by first one.
But if your designed fragments are sibling with each other, it depends on your designed code base. If you are using fragment manager directly, you can do the same approach as above and add second fragment instead of replacing it. But if you have to replace or you are using navigation component, there is a better way to handle it like we do between two activities.
You can add below code lines to first fragment:
setResultListener("yourKey") { key, Any ->
val result = anything you need to return
//Todo something about result
}
And in second activity when you want to set data after closing dialog or whenever:
val result = any result
setResult("yourKey",result)
Also you can check the below url for more information about it.
https://developer.android.com/training/basics/fragments/pass-data-between#kotlin

Layout fields of fragment are NULL on initialization

I have issue with passing data to fragments. It crashes 0.1% of all times on production. Let's say on 100k opening of activity it happens 100 times. It looks like not very often, but it very bothering me and I think that I am doing something wrong with fragments initialization with data. The thing is, that I create fragments only one time, and all other times I need to pass data to them I am doing it next way: myFragmentInstance.setData(Object someData); And crash happens because it tells that those view elements in fragment are not found and they are NULL, but everything should be fine if I have not recreated them. I am not rotating my phone, or have not enough of memory on it. It happens on network reconnect, because on network reconnect I am going to server for fresh data and then set that new data to my fragments. I have photos of fields of two fragments I use, maby some of you know what that data can tell about status of fragments at the moment of crash.
I am using library ButterKnife to initialize fields of fragments and activities, not initializing it with findById, maby it has some influence or no?
Here is link to simple project (only this issue on github): https://github.com/yozhik/Reviews/tree/master/app/src/main/java/com/ylet/sr/review
Description:
CollapsingActivity - activity with Collapsing AppBarLayout. Which loads one or two fragments into "fragment_content_holder" and it has TabLayout to switch between fragments in view pager.
In activity method onCreate() - I'm just simulating request to server (loadData), and when some fake data is loaded - I am showing fragments in view pager, on first call - I am creating new TabMenuAdapter extends FragmentPagerAdapter, populate it with fragments and save links to instances. On the next call - I don't create fragments from scratch and just populate them with fresh data.
MenuFragment1, MenuFragment1 - two fragments. MenuFragment1 - has method public void setupData(SomeCustomData data), to set new data, not recreating fragment on network reconnect.
NetworkStateReceiver - listens to network change and send notifications.
TabMenuAdapter - just simple class to hold fragments.
05-11 18:11:05.088 12279-12279/com.myProjectName E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.myProjectName, PID: 12279
java.lang.IllegalStateException: Fatal Exception thrown on Scheduler.
at io.reactivex.android.schedulers.HandlerScheduler$ScheduledRunnable.run(HandlerScheduler.java:111)
at android.os.Handler.handleCallback(Handler.java:739)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:135)
at android.app.ActivityThread.main(ActivityThread.java:5268)
at java.lang.reflect.Method.invoke(Native Method)
at java.lang.reflect.Method.invoke(Method.java:372)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:902)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:697)
Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'void android.widget.TextView.setText(java.lang.CharSequence)' on a null object reference
at com.yozhik.myProjectName.view.fragments.MyFinalTermsFragment.setupMyInformation(MyFinalTermsFragment.java:145)
at com.yozhik.myProjectName.view.fragments.MyFinalTermsFragment.setupWithData(MyFinalTermsFragment.java:133)
at com.yozhik.myProjectName.view.activity.MyFinalActivity.onDataLoaded(MyFinalActivity.java:742)
at com.yozhik.myProjectName.presenter.MyFinalPresenter$1.onNext(MyFinalPresenter.java:55)
at com.yozhik.myProjectName.presenter.MyFinalPresenter$1.onNext(MyFinalPresenter.java:47)
at io.reactivex.internal.operators.observable.ObservableObserveOn$ObserveOnObserver.drainNormal(ObservableObserveOn.java:200)
at io.reactivex.internal.operators.observable.ObservableObserveOn$ObserveOnObserver.run(ObservableObserveOn.java:252)
at io.reactivex.android.schedulers.HandlerScheduler$ScheduledRunnable.run(HandlerScheduler.java:109)
at android.os.Handler.handleCallback(Handler.java:739) 
at android.os.Handler.dispatchMessage(Handler.java:95) 
at android.os.Looper.loop(Looper.java:135) 
at android.app.ActivityThread.main(ActivityThread.java:5268) 
at java.lang.reflect.Method.invoke(Native Method) 
at java.lang.reflect.Method.invoke(Method.java:372) 
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:902) 
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:697) 
05-11 18:11:07.953 2155-3877/? E/WifiStateMachine: Did not find remoteAddress {192.168.200.1} in /proc/net/arp
05-11 18:11:07.966 2155-3877/? E/WifiStateMachine: WifiStateMachine CMD_START_SCAN source -2 txSuccessRate=3800.62 rxSuccessRate=4732.06 targetRoamBSSID=any RSSI=-68
05-11 18:11:07.967 2155-3877/? E/WifiStateMachine: WifiStateMachine L2Connected CMD_START_SCAN source -2 2324, 2325 -> obsolete
05-11 18:11:08.021 2155-3896/? E/ConnectivityService: Unexpected mtu value: 0, wlan0
05-11 18:11:08.579 13514-13366/? E/WakeLock: release without a matched acquire!
Fragment which is crashing in method setupData because data_1_txt is NULL sometimes.
public class MenuFragment1 extends Fragment {
public SomeCustomData transferedDataFromActivity;
private TextView data_1_txt;
public static MenuFragment1 newInstance(SomeCustomData data) {
Log.d("TEST", "MenuFragment1.newInstance");
MenuFragment1 fragment = new MenuFragment1();
Bundle args = new Bundle();
args.putSerializable("DATA_FROM_ACTIVITY", data);
fragment.setArguments(args);
return fragment;
}
#Override
public void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.d("TEST", "MenuFragment1.onCreate");
if (getArguments() != null) {
this.transferedDataFromActivity = (SomeCustomData) getArguments().getSerializable("DATA_FROM_ACTIVITY");
}
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
Log.d("TEST", "MenuFragment1.onCreateView");
View v = inflater.inflate(R.layout.menu_fragment_1, container, false);
data_1_txt = (TextView) v.findViewById(R.id.data_1_txt);
setupInOnCreateView();
return v;
}
protected void setupInOnCreateView() {
Log.d("TEST", "MenuFragment1.setupInOnCreateView");
//initialization of all view elements of layout with data is happens here.
setupData(transferedDataFromActivity);
}
public void setupData(SomeCustomData data) {
Log.d("TEST", "MenuFragment1.setupData");
this.transferedDataFromActivity = data;
if (transferedDataFromActivity != null) {
data_1_txt.setText(transferedDataFromActivity.Name);
}
}
}
Fragment layout:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#color/green"
android:orientation="vertical">
<TextView
android:id="#+id/data_1_txt"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#color/yellow"
android:text="Test"
android:textSize="20sp" />
<include layout="#layout/description_layout" />
</LinearLayout>
in my experience if there is an error in fragments it is usually because of pre-loading of fragments in viewpager and TabMenu so what I did and Suggest you do to is to check if the fragment is visible to user and if they were, get data and other things so here is my code:
public class Fragment1 extends Fragment {
boolean visible = false;
public static Fragment1 newInstance() {
return new Fragment1();
}
#Override
public View onCreateView(LayoutInflater inflater,
ViewGroup container, Bundle savedInstanceState) {
if (visible && isResumed()) {
onResume();
} else if (visible) {
getMyData();
}
return rootView;
}
#Override
public void setUserVisibleHint(boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
visible = isVisibleToUser;
if (isVisibleToUser) {
getMyData();
}
else {
}
}
#Override
public void onViewCreated(View view, #Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
}
}
this way if the view is not created yet and is not visible to user fragment won't do anything .
hope this helps.
I'm pretty sure your issues are due to keeping the references to fragments in an Array. Fragments have lifecycles and the references are not guaranteed to persist. As you said, it's hard to reproduce and track down exactly what's going wrong, but maybe you don't need to.
Some suggestions on how to fix this:
Do not store the references to fragments. Pretty much follow the example on Google's page (https://developer.android.com/training/animation/screen-slide) and instantiate a new fragment every time it's requested.
If you are worried about performance and caching is solving it, try using FragmentStatePagerAdapter - it caches pages and manages fragments' states.
If you need to access page fragments from the main fragment (or the activity), instead of storing references, use `findFragmentByTag' which will always return the currently active instance of the fragment.
I strongly believe that things aren't that complicated as others explained. If i understand it correctly, the delay caused by the network transaction is the culprit.
Consider this scenario.
You are making a network request which makes some changes in the view
at the end.
You switch the pager. (Fragment is detached, views are
destroyed)
Here comes the callback from the network request. Guess what! A crash.
So when dealing dealing with views of fragments, it's always a good idea to be more careful. I usually do this.
//in the base class
protected boolean isSafe()
{
return !(this.isRemoving() || this.getActivity() == null || this.isDetached()
|| !this.isAdded() || this.getView() == null);
}
//usage in derived classes
onNewtworkResult(Result result) {
if(!isSafe())
return;
//rest of the code
}
Alternatively, You can wrap the potential code in a try catch too. But that is more like a blind shot (At least in this case).

Attempt to invoke virtual method 'android.os.Handler android.support.v4.app.FragmentHostCallback.getHandler()' on a null object reference

My Application consist of 4 fragment as tabs being loaded inside a parent Fragment using FragmentPagerAdapter.
The problem is when I run the app and press back and re-open the app I receive this error log:
FATAL EXCEPTION: main
java.lang.NullPointerException: Attempt to invoke virtual method 'android.os.Handler android.support.v4.app.FragmentHostCallback.getHandler()' on a null object reference
at android.support.v4.app.FragmentManagerImpl.ensureExecReady(FragmentManager.java:1949)
at android.support.v4.app.FragmentManagerImpl.execSingleAction(FragmentManager.java:1965)
at android.support.v4.app.BackStackRecord.commitNowAllowingStateLoss(BackStackRecord.java:620)
at android.support.v4.app.FragmentPagerAdapter.finishUpdate(FragmentPagerAdapter.java:143)
at android.support.v4.view.ViewPager.setAdapter(ViewPager.java:513)
...
the line of code inside parent Fragment is:
viewPager.setAdapter(adapter);
tabLayout.setupWithViewPager(viewPager);
and ViewPage and Adapter both are not null!!
I have to mention that all my Fragments lifecycle is being managed and the null issue is happening inside the adapter!, and the same Adapter is working fine when I use an Activity as parent instead of Fragment!!!
After a lot of research, it looks like it is a support library issue:
Activity with fragment and ViewPager crashesh on configuration
change Android Support Library issue 24.1.0
I also found this answer, but it looks unusual adding a flag to my code:
Attempt to invoke virtual method ‘android.os.Handler android.support.v4.app.FragmentHostCallback.getHandler()’ on a null object
I override the finishUpdate method of my FragmentPagerAdapter like this and problem solved for now!
#Override
public void finishUpdate(ViewGroup container) {
try{
super.finishUpdate(container);
} catch (NullPointerException nullPointerException){
System.out.println("Catch the NullPointerException in FragmentPagerAdapter.finishUpdate");
}
}
any better answer? please let me know...
I have the same issue on my project. But it is my fault.
WHY
I call onBackPressed in a callback after the activity has invoked onDestroy();
Because the mHost instance in the FragmentManager is set to null after onDestroy() called.
and when call onBackPressed it will use the mHost instance again.
#Override
public void onBackPressed() {
// super.onBackPressed();
finish();
}
Override the onBackPressed in activity..
remove the super call : super.onBackPressed
and call finish.
I know it might be late, but here's my stupid mistake I didn't see straight away:
#Override
public void onStop() {
super.onDestroy(); //I was calling the wrong super
}
I had the same issue and it was caused by the reuse of ViewPager instance by different instances of a fragment. A solution was to null the adapter in onDestroyView() method of the fragment.
#Override public void onDestroyView() {
mViewPager.setAdapter(null);
super.onDestroyView();
}
Project demoing the issue and the solution can be found here.
I had the same issue. In my case the problem was that I was re-using without noticing the adapter from the previous time opened.
I looked into the FragmentManager.java class that send the exception and on the very same line that crashes says:
"Must be called from main thread of fragment host"
my code was:
PagerAdapter adapter = MPageAdapter.getInstance(getSupportFragmentManager());
viewPager.setAdapter(adapter);
because I was using my PageAdapter as a bridge to communicate between fragments I used a singleton pattern. That worked well for me but once I repeated this structure in another activity I got the same exception. And the reason was that I forgot to destroy the singleton of my adapter class at the end of every use.
#Override
protected void onDestroy() {
MPageAdapter.Destroy(); //I forgot this one, that makes singleton = null;
super.onDestroy();
}
I solved it by adding !isFinishing in onBackPressed() of my Activity.
override fun onBackPressed() {
if (!isFinishing) {
super.onBackPressed()
// your implementation specific code here..
}
}
The issue in my case was through some code flow, my DialogFragment was calling the onBackPressed() of its parent Activity just after calling finish() which causing this crash.
Hope it helps if someone is still looking for answer!
I was using the wrong activity reference to fetch
getSupportFragmentManager()
Using the correct activity fixed the crash for me.
Just comment super.onBackPressed() method and write finish() instead
#Override
public void onBackPressed() {
// super.onBackPressed();
finish();
}
In my case this worked!

Android activity and fragment lifecycle issue?

I have the following stack trace for a crash caused by an NPE:
Fatal Exception: java.lang.RuntimeException: Unable to start activity ComponentInfo{com.daybreak.my.app/com.daybreak.my.app.MainActivity}: java.lang.NullPointerException: Attempt to invoke virtual method 'void android.widget.ViewSwitcher.setOnClickListener(android.view.View$OnClickListener)' on a null object reference
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2430)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2490)
at android.app.ActivityThread.access$900(ActivityThread.java:153)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1358)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:148)
at android.app.ActivityThread.main(ActivityThread.java:5456)
at java.lang.reflect.Method.invoke(Method.java)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:735)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:625)
Caused by java.lang.NullPointerException: Attempt to invoke virtual method 'void android.widget.ViewSwitcher.setOnClickListener(android.view.View$OnClickListener)' on a null object reference
at com.daybreak.my.app.TimesFragment.onLocationChange(TimesFragment.java:446)
at com.daybreak.my.app.MainActivity.onLocationChange(MainActivity.java:289)
at com.daybreak.my.app.MainActivity.onCreate(MainActivity.java:112)
at android.app.Activity.performCreate(Activity.java:6302)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1108)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2383)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2490)
at android.app.ActivityThread.access$900(ActivityThread.java:153)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1358)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:148)
at android.app.ActivityThread.main(ActivityThread.java:5456)
at java.lang.reflect.Method.invoke(Method.java)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:735)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:625)
The way I have setup my app is as follows:
MainActivity
public class MainActivity extends AppCompatActivity implements LocationChangeListener {
#Override
public void onCreate(Bundle savedInstanceState) {
//...
onLocationChange(LocationManager.getSavedLocation(this)); // Manually calling onLocationChange() method
if (findViewById(R.id.fragment_container) != null) {
if (savedInstanceState != null) return;
showFragment(new TimesFragment(), TimesFragment.TAG);
}
}
#Override
public void onLocationChange(Locatin location) {
if (location == null) return;
//...
// Call attached onLocationChange() if it implements LocationChangeListener
Fragment f = getSupportFragmentManager().findFragmentById(R.id.fragment_container);
if (f instanceof LocationChangeListener)
((LocationChangeListener) f).onLocationChange(location);
}
}
TimesFragment
public class TimesFragment extends Fragment implements LocationChangeListener {
private ViewSwitcher viewSwitcher;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
//...
viewSwitcher = (ViewSwitcher) view.findViewById(R.id.view_switcher);
}
#Override
public void onLocationChange(Location location) {
this.location = location;
viewSwitcher.setOnClickListener(null); //<-- NPE Cause here
updateContent();
}
}
MY EXPECTATION
From what I understand, the Activity.onCreate() will only be called after a fresh start or after the user navigates back to the app after the app has been killed (explicitly by the user or by memory management when other apps need memory). If this happens the fragments will also be destroyed and will need to be created, i.e., fragment's onCreateView() will be called. Therefore calling onLocationChange() from the MainActivity.onCreate() before attaching the fragment is safe as findFragmentById() within the onLocationChange() would not find any fragment.
REALITY
From the stack trace we can see that the call initiated from MainActivity.onCreate(). But what's puzzling for me is that at the time onLocationChange() is called from within MainActivity.onCreate(), findFragmentById() within the onLocationChange() finds the fragment in the view container and calls the fragments onLocationChange(). When this happens the viewSwitcher is NULL and causes the app to crash.
Obviously, fragment has already been added to the view container and the fragments onCreateView() has not been called yet.
THE QUESTION
I am not able to recreate this crash, and not sure of the lifecycle process that is causing this.
So can anyone tell me
how to reproduce this error and
the lifecycle process that is responsible for the flow that is causing the NPE?
This is being caused by device rotation. Can recreate the stack trace by rotating the device.
NOTE: This can happen even when app's orientation is locked (as in my case); if the user is in another app in an orientation that is different than the orientation your app is locked to and they switch back to the app, the orientation lifecycle for your app will be fired.
SOLUTION
Add f != null && f.isResumed() before calling methods from the fragment. isResumed() will return false if fragment hasn't been resumed after recreation.

Call fragment method from activity but view is null

I am checking internet connection in my main activity.If connection is lost,I want to show reconnecting message in fragment.Example:If I run following code in main activity my app is crashing.
ConversationFragment conv1 = new ConversationFragment();
conv1.showReconnecting();
And this is the showReconnecting method in fragment:
public void showReconnecting() {
final RelativeLayout rel=(RelativeLayout)rootView.findViewById(R.id.reconnecting);
rel.setVisibility(View.VISIBLE);
}
I know why my app is crashing because rootView is setting in onCreateView and when I call this method from activity rootView is returning null.
What can I do for resolve this problem ?
Try this (Assuming reconnecting view is in fragment and your fragment is visible on screen),
public void showReconnecting() {
if (conv1 != null && conv1.getView()!=null) {
final RelativeLayout rel=(RelativeLayout)conv1.getView().findViewById(R.id.reconnecting);
rel.setVisibility(View.VISIBLE);
}
}
If conv1 is not available then please make it class variable.
According to what you want to do, a simple ProgressDialog will do the job. You can lauch it from your ChatFragment, like this:
First, declare it on your Fragment:
private ProgressDialog connectiongProgressDialog;
Then, when you want to show it, use:
connectiongProgressDialog = ProgressDialog.show(getActivity(), "My title", "Reconnecting");
When you want to dismiss it, use:
if (connectiongProgressDialog != null){
connectiongProgressDialog .dismiss();
}
It will display a simple dialog with the message you want.
Well, just looking at this and your explanation, I would think you can't call methods relating to a view (such as a fragment) without creating said fragment. An option, if the fragment is only used for showing the reconnecting, would be just to put some code in the activity and use a Toast.makeText(....).show();
which notifies the user that you are attempting to reconnect. So instead of conv1.showReconnecting(); maybe rather put, Toast.makeText(context,"Reconnecting", Toast.LONG).show(); and then do the necessary background work in an AsyncTask.
I hope this helps.

Categories

Resources