错误:客户端必须具有ACCESS_COARSE_LOCATION或ACCESS_FINE_LOCATION

时间:2016-07-18 08:09:13

标签: android android-permissions

我在另一个应用中使用了一个模块。在我实现模块之前,我将它设置在API Level 23(编译和目标)上,就像我的主项目一样 这个工作正常,除了这个错误。问题是,谷歌已经改变了自marshmellow以来的权限管理。最后,我不知道应该如何以及在哪里设置权限。

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

  

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>

模块的主要活动:

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 个答案:

答案 0 :(得分:8)

粗略和精细的位置被视为“危险”权限。因此,您必须在应用启动时询问它们(因为我假设您拥有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();
}

您可以在任何地方实现它,只需记住在使用需要它们之前请求权限!

修改

上面的内容应该在一个活动中调用。这是您在上面发布的onCreate方法:

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

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

这是'简单'的方式。你也可以强迫用户使用它。

在onCreate中调用checkPermissions正常。

但是,checkPermissions现在将包含一个intent。这在启动时调用很好,因为它会将活动更改为:

import android.Manifest;
import android.app.Activity;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
import android.view.View;
import android.widget.Button;


public class Permissions extends Activity implements View.OnClickListener{
    /**
     * This will handle permissions asking
     */
    Button button;
    //handles ask permissions/continue
    boolean completed = false;
    public void onCreate(Bundle sis){
        super.onCreate(sis);
        /**
         * I'm not going to create the layout for you, but you essentially need two things:
         * a TextView
         * a Button
         *
         * The TextView will explain **why** you need the permissions, while the button
         * will be pressed by the user to ask the permissions. No need to call the textview
         * inside this class
         */
        setContentView(R.layout.yourlayout);
        button = (Button) findViewById(R.id.button);

    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.button:
                if(!completed)
                    askPermissions();
                else{
                    //start the main activity
                }
                break;
        }
    }


    public void askPermissions(){
        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);
        }else if(ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED &&
                ContextCompat.checkSelfPermission(this,Manifest.permission.ACCESS_COARSE_LOCATION) == PackageManager.PERMISSION_GRANTED
                ){
            completed = true;
            button.setText("Permissions supplied. Press to continue");
        }

    }
}

活动中的新checkPermission:

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

       //start an intent to the class above. Do this because the app does not have enough permissions

    }
}

这将强制用户允许访问权限,否则他/她将无法访问该应用。如果权限被拒绝,则用户将无法访问,因为它会使应用程序崩溃(您可以在布局中的解释性文本视图中写入。

所以,我不打算给你一个布局,因为它真的取决于你。但是你需要两件基本的东西:

  • 按钮 - 用户在阅读文本视图后按此按钮(打算,您不能强迫用户阅读),按下按钮时会询问权限或重定向到
  • TextView - 将解释您要求的权限以及您需要的权限

此系统非常智能,因为如果提供了所有权限,则不会调用该屏幕。请记住,Build-VERSION.SDK_INT&gt; = Build.VERSION_CODES.M非常重要,否则会导致错误(因为它要求不支持它的平台上的权限)