Объект GoogleMap имеет нулевое значение при попытке перехода со старой версии. Код карты Google (Android Программирование приложений) - проблемы при переходе на более новую версию? - PullRequest
1 голос
/ 03 февраля 2020

Я новичок ie в Android Программирование (очень мало знаю о Android программировании). Я пытаюсь читать и кодировать проект в книге, но это старый проект, и большинство зависимостей (Android Studio, SDK, Google Maps, ...) устарели. Я пытаюсь перенести проект на более новую версию, но все еще возникают некоторые проблемы, и программа не запускается.

Здесь находится файл MapsActivity. java, большая часть которого посвящена отображению точки интереса ( POI) и показать карту с помощью Google Maps:

package hitlexamples.happywalk.activities;

import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Handler;
import android.os.IBinder;
import android.os.Bundle;
import android.os.Looper;
import android.support.v4.app.FragmentActivity;
import android.support.v7.app.ActionBarActivity;
import android.support.v7.app.AppCompatActivity;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;

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.CameraPosition;
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 java.util.ArrayList;
import java.util.Map;

import hitlexamples.happywalk.R;
import hitlexamples.happywalk.cluster.GeoClusterer;
import hitlexamples.happywalk.cluster.GeoItem;
import hitlexamples.happywalk.service.HappyWalkService;
import hitlexamples.happywalk.tasks.ThreadGetPoi;
import hitlexamples.happywalk.utilities.GlobalVariables;

public class MapsActivity extends FragmentActivity implements OnMapReadyCallback {

    /*  ------------ DEVELOPMENT NOTES ----------------------
    - TODO: Fix for devices with low DPI
    */

    private GoogleMap mMap; // Might be null if Google Play services APK is not available.
    private Handler uiHandler; //handler used by other classes to post tasks to the Ui Thread
    private Marker userMarker;
    private HappyWalkService hWService;

    /** This list contains all available POIs fetched at activity start **/
    private ArrayList<GeoItem> geoItems = new ArrayList<GeoItem>();
    private GeoClusterer clusterer;
    //Thread responsible for fetching POIs
    private Thread getpoi;

    //workflow variables
    private int requestCode = 99;

    // GETS AND SETS ------------------------------

    public void setGeoItems(ArrayList<GeoItem> geoItems) {
        this.geoItems = geoItems;
    }

    public Handler getUiHandler() {
        return uiHandler;
    }

    public GoogleMap getmMap() {
        return mMap;
    }

