错误:客户端必须具有 ACCESS_COARSE_LOCATION 或 ACCESS_FINE_LOCATION [重复]

Posted

技术标签:

【中文标题】错误:客户端必须具有 ACCESS_COARSE_LOCATION 或 ACCESS_FINE_LOCATION [重复]【英文标题】:Error: Client must have ACCESS_COARSE_LOCATION or ACCESS_FINE_LOCATION [duplicate] 【发布时间】:2016-11-20 18:11:42 【问题描述】:

我在另一个应用程序中使用了一个模块。在实现模块之前,我将它设置为 API 级别 23(编译和目标),就像我的主项目一样 这工作正常,除了这个错误。问题是,谷歌从棉花糖开始改变了权限管理。最后,我不知道应该如何以及在哪里设置权限。

启动应用程序时出现此错误:

java.lang.SecurityException:客户端必须具有 ACCESS_COARSE_LOCATION 或 ACCESS_FINE_LOCATION 权限才能执行任何操作位置。

能否请您帮助我并简要解释一下(我需要什么样的代码以及在哪里插入)以避免此错误?

来自模块的清单:

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

<!--

android:versionCode="3"
android:versionName="1.2.0" >


<uses-sdk
    android:minSdkVersion="9"
    android:targetSdkVersion="19" />

    -->

<!-- Grant the network access permission -->
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"           />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.INTERNET" />
<!-- Grant the location access permission -->
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<!-- Allows using PowerManager WakeLocks to keep processor from sleeping or screen from dimming -->
<uses-permission android:name="android.permission.WAKE_LOCK" />
<!-- Permission required to use Alarm Manager -->
<uses-permission android:name="com.android.alarm.permission.SET_ALARM" />
<uses-permission android:name="android.permission.GET_TASKS" />

<application
    android:allowBackup="true"
    android:supportsRtl="true"
    android:theme="@style/AppTheme">

    <!--    android:name="com.cs.android.weminder.MyApplication"
            android:label="@string/app_name">-->

    <!-- This meta-data tag is required to use Google Play Services. -->
    <meta-data
        android:name="com.google.android.gms.version"
        android:value="@integer/google_play_services_version" />
    <!-- Required for creating dialog of the ACRA report -->
    <activity
        android:name="org.acra.CrashReportDialog"
        android:excludeFromRecents="true"
        android:finishOnTaskLaunch="true"
        android:launchMode="singleInstance"
        android:theme="@style/AcraDialog" />
    <activity
        android:name="com.cs.android.weminder.MainActivity"
        android:launchMode="singleTop"
        android:screenOrientation="portrait" >

        <action android:name="android.intent.action.MAIN" />

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

    </activity>
    <activity android:name="com.cs.android.weminder.BaseScreen" />
    <activity android:name="com.cs.android.weminder.LocationBaseScreen" />
    <activity android:name="com.cs.android.weminder.SettingsActivity" />
    <activity android:name="com.cs.android.weminder.WeminderApplication" />


    <!-- Required by the AdMob Ads SDK -->



    <activity
        android:name="com.google.ads.AdActivity"
        android:configChanges="keyboard|keyboardHidden|orientation|screenLayout|uiMode|screenSize|smallestScreenSize" />
    <!-- Include the AdActivity configChanges and theme. -->
    <activity
        android:name="com.google.android.gms.ads.AdActivity"
        android:configChanges="keyboard|keyboardHidden|orientation|screenLayout|uiMode|screenSize|smallestScreenSize"
        android:theme="@android:style/Theme.Translucent" />


    <receiver
        android:name="com.cs.android.weminder.AlarmReceiver"
        android:process=":remote" >
        <intent-filter>
            <action android:name="android.intent.action.BOOT_COMPLETED" />
        </intent-filter>
    </receiver>

    <service android:name="com.cs.android.weminder.AlarmService" >
    </service>
</application>

Modul 的主要活动:

public class MainActivity extends SettingsActivity implements ActionTypes,
    ResultType 




private TextView tvLocation, tvHTemp, tvLTemp, tvCurrentTemp, tvTimestamp,
        tvDate, tvWindSpeed, tvPressure;
