安卓“gps需要ACCESS_FINE_LOCATION”错误,即使我的清单文件包含此项。

114
每次我运行应用程序,都会抛出SecurityException异常,调试器中的错误消息如下:

java.lang.SecurityException:“gps”位置提供程序需要ACCESS_COARSE_LOCATION或ACCESS_FINE_LOCATION权限。

这似乎是一个简单的错误,但我的清单文件完全正确。这是它的内容,这是我的MapActivity代码:
<?xml version="1.0" encoding="utf-8"?>
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<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" />
<uses-permission android:name="com.dev.cromer.jason.coverme.permission.MAPS_RECEIVE" />

<application
    android:allowBackup="true"
    android:icon="@mipmap/ic_launcher"
    android:label="@string/app_name"
    android:theme="@style/AppTheme" >
    <activity
        android:name=".MainActivity"
        android:label="@string/app_name" >
        <intent-filter>
            <action android:name="android.intent.action.MAIN" />

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

    <meta-data
        android:name="com.google.android.gms.version"
        android:value="@integer/google_play_services_version" />
    <meta-data
        android:name="com.google.android.maps.v2.API_KEY"
        android:value= "@string/google_maps_key" />

    <activity
        android:name=".MapActivity"
        android:label="@string/title_activity_map" >
    </activity>
</application>

我的活动:

    package com.dev.cromer.jason.coverme;

import android.location.Criteria;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.support.v4.app.FragmentActivity;
import android.os.Bundle;
import android.util.Log;

import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.api.GoogleApiClient;
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.SupportMapFragment;
import com.google.android.gms.maps.model.LatLng;
import com.google.android.gms.maps.model.MarkerOptions;

public class MapActivity extends FragmentActivity implements LocationListener {

    private GoogleMap mMap; // Might be null if Google Play services APK is not available.

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

        setUpMapIfNeeded();
    }

    @Override
    protected void onResume() {
        super.onResume();
        setUpMapIfNeeded();
    }



    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.
            mMap = ((SupportMapFragment) getSupportFragmentManager().findFragmentById(R.id.map))
                    .getMap();

            // Check if we were successful in obtaining the map.
            if (mMap != null) {
                //mMap.setMyLocationEnabled(true);
                //mMap.setOnMyLocationChangeListener(this);
                setUpMap();
            }
        }
    }


    private void setUpMap() {
        mMap.addMarker(new MarkerOptions().position(new LatLng(0, 0)).title("Marker"));
        mMap.setMyLocationEnabled(true);

        LocationManager locationManager = (LocationManager) getSystemService(LOCATION_SERVICE);

        try {
            Location myLocation = locationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER);

            if (myLocation != null) {
                Log.d("TAG", "Not null");
            }
            else {
                Log.d("TAG", "NULL");
                locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0, this);
            }
        }
        catch (SecurityException se) {
            Log.d("TAG", "SE CAUGHT");
            se.printStackTrace();
        }
    }


    @Override
    public void onLocationChanged(Location location) {
        Log.d("CHANGED", "LOCATION UPDATED");

    }

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

    }

    @Override
    public void onProviderEnabled(String provider) {

    }

    @Override
    public void onProviderDisabled(String provider) {

    }
}

你在测试哪个版本的安卓系统? - CommonsWare
5
无关紧要,但如果您请求精确定位,则无需请求粗略定位。它已包含在内。 - joey_g216
3个回答

138
ACCESS_COARSE_LOCATIONACCESS_FINE_LOCATIONWRITE_EXTERNAL_STORAGE都是Android 6.0运行时权限系统的一部分。除了像您所做的那样在清单中拥有它们之外,您还需要在运行时从用户那里请求它们(使用requestPermissions())并查看您是否拥有它们(使用checkSelfPermission())。
短期内的一个解决方法是将targetSdkVersion降低到23以下。
但是,最终,您将希望更新您的应用程序以使用运行时权限系统。
例如,此活动与五个权限一起工作。虽然目前仅处理三个(在WRITE_EXTERNAL_STORAGE添加到运行时权限名单之前已编写),其中四个是运行时权限。
/***
 Copyright (c) 2015 CommonsWare, LLC
 Licensed under the Apache License, Version 2.0 (the "License"); you may not
 use this file except in compliance with the License. You may obtain a copy
 of the License at http://www.apache.org/licenses/LICENSE-2.0. Unless required
 by applicable law or agreed to in writing, software distributed under the
 License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS
 OF ANY KIND, either express or implied. See the License for the specific
 language governing permissions and limitations under the License.

 From _The Busy Coder's Guide to Android Development_
 https://commonsware.com/Android
 */

