I am facing strange issue during call of my activity with Fragment usage. I am getting error like,
java.lang.IllegalStateException: Fragment ScoreFragment{ee2b833
id=0x7f0e0198} not attached to Activity
On line 146. My Fragment code which have error is line like below
if(mPageFlag.equalsIgnoreCase(getString(R.string.winners))){
And My full code for same is below,
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (getArguments() != null) {
mPageFlag = getArguments().getString(ARG_PAGE_FLAG);
}
}
#Override
public void setUserVisibleHint(boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
if(isVisibleToUser && !mIsPageLoaded){
mContext = getActivity();
mIsPageLoaded = true;
if(mPageFlag.equalsIgnoreCase(getString(R.string.winners))){
new getcontestscorewinners(mContext).execute();
}else{ //
new getcontestscorewinnersNew(mContext).execute();
}
}
}
public class getcontestscorewinners extends AsyncTask<String, Void, String> {
boolean response = false;
private Context mContext;
public getcontestscorewinners(Context context) {
mContext = context;
}
#Override
protected void onPreExecute() {
progress = ProgressDialog.show(mContext, "Processing...",
"Please wait....");
}
#Override
protected String doInBackground(String... params) {
NetworkTask.getContestScoreWinners(winnerHandler);
return "";
}
#Override
protected void onPostExecute(String result) {
}
#Override
protected void onProgressUpdate(Void... values) {
}
}
Let me know if someone can help me for get out of it. Thanks.
Try using isAdded():
Return true if the fragment is currently added to its activity.
So your code should be like this :
if(isAdded() && mPageFlag.equalsIgnoreCase(getString(R.string.winners))){
Related
I have a working AsyncTask to get data from a server and display it in a TextView. But is it possible to output the data into a TextView which is located in a fragment? So let's say, the AsyncTask is loaded in the MainActivity and the output will be in a fragment.
This is my AsyncTask:
private static class FtpDownload extends AsyncTask<String, String, String> {
private WeakReference<GuidanceActivity> activityWeakReference;
FtpDownload(GuidanceActivity activity) {
activityWeakReference = new WeakReference<>(activity);
}
#Override
protected void onPreExecute() {
super.onPreExecute();
}
#Override
protected String doInBackground(String... FTPconnection) {
GuidanceActivity activity = activityWeakReference.get();
if (activity == null || activity.isFinishing()) {
return null;
}
try {
FTPClient ftpClient = new FTPClient();
ftpClient.connect("", 21);
System.out.println(ftpClient.getReplyString());
ftpClient.enterLocalPassiveMode();
ftpClient.login("anonymous", "");
ftpClient.changeWorkingDirectory("/");
InputStream inStream = ftpClient.retrieveFileStream(".html");
activity.contents = IOUtils.toString(inStream, StandardCharsets.UTF_8);
System.out.println(activity.contents);
ftpClient.disconnect();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
#Override
protected void onPostExecute(String output) {
GuidanceActivity activity = activityWeakReference.get();
if (activity == null || activity.isFinishing()) {
return;
}
TextView textView = activity.findViewById(R.id.text_view);
textView.setText(Html.fromHtml(activity.contents));
}
}
In your fragment:
public class MyFragment extends Fragment {
TextView myTextView;
#Nullable
#Override
public View onCreateView(#NonNull LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
super.onCreateView(inflater, container, savedInstanceState);
View view = View root = inflater.inflate(R.layout.my_frag_layout, null);
myTextView = (TextView) view.findViewById(R.id.myTextView);
//rest of your implementation of onCreateView
return view;
}
//this will be called from activity
public void updateTextView(String text) {
myTextView.setText(text);
}
}
in your activity:
public class MyActivity extends AppCompatActivity {
private final String FRAGMENT_TAG = "myFragment";
private static class FtpDownload extends AsyncTask<String, String, String> {
//rest of your FtpDownload class
#Override
protected void onPostExecute(String output) {
GuidanceActivity activity = activityWeakReference.get();
if (activity == null || activity.isFinishing()) {
return;
}
MyFragment fragment = (MyFragment) getSupportFragmentManager().findFragmentByTag(FRAGMENT_TAG );
/* or
MyFragment fragment = (MyFragment) getSupportFragmentManager().findFragmentById(R.id.fragment );
if your fragment is in an xml layout */
fragment.updateTextView(Html.fromHtml(activity.contents));
}
}
}
Yes, it is possible. This is a sample for WiFi scanning from MainActivity and the results are show at fragment. I believe you can understand the logic and convert it for your project
MainActivity:
public class MainActivity extends AppCompatActivity implements FragmentDiscoverWiFi.ScanWiFi;
//public variable
FragmentDiscoverWiFi fragmentDiscoverWiFi;
//This will be called from your fragment
#Override
public void onScanWiFi(FragmentDiscoverWiFi fragment) {
fragmentDiscoverWiFi = fragment;
}
//use something like this when your want to update fragment
fragmentDiscoverWiFi.onScanWiFiComplete();
Fragment:
public class FragmentDiscoverWiFi extends Fragment {
private Context mContext;
public interface ScanWiFi {
public void onScanWiFi(FragmentDiscoverWiFi fragment);
}
public FragmentDiscoverWiFi() {
// Required empty public constructor
}
#Override
public void onAttach(Context context) {
mContext = context;
super.onAttach(context);
}
public void onScanWiFiComplete() {
if (!isDetached()) {
//Access your data from MainActivity like this:
Log.d(TAG, "Total APs:" + ((MainActivity) mContext).wifiScanResults.size());
}
}
#Override
public void onResume() {
super.onResume();
if (!getUserVisibleHint()) {
return;
}
pullToRefreshText = rootView.findViewById(R.id.pullToRefreshText);
pullToRefresh = rootView.findViewById(R.id.pullToRefresh);
pullToRefresh.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
#Override
public void onRefresh() {
Log.d(TAG, "refreshing...");
pullToRefresh.setRefreshing(true);
((MainActivity)mContext).onScanWiFi(FragmentDiscoverWiFi.this);
}
});
}
}
Im stuck here 2 days. I am trying to restore scroll position after device rotation.
Im saving an arraylist in the onSaveInstance, adding the movies i have to it and trying to set the adapter.
If i switch shorting criteria and rotate the device, it scrolls up to the top and not retaining its position. Here's my code
public class MainActivity extends AppCompatActivity {
public static final String MOVIE_LIST = "instanceMovieList";
public static final String RECYCLER_VIEW_STATE_KEY = "RECYCLER_VIEW_STATE_KEY";
private static final String LOG_TAG = MainActivity.class.getSimpleName();
Context mContext;
Toolbar mToolBar;
#BindView(R.id.prefSpinnner)
Spinner prefSpinner;
#BindView(R.id.noDataTv)
TextView noDataTv;
#BindView(R.id.rv_movies)
RecyclerView mMoviesRV;
private AppDatabase mDb;
private String userOption = "Most Popular";
private ArrayList<Movie> mMoviesList = new ArrayList<>();
private MovieRecycleViewAdapter movieRecycleViewAdapter;
private RecyclerView.LayoutManager mLayoutManager;
private Parcelable mListState;
#Override
protected void onSaveInstanceState(Bundle outState) {
if (mMoviesList != null) {
outState.putParcelableArrayList(MOVIE_LIST, mMoviesList);
mListState = mLayoutManager.onSaveInstanceState();
outState.putParcelable(RECYCLER_VIEW_STATE_KEY, mListState);
}
super.onSaveInstanceState(outState);
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ButterKnife.bind(this);
GridLayoutManager gridLayoutManager = new GridLayoutManager(this, 2);
mMoviesRV.setLayoutManager(gridLayoutManager);
mMoviesRV.setHasFixedSize(true);
movieRecycleViewAdapter = new MovieRecycleViewAdapter(MainActivity.this, mMoviesList);
mMoviesRV.setAdapter(movieRecycleViewAdapter);
mLayoutManager = mMoviesRV.getLayoutManager();
if (savedInstanceState != null && savedInstanceState.containsKey(MOVIE_LIST)) {
ArrayList<Movie> savedMovieList = savedInstanceState.getParcelableArrayList(MOVIE_LIST);
Log.d(LOG_TAG, "getting movies from instance ");
mMoviesList.addAll(savedMovieList);
movieRecycleViewAdapter = new MovieRecycleViewAdapter(MainActivity.this, mMoviesList);
mMoviesRV.setAdapter(movieRecycleViewAdapter);
}
mToolBar = findViewById(R.id.toolbar);
mToolBar.setTitle(getResources().getString(R.string.app_name));
ArrayAdapter<String> spinAdapter = new ArrayAdapter<String>(MainActivity.this, R.layout.pref_spinner_item_list, getResources().getStringArray(R.array.userPrefs));
spinAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
prefSpinner.setAdapter(spinAdapter);
prefSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
#Override
public void onItemSelected(AdapterView<?> adapterView, View view, int i, long l) {
userOption = prefSpinner.getSelectedItem().toString();
if (userOption.contentEquals("Most Popular") || userOption.contentEquals("Highest Rated")) {
PopulateMoviesTask newTask = new PopulateMoviesTask();
newTask.execute();
} else {
setUpViewModel();
}
}
#Override
public void onNothingSelected(AdapterView<?> adapterView) {
PopulateMoviesTask newTask = new PopulateMoviesTask();
newTask.execute();
}
});
mDb = AppDatabase.getInstance(getApplicationContext());
}
private void setUpViewModel() {
MainViewModel viewModel = ViewModelProviders.of(this).get(MainViewModel.class);
viewModel.getMovies().observe(this, new Observer<List<Movie>>() {
#Override
public void onChanged(#Nullable List<Movie> movies) {
Log.d(LOG_TAG, "updating list of movies from livedata in viewmodel");
movieRecycleViewAdapter.setMovies(movies);
}
});
}
#Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
if (savedInstanceState != null) {
mMoviesList = savedInstanceState.getParcelableArrayList(MOVIE_LIST);
mListState = savedInstanceState.getParcelable(RECYCLER_VIEW_STATE_KEY);
}
}
#Override
protected void onResume() {
super.onResume();
if (mListState != null) {
mLayoutManager.onRestoreInstanceState(mListState);
}
}
private class PopulateMoviesTask extends AsyncTask<URL, Void, String> {
#Override
protected String doInBackground(URL... urls) {
URL searchMovieObjectUrl = NetworkUtils.createUrl(userOption);
String jsonString = "";
try {
jsonString = NetworkUtils.makeHttpRequest(searchMovieObjectUrl);
} catch (IOException e) {
Log.e("Main Activity", "Problem making the HTTP request.", e);
}
return jsonString;
}
#Override
protected void onPostExecute(String jsonString) {
if (jsonString == null) {
mMoviesRV.setVisibility(View.GONE);
noDataTv.setVisibility(View.VISIBLE);
} else {
mMoviesRV.setVisibility(View.VISIBLE);
noDataTv.setVisibility(View.GONE);
mMoviesList = JsonUtils.extractFeatureFromJson(jsonString);
}
movieRecycleViewAdapter = new MovieRecycleViewAdapter(MainActivity.this, mMoviesList);
mMoviesRV.setAdapter(movieRecycleViewAdapter);
}
}
}
Use this
private Parcelable recyclerViewState;// global
onResume
#Override
public void onResume() {
super.onResume();
mMoviesRV.getLayoutManager().onRestoreInstanceState(recyclerViewState);
}
onPasue
#Override
public void onPause() {
super.onPause();
recyclerViewState= mMoviesRV.getLayoutManager().onSaveInstanceState();
}
Since i found out what was going on, i think i should post an answer.
#Override protected void onSaveInstanceState(Bundle outState) {
if (mMoviesList != null) {
outState.putParcelableArrayList(MOVIE_LIST, mMoviesList);
mListState = mLayoutManager.onSaveInstanceState();
outState.putParcelable(RECYCLER_VIEW_STATE_KEY, mListState);
}
super.onSaveInstanceState(outState);
}
is enough to save the moviesList through lifecycle and then retrieve it
if (savedInstanceState != null && savedInstanceState.containsKey(MOVIE_LIST)) {
mMoviesList = savedInstanceState.getParcelableArrayList(MOVIE_LIST);
This answers the original question
But the problem was in my adapter and WHEN i was initializing it.
The adapter should only be initialized in onCreate (in my case at least) and then notify it if the Arraylist data changed more info here.
I created Activity that includes ProgressDialog and its methods.
public class ProgressActivity extends AppCompatActivity implements Thread.UncaughtExceptionHandler {
public final ProgressDialog progressDialog;
public void dismissPD() {
if (progressDialog != null && progressDialog.isShowing()) {
progressDialog.dismiss();
}
}
public void setPDMessage(String msg) {
progressDialog.setMessage(msg);
}
public void showPD(String msg) {
ProgressDialog.show(this, "", msg);
}
#Override
public void uncaughtException(Thread thread, Throwable ex) {
ex.printStackTrace();
}
public ProgressActivity() {
progressDialog = new ProgressDialog(this);
}
#Override
protected void onDestroy() {
super.onDestroy();
dismissPD();
}
}
Now I extend my new activity from it, but I get NullpointerException inside constructur on progressDialog = new ProgressDialog(this)
As I see, I have a wrong context when constructor runs. Is there a right approach to realize it in my way?
I solved the problem by overriding oncreate mothod of the main Activity:
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
progressDialog = new ProgressDialog(this);
}
I am reading about how to interact between UI and background thread here.
This article has following note:
The AsyncTask does not handle configuration changes automatically,
i.e. if the activity is recreated. The programmer has to handle that
in his coding. A common solution to this is to declare the AsyncTask
in a retained headless fragment.
I dont understand what is retained headless fragment.
For example, in this way I can add fragment:
FragmentManager manager = getSupportFragmentManager();
FragmentTransaction transaction = manager.beginTransaction();
transaction.add(R.id.frame, new MyFragment());
transaction.commit();
And in fragment I can execute AsyncTask like this:
MyTask myTask = new MyTask();
String[] args = {"Hello"};
myTask.execute(args);
Is this called "to declare the AsyncTask in a retained headless fragment"?
Headless fragment is nothing but a fragment which does not have a view. In onCreate() of the fragment lifeCycle, use setRetainInstance(true);. This will not destroy the fragment even if the activity recreates. So if an AsyncTask is running in fragment, on recreation of the activity, you wont lose the AsyncTask.
In onCreate of the activity, you have to add the fragment with a tag. Before adding, check if the fragment exist using getFragmentManager().findFragmentByTag(TAG), if the fragment is null then create a new instance of the fragment and add it.
In Fragment there will not be any view inflated, so no need to override onCreateView().
An example of headlessFragment :
public class HeadlessProgressFragment extends Fragment {
private ProgressListener mProgressListener;
private AsyncTask<Void, Integer, Void> mProgressTask;
public interface ProgressListener {
void updateProgress(int progress);
}
#Override
public void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRetainInstance(true);
}
public void setProgressListener(Context context) {
mProgressListener = (ProgressListener) context;
}
public void startProgress(final int size) {
if (mProgressTask == null || mProgressTask.getStatus() != AsyncTask.Status.RUNNING || mProgressTask.getStatus() == AsyncTask.Status.FINISHED) {
mProgressTask = new AsyncTask<Void, Integer, Void>() {
#Override
protected Void doInBackground(Void... params) {
for (int index = 0; index < size; index++) {
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
publishProgress(index + 1);
}
}
return null;
}
#Override
protected void onProgressUpdate(Integer... values) {
super.onProgressUpdate(values);
if (mProgressListener != null) {
mProgressListener.updateProgress(values[0]);
}
}
};
mProgressTask.execute();
}
}
}
In Activity Something like this :
public class MainActivity extends FragmentActivity implements HeadlessProgressFragment.ProgressListener {
private static final String TAG = "progress_fragment";
private ProgressBar mProgressBar;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.dummy_view);
mHeadlessProgressFragment = (HeadlessProgressFragment) getSupportFragmentManager().findFragmentByTag(TAG);
if (mHeadlessProgressFragment == null) {
mHeadlessProgressFragment = new HeadlessProgressFragment();
getSupportFragmentManager().beginTransaction().add(mHeadlessProgressFragment,TAG).commit();
}
mHeadlessProgressFragment.setProgressListener(this);
mProgressBar = (ProgressBar) findViewById(R.id.progress_bar);
final Button startFillBtn = (Button) findViewById(R.id.btn_start_filling);
startFillBtn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
mHeadlessProgressFragment.startProgress(100);
}
});
}
#Override
public void updateProgress(int progress) {
mProgressBar.setProgress(progress);
}
}
As i simplified the complexity in my case by Just update your UI (if you have to) by checking the calling fragment or activity is present or not. Start the asynctask by assigning the weakreference of calling entity.
Textview value is crashing with null pointer exception
Below is the code which i am using. Is there any issue in the code.
public class MainActivity extends Activity {
Button btn;
int i =0;
SharedPreferences.Editor preferences;
TextView txt;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
btn = (Button)findViewById(R.id.button1);
preferences = PreferenceManager.getDefaultSharedPreferences(MainActivity.this).edit();
txt = (TextView) findViewById(R.id.textView1);
btn.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
new LongOperation().execute("");
}
});
}
private class LongOperation extends AsyncTask<String, Void, String> {
#Override
protected String doInBackground(String... params) {
for (int i = 0; i < 5; i++) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
Thread.interrupted();
}
}
return "Executed";
}
#Override
protected void onPostExecute(String result) {
txt.setText("Executed ----------------------- ");
}
#Override
protected void onPreExecute() {}
#Override
protected void onProgressUpdate(Void... values) {}
}
}
After it is coming to onPostExecute the textview value is not setting the value.
Please help me. Thanks in advance.
Textview value is not setting because the value of id may be null. check it.