In which case getFragmentManager() return null? - android

Problem : getFragmentManager() return null randomly.
Case: I have one activity with tab. On each tab press I'm replacing the content container with fragment. In case of two fragment I'm doing network hit to fetch data from a server. For that I have written the following code:
public class FetchMessagesyFragmentTask extends Fragment {
private static final String TAG_EXTRA = "tab_extra";
private static final String TAG = "Test";
private String mData;
public static final FetchMessagesyFragmentTask newInstance(String data) {
FetchMessagesyFragmentTask fragment = new FetchMessagesyFragmentTask();
Bundle bundle = new Bundle();
bundle.putString(TAG_EXTRA, data);
fragment.setArguments(bundle);
return fragment;
}
public static interface TaskCallbacks {
void onPreExecute();
void onCancelled();
void onPostExecute(MessageResponse response);
}
private TaskCallbacks mCallbacks;
private FetchMessage mTask;
private boolean mRunning;
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
if (!(getTargetFragment() instanceof TaskCallbacks)) {
throw new IllegalStateException(
"Target fragment must implement the TaskCallbacks interface.");
}
mCallbacks = (TaskCallbacks) getTargetFragment();
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRetainInstance(true);
mData = getArguments().getString(TAG_EXTRA);
}
#Override
public void onDestroy() {
super.onDestroy();
cancel();
}
public void execute(String data) {
if (!mRunning) {
mTask = new FetchMessage();
mTask.execute(data);
mRunning = true;
}
}
public void cancel() {
if (mRunning) {
mTask.cancel(false);
mTask = null;
mRunning = false;
}
}
public boolean isRunning() {
return mRunning;
}
private class FetchMessage extends AsyncTask<String, Void, MessageResponse> {
private ProgressDialogFragment progressDialog;
#Override
protected void onPreExecute() {
progressDialog = new ProgressDialogFragment.Builder().setMessage(
"Please wait...").build();
progressDialog.show(((Fragment) mCallbacks).getFragmentManager(),
"task_progress");
mCallbacks.onPreExecute();
mRunning = true;
}
#Override
protected MessageResponse doInBackground(String... params) {
//Doing network hit here and returning value.
return value;
}
#Override
protected void onCancelled() {
mCallbacks.onCancelled();
mRunning = false;
}
#Override
protected void onPostExecute(MessageResponse response) {
if (mCallbacks != null) {
FragmentManager manager = ((Fragment) mCallbacks)
.getFragmentManager();
//XXXXXXXX GETTING MANAGER AS NULL HERE SOMETIME XXXXXXXXXXXXXX
progressDialog.dismiss(manager);
if (response != null) {
if (Integer.parseInt(response.getResponseCode()) == NetworkConstant.SUCCESS
&& response.getChatMessage() != null) {
saveDataToDb(response);
}
}
mCallbacks.onPostExecute(response);
mRunning = false;
}
}
private void saveDataToDb(MessageResponse response) {
//SaveToDB
}
}
}
I'm following this url to handle network hit on orientation change. I have commented the line where I'm getting the issue.
Note
This code work fine in normal situation but crash when I switch tabs very fast.

Related

How to call a specific Fragment's getActivity()?

