Wrote a method for creating and overlaying a route between two points. The problem is that when it's triggered in normal mode (without breakpoints), it causes NullPointerException
or IllegalStateException
, but when I run my app in Debug-mode it throws nothing and works correctly.
I'm sorry for my english (i'm from Russia) and I apologize for formulation of the question, I'm still not really familiar with stackoverflow.
Here is my method:
List<com.google.maps.model.LatLng> path;
DirectionsRoute[] routes;
DisplayMetrics metricsB = new DisplayMetrics();
int width = metricsB.widthPixels;
int heith = metricsB.heightPixels;
PolylineOptions line = new PolylineOptions();
LatLngBounds.Builder latLngBuilder = new LatLngBounds.Builder();
String dist;
public void TravelCost(LatLng startGeoPoint, LatLng stopGeoPoint) throws InterruptedException,
ApiException, IOException {
GeoApiContext geoApiContext = new GeoApiContext.Builder()
.apiKey(maps_api_key)
.build();
DirectionsApiRequest apiRequest = DirectionsApi.newRequest(geoApiContext);
apiRequest.origin(new com.google.maps.model.LatLng(startGeoPoint.latitude, startGeoPoint.longitude));
apiRequest.destination(new com.google.maps.model.LatLng(stopGeoPoint.latitude, stopGeoPoint.longitude));
apiRequest.mode(TravelMode.DRIVING); //set travelling mode
apiRequest.setCallback(new com.google.maps.PendingResult.Callback<DirectionsResult>() {
@Override
public void onResult(DirectionsResult result) {
routes = result.routes;
path = routes[0].overviewPolyline.decodePath();
dist = routes[0].legs[0].distance.humanReadable;
for (int i = 0; i < path.size(); i++) {
line.add(new com.google.android.gms.maps.model.LatLng(path.get(i).lat, path.get(i).lng));
latLngBuilder.include(new com.google.android.gms.maps.model.LatLng(path.get(i).lat, path.get(i).lng));
}
}
@Override
public void onFailure(Throwable e) {
Toast.makeText(MapsActivity.this, "Error!", Toast.LENGTH_SHORT).show();
}
});
line.width(16f).color(R.color.purple_500);
map.addPolyline(line);
LatLngBounds latLngBounds = latLngBuilder.build();
CameraUpdate track = CameraUpdateFactory.newLatLngBounds(latLngBounds, 1080, 1920, 25);
map.moveCamera(track);
}
Here are errors from log:
E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.plukash.travelcost, PID: 19561
java.lang.IllegalStateException: Could not execute method for android:onClick
at androidx.appcompat.app.AppCompatViewInflater$DeclaredOnClickListener.onClick(AppCompatViewInflater.java:414)
at android.view.View.performClick(View.java:7448)
at com.google.android.material.button.MaterialButton.performClick(MaterialButton.java:1119)
at android.view.View.performClickInternal(View.java:7425)
at android.view.View.access$3600(View.java:810)
at android.view.View$PerformClick.run(View.java:28305)
at android.os.Handler.handleCallback(Handler.java:938)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:223)
at android.app.ActivityThread.main(ActivityThread.java:7656)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:592)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:947)
Caused by: java.lang.reflect.InvocationTargetException
at java.lang.reflect.Method.invoke(Native Method)
at androidx.appcompat.app.AppCompatViewInflater$DeclaredOnClickListener.onClick(AppCompatViewInflater.java:409)
at android.view.View.performClick(View.java:7448)
at com.google.android.material.button.MaterialButton.performClick(MaterialButton.java:1119)
at android.view.View.performClickInternal(View.java:7425)
at android.view.View.access$3600(View.java:810)
at android.view.View$PerformClick.run(View.java:28305)
at android.os.Handler.handleCallback(Handler.java:938)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:223)
at android.app.ActivityThread.main(ActivityThread.java:7656)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:592)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:947)
Caused by: java.lang.IllegalStateException: no included points
at com.google.android.gms.common.internal.Preconditions.checkState(Unknown Source:29)
at com.google.android.gms.maps.model.LatLngBounds$Builder.build(Unknown Source:21)
at com.plukash.travelcost.MapsActivity.TravelCost(MapsActivity.java:305)
at com.plukash.travelcost.MapsActivity.onMapSearch(MapsActivity.java:142)
at java.lang.reflect.Method.invoke(Native Method)
at androidx.appcompat.app.AppCompatViewInflater$DeclaredOnClickListener.onClick(AppCompatViewInflater.java:409)
at android.view.View.performClick(View.java:7448)
at com.google.android.material.button.MaterialButton.performClick(MaterialButton.java:1119)
at android.view.View.performClickInternal(View.java:7425)
at android.view.View.access$3600(View.java:810)
at android.view.View$PerformClick.run(View.java:28305)
at android.os.Handler.handleCallback(Handler.java:938)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:223)
at android.app.ActivityThread.main(ActivityThread.java:7656)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:592)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:947)
This is XML-layout file.
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/bottom_sheet_behavior">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="5dp"
android:orientation="horizontal">
<EditText
android:id="@+id/LocSearch"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="4"
android:hint="Куда поедем?"
android:inputType="text"
tools:ignore="Autofill,HardcodedText" />
<Button
android:id="@+id/search_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="0.5"
android:onClick="onMapSearch" <------- OnClick Method
android:text="Search"
tools:ignore="HardcodedText" />
</LinearLayout>
<fragment
android:id="@+id/map"
android:name="com.google.android.gms.maps.SupportMapFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MapsActivity"
tools:ignore="FragmentTagUsage" />
</LinearLayout>
<include layout="@layout/bottom_sheet" />
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="15dp"
android:src="@drawable/go_px"
app:layout_anchor="@+id/bottom_sheet"
app:layout_anchorGravity="top|end"
tools:ignore="ContentDescription" />
</androidx.coordinatorlayout.widget.CoordinatorLayout>
And here is a On-click method for Search-button that calls for "TravelCost" method.
public void onMapSearch(View view) throws InterruptedException, ApiException, IOException {
boolean bool = TrafficJam();
try {
Button search = findViewById(R.id.search_button);
String location = locationSearch.getText().toString();
List<Address> addressList;
Geocoder geocoder = new Geocoder(this);
try {
addressList = geocoder.getFromLocationName(location, 1);
} catch (IOException e) {
e.printStackTrace();
Toast toast = new Toast(this);
toast.setText("Введите корректный адрес!");
toast.show();
return;
}
if (addressList != null) {
Address address = addressList.get(0);
latLng = new LatLng(address.getLatitude(), address.getLongitude());
map.addMarker(new MarkerOptions().position(latLng).title("Marker"));
map.animateCamera(CameraUpdateFactory.newLatLng(latLng));
//Убирать ли текст в поисковом поле?
//locationSearch.setText("");
InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
imm.hideSoftInputFromWindow(search.getWindowToken(),
InputMethodManager.HIDE_NOT_ALWAYS);
} else {
Toast toast = new Toast(this);
toast.setText("Введите адрес!");
toast.show();
}
} catch (Exception e) {
Toast toast = new Toast(this);
toast.setText("Введите корректный адрес!");
toast.show();
return;
}
if (checker == 1) {
startpoint = latLng;
checker += 1;
locationSearch.setText("");
locationSearch.setHint("Куда поедем?");
} else if (checker == 2) {
LatLng endpoint = latLng;
locationSearch.setText("Откуда поедем?");
checker = 1;
TravelCost(startpoint, endpoint); <-------------- Method call
}
}
UPD
Need to return values from other Thread.
DirectionsRoute[] routes;
public void TravelCost(LatLng startGeoPoint, LatLng stopGeoPoint) {
GeoApiContext geoApiContext = new GeoApiContext.Builder()
.apiKey("AIzaSyA9qY28oZ-4TTdDt1jgxdCKLYh9T8Kh0Ss")
.build();
DirectionsApiRequest apiRequest = DirectionsApi.newRequest(geoApiContext);
apiRequest.origin(new com.google.maps.model.LatLng(startGeoPoint.latitude, startGeoPoint.longitude));
apiRequest.destination(new com.google.maps.model.LatLng(stopGeoPoint.latitude, stopGeoPoint.longitude));
apiRequest.mode(TravelMode.DRIVING);
apiRequest.language("Russian");//set travelling mode
AppExecutors.getInstance().networkIO().execute(() -> {
apiRequest.setCallback(new com.google.maps.PendingResult.Callback<DirectionsResult>() {
@Override
public void onResult(DirectionsResult result) {
routes = result.routes;
<---- **routes here contains values**
}
@Override
public void onFailure(Throwable e) {
Toast.makeText(MapsActivity.this, "Error!", Toast.LENGTH_SHORT).show();
}
});
});
<---- **Here routes is nullarray.**
List<com.google.maps.model.LatLng> path = routes[0].overviewPolyline.decodePath();
dist = routes[0].legs[0].distance.humanReadable;
for (int i = 0; i < path.size(); i++) {
line.add(new com.google.android.gms.maps.model.LatLng(path.get(i).lat, path.get(i).lng));
latLngBuilder.include(new com.google.android.gms.maps.model.LatLng(path.get(i).lat, path.get(i).lng));
}
line.width(16f).color(R.color.purple_500);
map.addPolyline(line);
LatLngBounds latLngBounds = latLngBuilder.build();
CameraUpdate track = CameraUpdateFactory.newLatLngBounds(latLngBounds, 1080, 1920, 25);
map.moveCamera(track);
}
Your problem seems to be with the android:onClick
the system is not finding the method and it's throwing an error because of it.
My hypothesis is that this is happening because you are running the app with proguard enabled, proguard may be changing the method name, and this will cause this exception.
I would advise using a click listener since it's considered to be bad practice to use the android:onClick
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Button button = findViewById(R.id.search_button);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
onMapSearch(v);
}
});
// implementation of an onclick listener using a lambda instead
button.setOnClickListener(v -> onMapSearch(v));
}
Also, you may consider deleting the view parameter in onMapSearch
since it's not used anymore
To fix the android.os.NetworkOnMainThreadException
you need to run the network call outside of the main thread, note that it's not advisable to use AsyncTask since it's has been deprecated for a long time now.
Worker example
public class AppExecutors {
private static final Object LOCK = new Object();
private static AppExecutors sInstance;
private static Executor processor;
private final Executor diskIO;
private final Executor networkIO;
private final Executor mainThread;
private AppExecutors(Executor diskIO, Executor networkIO, Executor mainThread){
this.diskIO = diskIO;
this.networkIO = networkIO;
this.mainThread = mainThread;
}
public static AppExecutors getInstance(){
if(sInstance == null){
synchronized (LOCK){
sInstance = new AppExecutors(Executors.newSingleThreadExecutor(),
Executors.newFixedThreadPool(3),
new MainThreadExecutor());
}
}
return sInstance;
}
public Executor diskIO() {
return diskIO;
}
public Executor networkIO() {
return networkIO;
}
public Executor getMainThread() {
return mainThread;
}
private static class MainThreadExecutor implements Executor {
private final Handler mainThreadHandler = new Handler(Looper.getMainLooper());
@Override
public void execute(@NotNull Runnable command) {
mainThreadHandler.post(command);
}
}
}
Usage
AppExecutors.getInstance().networkIO().execute(() -> {
// Network call goes here
});