    // --------------------------------------------

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_maps);
        SupportMapFragment mapFragment = (SupportMapFragment) getSupportFragmentManager().findFragmentById(R.id.map);
        mapFragment.getMapAsync(this);

        uiHandler = new Handler(Looper.getMainLooper());
        //initialize getpoi as an empty thread; will be replaced when necessary.
        getpoi = new Thread();
        //(Re)start our service.
        Intent startHWService = new Intent(this, HappyWalkService.class);
        startService(startHWService);

        initializeMapComponents();
    }

    private ServiceConnection hwConnection = new ServiceConnection() {
        public void onServiceConnected(ComponentName className, IBinder service) {
            /*
             This is called when the connection with the service has been established, giving us a service object we can use to interact with the service.  Because we have bound to a explicit service that we know is running in our own process, we can cast its IBinder to a concrete class and directly access it.
            */
            hWService = ((HappyWalkService.HappyWalkBinder)service).getService(MapsActivity.this);

            /* Now we check from where this activity is being initiated from */
            if (requestCode == GlobalVariables.AREQ_POI_DESCRIPTION_REQUEST) {
                /* if we come from POI description, we do nothing.
                * The camera should already be focused on the appropriate POI*/
                //revert requestCode
                requestCode = 99;
            }
            else {
                LatLng position;
                //If our service was previously running, let us focus the camera on the user's current position.
                if((position = hWService.getHwLocationListener().getActualposition()) !=null) {
                    updateUserMarkerPosition(position);
                    focusCameraOnPosition(position);
                }
                //if not, let us focus our camera onto the last known position
                else if ((position = hWService.getHwLocationListener().
                        getLastKnownPositionFromSharedPrefs()) != null){
                    focusCameraOnPosition(position);
                }
            }
        }

        public void onServiceDisconnected(ComponentName className) {
            // This is called when the connection with the service has been
            // unexpectedly disconnected -- that is, its process crashed.
            // Because it is running in our same process, we should never
            // see this happen.
            hWService = null;
        }
    };

    private void initializeMapComponents() {

        setUpMapIfNeeded();

        // Initialize the clusterer
        float screenDensity = this.getResources().getDisplayMetrics().density;
        clusterer = new GeoClusterer(this, screenDensity, mMap.getCameraPosition().target);
        GeoClusterer.ClusterMarkerListener clustLis = clusterer.new ClusterMarkerListener();
        GeoClusterer.CameraChangeListener cameraLis = clusterer.new CameraChangeListener();
        mMap.setOnMarkerClickListener(clustLis);
        mMap.setOnCameraChangeListener(cameraLis);
    }

    /**
     * Here we have to bind to our HappyWalk service again, since we unbinded on pause.
     */
    @Override
    protected void onResume() {
        //check the request code
        /* if we are coming from a POI description, ignore this step
        otherwise, we will wrongly overwrite the requestCode*/
        if (getIntent().getExtras() != null
                && getIntent().getExtras().containsKey(GlobalVariables.BND_EXTRA_REQ_CODE_KEY)
                && requestCode != GlobalVariables.AREQ_POI_DESCRIPTION_REQUEST) {
            requestCode = getIntent().getExtras().
                    getInt(GlobalVariables.BND_EXTRA_REQ_CODE_KEY);
            getIntent().removeExtra(GlobalVariables.BND_EXTRA_REQ_CODE_KEY);
        }
        bindHwService();
        super.onResume();
    }

    /**
     * Here we stop the notification requests, the accelerometer collection,
     * and the threads that fetch POIs and draw markers.
     */
    @Override
    protected void onPause() {
        getpoi.interrupt();
        clusterer.stopMarkerUpdate();
        unBindHwService();
        super.onPause();
    }

    /**
     * Since this activity is a single instance, this is called whenever we have a new intent
     */
    @Override
    protected void onNewIntent(Intent intent) {
        //update the intent
        setIntent(intent);
        super.onNewIntent(intent);
    }

    @Override
    public void onMapReady(GoogleMap map) {
        // Place your logic here
        mMap = map;
        map.setIndoorEnabled(true);
        map.setBuildingsEnabled(true);
        map.getUiSettings().setZoomControlsEnabled(false);

    }

    // MAP MANIPULATION METHODS ---------------------------

    private void setUpMapIfNeeded() {
        // Do a null check to confirm that we have not already instantiated the map.
        if (mMap == null) {
            SupportMapFragment mapFragment = (SupportMapFragment) getSupportFragmentManager().findFragmentById(R.id.map);
            mapFragment.getMapAsync(this);
        }
    }

    /**
    Updates the position of the user marker
     */
    public void updateUserMarkerPosition(final LatLng position) {
        if (userMarker == null) {
            userMarker = mMap.addMarker(new MarkerOptions().
                    //position(position).
                    //icon(BitmapDescriptorFactory.fromResource(R.drawable.marker_user_position)));
                    position(position));
        }
        else {
            userMarker.setPosition(position);
        }
    }

    /**
     * This function changes the camera to a certain position. It is used by
     * the HwLocationListener to change the camera the first time the user changed location
     */
    public void focusCameraOnPosition(final LatLng position) {
        mMap.getUiSettings().setAllGesturesEnabled(false);
        CameraPosition cameraPosition = new CameraPosition.Builder()
                .target(position)
                .zoom(GlobalVariables.ZOOM_USER_OVERVIEW)
                .build();
        mMap.animateCamera(CameraUpdateFactory.newCameraPosition(cameraPosition),1500,null);
        mMap.getUiSettings().setAllGesturesEnabled(true);
    }

    // ----------------------------------------
    // Workflow methods
    // ----------------------------------------

    /**
     * This class starts the thread necessary to request a fresh list of POIs on the App
     */
    public void refreshPOIs(double latitude, double longitude) {
        while (getpoi.isAlive()) {
            getpoi.interrupt();
        }
        getpoi = new ThreadGetPoi(this, latitude, longitude);
        getpoi.start();
    }

    /**
     * Performs clustering of the GeoItems in memory
     */
    public void refreshGeoItems() {
        /*
        refreshing the geoItems is kinda important, so we want it done
        ASAP and, thus, don't use our handler.
         */
        runOnUiThread(new Runnable() {
            public void run() {
                clusterer.refreshGeoItems(geoItems);
            }
        });
    }

    public void showPOIDetails(String idPoi) {
        Intent myIntent = new Intent(getApplicationContext(),
                POIDescription.class);
        myIntent.putExtra("idPoi", idPoi);
        startActivity(myIntent);
        requestCode = GlobalVariables.AREQ_POI_DESCRIPTION_REQUEST;
    }

    /**
     * This method binds our MapRouteActivity to the NotificationService
     */
    private void bindHwService() {
        bindService(new Intent(this, HappyWalkService.class), hwConnection, Context.BIND_AUTO_CREATE);
    }

    /**
     * This method unbinds our MapRouteActivity from the NotificationService
     */
    private void unBindHwService() {
        unbindService(hwConnection);
        hWService = null;
    }

    /**
     * This method is called when the user presses the "Exit" menu option
     */
    private void performExit() {
        //we don't need to unBind to the service, since we already do that onPause();
        stopService(new Intent(this, HappyWalkService.class));
        this.finish();
    }

    // ----------------------------------------
    // App Menu
    // ----------------------------------------
    /**
     * This method handles the options Menu
     */
    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        MenuInflater inflater = getMenuInflater();
        inflater.inflate(R.menu.map_menu, menu);
        return true;
    }

    /**
     * This method handles menu items
     */
    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Handle item selection
        switch (item.getItemId()) {
            case R.id.menuExit:
                performExit();
                return true;
            default:
                return super.onOptionsItemSelected(item);
        }
    }
}

