onCreate being called again when navigating away from Activity - java

Code works great. However, I am seeing something strange from an Activity Lifecycle perspective. The onCreate code basically sets the adapters to the relevant GridView and pulls items from a database and populates said GridView. Each item can be tapped by the user that will push the item's "playerId" to HubActivity.
The below is my onCreate code:
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Const.err("##> Running PlayerSearchActivity.onCreate");
setContentView(R.layout.activity_playersearch);
this.mPlayerSearchBtn=(Button) findViewById(R.id.playerSearchButton);
this.mPlayerSearchText=(TextView) findViewById(R.id.playerSearchText);
this.mPlayerSearchGridView=(GridView) findViewById(R.id.psearch_playersearchgrid);
this.mSavedPlayersGridView=(GridView) findViewById(R.id.psearch_savedplayersgrid);
mPlayerSearchBtn.setOnClickListener(this);
this.savedPlayersAdapter = new PlayerGridAdapter(this);
mSavedPlayersGridView.setAdapter(savedPlayersAdapter);
mSavedPlayersGridView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> arg0, View view, int position,long arg3) {
Intent intent = new Intent(getApplicationContext(), HubActivity.class);
intent.putExtra(Keys._PLAYERID,savedPlayersAdapter.getItemPlayerId(position));
startActivity(intent);
}
});
Const.err("<## Completed PlayerSearchActivity.onCreate");
}
I was curious about how each method was being called so I applied some logging around the functions (Const.err). The results are strange:
03-04 00:29:50.942: ##> Running PlayerSearchActivity.onCreate
03-04 00:29:50.982: <## Completed PlayerSearchActivity.onCreate
03-04 00:29:50.982: ##> Running PlayerSearchActivity.onResume
03-04 00:29:51.058: <## Completed PlayerSearchActivity.onResume
---Clicked item---
03-04 00:29:54.718: ##> Running PlayerSearchActivity.onCreate
03-04 00:29:54.730: <## Completed HubActivity.onCreate
..
Firstly, why is onCreate being called after I click an item? Also, I expected to see a "Completed PlayerSearchActivity.onCreate" message but this never occurrs.

I guess this is a copy-paste error, check your HubActivity.onCreate(), if you didn't paste by accident
Const.err("##> Running PlayerSearchActivity.onCreate");
instead of
Const.err("##> Running HubActivity.onCreate");

Related

Using buttons on android - newbie