private ImageView ivCurrentWeather, ivRefresh, ivUnit, ivSearch, ivRemind,
        ivMyLocation;
private HorizontalViewGallery forecastGallery;
// Search field
private MyCustomEditText etSearch;

// holding different weather icons presenting weather condition
// alternatively.
private IconFinder mIconFinder;

private ApiCallQueue requestsQueue = new ApiCallQueue();

// Ads View
private AdView adView;





private void setupUI(View view) 
    // Set up touch listener for non-text box views to hide keyboard.
    if (!(view instanceof EditText)) 
        view.setOnTouchListener(new OnTouchListener() 
            @Override
            public boolean onTouch(View v, MotionEvent event) 
                // Hide the search field if the user is touching anywhere
                // except the search field
                if (etSearch.isShown()) 
                    etSearch.startDeflation();
                    return true;
                
                return false;
            
        );
    

    // If a layout container, iterate over children and seed recursion.
    if (view instanceof ViewGroup) 
        for (int i = 0; i < ((ViewGroup) view).getChildCount(); i++) 
            View innerView = ((ViewGroup) view).getChildAt(i);
            setupUI(innerView);
        
    


/**
 * Initial the UI of current screen Initial variables;
 */
@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
private void initialActivity() 
    setupUI(findViewById(R.id.parent));
    // display the name of location
    tvLocation = (TextView) findViewById(R.id.tvLocation);
    // Set the location icon
    tvLocation.setCompoundDrawablesWithIntrinsicBounds(getResources()
            .getDrawable(R.drawable.icon_marker), null, null, null);
    // display today highest temperature
    tvHTemp = (TextView) findViewById(R.id.tvHTemp);
    // Set the highest temperature icon
    tvHTemp.setCompoundDrawablesWithIntrinsicBounds(getResources()
            .getDrawable(R.drawable.icon_highest), null, null, null);
    // display today lowest temperature
    tvLTemp = (TextView) findViewById(R.id.tvLTemp);
    // Set the lowest temperature icon
    tvLTemp.setCompoundDrawablesWithIntrinsicBounds(getResources()
            .getDrawable(R.drawable.icon_lowest), null, null, null);
    // display the current temperature
    tvCurrentTemp = (TextView) findViewById(R.id.tvCurrentTemp);
    // display the update time stamp
    tvTimestamp = (TextView) findViewById(R.id.tvTimestamp);
    // display the date of today
    tvDate = (TextView) findViewById(R.id.tvDate);
    // display the wind speed
    tvWindSpeed = (TextView) findViewById(R.id.tvWindSpeed);
    // Set wind speed icon
    tvWindSpeed.setCompoundDrawablesWithIntrinsicBounds(getResources()
            .getDrawable(R.drawable.icon_wind), null, null, null);
    // display the pressure
    tvPressure = (TextView) findViewById(R.id.tvPressure);
    // Set wind speed icon
    tvPressure.setCompoundDrawablesWithIntrinsicBounds(getResources()
            .getDrawable(R.drawable.icon_pressure), null, null, null);
    // visualize the current weather condition
    ivCurrentWeather = (ImageView) findViewById(R.id.ivCurrentWeather);
    // Scrollable forecast
    forecastGallery = (HorizontalViewGallery) findViewById(R.id.gForcast);
    // Search city button
    ivSearch = (ImageView) findViewById(R.id.ivSearch);
    // Setting button
    ivRemind = (ImageView) findViewById(R.id.ivRemind);
    // My location button
    ivMyLocation = (ImageView) findViewById(R.id.ivMyLocation);
    // Temp unit setting Button
    ivUnit = (ImageView) findViewById(R.id.ivUnit);
    if (getTempUnit().equals(PARAM_TEMP_UNIT_C))
        ivUnit.setImageDrawable(getResources().getDrawable(
                R.drawable.button_unit_f));
    else if (getTempUnit().equals(PARAM_TEMP_UNIT_F))
        ivUnit.setImageDrawable(getResources().getDrawable(
                R.drawable.button_unit_c));
    // Refresh button
    ivRefresh = (ImageView) findViewById(R.id.ivRefresh);
    // Search field
    etSearch = (MyCustomEditText) findViewById(R.id.etSearch);
    // set the animation of search field
    // Animation Duration in milliseconds;
    int duration = 500;
    // Inflate animation
    final AnimationSet inflate = new AnimationSet(true);
    ScaleAnimation scaleIn = new ScaleAnimation(0f, 1.0f, 1.0f, 1.0f,
            Animation.RELATIVE_TO_SELF, 1.0f, Animation.RELATIVE_TO_SELF,
            0.5f);
    scaleIn.setDuration(duration);
    inflate.addAnimation(scaleIn);
    inflate.setAnimationListener(new AnimationListener() 

        @Override
        public void onAnimationEnd(Animation animation) 
        

        @Override
        public void onAnimationRepeat(Animation animation) 
        

        @Override
        public void onAnimationStart(Animation animation) 
            etSearch.setVisibility(View.VISIBLE);
            etSearch.requestFocus();
            showSoftKeyboard(etSearch);
        
    );
    // Deflate animation
    final AnimationSet deflate = new AnimationSet(true);
    ScaleAnimation scaleDe = new ScaleAnimation(1.0f, 0f, 1.0f, 1.0f,
            Animation.RELATIVE_TO_SELF, 1.0f, Animation.RELATIVE_TO_SELF,
            0.5f);
    scaleDe.setDuration(duration);
    deflate.addAnimation(scaleDe);
    deflate.setAnimationListener(new AnimationListener() 

        @Override
        public void onAnimationEnd(Animation animation) 
            etSearch.setVisibility(View.INVISIBLE);
        

        @Override
        public void onAnimationRepeat(Animation animation) 
        

        @Override
        public void onAnimationStart(Animation animation) 
            hideSoftKeyboard(etSearch);
        
    );

    etSearch.setInflation(inflate);
    etSearch.setDeflation(deflate);

    // Running the change of digital clock on separate UI thread
    // to avoid any delay of other action on UI.
    runOnUiThread(new Runnable() 
        @Override
        public void run() 
            if (!Utils.androidMinimum(API_JELLY_BEAN_MR1)) 
                // Using the widget class @code DigitalClock if the
                // android api is less than 17
                DigitalClock dcClock = (DigitalClock) findViewById(R.id.dcClock);
                dcClock.addTextChangedListener(new TextWatcher() 

                    @Override
                    public void onTextChanged(CharSequence s, int start,
                            int before, int count) 
                    

                    @Override
                    public void beforeTextChanged(CharSequence s,
                            int start, int count, int after) 
                    

                    @Override
                    public void afterTextChanged(Editable s) 
                        // Removed seconds
                        if (s.length() >= 5) 
                            if (s.charAt(4) == ':') 
                                s.delete(4, s.length());
                             else if (s.length() >= 6
                                    && s.charAt(5) == ':') 
                                s.delete(5, s.length());
                            
                        
                    
                );
             else 
                // Using the widget class @code TextClock if the android
                // api is greater than or equal to 17
                TextClock dcClock = (TextClock) findViewById(R.id.dcClock);
                dcClock.addTextChangedListener(new TextWatcher() 

                    @Override
                    public void onTextChanged(CharSequence s, int start,
                            int before, int count) 
                    

                    @Override
                    public void beforeTextChanged(CharSequence s,
                            int start, int count, int after) 
                    

                    @Override
                    public void afterTextChanged(Editable s) 
                        // Removed seconds
                        if (s.length() >= 5) 
                            if (s.charAt(4) == ':') 
                                s.delete(4, s.length());
                             else if (s.length() >= 6
                                    && s.charAt(5) == ':') 
                                s.delete(5, s.length());
                            
                        
                    
                );
            
        
    );

    mIconFinder = new IconFinder(this);