My problem is that i have a recyclerview implemented on a fragment that uses Room. RecyclerView, Adapter and Database need to be together in one class (i guess).
My problem is that if I put them into StudentActivity,
recyclerView = findViewById(R.id.SubjectRecyclerView);
will be null, because the Fragment won't be available by the time this function called.
If i put them into StudentSubjectsFragment,
everything would be good, but NewSubjectDialogFragment tells me that StudentActivity needs to implement NewSubjectDialogFragment because of this:
private NewSubjectDialogListener listener;
#Override
public void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
FragmentActivity activity = getActivity();
if (activity instanceof NewSubjectDialogListener) {
listener = (NewSubjectDialogListener) activity;
} else {
throw new RuntimeException("Activity must implement the NewSubjectDialogListener interface!");
}
}
If at the
FragmentActivity activity = getActivity();
line somehow I would be able to call the getActivity() function of StudentSubjectsFragment class, I think it would solve my problem, and then the class that would implement the NewSubjectDialogFragment would be the StudentSubjectsFragment instead of StudentActivity.
I tried this:
FragmentActivity activity = getActivity().getSupportFragmentManager().findFragmentByTag("StudentSubjectsFragment").getActivity();
But activity is null.
StudentSubjectsFragment class:
public class StudentSubjectsFragment extends Fragment
implements NewSubjectDialogFragment.NewSubjectDialogListener,
SubjectAdapter.SubjectClickListener {
public static final String TAG = "StudentSubjectsFragment";
private RecyclerView recyclerView;
private SubjectAdapter adapter;
private SubjectDatabase database;
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container,
#Nullable Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.student_subjects, container, false);
FloatingActionButton fab = rootView.findViewById(R.id.fabbb);
fab.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
new NewSubjectDialogFragment().show(getActivity().getSupportFragmentManager(), NewSubjectDialogFragment.TAG);
}
});
database = Room.databaseBuilder(
getActivity(),
SubjectDatabase.class,
"subject-list"
).build();
recyclerView = rootView.findViewById(R.id.SubjectRecyclerView);
adapter = new SubjectAdapter(this);
loadItemsInBackground();
recyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
recyclerView.setAdapter(adapter);
return rootView;
}
private void loadItemsInBackground() {
new AsyncTask<Void, Void, List<Subject>>() {
#Override
protected List<Subject> doInBackground(Void... voids) {
return database.subjectDao().getAll();
}
#Override
protected void onPostExecute(List<Subject> subjects) {
adapter.update(subjects);
}
}.execute();
}
#Override
public void onItemChanged(final Subject item) {
new AsyncTask<Void, Void, Boolean>() {
#Override
protected Boolean doInBackground(Void... voids) {
database.subjectDao().update(item);
return true;
}
#Override
protected void onPostExecute(Boolean isSuccessful) {
Log.d("StudentActivity", "Subject update was successful");
}
}.execute();
}
#Override
public void onItemDeleted(final Subject item) {
new AsyncTask<Void, Void, Boolean>() {
#Override
protected Boolean doInBackground(Void... voids) {
database.subjectDao().deleteItem(item);
return true;
}
}.execute();
}
#Override
public void onSubjectCreated(final Subject newItem) {
new AsyncTask<Void, Void, Subject>() {
#Override
protected Subject doInBackground(Void... voids) {
newItem.id = database.subjectDao().insert(newItem);
return newItem;
}
#Override
protected void onPostExecute(Subject subject) {
adapter.addItem(subject);
}
}.execute();
}
}
StudentActivity class:
public class StudentActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_student);
ViewPager vpProfile = findViewById(R.id.vpStudent);
vpProfile.setAdapter(new StudentPagerAdapter(getSupportFragmentManager()));
}
}
NewSubjectDialogFragment class:
public class NewSubjectDialogFragment extends DialogFragment {
private EditText nameEditText;
public static final String TAG = "NewSubjectDialogFragment";
public interface NewSubjectDialogListener {
void onSubjectCreated(Subject newItem);
}
private NewSubjectDialogListener listener;
#Override
public void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
FragmentActivity activity = getActivity().getSupportFragmentManager().findFragmentByTag("StudentSubjectsFragment").getActivity();
if (activity instanceof NewSubjectDialogListener) {
listener = (NewSubjectDialogListener) activity;
} else {
throw new RuntimeException("Activity must implement the NewSubjectDialogListener interface!");
}
}
private boolean isValid() {
return nameEditText.getText().length() > 0;
}
private Subject getSubject() {
Subject subject = new Subject();
subject.name = nameEditText.getText().toString();
return subject;
}
#NonNull
#Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
return new AlertDialog.Builder(requireContext())
.setTitle(R.string.new_subject_item)
.setView(getContentView())
.setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialogInterface, int i) {
if (isValid()) {
listener.onSubjectCreated(getSubject());
}
}
})
.setNegativeButton(R.string.cancel, null)
.create();
}
private View getContentView() {
final View contentView = LayoutInflater.from(getContext()).inflate(R.layout.dialog_new_subject_item, null);
nameEditText = contentView.findViewById(R.id.SubjectNameEditText);
return contentView;
}
}
I think there is a way to change NewSubjectDialogFragment to work with StudentSubjectsFragment but I am a beginner in android developing and don't know how to do it.
Solution of Mike M. worked perfectly.
The working code parts are the following:
NewSubjectDialogFragment:
#Override
public void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Fragment fragment = getParentFragment();
if (fragment instanceof NewSubjectDialogListener) {
listener = (NewSubjectDialogListener) fragment;
} else {
throw new RuntimeException("Fragment must implement the NewSubjectDialogListener interface!");
}
}
StudentSubjectFragment:
FloatingActionButton fab = rootView.findViewById(R.id.fabbb);
fab.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
new NewSubjectDialogFragment().show(getChildFragmentManager(), NewSubjectDialogFragment.TAG);
}
});

Only the original thread that created a view hierarchy can touch its views. - Strange behavior