I am a newbie to android development, trying to get buttons working. every time i use this code below, the error message "unfortunately the app has stopped". but when i remove the code the app runs but obviously the buttons do nothing. here is the code ive tried
public class MyActivity extends Activity {
/**
* Called when the activity is first created.
*/
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
Button button1 = (Button) findViewById(R.id.ExerciseButton);
button1.setOnClickListener (new View.OnClickListener(){
public void onClick(View v) {
setContentView(R.layout.exercises);
}
});
}
}
anybody able to help me out there? thanks
Don't try to load another View in the current activity. Navigate to a new ExercisesActivity.
Use:
public void onClick(View v) {
Intent intent = new Intent(ExercisesActivity.this, WcActivity.class);
startActivity(intent);
}
You can't call setContentView anymore after the view has loaded (which it obviously has to receive button clicks). Use a different approach, like showing and hiding views or using a ViewFlipper (see Calling setContentView() multiple times), using fragments (see Fragments) or starting a new activity.
Well, from your code, I see a couple of things:
I am usually familiar to using the onClickListener of the Button class if I want to use it for a button. Makes sense, doesn't it?
buttonOne.setOnClickListener(new Button.OnClickListener() {
public void onClick(View v) {
//Do stuff here
}
Second thing:
Start a new Activity (if that is what you want) by using an Intent:
Intent myIntent = new Intent(this, exercises.class);
startActivity(myIntent);
You CAN absolutaly call setContentView on any event, even after the view has loaded
I tried your code in a demo project and it is working fine. So, i think the error will be some where in your layout.(Let me know more if you need more help on this)

Returning a thread result to the Fragment using interface implementation

I have an application that shows News articles in a listview inside a fragment.
When the fragment is first created, I start a thread that will fetch the list of articles (Stories) through an API call
#Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
mContext = getActivity();
new GetStoriesThread(mContext,this).start();
Both the Fragment and the Thread implement the same Interface for passing the data from the thread to the Fragment
public interface GetStoriesThreadInterface {
public void onGetStoriesThreadResult(final ArrayList<Story> result);
}
After the Thread is done processing, it will call the interface method and pass the data back to the calling Fragment.
The problem
Now when I get the result in the fragment, through this code:
#Override
public void onGetStoriesThreadResult(final ArrayList<Story> result)
{
if(result!=null)
{
mStoriesList.clear(); //mStoriesList is the list that i supply to the adapter of the ListView
mStoriesList.addAll(result);
adapter.notifyDataSetChanged(); //Exception here
}
}
I get the following exception:
04-28 18:03:58.432: E/ViewRootImpl(21513): com.says.news.Stories : Only the original thread that created a view hierarchy can touch its views.
I know that using getActivity().runOnUiThread(new Runnable... solves the issue, but I dont understand why. And sometimes getActivity() returns null and that`s a whole different issue.
Thanks in advance !!
Are you calling onGetStoriesThreadResult() from within the working thread? You shouldn't. Consider using AsyncTask<> instead of bare Thread , override the onPostExecute() method and call your events from there.

Cancel asynctask launched with onClickListener in an Adapter created inside onPostExecute

This is my context: I have an activity which downloads a list of video and set them into an adapter. When user clicks on each item, application downloads and streams video in an external application.
So, let's see some code. Main activity has this:
new TvAsyncTask(getActivity(), gridView, progressBar).execute();
TvAsyncTask download video list and sets it like here:
protected void onPostExecute(ArrayList<VideoPreviewData> list) {
progressBar.setVisibility(View.INVISIBLE);
final TvListAdapter adapter = new TvListAdapter(activity.getApplicationContext(), R.layout.item_video_list, list);
videoGridView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, final View view, int position, long id) {
String videoUrl = adapter.getItem(position).getLink();
//Launch asyncTask to download video an load it into an external media player
new GetVideoAsyncTask(activity).execute(videoUrl);
}
});
if (videoGridView!=null) videoGridView.setAdapter(adapter);
}
GetVideoAsyncTask load video link and lauch an Intent in an external application to stream that video. In order to do this, during onLoading I set a ProgressDialog that shows to the user a loading message. After that loading is completed, intent is launched.
Problem
When ProgressDialog is shown and user click "back button" the dialog is cancelled but the asynctask continues to work. So when it finishes it launches intent.
I want to prevent this. I have a onPause method in MainActivity, but I don't have a reference of GetVideoAsyncTask launched from adapter. So I cannot call method AsyncTask.cancel(true) in order to deny intent launch.
Any suggestion?
Create an object of your async task class;
TvAsyncTask tvTask = new TvAsyncTask(getActivity(), gridView, progressBar);
tvTask.execute();
Then in the TvAsyncTask class override the onCanceled function
#Override
protected void onCancelled()
{
super.onCancelled();
}
In your back button code check:
if(tvTask != null)
tvTask.cancel(true);
in main activity you can keep a reference to GetVideoAsyncTask and TvAsyncTask and in onPause cancel both of them.
inActivity:
GetVideoAsyncTask getVideoAsyncTask;
TvAsyncTask tvAsyncTask;
in onCreate:
tvAsyncTask=new TvAsyncTask(getActivity(), gridView, progressBar);
tvAsyncTask.execute();
and in onItemClick
getVideoAsyncTask=new GetVideoAsyncTask(activity);
getVideoAsyncTask.execute(videoUrl);
in onPause:
if(tvAsyncTask!=null&&!tvAsyncTask.isCancelled())
tvAsyncTask.cancel(true);
if(getVideoAsyncTask!=null&&!getVideoAsyncTask.isCancelled())
getVideoAsyncTask.cancel(true);

Do I need to include a default Intent in order to return to the previous Activity?

I am working on a To Do List Android application (it happens to be for a class assignment, but that's not what I'm asking about--I've tried to leave out as much code as I could). The main screen displays a list of ToDo items with a button at the bottom to open the Add New ToDo Item screen.
On the Add New ToDo Item screen, there is a Cancel button.
Relevant ToDoManagerActivity.java snippet:
public void onCreate(Bundle savedInstanceState) {
// Init and setup adapter, etc.
footerView.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
Intent intent = new Intent(ToDoManagerActivity.this, AddToDoActivity.class);
startActivityForResult(intent, ADD_TODO_ITEM_REQUEST);
}
});
// Attach the adapter to this ListActivity's ListView
}
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
log("Entered onActivityResult()");
// Check result code and request code.
// If user submitted a new ToDoItem
// Create a new ToDoItem from the data Intent
// and then add it to the adapter
}
Relevant AddToDoActivity.java snippet:
protected void onCreate(Bundle savedInstanceState) {
// Initialize default view, handle other events, etc.
final Button cancelButton = (Button) findViewById(R.id.cancelButton);
cancelButton.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
setResult(RESULT_CANCELED, new Intent());
finish();
}
});
}
The above code works. Previously, I was trying this in the onClick handler for cancelButton:
public void onClick(View v) {
finishActivity(RESULT_CANCELED);
}
When I clicked the Cancel button, I could see that the onActivityResult was being reached in the logs, but the screen was not reverting back to the main ToDo list screen.
Why does the above code not return me to the previous screen, but the following code does return me to the previous screen? What am I misunderstanding about the task backstack/activities?
public void onClick(View v) {
setResult(RESULT_CANCELED, new Intent());
finish();
}
According to the documentation:
public void finish ()
Call this when your activity is done and should be closed. The ActivityResult is propagated back to whoever launched you via
onActivityResult().
and
public void finishActivity (int requestCode)
Force finish another activity that you had previously started with startActivityForResult(Intent, int).
You should call finish() to close the current activity and finishActivity() to close another activity you started using startActivityForResult(Intent intent, int requestCode). Calling finishActivity() on the current activity will not close it.
Also, there's no point in creating a new Intent for setResult() as you are not passing back any data. Doing this would be sufficient:
setResult(RESULT_CANCELED);
finish();
From Android Docs:
public void finishActivity (int requestCode)
Force finish another activity that you had previously started with startActivityForResult(Intent, int).
finishActivity does not finish the current activity but calls finish for an activity called with requestCode
If you look at the documentation for finishActivity() it says that it will force finish an activity started with startActivityForResult(), but you have to pass in the request code that you used to start the other activity. In your case it would be ADD_TODO_ITEM_REQUEST.
This is probably not the API you want to use. Your 2nd method is cleaner in that you don't need to force close the child activity, but let it finish in the normal way.

Unable to create a new Loader after long periods of inactivity

I have an Android application whose Fragments rely on loaders to fetch data. Below is the skeleton code of my Fragment. Everything is the same except I have some custom code in the onLoadFinished method.
public class Events extends Fragment implements LoaderCallbacks<ArrayList<Event>> {
private Integer intWeek;
public static Events newInstance(Integer intWeek) {
Events pageFragment = new Events();
pageFragment.intWeek = intWeek;
pageFragment.setArguments(new Bundle());
return pageFragment;
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
return inflater.inflate(R.layout.events, null);
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getLoaderManager().initLoader(this.intWeek, savedInstanceState, this);
}
public Loader<ArrayList<Event>> onCreateLoader(int intLoader, Bundle bndBundle) {
return new Scraper(getActivity().getApplicationContext());
}
public void onLoadFinished(Loader<ArrayList<Event>> ldrEvents, final ArrayList<Event> lstEvents) {
//Do something with the returned data
}
public void onLoaderReset(Loader<ArrayList<Event>> ldrEvents) {
return;
}
#Override
public void onDestroy() {
super.onDestroy();
}
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
}
}
This Fragment is used inside a FragmentActivity which uses ViewPager which gets its Fragment´s using aFragmentPagerAdapter`.
This works fine and I'm able to page between the Fragments like the one shown above. Every time a new Fragment is added to the ViewPager, the onCreate method fires and it creates a new Loader.
When I press the "Home" button on my phone, the application pauses and goes into the background. I can always restore the application and it works just fine — after 5/10/20 minutes of being in the background.
...but If I leave the application in the background for a long time, an hour or more, the application crashes upon start and the stacktrace points to the following line in the onCreate method:
getLoaderManager().initLoader(this.intWeek, savedInstanceState, this);
Now I'm very lost as to why this is happening. It seems that the Android framework destroys something in the background after long periods of inactivity in the background and the initLoader method isn't able to create a Loader. The documentation about the initLoader method specifically says:
Ensures a loader is initialized and active. If the loader doesn't
already exist, one is created and (if the activity/fragment is
currently started) starts the loader. Otherwise the last created
loader is re-used.
Would anyone be able to point out what I'm doing wrong here? This seems to be a pretty difficult issue to debug because I can't replicate it at will. It is very random. Thanks
Stacktrace from logcat:
Transmitting stack trace: java.lang.RuntimeException: Unable to start activity ComponentInfo{com.mridang.stadi/com.mridang.stadi.Main}: java.lang.NullPointerException
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2079)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2104)
at android.app.ActivityThread.access$600(ActivityThread.java:132)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1157)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:137)
at android.app.ActivityThread.main(ActivityThread.java:4575)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:511)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:789)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:556)
at dalvik.system.NativeStart.main(Native Method)
Caused by: java.lang.NullPointerException
at com.mridang.stadi.events.Events.onCreate(Events.java:73)
at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:834)
at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1080)
at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1062)
at android.support.v4.app.FragmentManagerImpl.dispatchCreate(FragmentManager.java:1805)
at android.support.v4.app.FragmentActivity.onCreate(FragmentActivity.java:200)
at com.mridang.stadi.Main.onCreate(Main.java:23)
at android.app.Activity.performCreate(Activity.java:4465)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1049)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2033)
... 11 more
I ran into a similar problem with the loaders. What I found is that the Fragment/Loader lifecycle is complicated. I'd love to find a document that explains it in detail.
Anyhow, what I did to fix the problem was move the initLoader() call to the onActivityCreated() method. Give that a try.
Try using something like this:
boolean canFire = ((loader != null) && !loader.isReset());
// restart or initialise loader
if (canFire) {
getLoaderManager().restartLoader(loaderId, args, this);
} else {
getLoaderManager().initLoader(loaderId, args, this);
}

Categories