Android : How to use mFusedLocationClient callback (resulting location) in current mainthread

大城市里の小女人 提交于 2020-06-27 19:32:10

问题


Following is my adapter for a RecyclerView where i need to update one of the TextView which shows distance of current location to another location .

Now , to get the current location , i am using mFusedLocationClient.requestLocationUpdates(mFusedLocationRquest,mLocationCallback,null);

If i am going with the below code and have just enabled gps in app with runtime permission , i face NPE on line :

float distance = updatedLoc.distanceTo(des);

App does not crash when i open with gps already enabled (probably because the last known loc is not null in that case ).

So to make sure that updatedLoc is not null , i want to make sure that my TextView is updated after the location callback only , how would that be possible ?

In this API's callback , createLocationCallback() , i am unable to update the TextView properly (tried using holder as global var , but it was messy and items weren't updated properly .)

Thanks in advance.

public class PlaceListAdapter extends RecyclerView.Adapter<PlaceListAdapter.PlaceViewHolder> implements android.location.LocationListener{

    private static final long LOCATION_REFRESH_TIME = 2000;
    private static final float LOCATION_REFRESH_DISTANCE = 100;
    private static final int MY_PERMISSION_ACCESS_COURSE_LOCATION = 1001;
    private Context mContext;
    private PlaceBuffer mPlaces;
    Location updatedLoc = null;
    PlaceViewHolder myHolder;
    boolean canGetLocation = false;
    android.location.LocationListener locationListener;
    double lat1,long1;
    MySimpleCallback mySimpleCallback;
    String dist;
    private FusedLocationProviderClient mFusedLocationClient;
    private LocationRequest mFusedLocationRquest;
    private LocationCallback mLocationCallback;

    /**
     * Creates a callback for receiving location events.
     */
    private void createLocationCallback() {
        mLocationCallback = new LocationCallback() {
            @Override
            public void onLocationResult(LocationResult locationResult) {
                super.onLocationResult(locationResult);

                updatedLoc = locationResult.getLastLocation();

                Log.d("testingvalue",String.valueOf(updatedLoc==null));
                //mLastUpdateTime = DateFormat.getTimeInstance().format(new Date());

            }
        };
    }


    /**
     * Constructor using the context and the db cursor
     *
     * @param context the calling context/activity
     */
    public PlaceListAdapter(Context context, PlaceBuffer places) {
        this.mContext = context;
        this.mPlaces = places;
        mFusedLocationClient = LocationServices.getFusedLocationProviderClient(mContext);

        createLocationCallback();
        createLocationRequest();

        if (ActivityCompat.checkSelfPermission(mContext, android.Manifest.permission.ACCESS_FINE_LOCATION)
                != PackageManager.PERMISSION_GRANTED) {
            ActivityCompat.requestPermissions((Activity) mContext,
                    new String[]{android.Manifest.permission.ACCESS_FINE_LOCATION},
                    101);
        }

        mFusedLocationClient.requestLocationUpdates(mFusedLocationRquest,mLocationCallback,null);

    }

