Why is OnCreate() called twice but savedInstance remains null - android

I have ListActivty onCreate() where a list adapter is set with a cursor object holding all data within a database.
I have a search method in this ListActivity that calls a method in another database activity to retrieve a Cursor from a search, and returns the cursor to my ListActivity.
However after this method is called the ListActivty onCreate() is called again, where the the original list adapter with all data is called again, therefore I can never see the search results when i try to set the adapter with search cursor.
I have tried setting a boolean flag fro when savedInstance != null to set the correct search adapter but the savedInstance ALWAYS remains null......
So my question what is actually happening to the activity when the onCreate() is called twice?
EDIT: Added code
onCreate() I set the list apdter
public class ViewListOfTests extends ListActivity implements SearchView.OnQueryTextListener, SearchView.OnCloseListener{.....
this.setContentView(R.layout.list_activity);
// Associate searchable configuration with the SearchView
searchView = (SearchView) findViewById(R.id.search);
searchView.setIconifiedByDefault(false);
searchView.setOnQueryTextListener(this);
searchView.setOnCloseListener( this);
SearchManager searchManager =
(SearchManager) getSystemService(Context.SEARCH_SERVICE);
//SearchView searchView =(SearchView) menu.findItem(R.id.search).getActionView();
searchView.setSearchableInfo(
searchManager.getSearchableInfo(getComponentName()));
//(SearchView) this.findViewById(id) menu.findItem(R.id.search).getActionView();
/*call the aysnch inner class to load cursor form Db and set teh customer adter to this list view
*off the main UI thread, cast to type (getCursor)
*Asynch class does not have a constructor in this case
**/
if(savedInstanceState!=null){
//getCursorAysnch = (getCursor) new getCursor().execute();
searchPerformed=true;
}
//getCursorAysnch = (getCursor) new getCursor().execute();
try{
noOftimesOnCreateCalled = savedInstanceState.getInt("MyInt");
searchPerformed = savedInstanceState.getBoolean("MyBoolean");
}catch(Exception e){
e.printStackTrace();
Log.d("VIEWLISTTESTS", "noOFTOMESONCREATECALLED NOT CALLED");
}
if (noOftimesOnCreateCalled>0){
this.setListAdapter(searchAdapter);
Log.d("VIEWLISTOFDIVES", "IN ONCTREATE SERACHPERFOMED + TRUE");
}else if(noOftimesOnCreateCalled==0){
getCursorAysnch = (getCursor) new getCursor().execute();
Log.d("VIEWLISTOFDIVES", "IN ONCTREATE SERACHPERFOMED + FALSE");
noOftimesOnCreateCalled++;
}
I get the search query fem a searchView widget and call the following methow with the query, after this method the oCreate() is be called again...
#Override
public boolean onQueryTextChange(String newText) {
// TODO Auto-generated method stub
//handleIntent(intent);
Log.d("LIST ACTIVITY SEARCH", "onQueryTextCahanged called: "+ newText);
data = new database(this);
Cursor c = data.getWordMatches(newText, null);
Log.d("LISTACTIVITY", "CURSOR QUERY ROWS/COLS = "+ c.getCount()+" "+c.getColumnCount());
//now set bind cursor data to list view using custim ItemAdpter class
//ItemAdapter newAdapter = new ItemAdapter(ViewListOfTests.this, c);
//newAdapter.notifyDataSetChanged();
//ViewListOfTests.this.setListAdapter(newAdapter);
searchAdapter= new ItemAdapter(this, c);
//this.setListAdapter(searchAdapter);
searchPerformed=true;
return false;
}
Although savediNstanceState always remains nul I try to saved variables here:
#Override
public void onSaveInstanceState(Bundle savedInstanceState) {
super.onSaveInstanceState(savedInstanceState);
// Save UI state changes to the savedInstanceState.
// This bundle will be passed to onCreate if the process is
// killed and restarted ie when we go ot ItemAdter to craete a new adpter with serach
//savedInstanceState.putBoolean(key, false)
savedInstanceState.putBoolean("MyBoolean", true);
savedInstanceState.putDouble("myDouble", 1.9);
savedInstanceState.putInt("MyInt", 1);
savedInstanceState.putString("MyString", "Welcome back to Android");
// etc.
}
#Override
public void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
// Restore UI state from the savedInstanceState.
// This bundle has also been passed to onCreate.
searchPerformed = savedInstanceState.getBoolean("MyBoolean");
double myDouble = savedInstanceState.getDouble("myDouble");
noOftimesOnCreateCalled = savedInstanceState.getInt("MyInt");
String myString = savedInstanceState.getString("MyString");
}