package com.commonsware.android.permmonger;

import android.Manifest;
import android.app.Activity;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.TextView;
import android.widget.Toast;

public class MainActivity extends Activity {
  private static final String[] INITIAL_PERMS={
    Manifest.permission.ACCESS_FINE_LOCATION,
    Manifest.permission.READ_CONTACTS
  };
  private static final String[] CAMERA_PERMS={
    Manifest.permission.CAMERA
  };
  private static final String[] CONTACTS_PERMS={
      Manifest.permission.READ_CONTACTS
  };
  private static final String[] LOCATION_PERMS={
      Manifest.permission.ACCESS_FINE_LOCATION
  };
  private static final int INITIAL_REQUEST=1337;
  private static final int CAMERA_REQUEST=INITIAL_REQUEST+1;
  private static final int CONTACTS_REQUEST=INITIAL_REQUEST+2;
  private static final int LOCATION_REQUEST=INITIAL_REQUEST+3;
  private TextView location;
  private TextView camera;
  private TextView internet;
  private TextView contacts;
  private TextView storage;

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

    location=(TextView)findViewById(R.id.location_value);
    camera=(TextView)findViewById(R.id.camera_value);
    internet=(TextView)findViewById(R.id.internet_value);
    contacts=(TextView)findViewById(R.id.contacts_value);
    storage=(TextView)findViewById(R.id.storage_value);

    if (!canAccessLocation() || !canAccessContacts()) {
      requestPermissions(INITIAL_PERMS, INITIAL_REQUEST);
    }
  }

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

    updateTable();
  }

  @Override
  public boolean onCreateOptionsMenu(Menu menu) {
    getMenuInflater().inflate(R.menu.actions, menu);

    return(super.onCreateOptionsMenu(menu));
  }

  @Override
  public boolean onOptionsItemSelected(MenuItem item) {
    switch(item.getItemId()) {
      case R.id.camera:
        if (canAccessCamera()) {
          doCameraThing();
        }
        else {
          requestPermissions(CAMERA_PERMS, CAMERA_REQUEST);
        }
        return(true);

      case R.id.contacts:
        if (canAccessContacts()) {
          doContactsThing();
        }
        else {
          requestPermissions(CONTACTS_PERMS, CONTACTS_REQUEST);
        }
        return(true);

      case R.id.location:
        if (canAccessLocation()) {
          doLocationThing();
        }
        else {
          requestPermissions(LOCATION_PERMS, LOCATION_REQUEST);
        }
        return(true);
    }

    return(super.onOptionsItemSelected(item));
  }

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

    switch(requestCode) {
      case CAMERA_REQUEST:
        if (canAccessCamera()) {
          doCameraThing();
        }
        else {
          bzzzt();
        }
        break;

      case CONTACTS_REQUEST:
        if (canAccessContacts()) {
          doContactsThing();
        }
        else {
          bzzzt();
        }
        break;

      case LOCATION_REQUEST:
        if (canAccessLocation()) {
          doLocationThing();
        }
        else {
          bzzzt();
        }
        break;
    }
  }

  private void updateTable() {
    location.setText(String.valueOf(canAccessLocation()));
    camera.setText(String.valueOf(canAccessCamera()));
    internet.setText(String.valueOf(hasPermission(Manifest.permission.INTERNET)));
    contacts.setText(String.valueOf(canAccessContacts()));
    storage.setText(String.valueOf(hasPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE)));
  }

  private boolean canAccessLocation() {
    return(hasPermission(Manifest.permission.ACCESS_FINE_LOCATION));
  }

  private boolean canAccessCamera() {
    return(hasPermission(Manifest.permission.CAMERA));
  }

  private boolean canAccessContacts() {
    return(hasPermission(Manifest.permission.READ_CONTACTS));
  }

  private boolean hasPermission(String perm) {
    return(PackageManager.PERMISSION_GRANTED==checkSelfPermission(perm));
  }

  private void bzzzt() {
    Toast.makeText(this, R.string.toast_bzzzt, Toast.LENGTH_LONG).show();
  }

  private void doCameraThing() {
    Toast.makeText(this, R.string.toast_camera, Toast.LENGTH_SHORT).show();
  }

  private void doContactsThing() {
    Toast.makeText(this, R.string.toast_contacts, Toast.LENGTH_SHORT).show();
  }

  private void doLocationThing() {
    Toast.makeText(this, R.string.toast_location, Toast.LENGTH_SHORT).show();
  }
}