@Override
protected void onCreate(Bundle savedInstanceState) 
    super.onCreate(savedInstanceState);




    /*
     * Create a new location client, using the enclosing class to handle
     * callbacks.
     */
    mLocationClient = new LocationClient(this, this, this);

    // Create the LocationRequest object
    mLocationRequest = LocationRequest.create();
    // Use high accuracy
    mLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
    // Set the update interval to 5 seconds
    mLocationRequest.setInterval(UPDATE_INTERVAL);
    // Set the fastest update interval to 1 second
    mLocationRequest.setFastestInterval(FASTEST_INTERVAL);

    setContentView(R.layout.activity_main);
    .
    .
    .

您能否解释一下,管理新权限系统的最佳方式是什么。请用简单的方式给我解释一下

【问题讨论】:

去阅读关于这个主题的文档。它非常好,它会准确地告诉你你需要知道什么以及使用什么代码。 【参考方案1】:

粗略和精细的位置被认为是危险的权限。必须在 Android 6 及更高版本上明确请求这些权限。这是一种解决方法:

public void checkPermission()
    if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED ||
                       ContextCompat.checkSelfPermission(this,Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED
                    )//Can add more as per requirement

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

对于调用:

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) 
    checkPermission();

您可以在任何地方实现它。您可以在首次启动时或在需要时请求它。

