How to call fragment method from other fragments? [duplicate] - android

This question already has answers here:
how to call function from other fragment class
(2 answers)
Closed 5 years ago.
In my application I want call fragment methods from other fragments, I write below codes but when call this method show me below error in logcat and Force close application.
For show this two fragments into activity I use TabLayout and ViewPager.
My Review Fragment codes:
public void getComments(final Context context) {
JsonObject requestBean = new JsonObject();
requestBean.addProperty("entityType", 1);
requestBean.addProperty("reviewType", 5);
requestBean.addProperty("reviewUserType", 2);
requestBean.addProperty("entityID", serialID);
requestBean.addProperty("celebrityId", 0);
requestBean.addProperty("pageIndex", 1);
requestBean.addProperty("pageSize", 10);
InterfaceApi api = ApiClient.getClient().create(InterfaceApi.class);
Call<CommentResponse> call = api.getComments(token, requestBean);
call.enqueue(new Callback<CommentResponse>() {
#Override
public void onResponse(Call<CommentResponse> call, Response<CommentResponse> response) {
if (response.body().getData() != null) {
if (response.body().getData().size() > 0) {
reviewMovieFrag_NoComment.setText("");
} else {
reviewMovieFrag_NoComment.setText(context.getResources().getString(R.string.noReviews));
SerialReview_CastProgress.setVisibility(View.GONE);
}
commentModel.clear();
commentModel.addAll(response.body().getData());
commentsListAdapter.notifyDataSetChanged();
reviewMovieFrag_newsCommentsRecyclerView.setAdapter(commentsListAdapter);
reviewMovieFrag_newsCommentsUserTypeText.setText(userTypeStr);
reviewMovieFrag_newsCommentsReviewTypeText.setText(reviewTypeStr);
reviewMovieFrag_Progress.setVisibility(View.GONE);
}
}
#Override
public void onFailure(Call<CommentResponse> call, Throwable t) {
reviewMovieFrag_Progress.setVisibility(View.GONE);
}
});
}
I want call this method (getComments method) into InfoFragment and for this I write this code :
new MovieDetail_reviewFragment().getComments(getActivity());
But in LogCat show me this error :
java.lang.NullPointerException: Attempt to invoke virtual method 'void android.widget.TextView.setText(java.lang.CharSequence)' on a null object reference
at com.test.app.Fragments.MovieDetailFragments.MovieDetail_reviewFragment$6.onResponse(MovieDetail_reviewFragment.java:301)
at retrofit2.ExecutorCallAdapterFactory$ExecutorCallbackCall$1$1.run(ExecutorCallAdapterFactory.java:68)
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:5349)
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:908)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:703)
Show error for this line :
reviewMovieFrag_NoComment.setText(context.getResources().getString(R.string.noReviews));
ATTENTION : Dear moderators and dear users I know this error for NullPointer but I don't know how can I fix it?
I tried for fix this issue but I can't so I ask in StackOverFlow.
Please help me and don't give me negative points or duplicate my post!
Please help me, Thanks all

Here,
new MovieDetail_reviewFragment().getComments(getActivity());
Is creating your fragment class newly. You need to use all variables when Fragment class created at starting.
Use viewPager.setOffscreenPageLimit(fragmentNumber); - this will help you to create all fragment when tab initialized.
Then use instance to access any fragment method.
Declare at top of fragment class, private static FragmentClass instance = null;
inside your Fragment class override onCreate() and initialize instance,
#Override
public void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
instance = this;
}
Create getInstance() method in fragment contains your calling method,
public static FragmentClass getInstance(){
return instance;
}
Finally call method from another fragment,
FragmentClass.getInstance().yourMethod();

don't take it otherwise but what you are doing is not good design pattern,
you should use interfaces for communicating between any two fragments. It will make things easier for you. Look into this post Basic Communication between two fragments

Related

IllegalStateException: Fragment YoutubeLessonFragment not attached to a context