This is a very strange behavior and I don't know how to fix it.
I have an Activity as a Presenter (In a MVP Architecture).
When the activity starts, I attach a Fragment as a View. The fragment itself is very simple.
public class CurrentSaleFragment extends BaseFragment {
private MainMVP.SalesPresenterOps salesPresenterOps;
private SaleAdapter adapter;
private ListView lv;
#BindView(R.id.btn_sell)
FloatingActionButton btnAdd;
public static CurrentSaleFragment newInstance(){
CurrentSaleFragment fragment = new CurrentSaleFragment();
Bundle arguments = new Bundle();
arguments.putInt(LAYOUT_RES_ID, R.layout.fragment_quick_sale );
fragment.setArguments(arguments);
return fragment;
}
#Override
protected void init() {
super.init();
lv = (ListView)view.findViewById(R.id.lv_sale);
}
#OnClick(R.id.btn_sell)
public void addToSale(View view){
mPresenter.moveToFragment(SellProductFragment.newInstance());
}
#Override
public void onAttach(Context context) {
super.onAttach(context);
salesPresenterOps = (MainMVP.SalesPresenterOps)context;
}
#Override
public void onDetach() {
salesPresenterOps = null;
super.onDetach();
}
}
The BaseFragment from which this fragmend extends :
public class BaseFragment extends Fragment implements MainMVP.RequiredViewOps, View.OnClickListener,
LoaderRequiredOps{
protected View view;
protected MainMVP.PresenterOps mPresenter;
protected final static String LAYOUT_RES_ID = "layout_res_id";
#Override
public void showOperationResult(String message, final long rowId) {
Snackbar.make(view, message, Snackbar.LENGTH_LONG).setAction(
R.string.see, new View.OnClickListener() {
#Override
public void onClick(View v) {
onOperationResultClick(rowId);
}
}
).show();
}
#Override
public void showSnackBar(String msg) {
Snackbar.make(view, msg, Snackbar.LENGTH_SHORT).show();
}
#Override
public void showAlert(String msg) {}
protected void onOperationResultClick(long rowId){}
#Override
public void onAttach(Context context) {
super.onAttach(context);
mPresenter = (MainMVP.PresenterOps)context;
}
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
this.view = inflater.inflate(getArguments().getInt(LAYOUT_RES_ID), null);
init();
return view;
}
protected void addToClickListener(View ... params){
for (View v : params){
v.setOnClickListener(this);
}
}
protected void init() {
if (view != null){
ButterKnife.bind(this, view);
}
}
#Override
public void onDetach() {
mPresenter = null;
Log.d(getClass().getSimpleName(), "Fragment was detached");
super.onDetach();
}
#Override
public void onClick(View v) {}
#Override
public void onPreLoad() {
Dialogs.buildLoadingDialog(getContext(), "Loading...").show();
}
#Override
public void onLoad() {}
#Override
public void onDoneLoading() {
Dialogs.dismiss();
}
}
When I enter the method 'moveToFragment()' I just replace CurrentSaleFragment for a new Fragment:
protected void addFragment(BaseFragment fragment){
mView = fragment;
getSupportFragmentManager().beginTransaction().replace(R.id.fragment_holder,
fragment, null).addToBackStack(null).commit();
}
Then the new fragment is attached:
public class SellProductFragment extends BaseFragment{
private ListView listView;
private ProductListAdapter adapter;
private MainMVP.SalesPresenterOps mSalesPresenter;
public static SellProductFragment newInstance(){
SellProductFragment fragment = new SellProductFragment();
Bundle arguments = new Bundle();
arguments.putInt(LAYOUT_RES_ID, R.layout.fragment_inventory);
fragment.setArguments(arguments);
return fragment;
}
private void reload(){
final Loader loader = new Loader(this);
loader.execute();
}
#Override
public void onAttach(Context context) {
super.onAttach(context);
mSalesPresenter = (MainMVP.SalesPresenterOps)context;
}
#Override
protected void init() {
super.init();
listView = (ListView)view.findViewById(R.id.lv_inventory);
reload();
FloatingActionButton button = (FloatingActionButton)view.findViewById(R.id.btn_add);
addToClickListener(button);
}
#Override
public void onLoad() {
adapter = new ProductListAdapter(getActivity().getApplicationContext(), R.layout.row_product_item,
mSalesPresenter.getProducts());
try{
updateListView();
}catch (Exception e){
Log.w(getClass().getSimpleName(), e.getMessage());
}
}
private void updateListView(){
if (adapter != null && listView != null){
listView.setAdapter(adapter);
}else{
throw new RuntimeException();
}
}
}
See that This fragment also extends from BaseFragment and implements LoaderRequiredOps. The interface is used to 'load' any data. It adds a dialog and updated the adapter when the loading is done:
public class Loader extends AsyncTask<Void, Void, Void> {
private LoaderRequiredOps presenter;
public Loader(LoaderRequiredOps presenter){
this.presenter = presenter;
}
#Override
protected void onPreExecute() {
super.onPreExecute();
presenter.onPreLoad();
}
#Override
protected Void doInBackground(Void... params) {
presenter.onLoad();
return null;
}
#Override
protected void onPostExecute(Void aVoid) {
super.onPostExecute(aVoid);
presenter.onDoneLoading();
presenter = null;
}
}
Now, when I try to execute the method reload() from the SellProductFragment i get the 'Only the original thread that created a view hierarchy can touch its views.'
This does not happen if the SellProductFragment is attached first instead of CurrentSaleFragment.
What is happening here?
Your Async Loader class calls the presenters method onLoad() from a background thread during doInBackground().
My guess is that in the onLoad() method of the presenter, a view is referenced.
In order to change the view at this point, post the view logic as a Runnable to the UI thread (you said your presenter is the activity, so this should be possible from the onLoad method).
#Override
public void onLoad() {
runOnUiThread(new Runnable() {
#Override
public void run() {
// Your ui code here...
}
});
// Rest of your code here...
}
For an unknown reason, an unidentified configuration allows to execute the setting of an adapter for a ListView on the doInBackground() method.
Moved it to onPostExecute() and now it's working

