Search code examples
androidsoapandroid-asynctasknullpointerexceptionksoap2

Android AsyncTask as NOT an Inner class : Weird NullPointerException


This question is reproduced from this question, the code given is a minimalistic example of the problem. In this example, we are consuming the temperature conversion SOAP web service of w3schools, using ksoap2 library:

The problem is that I get a NullPointerException on ed = (EditText) mainActivity.findViewById(R.id.mainActivity_editText1); in MyTask. I know I can implement the AsyncTask as an inner class but I read that it can be implemented as a separate class, so why this problem.

This application has a main activity named MainActivity. The MainActivity has an onclick-listener method, inside which another activity by the name of SecondActivity is started.

MainActivity.java:

public class MainActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }

    public void StartSecondActivity(View v) {
        Intent intent = new Intent(this, SecondActivity.class);
        startActivity(intent);
    }
}

Then there is a separate AsyncTask class called MyTask. We provide a constructor of MyTask for assigning an activity (an instance of MainActivity here) passed to it to an activity object we are creating inside MyTask called mainActivity, the reason for which is to invoke Activity's method findViewById.

From the SecondActivity, we create an object of MyTask using that constructor and passing to it an instance of the main activity in our app; and then call execute on that MyTask object.

SecondActivity.java:

public class SecondActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_second);

        new MyTask(new MainActivity()).execute();
    }
}

Now inside MyTask, I am reading an EditText from the main activity. Then using it as a celsius value and adding it to the SOAP request to call the web service. Then in the end I am displaying the result from the web service in a TextView in the SecondActivity in onPostExecute.

MyTask.java:

public class MyTask extends AsyncTask<Void, Void, String> {
    Activity mainActivity;
    EditText ed;
    TextView tv;
    String searchString;

    private static final String TAG = MyTask.class.getSimpleName();

    public static final String SOAP_ACTION = "http://www.w3schools.com/webservices/CelsiusToFahrenheit";
    public static final String METHOD_NAME ="CelsiusToFahrenheit";
    public static final String URL = "http://www.w3schools.com/webservices/tempconvert.asmx";
    public static final String NAMESPACE = "http://www.w3schools.com/webservices/";

    MyTask(Activity activity) {
        mainActivity = activity;
    }

    @Override
    protected void onPreExecute() {
        if (mainActivity!=null) {
            ed = (EditText) mainActivity.findViewById(R.id.mainActivity_editText1);
        } else {
            Log.i(TAG, "mainActivity is null.");
        }
        searchString = ed.getText().toString();
    }

    @Override
    protected String doInBackground(Void... params) {
        SoapObject requestSoapObject = new SoapObject(NAMESPACE, METHOD_NAME);
        requestSoapObject.addProperty("Celsius", searchString);

        SoapSerializationEnvelope envelope = new SoapSerializationEnvelope(SoapEnvelope.VER11);
        envelope.dotNet = true;
        envelope.setOutputSoapObject(requestSoapObject);

        HttpTransportSE htse = new HttpTransportSE(URL);
        SoapPrimitive webServiceResultSoapPrimitive = null;
        try {
            htse.call(SOAP_ACTION, envelope);
            webServiceResultSoapPrimitive = (SoapPrimitive) envelope.getResponse(); 

        } catch (HttpResponseException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (XmlPullParserException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return webServiceResultSoapPrimitive.toString();
    }

    @Override 
    protected void onPostExecute (String webServiceResultString) {
        System.out.println("Sysout : " + webServiceResultString);
        tv = (TextView) mainActivity.findViewById(R.id.secondActivity_textView1); 
        tv.setText("Fahrenheit : " + webServiceResultString);
    }

}

Solution

  • As Pedro stated in his comment you don't want to try and instantiate an Activity the way you are.

    All you need to do is to pass the value from the first to second Activity. Then you can pass that value to the constructor of your AsyncTask.

    So, in your onClick() of the first Activity you pass it as an Extra.

    public void StartSecondActivity(View v) {
        Intent intent = new Intent(this, SecondActivity.class);
        EditText et = (EditText) findViewById(R.id.mainActivity_editText1);
        String temp = et.getText().toString();
        intent.putExtra("temp", temp);
        startActivity(intent);
    }
    

    then retrieve it and pass it to the task

    public class SecondActivity extends Activity {
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
    
            // get the String
            Intent i = getIntent();
            String temp = i.getStringExtra("temp");
            setContentView(R.layout.activity_second);
    
            // and pass it here
            new MyTask(temp).execute();
        }
    }
    

    now get it in your task

    MyTask(String value) {
        searchString= value; 
    }