Search code examples
javaandroidasynchronousandroid-fragmentsgoogle-geocoder

Error when I try to instantiate a new class in fragment life cycle


Description: I two classes as follows,

Pseudo code

public class A extends Fragment {
   public View onCreateView(LayoutInflater inflater, ViewGroup container,
        Bundle savedInstanceState) {
    // Do something 
    return rootView;
   }

    @Override
    public void onActivityCreated(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
     // Do something  
    } 



    public void onStart() {
        super.onStart()
    }

/* Etc more normal lifecycle callback methods.
 *
 */ 

// Second Class enclosed in first class
public class B extends FragmentActivity implements
        GooglePlayServicesClient.ConnectionCallbacks,
        GooglePlayServicesClient.OnConnectionFailedListener {    

// Defined methods to be used by Class A.


}

Now my question, when I try to initialize class B in Class A. Like B b = new B(); My crashes on the line I try to initialize class B on (I've tried to initialize class B in various Activity callback methods such as onActivityCreated(),OnCreateView() etc none of them worked. The specific error is as follows:

Null Pointer Exception

at

android.support.v4.app.FragmentManagerImpl.executePendingTransactions(FragmentManager.java:478)

& at

android.support.v4.app.FragmentPagerAdapter.finishUpdate(FragmentPagerAdapter.java:141)

Further details Class A is being called by a MainActivity which in turn populates a ActionBar with Two Tabs. Finally here is the code of the actual class B and the separate Asynchronous class it calls.

public class rookWork extends FragmentActivity implements
            GooglePlayServicesClient.ConnectionCallbacks,
            GooglePlayServicesClient.OnConnectionFailedListener {

        LocationClient mLocationClient = new LocationClient(this, this, this);

        public LocationClient setup() {

            return mLocationClient;
        }

        @Override
        public void onConnected(Bundle connectionHint) {
            Toast.makeText(getApplicationContext(), "Connected!",
                    Toast.LENGTH_SHORT).show();
        }

        @Override
        public void onDisconnected() {
            Toast.makeText(getApplicationContext(), "Disconnected!",
                    Toast.LENGTH_SHORT).show();
        }

        @Override
        public void onConnectionFailed(ConnectionResult connectionResult) {

            /*
             * Google Play services can resolve some errors it detects. If the
             * error has a resolution, try sending an Intent to start a Google
             * Play services activity that can resolve error.
             */
            if (connectionResult.hasResolution()) {
                try {

                    // Start an Activity that tries to resolve the error
                    connectionResult
                            .startResolutionForResult(
                                    this,
                                    LocationUtils.CONNECTION_FAILURE_RESOLUTION_REQUEST);

                    /*
                     * Thrown if Google Play services canceled the original
                     * PendingIntent
                     */

                } catch (IntentSender.SendIntentException e) {

                    // Log the error
                    e.printStackTrace();
                }
            } else {

                // If no resolution is available, display a dialog to the user
                // with the error.
                showErrorDialog(connectionResult.getErrorCode());
            }
        }

        public void getAddress(View v) {
            // In Gingerbread and later, use Geocoder.isPresent() to see if a
            // geocoder is available.
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD
                    && !Geocoder.isPresent()) {
                // No geocoder is present. Issue an error message
                Toast.makeText(getActivity(), R.string.no_geocoder_available,
                        Toast.LENGTH_LONG).show();
                return;
            }

            if (servicesConnected()) {
                // Get the current location
                Location currentLocation = mLocationClient.getLastLocation();

                // Turn the indefinite activity indicator on
                mActivityIndicator.setVisibility(View.VISIBLE);

                // Start the background task
                GetAddressTask getAddressTask = new GetAddressTask(
                        getActivity());
                getAddressTask.execute(currentLocation);
                // (new
                // GetAddressTask.GetAddressTask(this)).execute(currentLocation);
            } else {
                Toast.makeText(getActivity(), "servicesConnected() == false",
                        Toast.LENGTH_SHORT).show();
            }

        }

        private boolean servicesConnected() {
            // Check that Google Play services is available
            int resultCode = GooglePlayServicesUtil
                    .isGooglePlayServicesAvailable(this);

            // If Google Play services is available
            if (ConnectionResult.SUCCESS == resultCode) {
                // In debug mode, log the status
                Log.d(LocationUtils.APPTAG,
                        getString(R.string.play_services_available));

                // Continue
                return true;
                // Google Play services was not available for some reason
            } else {
                // Display an error dialog
                Dialog dialog = GooglePlayServicesUtil.getErrorDialog(
                        resultCode, this, 0);
                if (dialog != null) {
                    ErrorDialogFragment errorFragment = new ErrorDialogFragment();
                    errorFragment.setDialog(dialog);
                    errorFragment.show(getSupportFragmentManager(),
                            LocationUtils.APPTAG);
                }
                return false;
            }
        }

        private void showErrorDialog(int errorCode) {

            // Get the error dialog from Google Play services
            Dialog errorDialog = GooglePlayServicesUtil.getErrorDialog(
                    errorCode, this,
                    LocationUtils.CONNECTION_FAILURE_RESOLUTION_REQUEST);

            // If Google Play services can provide an error dialog
            if (errorDialog != null) {

                // Create a new DialogFragment in which to show the error dialog
                ErrorDialogFragment errorFragment = new ErrorDialogFragment();

                // Set the dialog in the DialogFragment
                errorFragment.setDialog(errorDialog);

                // Show the error dialog in the DialogFragment
                errorFragment.show(getSupportFragmentManager(),
                        LocationUtils.APPTAG);
            }
        }

        /**
         * Define a DialogFragment to display the error dialog generated in
         * showErrorDialog.
         */
        @SuppressLint("ValidFragment")
        public class ErrorDialogFragment extends DialogFragment {

            // Global field to contain the error dialog
            private Dialog mDialog;

            /**
             * Default constructor. Sets the dialog field to null
             */
            @SuppressLint("ValidFragment")
            public ErrorDialogFragment() {
                super();
                mDialog = null;
            }

            /**
             * Set the dialog to display
             * 
             * @param dialog
             *            An error dialog
             */
            public void setDialog(Dialog dialog) {
                mDialog = dialog;
            }

            /*
             * This method must return a Dialog to the DialogFragment.
             */
            public Dialog onCreateDialog(Bundle savedInstanceState) {
                return mDialog;
            }
        }

The Asynchronous Class:

/* 
 * Google Imports
 */

/*
 *  Android Imports
 */
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import android.os.AsyncTask;
import android.content.Context;
import android.location.Address;
import android.location.Geocoder;
import android.location.Location;
import android.util.Log;
import android.view.View;
import android.widget.EditText;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;

public class GetAddressTask extends AsyncTask<Location, Void, String> {
    Context mContext;
    String addressText;
    int test;

    public GetAddressTask(Context context) {
        super();
        mContext = context;
    }

    @Override
    protected String doInBackground(Location... params) {
        /*
         * Get a new geocoding service instance, set for localized addresses.
         * This example uses android.location.Geocoder, but other geocoders that
         * conform to address standards can also be used.
         */
        Geocoder geocoder = new Geocoder(mContext, Locale.getDefault());

        // Get the current location from the input parameter list
        Location location = params[0];

        // Create a list to contain the result address
        List<Address> addresses = null;

        // Try to get an address for the current location. Catch IO or network
        // problems.
        try {

            /*
             * Call the synchronous getFromLocation() method with the latitude
             * and longitude of the current location. Return at most 1 address.
             */
            addresses = geocoder.getFromLocation(location.getLatitude(),
                    location.getLongitude(), 1);

            // Catch network or other I/O problems.
        } catch (IOException exception1) {

            // Log an error and return an error message
            Log.e(LocationUtils.APPTAG,
                    mContext.getString(R.string.IO_Exception_getFromLocation));

            // print the stack trace
            exception1.printStackTrace();

            // Return an error message
            return (mContext.getString(R.string.IO_Exception_getFromLocation));

            // Catch incorrect latitude or longitude values
        } catch (IllegalArgumentException exception2) {

            // Construct a message containing the invalid arguments
            String errorString = mContext.getString(
                    R.string.illegal_argument_exception,
                    location.getLatitude(), location.getLongitude());
            // Log the error and print the stack trace
            Log.e(LocationUtils.APPTAG, errorString);
            exception2.printStackTrace();

            //
            return errorString;
        }
        // If the reverse geocode returned an address
        if (addresses != null && addresses.size() > 0) {

            // Get the first address
            Address address = addresses.get(0);

            // Format the first line of address
            String addressText = mContext.getString(
                    R.string.address_output_string,

                    // If there's a street address, add it
                    address.getMaxAddressLineIndex() > 0 ? address
                            .getAddressLine(0) : "",

                    // Locality is usually a city
                    address.getLocality(),

                    // The country of the address
                    address.getCountryName());

            // Return the text
            return addressText;

            // If there aren't any addresses, post a message
        } else {
            return mContext.getString(R.string.no_address_found);
        }
    }

    protected void onPostExecute(String result) {
        /*
         * Toast.makeText(mContext.getApplicationContext(), "Execution Done",
         * Toast.LENGTH_SHORT).show();
         */
        Toast.makeText(mContext.getApplicationContext(),
                "Params Length: " + Integer.toString(test), Toast.LENGTH_SHORT)
                .show();
    }
}

Last note

Everything runs fine if I comment out the instatiation of class B (RookWork). I have previously been able to run the asynchronous task with the geocoder fine. However that setup did not involve having a Class B embedded in class A.


Solution

  • the instatiation of class B (RookWork)

    RookWork is an Activity. Activities cannot be directly instantiated in Android. To create a new Activity, you must use Intents.

    If In understand correctly, you need an Activity instance because some of the Google Play Services related methods (such as startResolutionForResult()) require one.

    The simplest solution is probably to move all this code (i.e. the implementation of the interfaces) into the fragment itself, then use getActivity()whenever you need an Activity. It will return the activity to which the Fragment is currently attached.

    For example:

    public class A extends Fragment
        implements GooglePlayServicesClient.ConnectionCallbacks,
                   GooglePlayServicesClient.OnConnectionFailedListener
    {
        ...
        @Override
        public void onConnectionFailed(ConnectionResult connectionResult)
        {
            ...
            connectionResult.startResolutionForResult(getActivity(), LocationUtils.CONNECTION_FAILURE_RESOLUTION_REQUEST);
            ...
        }
    ...