I am using YouTubePlayerSupportFragment in one of my fragments but I am not using it inside the layout file but initialising it programmatically. Some of the users are facing this crash at runtime:
Fatal Exception: java.lang.IllegalStateException: Fragment YoutubeLessonFragment{3a2e875} 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.musicmuni.riyaz.youtubelesson.YoutubeLessonFragment.loadYoutubeVideo(YoutubeLessonFragment.java:168)
at com.musicmuni.riyaz.youtubelesson.YoutubeLessonPresenter$2.onModuleLoaded(YoutubeLessonPresenter.java:188)
at com.musicmuni.riyaz.data.AppDataRepositoryImpl$10.run(AppDataRepositoryImpl.java:187)
at android.os.Handler.handleCallback(Handler.java:754)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:163)
at android.app.ActivityThread.main(ActivityThread.java:6221)
at java.lang.reflect.Method.invoke(Method.java)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:904)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:794)
My code for initialising the youtube fragment goes like this:
private YouTubePlayerSupportFragment youtubePlayerFrag;
.........
public void loadYoutubeVideo(String videoId) {
mVideoId = videoId;
if(getContext() != null) {
youtubePlayerFrag = YouTubePlayerSupportFragment.newInstance();
youtubePlayerFrag.initialize(getString(R.string.youtube_api_developer_key),
this);
getChildFragmentManager().beginTransaction().add(R.id.flYoutubeVideoHolder,
youtubePlayerFrag).commit();
}
}
where loadYoutubeVideo(...) gets a callback from a background running thread loading the required video id. Any pointers here?
Probably your Fragment in not attached to the activity while you are calling getString() method.
because the docs says:
Fragments now have requireContext(), requireActivity(), requireHost(),
and requireFragmentManager() methods, which return a NonNull object of
the equivalent get methods or throw an IllegalStateException.
You might want to check if the fragment is attached to the activity or not, by calling isAdded() method of fragment.
Also you can pass the argument directly to newInstance(..args..) rather that creating initialise() method.

Caused by java.lang.IllegalStateException: Can't create ViewModelProvider for detached fragment

I need some help to ressolve the following crash. I am refreshing my list on Restart in Activity by calling fragment in viewpager to refresh its list. Following is the stacktrace of the crash:
Caused by java.lang.IllegalStateException: Can't create ViewModelProvider for detached fragment
at android.arch.lifecycle.ViewModelProviders.checkActivity(ViewModelProviders.java:51)
at android.arch.lifecycle.ViewModelProviders.of(ViewModelProviders.java:105)
at com.ui.fragments.mainpagerfragments.ConversationListFragment.searchConversation(ConversationListFragment.java:383)
at com.ui.activities.HomeActivity.onRestart(HomeActivity.java:288)
at android.app.Instrumentation.callActivityOnRestart(Instrumentation.java:1256)
at android.app.Activity.performRestart(Activity.java:6365)
at android.app.Activity.performResume(Activity.java:6376)
at android.app.ActivityThread.performResumeActivity(ActivityThread.java:3299)
at android.app.ActivityThread.handleResumeActivity(ActivityThread.java:3345)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1532)
at android.os.Handler.dispatchMessage(Handler.java:111)
at android.os.Looper.loop(Looper.java:207)
at android.app.ActivityThread.main(ActivityThread.java:5728)
at java.lang.reflect.Method.invoke(Method.java)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:789)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:679)
call from activity onRestart :
((MyListFragment) adapter.getFragmentAtPosition(0)).search("");
method in fragment :
public void search(String query) {
query = "%" + query + "%";
if (myListViewModel == null) {
myListViewModel = ViewModelProviders.of(this, viewModelFactory).get(MyListViewModel.class);
}
if (myListViewModel.mList != null && myListViewModel.mList.hasActiveObservers()) {
myListViewModel.mList.removeObservers(this);
}
myListViewModel.getFilteredList(query).observe(this, mainObserver);
}
If you're using a viewpager, be sure that the corresponding fragment is actually attached to its activity (this is what the error tries to tell you). It might happen that the fragment is not visible, thus it's not attached (depending on how the view pager was configured).
a quick&dirty fix:
public void search(String query) {
if(!isAdded()) return; //<---- returns if the fragment is not attached
query = "%" + query + "%";
if (myListViewModel == null) {
myListViewModel = ViewModelProviders.of(this, viewModelFactory).get(MyListViewModel.class);
}
if (myListViewModel.mList != null && myListViewModel.mList.hasActiveObservers()) {
myListViewModel.mList.removeObservers(this);
}
myListViewModel.getFilteredList(query).observe(this, mainObserver);
}
However, you should re-investigate in your architecture, because those checks often imply some other code smells. When you're working with the new Android Architecture Patterns, there should be no need of this check as the lifecycle pattern handles all this for you: https://developer.android.com/topic/libraries/architecture/lifecycle
So, basically, you should not directly call any function of a fragment directly, instead call the corresponding business logic, which notifies the fragment to update its views.
Since the error is that the fragment is not attached yet, it needs to be done when we are sure the fragment is actually attached. Its a good way of doing this; by putting the code inside the override method onAttach
#Override
public void onAttach(Context context) {
super.onAttach(context);
myListViewModel = ViewModelProviders.of(this, viewModelFactory).get(MyListViewModel.class);
}

Android LiveData null error when trying to update object