Вот ошибка, с которой я столкнулся:

02/02 16:01:23: Launching 'app' on Pixel 3 API 27.
$ adb shell am start -n "hitlexamples.happywalk/hitlexamples.happywalk.activities.MapsActivity" -a android.intent.action.MAIN -c android.intent.category.LAUNCHER
Waiting for process to come online...
Connected to process 3972 on device 'Pixel_3_API_27 [emulator-5554]'.
Capturing and displaying logcat messages from application. This behavior can be disabled in the "Logcat output" section of the "Debugger" settings page.
I/FirebaseInitProvider: FirebaseApp initialization unsuccessful
I/zzbx: Making Creator dynamically
I/zygote: The ClassLoaderContext is a special shared library.
I/chatty: uid=10080(hitlexamples.happywalk) identical 1 line
I/zygote: The ClassLoaderContext is a special shared library.
W/zygote: Unsupported class loader
W/zygote: Skipping duplicate class check due to unsupported classloader
I/Google Maps Android API: Google Play services client version: 11020000
I/Google Maps Android API: Google Play services package version: 11580470
I/zygote: Do partial code cache collection, code=30KB, data=15KB
I/zygote: After code cache collection, code=26KB, data=14KB
    Increasing code cache capacity to 128KB
I/zygote: Background concurrent copying GC freed 14373(3MB) AllocSpace objects, 1(148KB) LOS objects, 49% free, 1904KB/3MB, paused 12us total 192.833ms
D/AndroidRuntime: Shutting down VM
E/AndroidRuntime: FATAL EXCEPTION: main
    Process: hitlexamples.happywalk, PID: 3972
    java.lang.RuntimeException: Unable to start activity ComponentInfo{hitlexamples.happywalk/hitlexamples.happywalk.activities.MapsActivity}: java.lang.NullPointerException: Attempt to invoke virtual method 'com.google.android.gms.maps.model.CameraPosition com.google.android.gms.maps.GoogleMap.getCameraPosition()' on a null object reference
        at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2778)
        at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2856)
        at android.app.ActivityThread.-wrap11(Unknown Source:0)
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1589)
        at android.os.Handler.dispatchMessage(Handler.java:106)
        at android.os.Looper.loop(Looper.java:164)
        at android.app.ActivityThread.main(ActivityThread.java:6494)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:438)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:807)
     Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'com.google.android.gms.maps.model.CameraPosition com.google.android.gms.maps.GoogleMap.getCameraPosition()' on a null object reference
        at hitlexamples.happywalk.activities.MapsActivity.initializeMapComponents(MapsActivity.java:134)
        at hitlexamples.happywalk.activities.MapsActivity.onCreate(MapsActivity.java:87)
        at android.app.Activity.performCreate(Activity.java:7009)
        at android.app.Activity.performCreate(Activity.java:7000)
        at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1214)
        at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2731)
        at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2856) 
        at android.app.ActivityThread.-wrap11(Unknown Source:0) 
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1589) 
        at android.os.Handler.dispatchMessage(Handler.java:106) 
        at android.os.Looper.loop(Looper.java:164) 
        at android.app.ActivityThread.main(ActivityThread.java:6494) 
        at java.lang.reflect.Method.invoke(Native Method) 
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:438) 
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:807) 
W/DynamiteModule: Local module descriptor class for com.google.android.gms.googlecertificates not found.
W/zygote: Unsupported class loader
W/zygote: Skipping duplicate class check due to unsupported classloader
I/DynamiteModule: Considering local module com.google.android.gms.googlecertificates:0 and remote module com.google.android.gms.googlecertificates:4
    Selected remote version of com.google.android.gms.googlecertificates, version >= 4