尽管如此,总是记得在使用之前检查您是否有权限。该权限也可以在运行时撤销,这将绕过您在启动时进行的任何检查。

这是一种方法:

@Override
protected void onCreate(Bundle savedInstanceState) 
    super.onCreate(savedInstanceState);

    /*
    This is called before initializing the map because the map needs permissions(the cause of the crash)
    */
    if (Build.VERSION.SDK_INT == Build.VERSION_CODES.M ) 
        checkPermission();
    
    // Re-check before enabling. You can add an else statement to warn the user about the lack of functionality if it's disabled. 
    // "or" is used instead of "and" as per the error. If it requires both, flip it over to &&. (I'm not sure, I haven't used GPS stuff before)
    if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED ||
                   ContextCompat.checkSelfPermission(this,Manifest.permission.ACCESS_COARSE_LOCATION) == PackageManager.PERMISSION_GRANTED)
        /*
         * Create a new location client, using the enclosing class to handle
         * callbacks.
         */
        mLocationClient = new LocationClient(this, this, this);

        // Create the LocationRequest object
        mLocationRequest = LocationRequest.create();
        // Use high accuracy
        mLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
        // Set the update interval to 5 seconds
        mLocationRequest.setInterval(UPDATE_INTERVAL);
        // Set the fastest update interval to 1 second
        mLocationRequest.setFastestInterval(FASTEST_INTERVAL);
    
    setContentView(R.layout.activity_main);
    .
    .
    .

这是我推荐的方式。还有其他处理权限的方法,但无论您选择什么,请确保在访问依赖于危险权限的 API 之前检查您是否拥有权限。权限可以在运行时撤销而不会触发销毁生命周期事件,因此可以绕过启动时的任何检查以确保您拥有权限。因此,在使用 API 之前进行检查非常重要。

还有其他实现权限请求的方法。如果您正在寻找这种超级基本启动检查的替代方法,这里有一些想法:

如果您有设置活动,请为其添加权限请求。 不要在启动时请求权限,而是在需要时请求权限 如果权限检查在启动时失败,请将权限请求活动添加到您的启动流程中

还有比我知道的更多的方法来解决这个问题。我已经有几年没有接触过 Android 了,而且我对请求权限的所有方式也很不熟悉。另请参阅the documentation 以获取更多最新建议。

【讨论】:

可能想要使用&gt;= 而不是== 你必须在应用启动时要求他们这不是真的 您不必这样做,但这是最好的方法。在你需要它们之后请求它们 -> 崩溃 这是最好的方法这也是不正确的。您应该在用户想要做需要许可的事情时请求他们 我会说这取决于应用程序。在发布时询问他们,然后在需要时检查他们是否在那里也是一种解决方法

以上是关于错误:客户端必须具有 ACCESS_COARSE_LOCATION 或 ACCESS_FINE_LOCATION [重复]的主要内容,如果未能解决你的问题,请参考以下文章

切片索引必须是整数或 None 或具有 __index__ 方法

收到此错误:条件绑定的初始化程序必须具有可选类型,而不是“布尔”

如何修复“必需且已安装 (@@anaconda-CentOS-201303020151.x86_64/6.4)”之类的错误

具有分组依据的 PostgreSQL 聚合函数

Logits 和 Labels 必须具有相同的形状:Tensorflow

ValueError:客户端机密必须用于 Web 或已安装的应用程序