In order for onCreate()'s savedInstanceState to be non-null, you have to provide Android with the saved state when Android forces the Activity to exit.
To do this, you must override the void onSaveInstanceState(Bundle savedInstanceState) method and fill in the passed-in savedInstanceState Bundle with whatever state you want.

Issue was I had some code to configure the searchable widget with a ACTION_SERACH intent when user submitted query :
#Override
protected void onNewIntent(Intent intent) {
// call handleIntent from here to handle the query from serah widget
super.onNewIntent(intent);
handleIntent(intent);
}//end onNewIntent
// Associate searchable configuration with the SearchView
SearchManager searchManager =
(SearchManager) getSystemService(Context.SEARCH_SERVICE);
SearchView searchView =
(SearchView) menu.findItem(R.id.search).getActionView();
searchView.setSearchableInfo(
searchManager.getSearchableInfo(getComponentName()));
I had forgotten to delete this code as wasn't aiming to configure search view with a ACTION_SEARCH intent, but I believe this intent was called and then calling the onCreate() again.

Related

SetShareIntent when a new fragment is displayed

My question is similar to Android setShareIntent within fragment, but I read the answers there and couldn't figure out how to apply them to my situation.
Quick summary of my question
I'd like to setShareIntent each time the fragment changes, i.e. whenever a new fragment is shown to the user. How can I do that? Where should the setShareIntent call go?
Longer version with code snippets
Here's a skeleton of my code:
import android.support.v7.widget.ShareActionProvider;
public class SolvePuzzle extends ActionBarActivity {
private ShareActionProvider mShareActionProvider;
static AppSectionsPagerAdapter mAppSectionsPagerAdapter;
static ViewPager mViewPager;
...
public void onCreate(Bundle savedInstanceState) {
...
mAppSectionsPagerAdapter = new AppSectionsPagerAdapter(getSupportFragmentManager());
mViewPager = (ViewPager) findViewById(R.id.pager);
mViewPager.setAdapter(mAppSectionsPagerAdapter);
}
}
Then comes
#Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.solve_puzzle, menu);
MenuItem item = menu.findItem(R.id.menu_item_share);
mShareActionProvider = (ShareActionProvider) MenuItemCompat.getActionProvider(item);
if (mShareActionProvider == null) {
// Following https://stackoverflow.com/questions/19358510/why-menuitemcompat-getactionprovider-returns-null
mShareActionProvider = new ShareActionProvider(this);
MenuItemCompat.setActionProvider(item, mShareActionProvider);
Intent shareIntent = new Intent();
shareIntent.setAction(Intent.ACTION_SEND);
shareIntent.putExtra(Intent.EXTRA_TEXT, "Some text"); // For debugging
shareIntent.setType("text/plain");
setShareIntent(shareIntent);
Log.d(DEBUG_TAG, "Called new mShareActionProvider(this) and set share intent");
}
return true;
}
private void setShareIntent(Intent shareIntent) {
// Want to call this whenever new puzzle fragment is displayed
if (mShareActionProvider != null) {
mShareActionProvider.setShareIntent(shareIntent);
} else {
Log.d(DEBUG_TAG, "Could not set share intent: mShareActionProvider == null");
}
}
I then have a public class AppSectionsPagerAdapter extends FragmentPagerAdapter and a public class SolvePuzzleFragment extends Fragment implements OnClickListener.
Currently I call setShareIntent in the onCreateView of SolvePuzzleFragment.
That doesn't work: when I look at my logcat, I see that the setShareIntent call coming from the fragments (first two lines) happens before the onCreateOptionsMenu call from the SolvePuzzle class (last line):
12-12 12:39:26.505: D/Debug(3864): Could not set share intent:
mShareActionProvider == null
12-12 12:39:26.515: D/Debug(3864): Could not set share intent:
mShareActionProvider == null
12-12 12:39:26.565: D/Debug(3864): Called new
mShareActionProvider(this) and set share intent
...and it looks like there are two calls from the fragments. Is that because both the current fragment and the next one in line are created (have their onCreateView called), even though only the first one is being displayed?
It looks like calling setShareIntent in the fragment's onCreateView is a mistake. What I want to do is call setShareIntent when a new fragment is displayed to the user. How do I do that?
Edit: Additional information:
The share button currently works, but it sends the "Some text" message that I set for debugging purposes in onCreateOptionsMenu. I'd like that intent to be overwritten by a fragment-related intent as soon as the first fragment is displayed to the user (and overwritten each time a new fragment is displayed).
How can I do that? Where should the setShareIntent call go?
Register an OnPageChangeListener with your ViewPager via setOnPageChangeListener() and put your setShareIntent() call in onPageSelected() of the listener.

