Android 谷歌地图我的位置权限

Posted

技术标签:

【中文标题】Android 谷歌地图我的位置权限【英文标题】:Android GoogleMaps myLocation Permission 【发布时间】:2016-10-25 11:03:46 【问题描述】:

使用最新版本的 android...您应该在使用他们的位置信息之前检查用户是否已授予您权限。我已经浏览了 android 文档,这是我想出的代码。

我正在检查用户是否已授予权限,如果他们还没有...然后我们询问,然后在结果上有一个回调函数等等。

我已经多次单步执行代码,一切似乎都运行良好,除了:当我最终尝试使用 fusedLocationApi.getLastLocation 以编程方式获取用户的位置时......它返回 NULL!!

我已经重新上传了我拥有的代码。

任何能解决这个问题并且它确实对我有用的人,你可以获得我所有的代表分数......

package com.example.greg.rightnow;


import android.content.pm.PackageManager;
import android.location.Location;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.app.ActivityCompat;
import android.support.v4.app.FragmentActivity;
import android.os.Bundle;
import android.support.v4.content.ContextCompat;
import android.Manifest;
import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.api.Api;
import com.google.android.gms.common.api.GoogleApiClient.ConnectionCallbacks;
import com.google.android.gms.common.api.GoogleApiClient;
import com.google.android.gms.common.api.PendingResult;
import com.google.android.gms.common.api.Status;
import com.google.android.gms.drive.Drive;
import com.google.android.gms.location.LocationServices;
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.LatLng;
import com.google.android.gms.maps.model.MarkerOptions;
import com.google.android.gms.plus.Plus;

import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.concurrent.TimeUnit;

public class MapsActivity extends FragmentActivity implements OnMapReadyCallback, ConnectionCallbacks 

    private GoogleApiClient mGoogleApiClient;
    private GoogleMap mMap;
    private Location mLastLocation;


    @Override
    protected void onCreate(Bundle savedInstanceState) 
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_maps);
        // Obtain the SupportMapFragment and get notified when the map is ready to be used.
        SupportMapFragment mapFragment = (SupportMapFragment) getSupportFragmentManager()
                .findFragmentById(R.id.map);
        mapFragment.getMapAsync(this);

        // Create an instance of GoogleAPIClient.
        mGoogleApiClient = new GoogleApiClient.Builder(this)
                .addApi(Drive.API)
                .addApi(Plus.API)
                .addScope(Drive.SCOPE_FILE)
                .addConnectionCallbacks(this)
        //        .addOnConnectionFailedListener(this)
                .addApi(LocationServices.API)
                .build();


    


    @Override
    public void onConnected(@Nullable Bundle bundle) 

        





    protected void onStart() 
        mGoogleApiClient.connect();
        super.onStart();
    

    protected void onStop() 
        mGoogleApiClient.disconnect();
        super.onStop();
    




    /**
     * Manipulates the map once available.
     * This callback is triggered when the map is ready to be used.
     * This is where we can add markers or lines, add listeners or move the camera. In this case,
     * we just add a marker near Sydney, Australia.
     * If Google Play services is not installed on the device, the user will be prompted to install
     * it inside the SupportMapFragment. This method will only be triggered once the user has
     * installed Google Play services and returned to the app.
     * Manifest.permission.ACCESS_FINE_LOCATION
     */

    @Override
    public void onRequestPermissionsResult(int requestCode,
                                           String permissions[], int[] grantResults) 


        if(ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) == grantResults[0]) 
            mMap.setMyLocationEnabled(true);
        


        mLastLocation = LocationServices.FusedLocationApi.getLastLocation(mGoogleApiClient);

        LatLng myLat = new LatLng(mLastLocation.getLatitude(), mLastLocation.getLongitude());
        // Add a marker in Sydney and move the camera
        LatLng sydney = new LatLng(-34, 151);
        mMap.addMarker(new MarkerOptions().position(sydney).title("Marker in Sydney"));
        mMap.moveCamera(CameraUpdateFactory.newLatLng(myLat));


    





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

        if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION)
                == 0) 

            ActivityCompat.requestPermissions(this,
                    new String[]Manifest.permission.ACCESS_FINE_LOCATION, 1);

        

        else if(ContextCompat.checkSelfPermission(this,Manifest.permission.ACCESS_FINE_LOCATION) ==-1) 
            mLastLocation = LocationServices.FusedLocationApi.getLastLocation(mGoogleApiClient);

            LatLng myLat = new LatLng(mLastLocation.getLatitude(), mLastLocation.getLongitude());
            // Add a marker in Sydney and move the camera
            LatLng sydney = new LatLng(-34, 151);
            mMap.addMarker(new MarkerOptions().position(sydney).title("Marker in Sydney"));
            mMap.moveCamera(CameraUpdateFactory.newLatLng(myLat));


        

    

    @Override
    public void onConnectionSuspended(int i) 

    

