Kesalahan “GPS memerlukan ACCESS_FINE_LOCATION” Android, meskipun file manifes saya berisi ini

104

Setiap kali saya menjalankan aplikasi, SecurityException saya dilempar dan kesalahan dari debugger berbunyi sebagai berikut:

java.lang.SecurityException: penyedia lokasi "gps" memerlukan izin ACCESS_COARSE_LOCATION atau ACCESS_FINE_LOCATION.

Ini sepertinya kesalahan sederhana, namun file manifes saya sepenuhnya benar. Ini dia, dan ini juga kode MapActivity saya:

<?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>

Aktivitas Saya:

    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) {

    }
}
Jason Cromer
sumber
Pada versi Android apa Anda menguji ini?
CommonsWare
4
Tidak terkait, tetapi jika Anda meminta lokasi yang bagus, Anda tidak perlu meminta secara kasar. Sudah termasuk.
joey_g216

Jawaban:

136

ACCESS_COARSE_LOCATION,, ACCESS_FINE_LOCATIONdan WRITE_EXTERNAL_STORAGEmerupakan bagian dari sistem izin waktu proses Android 6.0 . Selain memilikinya di manifes seperti yang Anda lakukan, Anda juga harus memintanya dari pengguna pada waktu proses (menggunakan requestPermissions()) dan melihat apakah Anda memilikinya (menggunakan checkSelfPermission()).

Salah satu solusi dalam jangka pendek adalah menurunkan angka di targetSdkVersionbawah 23.

Namun, pada akhirnya, Anda ingin memperbarui aplikasi Anda untuk menggunakan sistem izin waktu proses.

Misalnya, aktivitas ini bekerja dengan lima izin. Empat adalah izin waktu proses, meskipun saat ini hanya menangani tiga (saya menulisnya sebelum WRITE_EXTERNAL_STORAGEditambahkan ke daftar izin waktu proses).

/***
 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();
  }
}

(dari proyek sampel ini )

Untuk fungsi requestPermissions (), haruskah parameternya hanya "ACCESS_COARSE_LOCATION"? Atau haruskah saya mencantumkan nama lengkap "android.permission.ACCESS_COARSE_LOCATION"?

Saya akan menggunakan konstanta yang ditentukan pada Manifest.permission, seperti yang ditunjukkan di atas.

Juga, apa kode permintaannya?

Itu akan dikembalikan kepada Anda sebagai parameter pertama ke onRequestPermissionsResult(), sehingga Anda bisa membedakan satu requestPermissions()panggilan dari yang lain.

CommonsWare
sumber
1
Untuk fungsi requestPermissions (), haruskah parameternya hanya "ACCESS_COARSE_LOCATION"? Atau haruskah saya mencantumkan nama lengkap "android.permission.ACCESS_COARSE_LOCATION"?
Jason Cromer
1
Terima kasih, ini menghilangkan kesalahan. Saya masih kesulitan mengakses lokasi saya, karena locationManager saya terus mengembalikan lokasi saya sebagai null, tetapi itu tidak relevan dengan bug ini. Terima kasih atas solusinya!
Jason Cromer
@CommonsWare: Apa yang Anda maksud dengan mengatakan 'akhirnya'? Maaf, saya tidak mengerti.
theapache64
1
@ theapache64: Suatu hari, sesuatu akan menyebabkan Anda ingin menyetel targetSdkVersionke 23 atau lebih tinggi. Pada tahap itu, Anda perlu mengadopsi sistem izin waktu proses. Sampai waktunya tiba, Anda dipersilakan untuk mempertahankan di targetSdkVersionbawah 23 dan mengabaikan izin runtime.
CommonsWare
@CommonsWare: Sekarang saya mengerti. :)
theapache64
39

Solusi sederhana saya adalah ini

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

atau Anda dapat membuka dialog izin di tempat lain seperti ini

} else {
   ActivityCompat.requestPermissions(this, new String[] {
      Manifest.permission.ACCESS_FINE_LOCATION, 
      Manifest.permission.ACCESS_COARSE_LOCATION }, 
      TAG_CODE_PERMISSION_LOCATION);
}
Vasil Valchev
sumber
Laways pindah ke bagian lain bro :(
Ashana.Jackol
2
tambahkan dialog untuk menambahkan izin di "lain" ini, dan Anda siap melakukannya.
Vasil Valchev
Ini sebenarnya tanpa keraguan perbaikan untuk Android 6. Perlu dicatat Anda harus memasukkan permintaan izin di lain.
Keith Adler
Saya mendapat kesalahan ini dengan Target SDK sebagai 22 dan di Android 5.1 pada perangkat S plus (GiONEE_WBL7511). Saya bingung mengapa kecelakaan ini terjadi. Ada petunjuk? java.lang.SecurityException: Klien harus memiliki izin ACCESS_FINE_LOCATION untuk meminta lokasi PRIORITY_HIGH_ACCURACY.
arpitgoyal2008
5

PENYEBAB: "Mulai Android 6.0 (API level 23), pengguna memberikan izin ke aplikasi saat aplikasi berjalan, bukan saat mereka menginstal aplikasi." Dalam kasus ini, "ACCESS_FINE_LOCATION" adalah "izin berbahaya dan untuk alasan itu, Anda mendapatkan 'java.lang.SecurityException: penyedia lokasi" gps "memerlukan izin ACCESS_FINE_LOCATION.' kesalahan ( https://developer.android.com/training/permissions/requesting.html ).

SOLUSI: Menerapkan kode yang disediakan di https://developer.android.com/training/permissions/requesting.html di bawah judul "Minta izin yang Anda perlukan" dan "Tangani respons permintaan izin".

Jaime Montoya
sumber