Save state of Fragment / View

I have a Fragment that contains a SearchView. This fragment is include in my HomeActivity and in my SearchResultsActivity.
When the user uses the SearchView in HomeActivity to perform a search, they are shown the results in SearchResultsActivity.
I am trying to make it so the when the user enters a query into the SearchView on HomeActivity, their query String is remembered by the SearchView on the SearchResultsActivity.
I have tried all of the suggestions for this similar issue, but (along with a lot of other developers) the only solution that seems to work does not seem an adequate one - that is, saving the query in a static String in the Fragment class.
Here is my code which shows this approach...
`public class ProductSearchViewFragment extends Fragment {
private static final String LOG_TAG = ProductSearchViewFragment.class.getSimpleName();
private static final String KEY_QUERY = "query";
private static String mQuery = null;
public ProductSearchViewFragment() {
setArguments(new Bundle());
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_product_search_view, container, false);
}
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
/*
* Populate the searchView if there is a previous query
*/
SearchView searchView = (SearchView)getView().findViewById(R.id.searchView);
if (mQuery != null) {
Log.d(LOG_TAG, "######################### Setting searchView query from static String: " + mQuery);
searchView.setQuery(mQuery, false);
}
else {
Bundle mySavedInstanceState = getArguments();
String query = mySavedInstanceState.getString(KEY_QUERY);
if (query != null) {
// THIS CONDITION IS NEVER MET. I.E., query IS ALWAYS NULL
Log.d(LOG_TAG, "######################### Setting searchView query from bundle arguments: " + query);
searchView.setQuery(query, false);
}
else {
Log.d(LOG_TAG, "######################### Not setting searchView query.");
}
}
}
/*
* Unfortunately, onSaveInstanceState(Bundle outState) is not called,
* so attempt to save the query in our own arguments bundle.
*
*/
#Override
public void onPause() {
super.onPause();
SearchView searchView = (SearchView)getView().findViewById(R.id.searchView);
// Save to static field
mQuery = searchView.getQuery().toString();
// Save to arguments bundle
getArguments().putString(KEY_QUERY, mQuery);
Log.d(LOG_TAG, "#################### saved query: " + mQuery);
}
}`
...I think there must be a better way though than using a static String like this. Please, what is it?
As you'll see from the code, I have tried saving the query in setArguments() but it is never retrieved.
I've spent some time looking through the Android docs too, which suggests using onSaveInstanceState(), but that is not called unless the HomeActivity is destroyed (which does not happen and neither do I want to force it).
If I'm reading your question right, it sounds like you have two different instances of the ProductSearchViewFragment -- on on your "home" screen, and one on your "search results" screen. So on your home screen it creates the fragment when the activity is created, and destroys it when the activity is destroyed. Then on the search results activity a new instance of that fragment is created (and destroyed when the activity is destroyed). So arguments set on one instance won't automatically carry over to a separate instance.
My recommendation would be that when the user hits the "search" button on your home activity, you pass the search query from the fragment to the home activity. Then, when the home activity creates an instance of the search results activity, it can pass along an extra in the launch intent specifying the search string, and then the search results activity can use it in the fragment arguments when creating the search fragment.