I am looking for help to better understand LiveData in Android.
I have created an application that allows a user to view, edit or create entries in a db.
I have used a viewmodel on the form activity to share information between the activity and fragments and to also make the data Lifecycle aware.
The code works great when I load a entry from the database. Any changes are captured and saved back to the database
However when I want to create a new entry using the same code I get
Caused by: java.lang.NullPointerException: Attempt to invoke virtual method ...null object reference when I try to update the object in the viewmodel. Sample below tries to set the Name value when a user enters a new one:
public void afterTextChanged(Editable s) {
patternViewModel.getPattern().getValue().setName(s.toString());
((FlyPatternDetailActivity)getActivity()).setHasChanged(true);
((FlyPatternDetailActivity)getActivity()).setActionBar(s.toString());
}
My question is how to update the livedata object of my entity if I dont get an object back from my database and I am going to create a new entry.
My viewmodel is:
public class PatternViewModel extends AndroidViewModel {
private PatternRepository repo;
private LiveData<FlyPattern> mObservablePattern;
public PatternViewModel(#NonNull Application application) {
super(application);
if (mObservablePattern == null) {
mObservablePattern = new MutableLiveData<>();
}
repo = PatternRepository.getInstance(((FlyTyingJournalApplicationClass) application).getDatabase());
}
public void loadPattern(final long id){
if(id != -1)
mObservablePattern = repo.getPattern(id);
}
public LiveData<FlyPattern> getPattern(){
return mObservablePattern;
}
public void insertPattern() {
repo.insertPattern(mObservablePattern.getValue());
}
public void updateFlyPattern(){
repo.updatePattern(mObservablePattern.getValue());
}
public void deleteFlyPattern(){
repo.deletePattern(mObservablePattern.getValue());
}
}
I understand why I am getting the nullpointException.
My question is how to instantiate my mObservablePattern so I can update it.
It gets instantiated if the loadPattern returns an object.
If the loaddata does not run or does return an object from the database then I cannot update mObservablePattern.
Stacktrace from the Error:
Process: com.rb.android.flytyingjournal, PID: 4957
java.lang.NullPointerException: Attempt to invoke virtual method 'void com.rb.android.flytyingjournal.entities.FlyPattern.setType(java.lang.String)' on a null object reference
at com.rb.android.flytyingjournal.flypatterndetails.PatternDetailsFragment$5.onItemSelected(PatternDetailsFragment.java:325)
at android.widget.AdapterView.fireOnSelected(AdapterView.java:931)
at android.widget.AdapterView.dispatchOnItemSelected(AdapterView.java:920)
at android.widget.AdapterView.-wrap1(AdapterView.java)
at android.widget.AdapterView$SelectionNotifier.run(AdapterView.java:890)
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:6119)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:886)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:776)
You are trying to call setName() on a NULL. Because patternViewModel.getPattern().getValue() returns the value that is held in the LiveData, which might be NULL in your case. You can add a null check:
if (patternViewModel.getPattern().getValue() == null) {
FlyPattern flyPattern = new FlyPattern();
flyPattern.setName("foo");
patternViewModel.getPattern().setValue(flyPattern);
} else {
patternViewModel.getPattern().getValue().setName("foo");
}
Or you can create a function in the ViewModel called e.g. setFlyPatternName() and use it to update your DB.
In your PatternViewModel.java
public void setFlyPatternName(String name) {
if (mObservablePattern.getValue == null) {
FlyPattern flyPattern = new FlyPattern();
flyPattern.setName(name);
mObservablePattern.setValue(flyPattern);
repo.insertFlyPattern();
} else {
mObservablePattern.getValue().setName(name);
repo.updateFlyPattern();
}
}
Edit: The proper way of doing this is actually a bit different.
Normally your repository functions need to work on the background thread, since you are dealing with i/o, but if you want them to work on the mainThread you need to at least create a callback and pass that callback object to your repository function, which will be called when the data is inserted/updated/deleted. And when the callback function is called you need to call the setValue() on the LiveData and set the data to your livedata object.
Create a callback interface:
public interface InsertCallback {
void onDataInserted(FlyPattern flyPattern);
void onDataInsertFailed();
}
Change your repositories insert function's body to accept the Callback object as parameter
public void insertFlyPattern(FlyPattern flyPattern, InsertCallback insertCallback) {
// Do your insertion and if it is successful call insertCallback.onDataInserted(flyPattern), otherwise call insertCallback.onDataInsertFailed();
}
In your ViewModel implement InsertCallback and it's method
public class PatternViewModel extends AndroidViewModel implements InsertCallback {
//....
public void onDataInserted(FlyPattern flyPattern) {
mObservablePattern.setValue(flyPattern);
}
public void onDataInsertFailed() {
//Show error message
}
}