W/zygote: Unsupported class loader
    Skipping duplicate class check due to unsupported classloader
Process 3972 terminated.

Может кто-нибудь помочь мне указать на проблему? Или, если вы считаете, что я предоставляю неадекватную информацию, пожалуйста, сообщите мне, чтобы опубликовать ее дальше. Я очень признателен, так как у меня нет большого опыта, и этот проект - только базовая архитектура для меня, чтобы применить знания в других областях.

Вы можете найти весь проект HappyWalk на этом git сайте. : https://git.dei.uc.pt/dsnunes/happywalk.git

Если кто-нибудь может помочь мне перенести весь проект на более новую версию Android и Карты Google, я очень признателен!

Отредактировано: у меня есть попытался изменить код, как сказал @Evan, но я все еще сталкивался с некоторыми ошибками (он показывает только карты всего мира, пока я ищу область рядом со мной). Может ли кто-нибудь помочь мне с этой ошибкой

02/10 10:10:45: Launching 'app' on Google Pixel 3.
$ adb shell am start -n "hitlexamples.happywalk/hitlexamples.happywalk.activities.MapsActivity" -a android.intent.action.MAIN -c android.intent.category.LAUNCHER
Waiting for process to come online...
Connected to process 20363 on device 'google-pixel_3-973X22WD3'.
Capturing and displaying logcat messages from application. This behavior can be disabled in the "Logcat output" section of the "Debugger" settings page.
I/mples.happywal: The ClassLoaderContext is a special shared library.
W/hitlexamples.happywalk: type=1400 audit(0.0:525): avc: denied { read } for comm=45474C20496E6974 name="u:object_r:vendor_default_prop:s0" dev="tmpfs" ino=24252 scontext=u:r:untrusted_app:s0:c181,c256,c512,c768 tcontext=u:object_r:vendor_default_prop:s0 tclass=file permissive=0
E/libc: Access denied finding property "vendor.debug.egl.profiler"
W/mples.happywal: Accessing hidden method Landroid/graphics/drawable/Drawable;->getOpticalInsets()Landroid/graphics/Insets; (light greylist, linking)
    Accessing hidden field Landroid/graphics/Insets;->left:I (light greylist, linking)
    Accessing hidden field Landroid/graphics/Insets;->right:I (light greylist, linking)
    Accessing hidden field Landroid/graphics/Insets;->top:I (light greylist, linking)
    Accessing hidden field Landroid/graphics/Insets;->bottom:I (light greylist, linking)
W/mples.happywal: Accessing hidden method Landroid/view/View;->computeFitSystemWindows(Landroid/graphics/Rect;Landroid/graphics/Rect;)Z (light greylist, reflection)
    Accessing hidden method Landroid/view/ViewGroup;->makeOptionalFitsSystemWindows()V (light greylist, reflection)
W/mples.happywal: Accessing hidden method Landroid/widget/TextView;->getTextDirectionHeuristic()Landroid/text/TextDirectionHeuristic; (light greylist, linking)
I/zzbz: Making Creator dynamically
W/mples.happywal: Unsupported class loader
W/mples.happywal: Skipping duplicate class check due to unsupported classloader
I/DynamiteModule: Considering local module com.google.android.gms.maps_dynamite:0 and remote module com.google.android.gms.maps_dynamite:222
    Selected remote version of com.google.android.gms.maps_dynamite, version >= 222