searchView setQuery does not work

I am using the Search interface in andriod, and folowing the android guide I have set up the the interface.
I create a SearchableActivity which will handle both the user input the search result.
Now I meet a problem in that when user input something in the SearchView and hit the search key, the Search requset will be submited, then I will receive the query string and do the search, but at this moment, the text in the SearchView was changed to the default SearchHit value while I want it to be the query string.
So I tried to use the searchView.setQuery(query, false);, but it does not work, what's the problem?
The core Activity codes:
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.map_activity_layout);
Log.d("map", "create");
handleIntent(getIntent());
}
public void onNewIntent(Intent intent) {
setIntent(intent);
handleIntent(intent);
}
private void handleIntent(Intent intent) {
Log.d("map", "handleIntent");
if (Intent.ACTION_SEARCH.equals(intent.getAction())) {
String query = intent.getStringExtra(SearchManager.QUERY);
//try search
Log.d("map", "search and set query:" + query);
searchView.setQuery(query, false); // reset the value in of the SearchView
}
}
The AndroidManifest.xml:
<activity
android:name=".ui.MapActivity"
android:launchMode="singleTop">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
<action android:name="android.intent.action.SEARCH" />
</intent-filter>
<meta-data
android:name="android.app.searchable"
android:resource="#xml/searchable" />
</activity>
I suggest you to delay the setting until the next event loop (add it to the message queue), so the code may looks like this:
private void handleIntent(Intent intent) {
if (Intent.ACTION_SEARCH.equals(intent.getAction())) {
final String query = intent.getStringExtra(SearchManager.QUERY);
searchView.post(new Runnable() {
#Override
public void run() {
// Important! Make sure searchView has been initialized
// and referenced to the correct (current) SearchView.
// Config changes (e.g. screen rotation) may make the
// variable value null.
searchView.setQuery(query, false);
}
};
}
}
This doesn't work
mSearchView.setQuery(mKeyword, true);
MenuItemCompat.expandActionView(mMenuSearch);
This does
MenuItemCompat.expandActionView(mMenuSearch);
mSearchView.setQuery(mKeyword, true);
It seems it only remembers it after you expanded the action view.
I'm using MenuItemCompat just for backwards compatibility
need to put code in post handler with MenuItemCompat
inflater.inflate(R.menu.menu_my_team, menu);
// search manager integration for search widget
SearchManager searchManager = (SearchManager) getActivity()
.getSystemService(Context.SEARCH_SERVICE);
final MenuItem searchMenuItem = menu.findItem(R.id.action_search);
// MenuItem helpTutorialMenuItem = menu.findItem(R.id.action_help);
mSearchView = (SearchView) MenuItemCompat.getActionView(searchMenuItem);
mSearchView.setSearchableInfo(searchManager
.getSearchableInfo(getActivity().getComponentName()));
mSearchView.setQueryHint("Search by");
if (!TextUtils.isEmpty(queryText)) {
mSearchView.post(new Runnable() {
#Override
public void run() {
MenuItemCompat.expandActionView(searchMenuItem);
mSearchView.setQuery(queryText, false);
}
});
}
mSearchView.clearFocus();
It seems that when the intent with Action Intent.ACTION_SEARCH is received, the following method will be called:
handleIntent();
onCreateOptionsMenu();
And the handleIntent will run before the onCreateOptionsMenu.
At the same time, people used to setup the searchView at the onCreateOptionsMenu.
Which means that the option menu will be re-inflated, and the searchView will be re-build, then all the settings in the handleIntent will not work.
I think this is the reason.
Please feel free to fix me if I am wrong.
In my case, it wasn't working because the fragment I was in was no longer attached to the window. Moving it to above where I swapped in the new search results fragment fixed it.

onResume causing problems on start up

I have an activity that allows the user to start a second activity. The second activity has a list of items which I add to an array list. When I return to the previous activity I want to display the size of the array list.
However I am having a problem with onResume(). It is called when my first activity is created and as a result generates an error as the array list does not exist when it is first launched!
onResume():
#Override
protected void onResume() {
// TODO Auto-generated method stub
super.onResume();
getIntentData();
calcSubTotal(orderData);
}
getIntentData():
public void getIntentData(){
b = new Bundle();
b = getIntent().getExtras();
orderData = b.getParcelable("order");
Toast.makeText(this.getApplicationContext(), orderData.size(), Toast.LENGTH_LONG).show();
}
onCreate() of second activity:
#Override
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_starters);
createTestData();
b = new Bundle();
orderData = new MenuItemList();
adapter = new MenuItemArrayAdapter(this, starters);
this.setListAdapter(adapter);
}
#Override
protected void onListItemClick(ListView l, View v, int position, long id) {
Toast.makeText(this.getApplicationContext(), l.getItemAtPosition(position).toString() + " clicked", Toast.LENGTH_LONG).show();
//add clicked item to orderData....
MenuItem m = (MenuItem)l.getItemAtPosition(position);
//create new item
orderData.add(m);
}
Any idea how I might be able to control this?
ERROR:
java.lang.RuntimeException: Unable to resume activity {com.example.waitronproto3/com.example.waitronproto3.SectionsActivity}: java.lang.NullPointerException
I think you may want to have a look at startActivityForResult instead, when you're starting your second Activity. It'll allow your second activity to return a result back to your first activity. You can read up on it at the Activity documentation, specifically the "Starting Activities and Getting Results" section of the document.
Edit: By the looks of your code - nothing you're doing is either storing a bundle from the second activity and sending it back to the first. So you'll never get the proper Bundle data in your first activity. As suggested, look into startActivityForResult to launch your second activity with. This will allow you to return data back into your first activity with ease.
However I am having a problem with onResume(). It is called when my first activity is created and as a result generates an error as the array list does not exist when it is first launched!
I recommend changing getIntentData() to check if the appropriate data exists first:
public void getIntentData(){
Intent intent = getIntent();
if(intent != null && intent.hasExtra("order")) {
orderData = b.getParcelable("order");
Toast.makeText(this.getApplicationContext(), orderData.size(), Toast.LENGTH_LONG).show();
calculateSubTotal(order);
}
}
And update onResume():
#Override
protected void onResume() {
super.onResume();
getIntentData();
}
(Though you could simply put getIntentData() in onResume() now.)
Your onResume() will be called after onCreate() according to the Android Lifecycle so you will want to check that the data is not null before trying to use it.
`if(intentData != null)
//do something`