Accessing resources in fragment background task returns illegalStateException often

I am making an asynchronous network request to fetch reviews and trailers.
If the reviews and trailers are present then they are set as TextView texts. If there are no reviews present then I am setting the textview test as "No reviews"(message_no_reviews) which is a predefined string variable in strings.xml file.
Now in order to display that text when no reviews are available I am fetching that using
getResources().getString(R.string.message_no_reviews)
The problem is that call to getResources().getString(R.string.message_no_reviews) returns java.lang.IllegalStateException: Fragment MovieDetailFragment{1fdfd76} not attached to Activity many times whereas call to getResources().getString(R.string.api_key) does not cause any Exception.
Following is the code snippet.
public void onActivityCreated(#Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
getReviews();
getTrailers();
}
public void getReviews(){
Call<JsonObject> getReviewsCall = apiService.getReview(movie.getId(),getResources().getString(R.string.api_key));
getReviewsCall.enqueue(new Callback<JsonObject>() {
#Override
public void onResponse(Response<JsonObject> response, Retrofit retrofit) {
if (response.body() != null) {
JsonArray reviews=(JsonArray) response.body().get("results");
System.out.println(reviews);
if(reviews.size()>0) {
tvReviewText.setText(((JsonObject) reviews.get(0)).get("content").getAsString());
tvReviewAuthor.setText(((JsonObject) reviews.get(0)).get("author").getAsString());
}
else{
tvReviewAuthor.setText(getResources().getString(R.string.message_no_reviews));
tvReviewText.setText("");
}
}
}
#Override
public void onFailure(Throwable t) {
}
});
}
Any thoughts on why this might be happening?
below is the logcat
FATAL EXCEPTION: main
Process: com.vipin.www.popularmovies, PID: 21580
java.lang.IllegalStateException: Fragment MovieDetailFragment{1fdfd76} not attached to Activity
at android.support.v4.app.Fragment.getResources(Fragment.java:636)
at com.vipin.www.popularmovies.MovieDetailFragment$2.onResponse(MovieDetailFragment.java:168)
at retrofit.ExecutorCallAdapterFactory$ExecutorCallback$1.run(ExecutorCallAdapterFactory.java:86)
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:5221)
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:899)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:694)
Your fragment is not attached to activity means:your fragment is not visible to user at that time since you made a network call from a button it create a another background thread and then you either backStack fragment or remove it from activity . So when response comes to onResponse method its context not available.
So You should use:
Fragment.this.getResources().getString(R.string.message_no_reviews));
or create a boolean whether fragment is attached or not in onCreate() and onDestroy()
tvReviewAuthor.setText(getActivity().getResources().getString(R.string.message_no_reviews));
This should do the work You need Context Object to access String Resources
Use onAttach() method to know when your fragment is attached to activity and also get context in it and use it to call other methods like this
#Override
public void onAttach(Context context) {
super.onAttach(context);
activity = context;
}
and then you can do this
tvReviewAuthor.setText(activity.getResources().getString(R.string.message_no_reviews));

Android Attempt to read from field on a null object reference

I have an Activity with some Fragments used in a ViewPager, sometimes user sends me some crash report that i don't understand.
I can not produce this crash and most of the user have no problem at all.
The crash Log says:
java.lang.NullPointerException: Attempt to read from field 'com.mehdok.views.treeview.TreeNode$TreeNodeClickListener com.mehdok.mafatih.MainActivity.nodeClickListener2' on a null object reference
The nodeClickListener2 is in my main activity:
public TreeNode.TreeNodeClickListener nodeClickListener2 = new TreeNode.TreeNodeClickListener()
{
#Override
public void onClick(TreeNode node, Object value)
{
TreeViewHolderNoBorder.TreeViewItem item = (TreeViewHolderNoBorder.TreeViewItem) value;
if(node.isLeaf() && !item.nodeNavUri.equals(""))
{
openNodePage2(item);
}
}
};
And in fragmets i use it like this:
tView.setDefaultNodeClickListener(((MainActivity) getActivity()).nodeClickListener2);
What can i do to prevent this crash?
tnx in advance.
Where do you access your activity field from your fragment? You should do it starting from onAttach() up until onDetach().
If you try to access it too soon like in your constructor or in your fragment's onCreate(), this would explain the crash. Same if you try to access it after the fragment is detached from the Activity.
That is possible, sometimes Activity will be recycled, when you call getActivity() in Fragment, it will return null. so before you call getActivity() you can write code as below:
if (isAdded()) {
tView.setDefaultNodeClickListener(((MainActivity) getActivity()).nodeClickListener2);
...
}

Categories

Resources