How can I fix 'void android.widget.TextView.setText(java.lang.CharSequence)' on a null object reference?

こ雲淡風輕ζ 提交于 2021-02-11 17:25:38

问题


I'm new to android. I am currently developing an app that displays multiple locations on a map which has its marker locations taken from a central database that I have already set up and I know is working how can I fix my problem.

Here is my code for MainActivity.java

package com.example.defiblocator;
import android.Manifest;
import android.content.pm.PackageManager;
import android.location.Location;
import android.os.Build;
import android.os.Bundle;
import android.widget.TextView;

import androidx.annotation.NonNull;
import androidx.core.content.ContextCompat;
import androidx.fragment.app.FragmentActivity;
import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.api.GoogleApiClient;
import com.google.android.gms.location.LocationListener;
import com.google.android.gms.location.LocationRequest;
import com.google.android.gms.location.LocationServices;
import com.google.android.gms.maps.CameraUpdateFactory;
import com.google.android.gms.maps.GoogleMap;
import com.google.android.gms.maps.OnMapReadyCallback;
import com.google.android.gms.maps.SupportMapFragment;
import com.google.android.gms.maps.model.BitmapDescriptorFactory;
import com.google.android.gms.maps.model.LatLng;
import com.google.android.gms.maps.model.Marker;
import com.google.android.gms.maps.model.MarkerOptions;

import org.w3c.dom.Text;


public class MainActivity extends FragmentActivity implements OnMapReadyCallback,
        LocationListener,GoogleApiClient.ConnectionCallbacks,
        GoogleApiClient.OnConnectionFailedListener {

    GoogleMap mapAPI;
    SupportMapFragment mapFragment;
    Location mLastLocation;
    Marker mCurrLocationMarker;
    GoogleApiClient mGoogleApiClient;
    LocationRequest mLocationRequest;
    public static TextView data;



    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        fetchData process = new fetchData();
        process.execute();
        mapFragment = (SupportMapFragment) getSupportFragmentManager()
                .findFragmentById(R.id.mapAPI);
        mapFragment.getMapAsync(this);

    }



    @Override
    public void onMapReady(GoogleMap googleMap) {

        mapAPI = googleMap;
        LatLng fox = new LatLng(52.187907,-0.143284);
        mapAPI.addMarker(new MarkerOptions().position(fox).title("test"));
        mapAPI.moveCamera(CameraUpdateFactory.newLatLng(fox));
        if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            if (ContextCompat.checkSelfPermission(this,
                    Manifest.permission.ACCESS_FINE_LOCATION)
                    == PackageManager.PERMISSION_GRANTED) {
                buildGoogleApiClient();
                mapAPI.setMyLocationEnabled(true);
            }
        } else {
            buildGoogleApiClient();
            mapAPI.setMyLocationEnabled(true);
        }

    }

    protected synchronized void buildGoogleApiClient() {
        mGoogleApiClient = new GoogleApiClient.Builder(this)
                .addConnectionCallbacks(this)
                .addOnConnectionFailedListener(this)
                .addApi(LocationServices.API).build();
        mGoogleApiClient.connect();
    }

    @Override
    public void onConnected(Bundle bundle) {

        mLocationRequest = new LocationRequest();
        mLocationRequest.setInterval(1000);
        mLocationRequest.setFastestInterval(1000);
        mLocationRequest.setPriority(LocationRequest.PRIORITY_BALANCED_POWER_ACCURACY);
        if (ContextCompat.checkSelfPermission(this,
                Manifest.permission.ACCESS_FINE_LOCATION)
                == PackageManager.PERMISSION_GRANTED) {
            LocationServices.FusedLocationApi.requestLocationUpdates(mGoogleApiClient, mLocationRequest, this);
        }

    }

    @Override
    public void onConnectionSuspended(int i) {

    }

    @Override
    public void onLocationChanged(Location location) {

        mLastLocation = location;
        if (mCurrLocationMarker != null) {
            mCurrLocationMarker.remove();
        }
        //Place current location marker
        LatLng latLng = new LatLng(location.getLatitude(), location.getLongitude());
        MarkerOptions markerOptions = new MarkerOptions();
        markerOptions.position(latLng);
        markerOptions.title("Current Position");
        markerOptions.icon(BitmapDescriptorFactory.defaultMarker(BitmapDescriptorFactory.HUE_AZURE));
        mCurrLocationMarker = mapAPI.addMarker(markerOptions);

        //move map camera
        mapAPI.moveCamera(CameraUpdateFactory.newLatLng(latLng));
        mapAPI.animateCamera(CameraUpdateFactory.zoomTo(11));

        //stop location updates
        if (mGoogleApiClient != null) {
            LocationServices.FusedLocationApi.removeLocationUpdates(mGoogleApiClient, this);
        }

    }

    @Override
    public void onConnectionFailed(@NonNull ConnectionResult connectionResult) {

    }

}

And this is my code for fetching the data from the database and converting it into a JSON.

fetchData.java

package com.example.defiblocator;

import android.os.AsyncTask;
import android.widget.TextView;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;

public class fetchData extends AsyncTask <Void,Void,Void>{

