-1

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;
    }
}
}

2 Answers2

0

Asynctask is a Thread, which only executes the tasks which it should and doesn't need a Looper and though there is no Looper called for this Thread. The Locationlistener works different, than that, what you have tried. OnLocationchanged get's called as long as you don't stop the listener, but this is happening asynchronously and you can't just wait inside an asynctask for this to finish. You have to start the locationlistener from your Mainthread and whenever your Location gets changed the function onLocationChanged gets called. You could for example put your onPostExecution inside the 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();
           FrgPersonInfo frgPersonInfo = new FrgPersonInfo();
           System.out.println(city);
           frgPersonInfo.saveUserToTable(s);
        } catch (IOException e) {
            e.printStackTrace();
        } catch (Exception e){
            e.getMessage();
        }
    }
Dominik Wuttke
  • 535
  • 1
  • 4
  • 12
  • when i tried it like that it called the saveUserToTable like 10 times – NoobProgrammer Jan 15 '20 at 18:14
  • sure, because you are getting infinite location updates. You should either ask for a singleupdate or stop the updates, when you got enought calls – Dominik Wuttke Jan 15 '20 at 18:16
  • you can enter null as looper for requestSingleUpdate – Dominik Wuttke Jan 15 '20 at 18:18
  • locationManager.removeUpdates(listener). You have to keep a reference to the registered listener to remove them later https://stackoverflow.com/questions/14757980/how-to-stop-location-listeners – Dominik Wuttke Jan 15 '20 at 18:23
  • im lost in my own code, don't know what to do ... i need the AsyncTask or not? – NoobProgrammer Jan 15 '20 at 18:26
  • no, you don't need the asynctask. Locationupdates are async on their own. You need to request updates from your mainthread and keep a reference to the listener to remove it later on, or you just use requestsingleupdate to get only one update and put your output into the listener as shown in my code. – Dominik Wuttke Jan 15 '20 at 18:30
0

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.

Dominik Wuttke
  • 535
  • 1
  • 4
  • 12
  • Missing Permissions Maybe? Or it is not able to get a GPS Signal. Check in your Settings for the app if you turned on the permissions for location – Dominik Wuttke Jan 15 '20 at 20:42
  • where do i create the listener? in the fragment or the GeocoderTask? the permissions are turned on i checked – NoobProgrammer Jan 15 '20 at 20:50
  • Doesn't matter as Long as you can use it for the request. You can create him inside the GeoCoderTask and don't even Need to supply a listener as value for getUpdate, or you can create it in your Fragment and use it as a value to call getUpdate – Dominik Wuttke Jan 15 '20 at 20:52
  • if i put it in the Geocoded i need to create it inside the getUpdate? – NoobProgrammer Jan 15 '20 at 20:56
  • I edited it, this way the LocationListener is created inside the class and called inside the update – Dominik Wuttke Jan 15 '20 at 21:00
  • only difference that i call the saveUserToTable from the `try` Attempt to invoke virtual method 'android.text.Editable android.widget.EditText.getText()' on a null object reference at com.myapppack.mymatcherarad.fragments.FrgPersonInfo.saveUserToTable(FrgPersonInfo.java:133) – NoobProgrammer Jan 15 '20 at 21:08
  • If you don't have a reference to the EditText you need to get one, but i don't know how and where you already got it or can get it from – Dominik Wuttke Jan 15 '20 at 21:17
  • i do, i will edit and post the full fragment class it is in the onCreateView method – NoobProgrammer Jan 15 '20 at 21:27
  • after i try to call the getUpdate() method, it calls the saveUserToTable() method and it brings back this error – NoobProgrammer Jan 15 '20 at 21:39
  • Probably City is still null, because you never assign it anywhere – Dominik Wuttke Jan 15 '20 at 21:40
  • instead of creating a whole new class for GeoCoder you could do everything in your fragment and assign city on locationchanged and than call saveUserToTable when you re assigned city – Dominik Wuttke Jan 15 '20 at 21:42
  • and if i use the singleUpdate it will only run one time ? – NoobProgrammer Jan 15 '20 at 21:45
  • Yes, that's why it is called singleUpdate – Dominik Wuttke Jan 15 '20 at 21:48
  • i ran with debugger on my phone, it brings back the cityAddress as null. in the listener onLoactionChanged() method. i have the permissions on and i checked it. is it possible that the problem is the phone? – NoobProgrammer Jan 15 '20 at 22:02
  • Yes, but you should read the Description of geocode, i guess it describes, in which circumstances the response is null – Dominik Wuttke Jan 15 '20 at 22:05
  • apparently it was unknown so it brought back null, so i put it in if statement that if the locality is null the write the "admin" to the saveUserToTable method. thank you very much! – NoobProgrammer Jan 15 '20 at 22:43