Search code examples
javaandroidandroid-asynctaskgeocoder

Can't create handler inside thread that has not called Looper.prepare() in AsyncTask


im trying to get the city using Geocoder that android have its app we creating as homework. I'm trying to do it inside a AsyncTask but i get the this exception:

Can't create handler inside thread that has not called Looper.prepare()

the AsyncTask code:

public class GeocoderTask extends AsyncTask<Void, Void, String> {

LocationManager locationManager;
Location location;
Context context;
String city;

public GeocoderTask(Context context) {
    this.context = context;
    locationManager = (LocationManager)context.getSystemService(Context.LOCATION_SERVICE);
}

@SuppressLint("MissingPermission")
@Override
protected String doInBackground(Void... voids) {
    locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0, new LocationListener() {
        @Override
        public void onLocationChanged(Location location) {
            GeocoderTask.this.location = location;
            Geocoder geocoder = new Geocoder(context, Locale.getDefault());
            List<Address> addresses;
            try {
               addresses  = geocoder.getFromLocation(GeocoderTask.this.location.getLatitude(), GeocoderTask.this.location.getLongitude(), 3);
               city = addresses.get(0).getLocality();
            } catch (IOException e) {
                e.printStackTrace();
            } catch (Exception e){
                e.getMessage();
            }
        }

        @Override
        public void onStatusChanged(String provider, int status, Bundle extras) {

        }

        @Override
        public void onProviderEnabled(String provider) {

        }

        @Override
        public void onProviderDisabled(String provider) {

        }
    });
    return city;
}

@Override
protected void onPostExecute(String s) {
    super.onPostExecute(s);
    FrgPersonInfo frgPersonInfo = new FrgPersonInfo();
    System.out.println(s);
    frgPersonInfo.saveUserToTable(s);
}

}

I'm calling the AsyncTask from fragment the calling from the fragment:

 view.findViewById(R.id.btnRegister).setOnClickListener(new View.OnClickListener() {
        @SuppressLint("MissingPermission")
        @Override
        public void onClick(View v) {
            checkPermissions();
            GeocoderTask geocoderTask = new GeocoderTask(context);
            geocoderTask.execute();
        }
    });

the method I'm calling in onPostExecute in the AsyncTask:

public void saveUserToTable(String city) {
    String age = uAge.getText().toString();
    String name = fName.getText().toString();

    UserInfo userInfo = new UserInfo();
    userInfo.setIsConnected(true);
    userInfo.setUserImage(imageUrl);
    userInfo.setAge(Integer.valueOf(age));
    userInfo.setName(name);
    userInfo.setCity(city);

    Backendless.Data.of(UserInfo.class).save(userInfo, new AsyncCallback<UserInfo>() {
        @Override
        public void handleResponse(UserInfo response) {
            System.out.println("Bitch, im here again!");
            ((TextView)parentView.findViewById(R.id.loginFrgBtn)).setTextColor(Color.BLUE);
            ((TextView)parentView.findViewById(R.id.registerFrgBtn)).setTextColor(Color.BLACK);

            FragmentTransaction ft = fm.beginTransaction();
            ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
            FrgLogin frgLogin = new FrgLogin();
            ft.replace(R.id.container, frgLogin);
            ft.commit();
            TastyToast.makeText(context, "Welcome!", TastyToast.LENGTH_LONG, TastyToast.SUCCESS).show();
        }

        @Override
        public void handleFault(BackendlessFault fault) {
            TastyToast.makeText(context, fault.getMessage(), TastyToast.LENGTH_LONG, TastyToast.ERROR).show();
        }
    });
}

the checkPermission:

 private void checkPermissions() {
    List<String> neededPerms = new ArrayList<>();
    int fineGpsPerm = context.checkSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION);
    int coarseGpsPerm = context.checkSelfPermission(Manifest.permission.ACCESS_COARSE_LOCATION);

    if (fineGpsPerm != PackageManager.PERMISSION_GRANTED || coarseGpsPerm != PackageManager.PERMISSION_GRANTED) {
        neededPerms.add(Manifest.permission.ACCESS_FINE_LOCATION);
        neededPerms.add(Manifest.permission.ACCESS_COARSE_LOCATION);
    }

    if (!neededPerms.isEmpty()) {
        ActivityCompat.requestPermissions( getActivity(), neededPerms.toArray(new String[neededPerms.size()]), GPS_PERM_CODE);
    }
}

@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
    super.onRequestPermissionsResult(requestCode, permissions, grantResults);
    switch (requestCode){
        case GPS_PERM_CODE:
            if (grantResults[0] != PackageManager.PERMISSION_GRANTED|| grantResults[1] != PackageManager.PERMISSION_GRANTED) {
                Toast.makeText(context, "Need to Allow perms First", Toast.LENGTH_SHORT).show();
                checkPermissions();
            }
            break;
    }
}

the fragment class:

public class FrgPersonInfo extends Fragment{
public static final int GPS_PERM_CODE = 103;
Context context;
EditText fName, uAge, uCity;
String imageUrl = "";

FragmentManager fm;
View parentView;

LocationManager locationManager;
Location location;
boolean isLocEnabled = false;
String cityAddress = "";


@Override
public void onAttach(@NonNull Context context) {
    super.onAttach(context);
    this.context = context;
    System.out.println("in person onattach");
}

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

@SuppressLint("MissingPermission")
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
    //inflating the wanted fragmentView
    View myView = inflater.inflate(R.layout.frg_person_info, container, false);
    // init the fields
    fName = myView.findViewById(R.id.fName);
    uAge = myView.findViewById(R.id.userAge);
    uCity = myView.findViewById(R.id.userCity);
    fm = getActivity().getSupportFragmentManager();
    locationManager = (LocationManager)context.getSystemService(Context.LOCATION_SERVICE);
    return myView;
}