V/DynamiteModule: Dynamite loader version >= 2, using loadModule2NoCrashUtils
I/DynamiteLoaderV2: [71] Mapsdynamite
W/mples.happywal: Unsupported class loader
W/mples.happywal: Skipping duplicate class check due to unsupported classloader
I/Google Maps Android API: Google Play services client version: 12451000
I/Google Maps Android API: Google Play services package version: 20104028
W/mples.happywal: Accessing hidden field Ljava/nio/Buffer;->address:J (light greylist, reflection)
D/OpenGLRenderer: Skia GL Pipeline
I/Adreno: QUALCOMM build                   : 3f88ca2, I42f6fe38fb
    Build Date                       : 07/13/18
    OpenGL ES Shader Compiler Version: EV031.24.00.00
    Local Branch                     : 50.04
    Remote Branch                    : 
    Remote Branch                    : 
    Reconstruct Branch               : 
    Build Config                     : S P 4.0.10 AArch64
W/RenderThread: type=1400 audit(0.0:526): avc: denied { read } for name="u:object_r:vendor_default_prop:s0" dev="tmpfs" ino=24252 scontext=u:r:untrusted_app:s0:c181,c256,c512,c768 tcontext=u:object_r:vendor_default_prop:s0 tclass=file permissive=0
E/libc: Access denied finding property "ro.vendor.graphics.memory"
I/Adreno: PFP: 0x016ee170, ME: 0x00000000
I/ConfigStore: android::hardware::configstore::V1_0::ISurfaceFlingerConfigs::hasWideColorDisplay retrieved: 1
    android::hardware::configstore::V1_0::ISurfaceFlingerConfigs::hasHDRDisplay retrieved: 1
I/OpenGLRenderer: Initialized EGL, version 1.4
D/OpenGLRenderer: Swap behavior 2
E/libc: Access denied finding property "vendor.gralloc.enable_ahardware_buffer"
W/RenderThread: type=1400 audit(0.0:527): avc: denied { read } for name="u:object_r:vendor_default_prop:s0" dev="tmpfs" ino=24252 scontext=u:r:untrusted_app:s0:c181,c256,c512,c768 tcontext=u:object_r:vendor_default_prop:s0 tclass=file permissive=0
D/NetworkSecurityConfig: No Network Security Config specified, using platform default
W/DynamiteModule: Local module descriptor class for com.google.android.gms.googlecertificates not found.
I/DynamiteModule: Considering local module com.google.android.gms.googlecertificates:0 and remote module com.google.android.gms.googlecertificates:4
    Selected remote version of com.google.android.gms.googlecertificates, version >= 4
I/DynamiteLoaderV2: [71] Googlecertificates
W/mples.happywal: Unsupported class loader
W/mples.happywal: Skipping duplicate class check due to unsupported classloader
E/SchedPolicy: set_timerslack_ns write failed: Operation not permitted
Process 20363 terminated.

1 Ответ

1 голос
/ 05 февраля 2020

Я скачал ваш проект и применил некоторые изменения, чтобы помочь вам запустить его. Попробуйте изменить код, как показано ниже, и убедитесь, что вы переносите свое приложение на AndroidX, используя refactor .

MapsActivity. java

import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;

import androidx.appcompat.app.AppCompatActivity;

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.CameraPosition;
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 java.util.ArrayList;

import hitlexamples.happywalk.R;
import hitlexamples.happywalk.cluster.GeoClusterer;
import hitlexamples.happywalk.cluster.GeoItem;
import hitlexamples.happywalk.service.HappyWalkService;
import hitlexamples.happywalk.tasks.ThreadGetPoi;
import hitlexamples.happywalk.utilities.GlobalVariables;

public class MapsActivity extends AppCompatActivity implements OnMapReadyCallback {

    /*  ------------ DEVELOPMENT NOTES ----------------------
    - TODO: Fix for devices with low DPI
    */

    private GoogleMap mMap; // Might be null if Google Play services APK is not available.
    private Handler uiHandler; //handler used by other classes to post tasks to the Ui Thread
    private Marker userMarker;
    private HappyWalkService hWService;

    /**
     * This list contains all available POIs fetched at activity start
     **/
    private ArrayList<GeoItem> geoItems = new ArrayList<GeoItem>();
    private GeoClusterer clusterer;
    //Thread responsible for fetching POIs
    private Thread getpoi;

    //workflow variables
    private int requestCode = 99;

    // GETS AND SETS ------------------------------

    public void setGeoItems(ArrayList<GeoItem> geoItems) {
        this.geoItems = geoItems;
    }

    public Handler getUiHandler() {
        return uiHandler;
    }

    public GoogleMap getmMap() {
        return mMap;
    }