    String data = "";
    String json_url;
    String dataParsed = "";
    String singleParsed = "";



    public Integer x = 0;
    @Override
    protected void onPreExecute(){
        json_url = "http://defiblocator.ml/json_get_data.php";
    }
    @Override
    protected Void doInBackground(Void... voids) {


        try {
            URL url = new URL(json_url);
            HttpURLConnection httpURLConnection = (HttpURLConnection)url.openConnection();
            InputStream inputStream = httpURLConnection.getInputStream();
            BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));

            String line = "";
            data = data + line;


            while(line != null){
                line = bufferedReader.readLine();
            }

            JSONArray JA = new JSONArray(data);
            for(int i =0 ; i <JA.length(); i++){
                JSONObject JO = (JSONObject) JA.get(i);
                singleParsed = JO.get("name") + "," + JO.get("lat") + "," +JO.get("lng") + ",";
                dataParsed = dataParsed + singleParsed ;
            }
        } catch (MalformedURLException e) {
            e.printStackTrace();
        } catch (IOException | JSONException e){
            e.printStackTrace();
        }

        return null;
    }

    @Override
    protected void onPostExecute(Void aVoid) {
        MainActivity.data.setText(dataParsed);

    }
}

My error only occurs in PostExecute() when I try and send my converted data back to the main activity fro processing into name, latitude, and longitude. I will do this with 3 separate arrays if there is an easier method than what I have please share but otherwise any help would be greatly appreciated as I believe I can change the text view into a string and split it into its constituent parts from there.


回答1:


As mentioned in the comments, you need to implement a callback using an interface to use the results of an Async Task. First create an interface.

New -> Jave Class -> Interface -> CallbackInterface .java. Define the method prototypes.

CallbackInterface.java

public interface CallbackInterface {
    public void onSuccess(String callbackData);
    public void onFailure();
}

In your MainActivity, define a class which implements the interface. Note that, the methods must be defined here and must contain the code to make use of the result of your Async Task MainActivity.java

public class MainActivity extends AppCompatActivity {
    TextView data;
    Button performAsync;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        //Add this without fail.
        data = findViewById(R.id.dataTV);

        performAsync = findViewById(R.id.performAsync);

        performAsync.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {


       //Recreating your "process"
            new AsyncTaskClass(new CallbackClass()).execute();
        }
    });

}

/*Create a class which implements the interface. It is a good practise to have
onSuccess and onFailure methods */
public class CallbackClass implements CallbackInterface {

    @Override
    public void onSuccess(String callbackData) {
        //do your callback functions here. In your case, setting the textview to result of parsed data.
        data.setText(callbackData);
    }

    @Override
    public void onFailure() {

    }
}

}

Finally, in your fetchData class, (AsyncTaskClass in my code) , do your asynchronous operations and finally in postExecute, call the onSuccess method with appropriate parameters. Note that you should pass the class as a parameter to the fetchData class and using the constructor, you must assign it to the instance of the interface. AsyncTaskClass.java

public class AsyncTaskClass extends AsyncTask {
    CallbackInterface callbackInterface;
    String dataParsed;
    public AsyncTaskClass(CallbackInterface callbackInterface) {
        this.callbackInterface = callbackInterface;
    }

    @Override
    protected Object doInBackground(Object[] objects) {
        dataParsed = "Processed Data";
        return null;
    }

    @Override
    protected void onPreExecute() {
        dataParsed = "";
    }

    @Override
    protected void onPostExecute(Object o) {
        callbackInterface.onSuccess(dataParsed);
    }
}

While this may seem too much, this is the only way you can communicate between the AsyncTaskClass and the Class which makes use of it. Feel free to ask for any clarifications in case you can't understand. I checked the above answer in my project and it works perfectly.




回答2:


A simpler alternative to the callback approach would be to provide the fetchData with a reference to the TextView. This solution also highlights that you omitted the retrieval of the TextView initially.

So in MainActivity.onCreate():

@Override
protected void onCreate(Bundle savedInstanceState) {

    //....

    fetchData process = new fetchData((TextView)findViewById(R.id.<text_view_id_in_xml>);

    //...
}

And in fetchData add a constructor to accept the text view:

public class fetchData extends AsyncTask <Void,Void,Void>{

    //....

    private TextView myTv;

    public fetchData(TextView tv) {
        super();
        myTv = tv;
    }


    //....

    @Override
    protected void onPostExecute(Void aVoid) {
        myTv.setText(dataParsed);
        myTv = null;
    }
}

You may find the callback approach provided in other solutions more generally suitable for many situations but in this specific case this approach seems simpler.

Note also that AsyncTask has been deprecated in Release R.

Also, in your onLocationChanged you don't have to remove/add a marker to move it - you can just update the saved marker position:

LatLng latLng = new LatLng(location.getLatitude(), location.getLongitude());

if (mCurrLocationMarker != null) {
    // previously added - move it
    mCurrLocationMarker.setPosition(latLng);
} else {
    // first time - add it - same as OP code.
}


来源:https://stackoverflow.com/questions/62213662/how-can-i-fix-void-android-widget-textview-settextjava-lang-charsequence-on

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!