(来自这个示例项目)

对于requestPermissions()函数,参数应该只是"ACCESS_COARSE_LOCATION"吗?还是我应该包括完整名称"android.permission.ACCESS_COARSE_LOCATION"?

我会使用在Manifest.permission上定义的常量,如上所示。

另外,请求代码是什么?

作为onRequestPermissionsResult()的第一个参数传回给您,以便您可以区分一个requestPermissions()调用和另一个。


1
对于requestPermissions()函数,参数应该只是“ACCESS_COARSE_LOCATION”吗?还是应该包括完整名称“android.permission.ACCESS_COARSE_LOCATION”? - Jason Cromer
1
谢谢,这样就解决了错误。我仍然无法访问我的位置,因为我的locationManager返回的位置为空,但与此错误无关。感谢您提供的解决方案! - Jason Cromer
1
@theapache64:总有一天,某些事情会让你想将targetSdkVersion设置为23或更高版本。在那时,你需要采用运行时权限系统。在此之前,你可以将targetSdkVersion保持在23以下并忽略运行时权限。 - CommonsWare
如果我将targetSDK版本降至23以下,则可以正常工作,但是如果我手动去拒绝应用程序设置中的位置权限,则应用程序会崩溃并出现SecurityException。即使在这种情况下,权限检查也被视为已授权。有没有解决方案? - abhishek
@abhishek:“但是如果我手动从应用程序设置中拒绝位置权限,那么应用程序会崩溃并出现SecurityException异常。”-- 嗯嗯...它应该只是拒绝给你位置。您可能希望提问一个单独的Stack Overflow问题,在其中提供一个[mcve](随异常一起的堆栈跟踪和触发异常的代码)。 - CommonsWare
显示剩余4条评论

41

我的简单解决方案是这样的

if (ContextCompat.checkSelfPermission(this, android.Manifest.permission.ACCESS_FINE_LOCATION) ==
        PackageManager.PERMISSION_GRANTED &&
        ContextCompat.checkSelfPermission(this, android.Manifest.permission.ACCESS_COARSE_LOCATION) ==
        PackageManager.PERMISSION_GRANTED) {
    googleMap.setMyLocationEnabled(true);
    googleMap.getUiSettings().setMyLocationButtonEnabled(true);
} else {
    Toast.makeText(this, R.string.error_permission_map, Toast.LENGTH_LONG).show();
}

或者你可以像这样在其他地方打开权限对话框

} else {
   ActivityCompat.requestPermissions(this, new String[] {
      Manifest.permission.ACCESS_FINE_LOCATION, 
      Manifest.permission.ACCESS_COARSE_LOCATION }, 
      TAG_CODE_PERMISSION_LOCATION);
}

总是进入else部分啊兄弟 :( - Ashana.Jackol
2
在这个“else”中添加对话框以添加权限,然后你就可以开始了。 - Vasil Valchev
这实际上毫无疑问是针对Android 6的修复方法。需要注意的是,您必须将权限请求放在else语句中。 - Keith Adler
我在Target SDK为22且在Android 5.1上的S plus(GiONEE_WBL7511)设备上遇到了这个错误。我不明白为什么会出现这个崩溃。有任何线索吗? java.lang.SecurityException: 客户端必须具有ACCESS_FINE_LOCATION权限才能请求PRIORITY_HIGH_ACCURACY位置。 - satish123

5
原因:"从Android 6.0(API级别23)开始,用户在应用程序运行时授予权限,而不是在安装应用程序时授予权限。" 在这种情况下,“ACCESS_FINE_LOCATION”是一个“危险权限”,因此您会收到此错误:“java.lang.SecurityException:“gps”位置提供程序需要ACCESS_FINE_LOCATION权限。” (https://developer.android.com/training/permissions/requesting.html)。
解决方案:根据“请求所需的权限”和“处理权限请求响应”标题,在https://developer.android.com/training/permissions/requesting.html上实现提供的代码。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接