    // --------------------------------------------

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_maps);

        uiHandler = new Handler(Looper.getMainLooper());
        //initialize getpoi as an empty thread; will be replaced when necessary.
        getpoi = new Thread();
        //(Re)start our service.
        Intent startHWService = new Intent(this, HappyWalkService.class);
        startService(startHWService);
        initializeMapComponents();
    }

    private ServiceConnection hwConnection = new ServiceConnection() {
        public void onServiceConnected(ComponentName className, IBinder service) {
            /*
             This is called when the connection with the service has been established, giving us a service object we can use to interact with the service.  Because we have bound to a explicit service that we know is running in our own process, we can cast its IBinder to a concrete class and directly access it.
            */
            hWService = ((HappyWalkService.HappyWalkBinder) service).getService(MapsActivity.this);

            /* Now we check from where this activity is being initiated from */
            if (requestCode == GlobalVariables.AREQ_POI_DESCRIPTION_REQUEST) {
                /* if we come from POI description, we do nothing.
                 * The camera should already be focused on the appropriate POI*/
                //revert requestCode
                requestCode = 99;
            } else {
                LatLng position;
                //If our service was previously running, let us focus the camera on the user's current position.
                if ((position = hWService.getHwLocationListener().getActualposition()) != null) {
                    updateUserMarkerPosition(position);
                    focusCameraOnPosition(position);
                }
                //if not, let us focus our camera onto the last known position
                else if ((position = hWService.getHwLocationListener().
                        getLastKnownPositionFromSharedPrefs()) != null) {
                    focusCameraOnPosition(position);
                }
            }
        }

        public void onServiceDisconnected(ComponentName className) {
            // This is called when the connection with the service has been
            // unexpectedly disconnected -- that is, its process crashed.
            // Because it is running in our same process, we should never
            // see this happen.
            hWService = null;
        }
    };

    private void initializeMapComponents() {
        setUpMapIfNeeded();
    }

    /**
     * Here we have to bind to our HappyWalk service again, since we unbinded on pause.
     */
    @Override
    protected void onResume() {
        //check the request code
        /* if we are coming from a POI description, ignore this step
        otherwise, we will wrongly overwrite the requestCode*/
        if (getIntent().getExtras() != null
                && getIntent().getExtras().containsKey(GlobalVariables.BND_EXTRA_REQ_CODE_KEY)
                && requestCode != GlobalVariables.AREQ_POI_DESCRIPTION_REQUEST) {
            requestCode = getIntent().getExtras().
                    getInt(GlobalVariables.BND_EXTRA_REQ_CODE_KEY);
            getIntent().removeExtra(GlobalVariables.BND_EXTRA_REQ_CODE_KEY);
        }
        bindHwService();
        super.onResume();
    }

    /**
     * Here we stop the notification requests, the accelerometer collection,
     * and the threads that fetch POIs and draw markers.
     */
    @Override
    protected void onPause() {
        getpoi.interrupt();
        clusterer.stopMarkerUpdate();
        unBindHwService();
        super.onPause();
    }

    /**
     * Since this activity is a single instance, this is called whenever we have a new intent
     */
    @Override
    protected void onNewIntent(Intent intent) {
        //update the intent
        setIntent(intent);
        super.onNewIntent(intent);
    }

    // MAP MANIPULATION METHODS ---------------------------

    private void setUpMapIfNeeded() {
        // Do a null check to confirm that we have not already instantiated the map.
        if (mMap == null) {
            // Try to obtain the map from the SupportMapFragment.
            ((SupportMapFragment) getSupportFragmentManager().findFragmentById(R.id.map)).getMapAsync(this);
        }
    }

    /**
     * Updates the position of the user marker
     */
    public void updateUserMarkerPosition(final LatLng position) {
        if (userMarker == null) {
            userMarker = mMap.addMarker(new MarkerOptions().
                    //position(position).
                    //icon(BitmapDescriptorFactory.fromResource(R.drawable.marker_user_position)));
                            position(position));
        } else {
            userMarker.setPosition(position);
        }
    }

    /**
     * This function changes the camera to a certain position. It is used by
     * the HwLocationListener to change the camera the first time the user changed location
     */
    public void focusCameraOnPosition(final LatLng position) {
        mMap.getUiSettings().setAllGesturesEnabled(false);
        CameraPosition cameraPosition = new CameraPosition.Builder()
                .target(position)
                .zoom(GlobalVariables.ZOOM_USER_OVERVIEW)
                .build();
        mMap.animateCamera(CameraUpdateFactory.newCameraPosition(cameraPosition), 1500, null);
        mMap.getUiSettings().setAllGesturesEnabled(true);
    }

    // ----------------------------------------
    // Workflow methods
    // ----------------------------------------

    /**
     * This class starts the thread necessary to request a fresh list of POIs on the App
     */
    public void refreshPOIs(double latitude, double longitude) {
        while (getpoi.isAlive()) {
            getpoi.interrupt();
        }
        getpoi = new ThreadGetPoi(this, latitude, longitude);
        getpoi.start();
    }

    /**
     * Performs clustering of the GeoItems in memory
     */
    public void refreshGeoItems() {
        /*
        refreshing the geoItems is kinda important, so we want it done
        ASAP and, thus, don't use our handler.
         */
        runOnUiThread(new Runnable() {
            public void run() {
                clusterer.refreshGeoItems(geoItems);
            }
        });
    }

    public void showPOIDetails(String idPoi) {
        Intent myIntent = new Intent(getApplicationContext(),
                POIDescription.class);
        myIntent.putExtra("idPoi", idPoi);
        startActivity(myIntent);
        requestCode = GlobalVariables.AREQ_POI_DESCRIPTION_REQUEST;
    }

    /**
     * This method binds our MapRouteActivity to the NotificationService
     */
    private void bindHwService() {
        bindService(new Intent(this, HappyWalkService.class), hwConnection, Context.BIND_AUTO_CREATE);
    }

    /**
     * This method unbinds our MapRouteActivity from the NotificationService
     */
    private void unBindHwService() {
        unbindService(hwConnection);
        hWService = null;
    }

    /**
     * This method is called when the user presses the "Exit" menu option
     */
    private void performExit() {
        //we don't need to unBind to the service, since we already do that onPause();
        stopService(new Intent(this, HappyWalkService.class));
        this.finish();
    }

    // ----------------------------------------
    // App Menu
    // ----------------------------------------

    /**
     * This method handles the options Menu
     */
    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        MenuInflater inflater = getMenuInflater();
        inflater.inflate(R.menu.map_menu, menu);
        return true;
    }

    /**
     * This method handles menu items
     */
    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Handle item selection
        switch (item.getItemId()) {
            case R.id.menuExit:
                performExit();
                return true;
            default:
                return super.onOptionsItemSelected(item);
        }
    }

    @Override
    public void onMapReady(GoogleMap googleMap) {
        mMap = googleMap;

        // Initialize the clusterer
        float screenDensity = this.getResources().getDisplayMetrics().density;
        clusterer = new GeoClusterer(this, screenDensity, mMap.getCameraPosition().target);
        GeoClusterer.ClusterMarkerListener clustLis = clusterer.new ClusterMarkerListener();
        GeoClusterer.CameraChangeListener cameraLis = clusterer.new CameraChangeListener();
        mMap.setOnMarkerClickListener(clustLis);
    }
}