    private void createLocationRequest() {
        mFusedLocationRquest = new LocationRequest();

        // Sets the desired interval for active location updates. This interval is
        // inexact. You may not receive updates at all if no location sources are available, or
        // you may receive them slower than requested. You may also receive updates faster than
        // requested if other applications are requesting location at a faster interval.
        mFusedLocationRquest.setInterval(100);

        // Sets the fastest rate for active location updates. This interval is exact, and your
        // application will never receive updates faster than this value.
        mFusedLocationRquest.setFastestInterval(100);

        mFusedLocationRquest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);

    }

    /**
     * Called when RecyclerView needs a new ViewHolder of the given type to represent an item
     *
     * @param parent   The ViewGroup into which the new View will be added
     * @param viewType The view type of the new View
     * @return A new PlaceViewHolder that holds a View with the item_place_card layout
     */
    @Override
    public PlaceViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        // Get the RecyclerView item layout
        LayoutInflater inflater = LayoutInflater.from(mContext);
        View view = inflater.inflate(R.layout.item_place_card, parent, false);
        return new PlaceViewHolder(view);
    }

    /**
     * Binds the data from a particular position in the cursor to the corresponding view holder
     *
     * @param holder   The PlaceViewHolder instance corresponding to the required position
     * @param position The current position that needs to be loaded with data
     */
    @Override
    public void onBindViewHolder(PlaceViewHolder holder, int position) {
        String placeName = mPlaces.get(position).getName().toString();
        String placeAddress = mPlaces.get(position).getAddress().toString();

        double lat = mPlaces.get(position).getLatLng().latitude;
        double lng = mPlaces.get(position).getLatLng().longitude;

        //Location currentLocation = getCurrentLocation();

        //Location currLoc = new GPSTracker(mContext).getLocation();

        //Toast.makeText(mContext, "distance is" + dist + "km", Toast.LENGTH_SHORT).show();

        Location des = new Location("destination");

        des.setLatitude(lat);
        des.setLongitude(lng);

        float distance = updatedLoc.distanceTo(des);

        distance = distance / 1000;

        dist = "";
        if (distance < 1) {
            dist = "less than km away";
        } else {
            dist = String.format("%.2f", distance) + " km";
        }


        holder.nameTextView.setText(placeName);
        holder.addressTextView.setText(dist);

        holder.taskTextView.setText(MainActivity.preference.getString(mPlaces.get(position).getId(), "chv"));
    }

    public void swapPlaces(PlaceBuffer newPlaces) {
        mPlaces = newPlaces;
        if (mPlaces != null) {
            // Force the RecyclerView to refresh
            this.notifyDataSetChanged();

        }
    }

    /**
     * Returns the number of items in the cursor
     *
     * @return Number of items in the cursor, or 0 if null
     */
    @Override
    public int getItemCount() {
        if (mPlaces == null) {
            Log.d("countcheck", "zero bro");
            return 0;
        }
        return mPlaces.getCount();

    }


    @Override
    public void onLocationChanged(Location location) {
        updatedLoc = location;
    }

    @Override
    public void onStatusChanged(String s, int i, Bundle bundle) {

    }

    @Override
    public void onProviderEnabled(String s) {

    }

    @Override
    public void onProviderDisabled(String s) {

    }



    /**
     * PlaceViewHolder class for the recycler view item
     */
    class PlaceViewHolder extends RecyclerView.ViewHolder {

        TextView nameTextView;
        TextView addressTextView;
        TextView taskTextView;

        public PlaceViewHolder(View itemView) {
            super(itemView);
            nameTextView = (TextView) itemView.findViewById(R.id.name_text_view);
            addressTextView = (TextView) itemView.findViewById(R.id.address_text_view);
            taskTextView = (TextView) itemView.findViewById(R.id.task_text_view);
        }

    }

}

回答1:


When the onBindViewHolder is called, updatedLoc is still null, means location provider has not received location yet.

You should wrap the updatedLoc in onBindViewHolder against to null case like below

if(updatedLoc != null){
    float distance = updatedLoc.distanceTo(des);
    distance = distance / 1000;
    dist = "";

    if (distance < 1) {
        dist = "less than km away";
    } else {
        dist = String.format("%.2f", distance) + " km";
    }
} else {
    dist = "Waiting for location...";
} 

holder.nameTextView.setText(placeName);
holder.addressTextView.setText(dist);

And whenever you receive the location, onBindViewHolder must be called again to calculate and show distance instead of Waiting for location...

So you need to update createLocationCallback method. Just add notifyDataSetChanged()

private void createLocationCallback() {
    mLocationCallback = new LocationCallback() {
        @Override
        public void onLocationResult(LocationResult locationResult) {
            super.onLocationResult(locationResult);

            updatedLoc = locationResult.getLastLocation();

            Log.d("testingvalue",String.valueOf(updatedLoc==null));
             //mLastUpdateTime = DateFormat.getTimeInstance().format(new Date());

            notifyDataSetChanged();
        }
    };
}



回答2:


I'm not sure but I can't see notifyDataSetChanged() inside your callback. Try to add it.



来源:https://stackoverflow.com/questions/45209085/android-how-to-use-mfusedlocationclient-callback-resulting-location-in-curre

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