你会注意到我不得不注释掉这一行:

 //   .addOnConnectionFailedListener(this)

如果我取消对此的注释...“this”带有红色下划线,我收到一条错误消息“... in Builder cannot be applied to com.example.greg.MapsActivity etc”

【问题讨论】:

好吧,'this'(即您的 MapsActivity 类)没有实现 GoogleApiClient.OnConnectionFailedListener,这是您应该传递给 addOnConnectionFailedListener()developers.google.com/android/reference/com/google/android/gms/… 的类型 任何答案有用吗?您需要更多帮助吗? 当您找到答案时,不要修改问题,而是添加一个新答案并将其标记为已接受!通过这种方式,它仍然可以为其他人重复使用...... 【参考方案1】:

ContextCompat.checkSelfPermission检查权限后,如果没有授予,可以申请。来自the documentation:

另外,在使用GoogleApiClient (documentation) 时,您需要处理连接失败。在您的代码中,连接失败并且您没有在 onConnectionFailed 上管理它。

最后,我从您的代码中删除了以下内容(这会导致连接失败,无需使用 LocationServices 并获取最后一个位置):

.addApi(Drive.API)
.addApi(Plus.API)
.addScope(Drive.SCOPE_FILE)

这是一个工作示例:

public class MapsActivity extends FragmentActivity implements OnMapReadyCallback, GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener 
    private static final int FINE_LOCATION_PERMISSION_REQUEST = 1;
    private static final int CONNECTION_RESOLUTION_REQUEST = 2;
    private GoogleApiClient mGoogleApiClient;
    private GoogleMap mMap;
    private Location mLastLocation;

    @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);

        buildGoogleAPIClient();
    

    @Override
    protected void onResume() 
        super.onResume();

        buildGoogleAPIClient();
    

    private void buildGoogleAPIClient() 
        if (mGoogleApiClient == null) 
            mGoogleApiClient = new GoogleApiClient.Builder(this)
                    .addConnectionCallbacks(this)
                    .addOnConnectionFailedListener(this)
                    .addApi(LocationServices.API)
                    .build();
        
    

    @Override
    public void onConnected(@Nullable Bundle bundle) 
        findLocation();
    

    protected void onStart() 
        mGoogleApiClient.connect();
        super.onStart();
    

    protected void onStop() 
        mGoogleApiClient.disconnect();
        super.onStop();
    

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

    @Override
    public void onConnectionSuspended(int i) 
        Toast.makeText(this, "Connection suspended", Toast.LENGTH_SHORT).show();
    

    @Override
    public void onConnectionFailed(@NonNull final ConnectionResult connectionResult) 
        if (connectionResult.hasResolution()) 
            try 
                connectionResult.startResolutionForResult(this, CONNECTION_RESOLUTION_REQUEST);
             catch (IntentSender.SendIntentException e) 
                // There was an error with the resolution intent. Try again.
                mGoogleApiClient.connect();
            
         else 
            Dialog dialog = GooglePlayServicesUtil.getErrorDialog(connectionResult.getErrorCode(), this, 1);
            dialog.show();
        
    

    @Override
    protected void onActivityResult(int requestCode, int resultCode,
                                    Intent data) 
        super.onActivityResult(requestCode, resultCode, data);
        if (requestCode == CONNECTION_RESOLUTION_REQUEST && resultCode == RESULT_OK) 
            mGoogleApiClient.connect();
        
    

    private void findLocation() 
        if (ContextCompat.checkSelfPermission(this,
                Manifest.permission.ACCESS_FINE_LOCATION)
                != PackageManager.PERMISSION_GRANTED) 
            ActivityCompat.requestPermissions(this,
                    new String[]Manifest.permission.ACCESS_FINE_LOCATION,
                    FINE_LOCATION_PERMISSION_REQUEST);
         else 
            mLastLocation = LocationServices.FusedLocationApi.getLastLocation(mGoogleApiClient);

            LatLng myLat = new LatLng(mLastLocation.getLatitude(), mLastLocation.getLongitude());
            // Add a marker in Sydney and move the camera
            LatLng sydney = new LatLng(-34, 151);
            mMap.addMarker(new MarkerOptions().position(sydney).title("Marker in Sydney"));
            mMap.moveCamera(CameraUpdateFactory.newLatLng(myLat));
        
    

    @Override
    public void onRequestPermissionsResult(int requestCode,
                                           String permissions[], int[] grantResults) 
        switch (requestCode) 
            case FINE_LOCATION_PERMISSION_REQUEST: 
                if (grantResults.length > 0
                        && grantResults[0] == PackageManager.PERMISSION_GRANTED) 
                    findLocation();
                
            
        
    