Fragment able to start task which is not interrupted during configuration changes

Sorry for my English
This is not question. I post it because this is may be helpful for someone. My solution based on the solution which was placed here several months ago, but later its author removed it. But there were several bugs in his solution, and I tried to fix them.
My solution works fine even if you'll open another app while there is a long-executed task executed in your app, then change screen orientation in another app, and return to your app after task will be finished.
If you'll find a bug in my solution, please let me know.
Have a look:
public interface AbleToStartTask {
void startTask(int requestCode, TaskNotBeInterruptedDuringConfigurationChange task);
void startTask(int requestCode, TaskNotBeInterruptedDuringConfigurationChange task, Integer titleId);
}
public interface TaskResultReceiver {
void onTaskResult(int requestCode, int resultCode, Serializable result);
}
public abstract class TaskNotBeInterruptedDuringConfigurationChange extends AsyncTask<Void, Void, Serializable> {
private TaskFragment fragment;
final void setFragment(TaskFragment fragment) {
this.fragment = fragment;
}
#Override
protected final void onPostExecute(Serializable result) {
if (fragment != null) {
fragment.onTaskFinished(result);
}
}
}
public class TaskFragment extends android.support.v4.app.DialogFragment {
public static final Integer NO_TITLE_ID = null;
private boolean isNeedToReturnResult = false;
private boolean isResultReturned = false;
private TaskNotBeInterruptedDuringConfigurationChange task;
private int resultCode = Activity.RESULT_CANCELED;
private Serializable result = null;
private Integer titleId;
public void setTask(TaskNotBeInterruptedDuringConfigurationChange task) {
task.setFragment(this);
this.task = task;
}
public void setTitle(Integer titleId) {
this.titleId = titleId;
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRetainInstance(true);
if (savedInstanceState == null && task != null) {
task.execute();
}
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
initDialog();
return inflater.inflate(R.layout.fragment_task, container);
}
private void initDialog() {
if (titleId == NO_TITLE_ID) {
getDialog().getWindow().requestFeature(Window.FEATURE_NO_TITLE);
getDialog().getWindow().setBackgroundDrawableResource(android.R.color.transparent);
} else {
getDialog().setTitle(titleId);
}
getDialog().setCanceledOnTouchOutside(false);
}
#Override
public void onDestroyView() {
if ((getDialog() != null) && getRetainInstance()) {
getDialog().setDismissMessage(null);
}
super.onDestroyView();
}
#Override
public void onDismiss(DialogInterface dialog) {
super.onDismiss(dialog);
if (task != null) {
task.cancel(false);
}
tryReturnResult();
}
private void tryReturnResult() {
task = null;
if (isResultReturned) {
return;
}
if (getTargetFragment() != null) {
returnResult();
}
}
private void returnResult() {
isResultReturned = true;
FragmentAbleToStartTask ableToStartTask = (FragmentAbleToStartTask) getTargetFragment();
if (ableToStartTask.isAbleToReceiveResult()) {
ableToStartTask.onResult(FragmentAbleToStartTask.TASK_FRAGMENT_TARGET, resultCode, result);
isNeedToReturnResult = false;
finishFragment();
} else {
isNeedToReturnResult = true;
}
}
private void finishFragment() {
if (isResumed()) {
dismiss();
}
}
#Override
public void onResume() {
super.onResume();
if (task == null) {
dismiss();
}
}
public void onTaskFinished(Serializable result) {
setResult(Activity.RESULT_OK, result);
tryReturnResult();
}
private void setResult(int resultCode, Serializable result) {
this.resultCode = resultCode;
this.result = result;
}
public void getResultIfItReturnedDuringPause() {
if (isNeedToReturnResult) {
returnResult();
}
}
}
public abstract class FragmentAbleToStartTask extends android.support.v4.app.Fragment
implements AbleToStartTask, TaskResultReceiver {
public static final int TASK_FRAGMENT_TARGET = 123;
protected static final String TAG_TASK_FRAGMENT = FragmentAbleToStartTask.class.getName() + "TAG_TASK_FRAGMENT";
private static final String KEY_TASK_REQUEST_CODE = "KEY_TASK_REQUEST_CODE";
private FragmentManager fragmentManager;
private int taskRequestCode;
#Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putInt(KEY_TASK_REQUEST_CODE, taskRequestCode);
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (savedInstanceState != null) {
restoreState(savedInstanceState);
}
fragmentManager = getFragmentManager();
TaskFragment taskFragment = getTaskFragment();
if (taskFragment != null) {
taskFragment.setTargetFragment(this, TASK_FRAGMENT_TARGET);
}
}
private void restoreState(Bundle savedInstanceState) {
taskRequestCode = savedInstanceState.getInt(KEY_TASK_REQUEST_CODE);
}
private TaskFragment getTaskFragment() {
return (TaskFragment) fragmentManager.findFragmentByTag(TAG_TASK_FRAGMENT);
}
public void startTask(int requestCode, TaskNotBeInterruptedDuringConfigurationChange task) {
startTask(requestCode, task, TaskFragment.NO_TITLE_ID);
}
public void startTask(int requestCode, TaskNotBeInterruptedDuringConfigurationChange task, Integer title) {
this.taskRequestCode = requestCode;
TaskFragment taskFragment = new TaskFragment();
taskFragment.setTitle(title);
taskFragment.setTask(task);
taskFragment.setTargetFragment(this, TASK_FRAGMENT_TARGET);
startFragment(taskFragment);
}
private void startFragment(TaskFragment taskFragment) {
FragmentTransaction transaction = fragmentManager.beginTransaction();
Fragment previousTaskFragment = fragmentManager.findFragmentByTag(TAG_TASK_FRAGMENT);
if (previousTaskFragment != null) {
transaction.remove(previousTaskFragment);
}
transaction.add(taskFragment, TAG_TASK_FRAGMENT);
transaction.commit();
}
public final void onResult(int requestCode, int resultCode, Serializable result) {
if ((requestCode == TASK_FRAGMENT_TARGET)) {
onTaskResult(this.taskRequestCode, resultCode, result);
}
}
public abstract void onTaskResult(int requestCode, int resultCode, Serializable result);
public boolean isAbleToReceiveResult() {
return isResumed();
}
#Override
public void onResume() {
super.onResume();
TaskFragment taskFragment = getTaskFragment();
if (taskFragment != null) {
taskFragment.getResultIfItReturnedDuringPause();
}
}
}
So each inheritor of FragmentAbleToStartTask must implement onTaskResult(requestCode, resultCode, result). In onTaskResult() - requestCode is value that was passed to startTask(requestCode, ...). And note, that this fragment is able to execute only one task at the same time.
Example of TaskNotBeInterruptedDuringConfigurationChange's subclass:
public class SomeLongTask extends TaskNotBeInterruptedDuringConfigurationChange {
private final Context context;
private final Worker worker;
public SomeLongTask (Worker worker, Context context) {
this.worker= worker;
this.context = context;
}
#Override
protected Serializable doInBackground(Void... voids) {
return (Serializable) worker.work(context);
}
}
And example of FragmentAbleToStartTask's subclass:
public class NameListFragment extends FragmentAbleToStartTask {
private static final int TASK_REQUEST_CODE_RECEIVING_NAMES = 461;
private static final String KEY_PATTERN = "KEY_PATTERN";
private static final String KEY_NAMES = "KEY_NAMES";
private List<String> names;
private ListView listView;
public static Fragment newInstance(String patternToSearchNames) {
Fragment fragment = new NameListFragment();
Bundle args = new Bundle();
args.putString(KEY_PATTERN, patternToSearchNames);
fragment.setArguments(args);
return fragment;
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View root = inflater.inflate(R.layout.example_list_fragment, null);
listView = (ListView) root.findViewById(R.id.listView);
return root;
}
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
if (savedInstanceState == null) {
startTaskReceivingNames();
} else {
restoreState(savedInstanceState);
}
}
// !!! Key Point
private void startTaskReceivingNames() {
String pattern = getArguments().getString(KEY_PATTERN);
TaskNotBeInterruptedDuringConfigurationChange task = new NamesReceiverTask(getActivity(), pattern);
startTask(TASK_REQUEST_CODE_RECEIVING_NAMES, task);
}
private void restoreState(Bundle savedInstanceState) {
names = (List<String>) savedInstanceState.getString(KEY_NAMES);
if (names != null) {
initList();
}
}
private void initList() {
setEmptyListView();
ArrayList<String> adapter = new ArrayList<String>(getActivity(), android.R.layout.simple_list_item_1, names);
listView.setAdapter(adapter);
}
private void setEmptyListView() {
View emptyListView = getView().findViewById(R.id.emptyListView);
listView.setEmptyView(emptyListView);
}
#Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putSerializable(KEY_NAMES, (Serializable) names);
}
// !!! Key Point
#Override
public void onTaskResult(int requestCode, int resultCode, Serializable result) {
if (requestCode == TASK_REQUEST_CODE_RECEIVING_NAMES) {
onReceivingNamesFinished(resultCode, result);
}
}
private void onReceivingNamesFinished(int resultCode, Serializable result) {
if (resultCode != Activity.RESULT_OK) {
getActivity().finish();
return;
}
this.names = (List<String>) result;
initList();
}
}