@SuppressLint("MissingPermission")
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
    parentView = view.getRootView();
    //creating a bundle so we can (in this case) get data
    Bundle bundle = getArguments();
    //use the get.. method to get data by key
//        imageUrl = bundle.getString(FrgRegister.IMAGE_KEY);
    checkPermissions();

    if (isLocEnabled) {

    } else {
        Toast.makeText(context, "dont have perms", Toast.LENGTH_SHORT).show();
    }


    view.findViewById(R.id.btnRegister).setOnClickListener(new View.OnClickListener() {
        @SuppressLint("MissingPermission")
        @Override
        public void onClick(View v) {
            checkPermissions();
            GeocoderTask geocoderTask = new GeocoderTask(context);
            geocoderTask.getUpdate();
        }
    });
}


@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
    super.onActivityCreated(savedInstanceState);
}

@Override
public void onDetach() {
    super.onDetach();
    this.fName = null;
    this.uAge = null;
    this.uCity = null;
    this.fm = null;
    this.imageUrl = null;
}


public void saveUserToTable(String city) {
    String age = uAge.getText().toString();
    String name = fName.getText().toString();

    UserInfo userInfo = new UserInfo();
    userInfo.setIsConnected(true);
    userInfo.setUserImage(imageUrl);
    userInfo.setAge(Integer.valueOf(age));
    userInfo.setName(name);
    userInfo.setCity(city);

    Backendless.Data.of(UserInfo.class).save(userInfo, new AsyncCallback<UserInfo>() {
        @Override
        public void handleResponse(UserInfo response) {
            System.out.println("Bitch, im here again!");
            ((TextView)parentView.findViewById(R.id.loginFrgBtn)).setTextColor(Color.BLUE);
            ((TextView)parentView.findViewById(R.id.registerFrgBtn)).setTextColor(Color.BLACK);

            FragmentTransaction ft = fm.beginTransaction();
            ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
            FrgLogin frgLogin = new FrgLogin();
            ft.replace(R.id.container, frgLogin);
            ft.commit();
            TastyToast.makeText(context, "Welcome!", TastyToast.LENGTH_LONG, TastyToast.SUCCESS).show();
        }

        @Override
        public void handleFault(BackendlessFault fault) {
            TastyToast.makeText(context, fault.getMessage(), TastyToast.LENGTH_LONG, TastyToast.ERROR).show();
        }
    });
}


private void checkPermissions() {
    List<String> neededPerms = new ArrayList<>();
    int fineGpsPerm = context.checkSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION);
    int coarseGpsPerm = context.checkSelfPermission(Manifest.permission.ACCESS_COARSE_LOCATION);

    if (fineGpsPerm != PackageManager.PERMISSION_GRANTED || coarseGpsPerm != PackageManager.PERMISSION_GRANTED) {
        neededPerms.add(Manifest.permission.ACCESS_FINE_LOCATION);
        neededPerms.add(Manifest.permission.ACCESS_COARSE_LOCATION);
    }

    if (!neededPerms.isEmpty()) {
        ActivityCompat.requestPermissions( getActivity(), neededPerms.toArray(new String[neededPerms.size()]), GPS_PERM_CODE);
    } else {
        isLocEnabled = true;
    }
}

@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
    super.onRequestPermissionsResult(requestCode, permissions, grantResults);
    switch (requestCode){
        case GPS_PERM_CODE:
            if (grantResults[0] != PackageManager.PERMISSION_GRANTED || grantResults[1] != PackageManager.PERMISSION_GRANTED) {
                checkPermissions();
            } else {
                isLocEnabled = true;
            }
            break;
    }
}
}

Solution

  • Create the locationListener to keep a reference to it.

    Create a class to trigger the LocationUpdates

    public class GeocoderTask {
    
    LocationManager locationManager;
    Location location;
    Context context;
    String city;
    
    LocationListener locationListener = new LocationListener(){
    @Override
        public void onLocationChanged(Location location) {
            GeocoderTask.this.location = location;
            Geocoder geocoder = new Geocoder(context, Locale.getDefault());
            List<Address> addresses;
            FrgPersonInfo frgPersonInfo = new FrgPersonInfo();
            System.out.println(city);
            frgPersonInfo.saveUserToTable(s);
            try {
               addresses  = geocoder.getFromLocation(GeocoderTask.this.location.getLatitude(), GeocoderTask.this.location.getLongitude(), 3);
               city = addresses.get(0).getLocality();
            } catch (IOException e) {
                e.printStackTrace();
            } catch (Exception e){
                e.getMessage();
            }
        }
        @Override
        public void onStatusChanged(String provider, int status, Bundle extras) {}
        @Override
        public void onProviderEnabled(String provider) {}
        @Override
        public void onProviderDisabled(String provider) {}
    }
    public GeocoderTask(Context context) {
    this.context = context;
    locationManager = (LocationManager)context.getSystemService(Context.LOCATION_SERVICE);
    }
    
    public void getUpdate(){
    locationManager.requestSingleUpdate(LocationManager.GPS_PROVIDER,locationListener,null
    }
    

    You just have to create the class and use the locationListener to call the Function getUpdate, this will call the Update once. You could also use requestUpdates and create a function to remove the listener if you want more updates.