【讨论】:

我已经重新上传了我拥有的代码。我很确定此时我正在正确使用 requestPermission 回调方法......但是在调用 fusedLocationApi.getLastLocation() 时我仍然得到 NULL !! 根据***.com/questions/16830047/… "融合的位置提供者只有在至少有一个客户端连接到它的情况下才会维护后台位置。一旦第一个客户端连接,它将立即尝试获取位置。如果您的活动是第一个连接的客户端,您在 onConnected() 中立即调用 getLastLocation(),这可能没有足够的时间让第一个位置进入” 这行得通。我只需要添加已安装的位置服务。【参考方案2】:

在获得最后一个已知位置之前,您需要检查四件事:

    GoogleApiClient 是否已连接? 是否已授予权限? 是否启用了定位? GoogleMap 准备好了吗?

我建议对每件事都使用状态机或布尔值,并且仅当所有四个都为真时才询问位置。例如:

protected void getLastKnownLocationIfReady()
    if(isGoogleApiConnected && isPermissionGranted && isLocationEnabled && isGoogleMapReady)
        mLastLocation = LocationServices.FusedLocationApi.getLastLocation(mGoogleApiClient);

        LatLng myLat = new LatLng(mLastLocation.getLatitude(), mLastLocation.getLongitude());
        // Add a marker in Sydney and move the camera
        LatLng sydney = new LatLng(-34, 151);
        mMap.addMarker(new MarkerOptions().position(sydney).title("Marker in Sydney"));
        mMap.moveCamera(CameraUpdateFactory.newLatLng(myLat));
    

每次更改布尔变量之一时调用 getLastKnownLocationIfReady() 函数。

所以你的onConnected 看起来像:

@Override
public void onConnected(@Nullable Bundle bundle) 
    isGoogleApiConnected = true;
    getLastKnownLocationIfReady();

检查权限如:

    if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) 
        ActivityCompat.requestPermissions(this, new String[]Manifest.permission.ACCESS_FINE_LOCATION, FINE_LOCATION_PERMISSION_REQUEST);
     else 
        isPermissionGranted = true;
        getLastKnownLocationIfReady();
    


@Override
public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) 
    switch (requestCode) 
        case FINE_LOCATION_PERMISSION_REQUEST: 
            if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) 
                isPermissionGranted = true;
                getLastKnownLocationIfReady();
            
        
    

您可以要求启用该位置,例如:

public void requestLocationAccess()
    if(mLocationRequest == null) createLocationRequest();
    final LocationSettingsRequest.Builder builder = new LocationSettingsRequest.Builder().addLocationRequest(mLocationRequest);
    builder.setAlwaysShow(true); //this is the key ingredient

    com.google.android.gms.common.api.PendingResult<LocationSettingsResult> result =
            LocationServices.SettingsApi.checkLocationSettings(mGoogleApiClient, builder.build());
    result.setResultCallback(new ResultCallback<LocationSettingsResult>()
        @Override
        public void onResult(@NonNull LocationSettingsResult result)
            final Status resultStatus = result.getStatus();
            switch(resultStatus.getStatusCode())
                case LocationSettingsStatusCodes.SUCCESS:
                    // All location settings are satisfied.
                    isLocationEnabled = true;
                    getLastKnownLocationIfReady();
                    break;
                case LocationSettingsStatusCodes.RESOLUTION_REQUIRED:
                    // Location settings are not satisfied. But could be fixed by showing the user a dialog. You need to override OnActivityResult if you want to handle the user's interaction with the dialog.
                    resultStatus.startResolutionForResult(this, REQUEST_LOCATION);
                    break;

                default:
                    break;
            
        
    );

修改你的onMapReady 喜欢:

@Override
public void onMapReady(GoogleMap googleMap) 
    mMap = googleMap;
    isGoogleMapReady = true;
    getLastKnownLocationIfReady();

【讨论】:

【参考方案3】:

我遇到了和你一样的错误。为了节省电池,我想使用 lastKnownLocation 而不是一直轮询。有时,无论我之前检查过什么,lastKnownLocation 都是空的。

如果你看一下文档,据说:

getLastLocation() 方法返回一个 Location 对象,您可以从中检索地理位置的纬度和经度坐标。 在极少数情况下,当位置不可用时,返回的位置对象可能为空

为了解决这个问题,我实现了使用 lastKnownLocation 和轮询新位置作为后备的组合。

我这里的基本结构是

    检查是否授予权限 检查位置是否启用 尝试获取 lastKnownLocation,如 in the docs 所述 如果为空,请求位置更新in this docs example

这种方法很有效。到目前为止,我还没有找到更好的解决方案,但我建议你尝试一下。

【讨论】:

【参考方案4】:

问题出在这里: Android Emulator

向下滚动到“使用扩展控件、设置和帮助”。 长话短说:如果您使用的是模拟器,则必须伪造您的位置数据!!!!!!模拟器不支持 wifi(或者据我所知,也不支持 GPS 数据)。

点击您的“手机”(您的模拟器)以确保它已被选中,然后按 ctrl+shift+L 输入您想要的任何位置,然后点击“发送”和中提琴......您将拥有一个位置。当我的模拟器上的 GoogleMaps 甚至无法正常工作时,我就知道有什么事情发生了。我会搜索方向,它会说“等待位置”,什么都不会发生。

该死的摆脱。

【讨论】:

【参考方案5】:

另外,检查您是否拥有权限和实际请求权限是两件不同的事情。

正如android developer blog 建议的那样,您需要以如下方式请求权限:

ActivityCompat.requestPermissions(this, new String[]Manifest.permission.ACCESS_FINE_LOCATION, PERMISSIONS_RESULT_CODE);

然后通过覆盖onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults)来处理请求的结果。

【讨论】:

我认为这会起作用...但现在 PERMISSIONS_RESULT_CODE 是红色的并且“无法解决” 这只是一个 int,用于跟踪您的请求。您将它与onRequestPermissionResult 中的requestCode 匹配 好的..现在接近了。唯一的问题是权限消息似乎是异步的......在我选择权限之前,我的其余代码正在运行 是的,这不是好的设计。阅读我链接的文档并查看一些示例,以了解应该如何完成。提示:如果用户同意请求,您应该只做其余的事情 现在是凌晨 3 点,我非常讨厌谷歌。我在 onRequestPermission 回调方法中做所有事情。如果我使用这种方法时使用的参数并不重要......因为在某些时候我必须这样做: if(ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) == 1) mmap.setMyLocationEnabled(true);并且权限没有改变! ActivityCompat.requestPermissions 方法似乎没有改变任何东西。

以上是关于Android 谷歌地图我的位置权限的主要内容,如果未能解决你的问题,请参考以下文章

如何让谷歌地图再次请求位置权限?

Android 谷歌地图 API 位置

Android:谷歌地图位置电池使用率低

如何在android的谷歌地图上用标记显示我的当前位置

Android 实时谷歌地图位置跟踪

Android:如何使用谷歌地图在我的应用中显示用户当前位置?