Call method of interface implements with child fragment From container activity Android

I'm stuck with communication between activity and fragment using interface. I have created activity with child fragment. I wanna do some stuff with continuous thread defined in activity and during that thread when I'm getting some result at that time I wanna trigger to child fragment to do something.
My Container Activity
public class MySpaceActivity extends BaseDrawerActivity {
private OnSetLastSeenListener mListner;
public static Thread mThread = null;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
super.setHeaders(Const.MY_SPACE);
super.setSubmenus(Const.MY_SPACE,
Utils.getSubmenuList(Const.MY_SPACE, MySpaceActivity.this),
submenuBean);
// super.attachFragment(submenuBean);
}
#Override
public void setHeaderSubMenu(SubmenuBean subMenuBean) {
// txt_submenu.setText(subMenuBean.getSubmenu_name());
this.submenuBean = subMenuBean;
Log.print("::::: setHeaderSubMenu ::::");
super.attachFragment(submenuBean);
}
public void setsubFragment(SubmenuBean subMenuBean) {
this.submenuBean = subMenuBean;
super.attachSubFragment(submenuBean);
}
#Override
public void onBackPressed() {
super.onBackPressed();
popLastFragment();
}
private void popLastFragment() {
if (super.getNumberOfChilds() > 1) {
super.popSubFragment();
} else {
finish();
}
}
#Override
protected Fragment getFragement() {
StudentsFragment fragment = new StudentsFragment(Const.MY_SPACE,
getSubmenubean());
return fragment;
}
public SubmenuBean getSubmenubean() {
return submenuBean;
}
#Override
protected void onResume() {
// TODO Auto-generated method stub
super.onResume();
mThread = new Thread(new CountDownTimer(MySpaceActivity.this));
mThread.start();
}
#Override
protected void onStop() {
// TODO Auto-generated method stub
super.onStop();
if (mThread.isAlive()) {
mThread.interrupt();
mThread = null;
}
}
public void updateLastSeen(){
Log.print("::::::Call Interface::::::");
mListner.updateLastSeen();
}
class CountDownTimer implements Runnable {
private Context mContext;
private JSONObject mJsonObject;
private JSONArray mJsonArray;
public CountDownTimer(Context mContext) {
this.mContext = mContext;
}
// #Override
public void run() {
while (!Thread.currentThread().isInterrupted()) {
try {
HttpChatLastSeen mChat = new HttpChatLastSeen();
mJsonObject = mChat.Http_ChatLastSeen(mContext);
String mResult = mJsonObject.getString("Result");
if (mResult.equalsIgnoreCase(String
.valueOf(Const.RESULT_OK))) {
mJsonArray = mJsonObject.getJSONArray("UserData");
for (int i = 0; i < mJsonArray.length(); i++) {
mJsonObject = mJsonArray.getJSONObject(i);
new DbStudentMasterBll(mContext).update(
"last_seen", mJsonObject
.getString("LastSeen"), Integer
.parseInt(mJsonObject
.getString("UserId")));
}
} else {
Log.print("MY LAST SEEN Response : "
+ mJsonObject.toString());
}
updateLastSeen();
Thread.sleep(15000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} catch (Exception e) {
Log.print("ChatLastSeenThread : ", e.getMessage());
}
}
}
}
}
My Child Fragment With Interface :
public class StudentsFragment extends Fragment implements OnSetLastSeenListener{
TextView txt_submenu;
ListView list_students;
SubmenuBean submenuBean;
int Mainmenu;
MySpaceActivity mMySpaceActivity;
ArrayList<DbStudentMasterBean> studentsList;
StudentsAdapter mAdapter = null;
OnSetLastSeenListener mListner;
public StudentsFragment() {
super();
}
public StudentsFragment(int Mainmenu, SubmenuBean submenuBean) {
this.submenuBean = submenuBean;
this.Mainmenu = Mainmenu;
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_students, container,
false);
mMySpaceActivity = (MySpaceActivity) getActivity();
txt_submenu = (TextView) view.findViewById(R.id.txt_submenu);
txt_submenu.setText(submenuBean.getSubmenu_name());
txt_submenu.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
mMySpaceActivity.openDrawer();
}
});
list_students = (ListView) view.findViewById(R.id.list_colleagues);
studentsList = new DbStudentMasterBll(getActivity()).getAllRecords();
mAdapter = new StudentsAdapter(getActivity(), studentsList, handler);
list_students.setAdapter(mAdapter);
list_students.setOnItemClickListener(new OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view,
int position, long id) {
DbStudentMasterBean bean = (DbStudentMasterBean) parent
.getAdapter().getItem(position);
Message msg = new Message();
msg.what = CHAT;
msg.obj = bean;
handler.sendMessage(msg);
}
});
return view;
}
Handler handler = new Handler() {
public void handleMessage(android.os.Message msg) {
switch (msg.what) {
case CHAT:
submenuBean.setTag(VIEWCHATSTUDENT);
DbStudentMasterBean bean = (DbStudentMasterBean) msg.obj;
mMySpaceActivity.setsubFragment(submenuBean);
break;
}
};
};
#Override
public void updateLastSeen() {
// TODO Auto-generated method stub
Log.print("!!!!!!!!!Refresh Adapter!!!!!!!!!!!");
mAdapter.notifyDataSetChanged();
}
}
My Interface :
public interface OnSetLastSeenListener {
public void updateLastSeen();
}
So I have implemented interface OnSetLastSeenListener with my child fragment StudentsFragment . Now I'm calling method of tht interface updateLastSeen() from my container activity with thread. But it is not getting trigger to child fragment where I have implemented interface. So I don't know whether it is good way to communicate or not? Let me take your help to suggest on this solution or best way to communicate from child fragment to parent activity.
Thanks,
It is better to use interface when you want to communicate something from Fragment to Activity and not vice versa.
In your case, you can directly call the method in Fragment from Activity through fragment object. No need to use interface.
Something like this (For static fragments)
StudentsFragment fragment = (StudentsFragment) getFragmentManager()
.findFragmentById(R.id.fragmentid);
if (fragment != null && fragment.isInLayout()) {
fragment.updateLastSeen();
}
For dynamic fragment you can use the fragment object directly.