сборка верхнего уровня. gradle

buildscript {
    repositories {
        jcenter()
        maven {
            url 'https://maven.google.com/'
            name 'Google'
        }
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:3.5.3'

        // NOTE: Do not place your application dependencies here; they belong
        // in the individual module build.gradle files
    }
}

allprojects {
    repositories {
        jcenter()
        maven {
            url 'https://maven.google.com/'
            name 'Google'
        }
    }
}

на уровне модуля build.gradle

apply plugin: 'com.android.application'

android {
    compileSdkVersion 29
    buildToolsVersion '29.0.2'
    useLibrary 'org.apache.http.legacy'

    defaultConfig {
        applicationId "hitlexamples.happywalk"
        minSdkVersion 16
        targetSdkVersion 29
        versionCode 1
        versionName "1.0"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
    compileOptions {
        sourceCompatibility = 1.8
        targetCompatibility = 1.8
    }
}

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation 'androidx.appcompat:appcompat:1.1.0'
    implementation 'com.google.android.gms:play-services-maps:17.0.0'
    implementation "androidx.preference:preference:1.1.0"
    implementation project(':com.ubhave.sensormanager')
}

activity_maps. xml

<fragment xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent"
    android:layout_height="match_parent" android:id="@+id/map" tools:context=".activities.MapsActivity"
    android:name="com.google.android.gms.maps.SupportMapFragment" />

