ixx
ixx

Reputation: 32273

Can't find suitable onCreate() equivalent for fragment

I already read this: http://developer.android.com/guide/components/fragments.html

I'm moving some activity code to a brand new fragment, but have found a problem with the code previously in onCreate().

Where fragment1, fragment2, etc. Are instance variables of the activity, instantiated in onCreate:

fragment1 = new Fragment1();
//etc.

I'm calling in onCreate() a webservice to get data to populate a list. According to the lifecycle of a fragment, there are a few candidates to put this code, but none works entirely:

  1. onCreate(): This is called before onCreateView(). So the view hierarchy isn't initialized yet and it would be unsafe to call the webservice.

  2. onCreateView(): This is called always when I switch tab / run the shown code snippet.

  3. onActivityCreated(): I thought this would be called only after the activity was created, but it's called always after Fragment.onCreateView()

As a side question, do you recommend calling webservices in the fragments? Haven't find anything about this in the web, but wouldn't know why it's incorrect.

Thanks in advance!

Upvotes: 1

Views: 1353

Answers (2)

Streets Of Boston
Streets Of Boston

Reputation: 12596

Use Loaders:

Create a subclass of an AsyncLoader and start/initialize these loader in your Fragment's onActivityCreated implementation:

public abstract class MyLoader extends AsyncTaskLoader<String> {
public MyLoader(Context context) {
    super(context);
}

private String result;
protected String error;

@Override
public final String loadInBackground() {
    try {
        error = null;
        // Load your data from the server using HTTP and store result as string in 'result'.
        ...
        result = ...
        ...
        return result;
    }
    catch (Exception e) {
        Logger.e("ResourceLoader", "Loading resource failed.", e);
        error = e.getMessage();
    }
    return null;
}

@Override
protected void onStartLoading() {
    if (!TextUtils.isEmpty(error)) {
        deliverResult(result);
    }

    if (takeContentChanged()) {
        forceLoad();
    }
}

@Override
public void deliverResult(String data) {
    if (isReset()) {
        return;
    }

    result = data;

    if (isStarted()) {
        try {
            super.deliverResult(data);
        }
        catch(Exception e) { 
            Log.e("ResourceLoader", "Caught exception while delivering result.", e);
        }
    }
}

public String getError() {
    return error;
}
}

In your Fragment, you can initialize this loader:

public class Fragment1 extends Fragment implements LoaderCallbacks<String> {
....
....
String message;
TextView textView;

    @Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    setRetainInstance(true);
        ....
}

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    View view = ....
    ...
    textView = (TextView)view.findViewById(R.id.....);
    textView.setMessage(message); // in case this Fragment1 survived an orientation change.
}

@Override
public void onActivityCreated(Bundle savedInstanceState) {
    super.onActivityCreated(savedInstanceState);
    getLoaderManager().initLoader(0, getArguments(), this); // Start loading data after activity has been created.
}

@Override
public Loader<String> onCreateLoader(int id, Bundle args) {
    return new MyLoader(getActivity()); // Load data using MyLoader
}

@Override
public void onLoadFinished(Loader<String> loader, String result) {
    // Here you have the result in 'result'.
    message = result;
    if (textView != null) {
        // Update UI according to result.
        textView.setText(message);
        ....
    }
    ...
}
....
}

In the onLoadFinished, you store the result in 'message'. If a simple String is not sufficient as a result, you can change MyLoader and LoaderCallbacks to return and handle more complex data-structures (e.g. List<String>, for example).

Upvotes: 1

Audrius Meškauskas
Audrius Meškauskas

Reputation: 21728

For me, onActivityCreated seems more or less the matching stage (at this time it is already possible to call findViewById on the parent activity and setup components / assign fields in the fragment instance / set (not add) listeners. My onActivityCreated is such that running it more than once does not make any harm and the passed Bundle parameter is taken into consideration.

Speaking about web services, you probably can call from anywhere but you cannot call from the GUI thread (and Android will not allow) - so create your own therad. You probably do not need to support fully switching between fragments and activities while some call to the web service is pending; if you do need, use the savedInstanceState bundle to save and resume the state when requested.

Upvotes: 1

Related Questions