Android AsyncLoader: Share objects between fragmentactivity/fragment

I have a problem regarding with FragmentActivity and mutltiple Fragments inside a ViewPager.
In the FragmentActivity an object is loaded, with a AsyncTask which is used in all the other fragments. I have used the android:configChanges="orientation|keyboardHidden|keyboard" "hack" to make sure the object is only loaded once, even during a screen rotation.
However, now I would to like to display more infromation in landscape modus in one of the Fragments, so now that hack doesn't work.
I've tried implementing a AsyncLoader and the FragmentRetainInstanceSupport from the Android samples. But none of the things work:
1 - I can't get the FragmentRetainInstanceSupport get to work within the ViewPager, when I follow the sample code the onCreate() method isn't called in the worker-fragment
2 - The AsyncLoader crashes during a screen rotation...
Here is my code in which I (tried to) implement the AsyncLoader:
public class TeamActivity extends SherlockFragmentActivity implements LoaderManager.LoaderCallbacks<Response<Team>> {
ViewPager mPager;
PageIndicator mIndicator;
FragmentPagerAdapter mAdapter;
private final int MENU_FOLLOW = Menu.FIRST;
private final int MENU_UNFOLLOW = Menu.FIRST + 1;
Team team = null;
static int team_id;
public Team getTeam(){
return team;
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
team_id = this.getIntent().getIntExtra("index", 0);
Log.d("Teamid",""+team_id);
getSupportLoaderManager().initLoader(0, null, this);//.forceLoad();
//getSupportLoaderManager().getLoader(0).startLoading();
//new getTeam().execute();
}
#Override
public boolean onPrepareOptionsMenu(Menu menu) {
if(team != null) {
team.getNaam();
SharedPreferences keyValues = this.getSharedPreferences("teams_follow", Context.MODE_PRIVATE);
MenuItem menuItem_volg = menu.findItem(MENU_FOLLOW);
MenuItem menuItem_delete = menu.findItem(MENU_UNFOLLOW);
if(keyValues.contains(String.valueOf(team.getStartnummer()))) {
menuItem_volg.setVisible(false);
menuItem_delete.setVisible(true);
} else {
menuItem_volg.setVisible(true);
menuItem_delete.setVisible(false);
}
}
return super.onPrepareOptionsMenu(menu);
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
menu.add(0,MENU_UNFOLLOW,Menu.NONE, R.string.ab_verwijderen)
.setIcon(R.drawable.ic_action_delete)
.setVisible(false)
.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
menu.add(0,MENU_FOLLOW,Menu.NONE, R.string.ab_volgen)
.setIcon(R.drawable.ic_action_star)
.setVisible(false)
.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
return super.onCreateOptionsMenu(menu);
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
Utils.goHome(getApplicationContext());
break;
case MENU_FOLLOW:
Utils.addFavoTeam(getApplicationContext(), team);
invalidateOptionsMenu();
break;
case MENU_UNFOLLOW:
Utils.removeFavoteam(getApplicationContext(), team.getID());
invalidateOptionsMenu();
break;
}
return super.onOptionsItemSelected(item);
}
class TeamFragmentAdapter extends FragmentPagerAdapter implements TitleProvider {
ArrayList<Fragment> fragments = new ArrayList<Fragment>();
ArrayList<String> titels = new ArrayList<String>();
public TeamFragmentAdapter(FragmentManager fm) {
super(fm);
fragments.add(new TeamInformatieFragment());
titels.add("Informatie");
fragments.add(new TeamLooptijdenFragment());
titels.add("Routetijden");
}
#Override
public Fragment getItem(int position) {
return fragments.get(position);
}
#Override
public int getCount() {
return fragments.size();
}
#Override
public String getTitle(int position) {
return titels.get(position);
}
}
private class getTeam extends AsyncTask<Void, Void, Void> {
private ProgressDialog progressDialog;
Response<Team> response;
protected void onPreExecute() {
progressDialog = ProgressDialog.show(TeamActivity.this,
"Bezig met laden", "Team wordt opgehaald...", true);
progressDialog.setCancelable(true);
progressDialog.setOnCancelListener(new OnCancelListener() {
public void onCancel(DialogInterface dialog) {
cancel(true);
Utils.goHome(TeamActivity.this);
}
});
}
#Override
protected Void doInBackground(Void... arg0) {
if(!isCancelled())
response = api.getTeamByID(team_id);
return null;
}
#Override
protected void onPostExecute(Void result) {
if(Utils.checkResponse(TeamActivity.this, response)) {
setContentView(R.layout.simple_tabs);
team = response.getResponse();
mAdapter = new TeamFragmentAdapter(getSupportFragmentManager());
mPager = (ViewPager)findViewById(R.id.pager);
mPager.setAdapter(mAdapter);
mIndicator = (TabPageIndicator)findViewById(R.id.indicator);
mIndicator.setViewPager(mPager);
invalidateOptionsMenu();
progressDialog.dismiss();
}
}
}
public static class AppListLoader extends AsyncTaskLoader<Response<Team>> {
Response<Team> response;
public AppListLoader(Context context) {
super(context);
}
#Override public Response<Team> loadInBackground() {
response = api.getTeamByID(team_id);
return response;
}
#Override public void deliverResult(Response<Team> response) {
if (isReset()) {
return;
}
this.response = response;
super.deliverResult(response);
}
#Override protected void onStartLoading() {
if (response != null) {
deliverResult(response);
}
if (takeContentChanged() || response == null) {
forceLoad();
}
}
#Override
protected void onStopLoading() {
// Attempt to cancel the current load task if possible.
cancelLoad();
}
#Override
protected void onReset() {
super.onReset();
// Ensure the loader is stopped
onStopLoading();
response = null;
}
}
private ProgressDialog progressDialog;
#Override
public Loader<Response<Team>> onCreateLoader(int arg0, Bundle arg1) {
progressDialog = ProgressDialog.show(TeamActivity.this,
"Bezig met laden", "Team wordt opgehaald...", true);
progressDialog.setCancelable(true);
progressDialog.setOnCancelListener(new OnCancelListener() {
public void onCancel(DialogInterface dialog) {
finish();
}
});
return new AppListLoader(this);
}
#Override
public void onLoadFinished(Loader<Response<Team>> loader, Response<Team> response) {
//Log.d("Loader", "Klaar");
if(Utils.checkResponse(TeamActivity.this, response)) {
team = response.getResponse();
setContentView(R.layout.simple_tabs);
mAdapter = new TeamFragmentAdapter(getSupportFragmentManager());
mPager = (ViewPager)findViewById(R.id.pager);
mPager.setAdapter(mAdapter);
mIndicator = (TabPageIndicator)findViewById(R.id.indicator);
mIndicator.setViewPager(mPager);
invalidateOptionsMenu();
progressDialog.dismiss();
}
}
#Override
public void onLoaderReset(Loader<Response<Team>> arg0) {
//Utils.goHome(this);
}
}
Fragment (example):
public class TeamInformatieFragment extends SherlockFragment {
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Team team = ((TeamActivity)this.getActivity()).getTeam();
//ERROR ON NEXT LINE AFTER SCREEN ROTATION:
getSherlockActivity().getSupportActionBar().setTitle(team.getNaam());
View view = inflater.inflate(R.layout.team_informatie, container, false);
return view;
}
}
The method is called from the fragments (with getActivity().getTeam()) but after a screen rotation getTeam() returns null;
I think the fragments are calling getTeam() too fast, before the variable team has been initialized(?)
Can you please help me?
Thank you!
This is probably not what you want to hear, but I recommend getting rid of
android:configChanges="orientation|keyboardHidden|keyboard"
It's an ugly hack, and a lot of the newer SDK elements like Loaders will break if you don't handle configuration changes correctly. Let Android handle the config changes, and design your code around that.

Categories

Resources