AndroidManifest. xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    package="hitlexamples.happywalk" >

    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <uses-permission android:name="com.google.android.providers.gsf.permission.READ_GSERVICES" />
    <!--
 The ACCESS_COARSE/FINE_LOCATION permissions are not required to use
         Google Maps Android API v2, but are recommended.
    -->
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />

    <application
        android:allowBackup="true"
        android:icon="@mipmap/happywalklogo"
        android:label="@string/app_name"
        android:theme="@style/AppTheme"
        tools:replace="android:icon,android:theme"
        tools:ignore="GoogleAppIndexingWarning">
        <meta-data
            android:name="com.google.android.gms.version"
            android:value="@integer/google_play_services_version" />
        <meta-data
            android:name="com.google.android.geo.API_KEY"
            android:value="@string/google_maps_key" />

        <activity
            android:name=".activities.MapsActivity"
            android:label="@string/title_activity_maps"
            android:theme="@style/Theme.AppCompat.Light.DarkActionBar"
            android:launchMode="singleInstance"
            android:screenOrientation="portrait">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

        <service
            android:name=".service.HappyWalkService"
            android:enabled="true"
            android:exported="false" >
        </service>

        <activity
            android:name=".activities.POIDescription"
            android:label="@string/title_activity_poidescription"
            android:screenOrientation="portrait">
        </activity>
    </application>

</manifest>

Снимок экрана:

enter image description here

Редактировать: Чтобы центрировать карту на своем текущем местоположении, вы можете следовать этому руководству Google . Т.е. добавьте этот код:

/**
 * Gets the current location of the device, and positions the map's camera.
 */
private void getDeviceLocation() {
    /*
     * Get the best and most recent location of the device, which may be null in rare
     * cases when a location is not available.
     */
    try {
        if (mLocationPermissionGranted) {
            Task<Location> locationResult = mFusedLocationProviderClient.getLastLocation();
            locationResult.addOnCompleteListener(this, new OnCompleteListener<Location>() {
                @Override
                public void onComplete(@NonNull Task<Location> task) {
                    if (task.isSuccessful()) {
                        // Set the map's camera position to the current location of the device.
                        mLastKnownLocation = task.getResult();
                        if (mLastKnownLocation != null) {
                            mMap.moveCamera(CameraUpdateFactory.newLatLngZoom(
                                    new LatLng(mLastKnownLocation.getLatitude(),
                                            mLastKnownLocation.getLongitude()), DEFAULT_ZOOM));
                        }
                    } else {
                        Log.d(TAG, "Current location is null. Using defaults.");
                        Log.e(TAG, "Exception: %s", task.getException());
                        mMap.moveCamera(CameraUpdateFactory
                                .newLatLngZoom(mDefaultLocation, DEFAULT_ZOOM));
                        mMap.getUiSettings().setMyLocationButtonEnabled(false);
                    }
                }
            });
        }
    } catch (SecurityException e)  {
        Log.e("Exception: %s", e.getMessage());
    }
}


/**
 * Prompts the user for permission to use the device location.
 */
private void getLocationPermission() {
    /*
     * Request location permission, so that we can get the location of the
     * device. The result of the permission request is handled by a callback,
     * onRequestPermissionsResult.
     */
    if (ContextCompat.checkSelfPermission(this.getApplicationContext(),
            android.Manifest.permission.ACCESS_FINE_LOCATION)
            == PackageManager.PERMISSION_GRANTED) {
        mLocationPermissionGranted = true;
    } else {
        ActivityCompat.requestPermissions(this,
                new String[]{android.Manifest.permission.ACCESS_FINE_LOCATION},
                PERMISSIONS_REQUEST_ACCESS_FINE_LOCATION);
    }
}

/**
 * Handles the result of the request for location permissions.
 */
@Override
public void onRequestPermissionsResult(int requestCode,
                                       @NonNull String[] permissions,
                                       @NonNull int[] grantResults) {
    mLocationPermissionGranted = false;
    switch (requestCode) {
        case PERMISSIONS_REQUEST_ACCESS_FINE_LOCATION: {
            // If request is cancelled, the result arrays are empty.
            if (grantResults.length > 0
                    && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                mLocationPermissionGranted = true;
            }
        }
    }
}

и вызовите функции в onMapReady:

@Override
public void onMapReady(GoogleMap map) {
    mMap = map;

    getLocationPermission();
    getDeviceLocation();
}

Убедитесь, что вы установили свои собственные значения по умолчанию.

Надеюсь, это поможет вам ! :)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...