Problem passing a Bundle with onSearchRequested

I'm actually trying to use the built-in search interface of Android, but I have some issues when I try to pass data with the search query.
Here is a brief explanation : I have an object in a first Activity (FirstActivity) called "Category" which implements Serializable (I already pass it successfuly between Activities) and I want to perform a search related to that category, and display the results in a second Activity (SecondActivity).
So, in FirstActivity I override the onSearchRequest method :
#Override
public boolean onSearchRequested() {
Bundle appData = new Bundle();
appData.putSerializable("category", _currentCategory);
Log.d(Utils.LOG_TAG, "Bundle : "+appData.keySet());
startSearch(null, false, appData, false);
return true;
}
And in SecondActivity, I try to get this Bundle :
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
...
handleIntent(getIntent());
}
private void handleIntent(Intent intent){
Bundle appData = intent.getBundleExtra(SearchManager.APP_DATA);
if(appData == null) Log.d(Utils.LOG_TAG, "appData == null");
Log.d(Utils.LOG_TAG, "Extras : "+intent.getExtras().keySet());
}
Problem is that appData seems to be equals to null everytime. Here is the logcat output :
Bundle : [category]
appData == null
Extras : [query, user_query]
I tried to add some other objects into the Bundle (Booleans, etc...) but it doesn't change anything at all and I keep having a null appData.
I had problems figuring this out as well, and the examples I found didn't really help. A lot of them suggested overriding onSearchRequested(), but that actually doesn't work for SearchWidget. I ended up using the following (from danada) as a solution, since it seemed much simpler for me than setting up the OnQueryTextListener. I just overrode startActivity (in the first, search Activity) like so:
#Override
public void startActivity(Intent intent) {
//check if search intent
if(Intent.ACTION_SEARCH.equals(intent.getAction())) {
intent.putExtra("KEY", "VALUE");
}
super.startActivity(intent);
}
Then in the second, searchable Activity, I pulled out the info like so (called from onCreate() or from overriding onNewIntent() (if using singleTop)):
private void handleIntent(Intent intent){
if(Intent.ACTION_SEARCH.equals(intent.getAction())){
mSearchedQuery = intent.getStringExtra(SearchManager.QUERY);
mExtraData = intent.getStringExtra("KEY");
}
Simple, and worked like a charm! Check the link to the article above if you would like a little more explanation about it.
If you're using SearchView, it will not send your appData. Instead, consider using OnQueryTextListener. For example:
...
#Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.your-menu-id, menu);
/*
* Get the SearchView and set the searchable configuration.
*/
SearchManager searchManager = (SearchManager) getSystemService(Context.SEARCH_SERVICE);
SearchView searchView = (SearchView) menu.findItem(R.id.your-search-menuitem-id)
.getActionView();
searchView.setSearchableInfo(searchManager.getSearchableInfo(getComponentName()));
/*
* Set query text listener here.
*/
searchView.setOnQueryTextListener(mSearchViewOnQueryTextListener);
return true;
}// onCreateOptionsMenu()
...
private final SearchView.OnQueryTextListener mSearchViewOnQueryTextListener = new SearchView.OnQueryTextListener() {
#Override
public boolean onQueryTextSubmit(String query) {
/*
* You don't need to deal with "appData", because you already
* have the search query here.
*/
// Tell the SearchView that we handled the query.
return true;
}// onQueryTextSubmit()
#Override
public boolean onQueryTextChange(String newText) {
// TODO Auto-generated method stub
return false;
}// onQueryTextChange()
};// mSearchViewOnQueryTextListener
Note: You still need to keep the old way (using appData inside onSearchRequested()). In your onCreate(), if the extra for SearchManager.APP_DATA is null, that means you already handled the search query in the listener.
Conclusion:
If the SearchView is inactive, and you invoke it via onSearchRequested(), this will happen: onSearchRequested() >> onCreate() (ACTION_SEARCH contains SearchManager.APP_DATA).
If the SearchView is active, the user types and submits search, this will happen: SearchView.OnQueryTextListener.onQueryTextSubmit() >> onCreate() (ACTION_SEARCH without SearchManager.APP_DATA).
While putting data and retrieving it you are using two different keys. while putting you are using "category" and while retrieving you are using SearchManager.APP_DATA instead of using "category"
Try with
Bundle appData = intent.getBundleExtra("category");
Thanks
Deepak
In your example, you are asking for the keyset on the original intent object, and not the Bundle containing your appData. Here is an example that should work:
private void handleIntent(Intent intent){
final Bundle appData = intent.getBundleExtra(SearchManager.APP_DATA);
for (String key : appData.keySet()) {
Log.d(TAG, "key="+appData.getString(key));
}
}

Categories

Resources