zoukankan      html  css  js  c++  java
  • 定位与权限

    就昨天,我遇到一个神奇的问题,就是android原生的GPS定位在我的魅族4上跑的顺顺利利,但是一到了红米note4x或者最近的新android上就不能定位了,我把高德百度的定位接口都用过,但是就是报没权限的错误,我检查了一下AndroidManifest配置文件,ok都有,什么高德的key,权限等等都有。

    查了一下报错,发现在android6.0版本后为了用户安全,有几个权限设置运行时权限,如果不加动态获取权限的代码,是不会提示的,没有得到权限,当然无法定位。

    正常的解析是:

    对于6.0以下的权限及在安装的时候,根据权限声明产生一个权限列表,用户只有在同意之后才能完成app的安装,造成了我们想要使用某个app,就要默默忍受其一些不必要的权限(比如是个app都要访问通讯录、短信等)。而在6.0以后,我们可以直接安装,当app需要我们授予不恰当的权限的时候,我们可以予以拒绝(比如:单机的象棋对战,请求访问任何权限,我都是不同意的)。当然你也可以在设置界面对每个app的权限进行查看,以及对单个权限进行授权或者解除授权。

    新的权限机制更好的保护了用户的隐私,Google将权限分为两类,一类是Normal Permissions,这类权限一般不涉及用户隐私,是不需要用户进行授权的,比如手机震动、访问网络等;另一类是Dangerous Permission,一般是涉及到用户隐私的,需要用户进行授权,比如读取sdcard、访问通讯录等。

    需要动态获取权限(Runtime Permissions)的主要有以下几个:

    动态获取权限有:

    参考大神:http://blog.csdn.net/lmj623565791/article/details/50709663

      

    group:android.permission-group.CONTACTS
      permission:android.permission.WRITE_CONTACTS
      permission:android.permission.GET_ACCOUNTS
      permission:android.permission.READ_CONTACTS
    
    group:android.permission-group.PHONE
      permission:android.permission.READ_CALL_LOG
      permission:android.permission.READ_PHONE_STATE
      permission:android.permission.CALL_PHONE
      permission:android.permission.WRITE_CALL_LOG
      permission:android.permission.USE_SIP
      permission:android.permission.PROCESS_OUTGOING_CALLS
      permission:com.android.voicemail.permission.ADD_VOICEMAIL
    
    group:android.permission-group.CALENDAR
      permission:android.permission.READ_CALENDAR
      permission:android.permission.WRITE_CALENDAR
    
    group:android.permission-group.CAMERA
      permission:android.permission.CAMERA
    
    group:android.permission-group.SENSORS
      permission:android.permission.BODY_SENSORS
    
    group:android.permission-group.LOCATION
      permission:android.permission.ACCESS_FINE_LOCATION
      permission:android.permission.ACCESS_COARSE_LOCATION
    
    group:android.permission-group.STORAGE
      permission:android.permission.READ_EXTERNAL_STORAGE
      permission:android.permission.WRITE_EXTERNAL_STORAGE
    
    group:android.permission-group.MICROPHONE
      permission:android.permission.RECORD_AUDIO
    
    group:android.permission-group.SMS
      permission:android.permission.READ_SMS
      permission:android.permission.RECEIVE_WAP_PUSH
      permission:android.permission.RECEIVE_MMS
      permission:android.permission.RECEIVE_SMS
      permission:android.permission.SEND_SMS
      permission:android.permission.READ_CELL_BROADCASTS

    获取动态权限步骤:

    package com.tfot.hotel.yichengyiyu.Activity;
    
    import android.Manifest;
    import android.content.Context;
    import android.content.Intent;
    import android.content.SharedPreferences;
    import android.content.pm.PackageManager;
    import android.location.Address;
    import android.location.Geocoder;
    import android.location.Location;
    import android.location.LocationListener;
    import android.location.LocationManager;
    import android.net.ConnectivityManager;
    import android.net.NetworkInfo;
    import android.os.Build;
    import android.os.Bundle;
    import android.os.Handler;
    import android.support.annotation.Nullable;
    import android.support.v4.app.ActivityCompat;
    import android.util.Log;
    import android.widget.Toast;
    
    import com.amap.api.location.AMapLocation;
    import com.amap.api.location.AMapLocationClient;
    import com.amap.api.location.AMapLocationClientOption;
    import com.amap.api.location.AMapLocationListener;
    import com.amap.api.services.core.LatLonPoint;
    import com.amap.api.services.geocoder.GeocodeAddress;
    import com.amap.api.services.geocoder.GeocodeResult;
    import com.amap.api.services.geocoder.GeocodeSearch;
    import com.amap.api.services.geocoder.RegeocodeQuery;
    import com.amap.api.services.geocoder.RegeocodeResult;
    import com.tfot.hotel.yichengyiyu.MainActivity;
    import com.tfot.hotel.yichengyiyu.R;
    import com.tfot.hotel.yichengyiyu.Util.base.BaseActivity;
    
    import java.io.IOException;
    import java.text.SimpleDateFormat;
    import java.util.Date;
    import java.util.List;
    
    import static java.lang.Math.atan2;
    import static java.lang.Math.cos;
    import static java.lang.Math.sin;
    import static java.lang.Math.sqrt;
    
    /**
     * Created by ACER on 2017/1/22.
     */
    
    public class SplashActivity extends BaseActivity implements AMapLocationListener{
        private SharedPreferences sp;
        private SharedPreferences.Editor editor;
        //声明mLocationOption对象
        public AMapLocationClientOption mLocationOption = null;
        public AMapLocationClient mlocationClient = null;
        double lat; //维度
        double lon ;//经度
        @Override
        protected void onCreate(@Nullable Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_splash);
             sp = getSharedPreferences("sp_demo", Context.MODE_PRIVATE);
             editor = sp.edit();
    
    
            //判断是否为android6.0系统版本,如果是,需要动态添加权限
            if (Build.VERSION.SDK_INT>=23){
                showContacts();
            }else{
                new GaoDeDingWei().aMapLocationByNet();
            }
    
    
    
    
            new Handler().postDelayed(new Runnable() {
                public void run() {
                    /* Create an Intent that will start the Main WordPress Activity. */
                    Intent mainIntent = new Intent(SplashActivity.this, MainActivity.class);
                    SplashActivity.this.startActivity(mainIntent);
                    SplashActivity.this.finish();
                }
            }, 3000);
        }
    
    
    
    
        //6.0定位需要的权限申请
         public void showContacts(){
            if (ActivityCompat.checkSelfPermission(this, "android.permission.ACCESS_COARSE_LOCATION")
                    != PackageManager.PERMISSION_GRANTED){
                Toast.makeText(getApplicationContext(),"没有权限,请手动开启定位权限", Toast.LENGTH_SHORT).show();
                // 申请一个(或多个)权限,并提供用于回调返回的获取码(用户定义)
                ActivityCompat.requestPermissions(SplashActivity.this,new String[]{"android.permission.ACCESS_COARSE_LOCATION"}, 100);
            }else{
                System.out.println("定位开始!!!!!!!!!!!!!!!!");
                new GaoDeDingWei().aMapLocationByNet();
            }
        }
    
    
    
    
    
        //Android6.0申请权限的回调方法
        @Override
        public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
            super.onRequestPermissionsResult(requestCode, permissions, grantResults);
            switch (requestCode) {
                // requestCode即所声明的权限获取码,在checkSelfPermission时传入
                case 100:
                    if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                        // 获取到权限,作相应处理(调用定位SDK应当确保相关权限均被授权,否则可能引起定位失败)
                        new GaoDeDingWei().aMapLocationByNet();
                    } else {
                        // 没有获取到权限,做特殊处理
                        Toast.makeText(getApplicationContext(), "获取位置权限失败,请手动开启", Toast.LENGTH_SHORT).show();
                    }
                    break;
                default:
                    break;
            }
        }
    
    
        //高德定位
        class GaoDeDingWei {
            public GaoDeDingWei(){
    
            }
            public  void  aMapLocationByNet(){
                //获取网络管理对象
                ConnectivityManager connmanager = (ConnectivityManager)getSystemService(Context.CONNECTIVITY_SERVICE);
                //获取当前活跃的网络
                NetworkInfo info = connmanager.getActiveNetworkInfo();
                if (info == null) {
    //                Intent intent = new Intent();
    //                intent.setAction(Settings.ACTION_AIRPLANE_MODE_SETTINGS);//跳转打开网络的手机界面
    //                startActivity(intent);
                    Toast.makeText(SplashActivity.this,"请打开网络",Toast.LENGTH_SHORT).show();
                } else {
                    //city_tv.setText("定位中,请稍等");
                    aMapLocation();
                }
            }
            public  void aMapLocation(){
                mlocationClient = new AMapLocationClient(SplashActivity.this);
                //初始化定位参数
                mLocationOption = new AMapLocationClientOption();
                //设置定位监听
                mlocationClient.setLocationListener(SplashActivity.this);
                //设置定位模式为高精度模式,Battery_Saving为低功耗模式,Device_Sensors是仅设备模式
                mLocationOption.setLocationMode(AMapLocationClientOption.AMapLocationMode.Hight_Accuracy);
                //设置定位间隔,单位毫秒,默认为2000ms
                mLocationOption.setInterval(2000);
                //设置定位参数
                mlocationClient.setLocationOption(mLocationOption);
                // 此方法为每隔固定时间会发起一次定位请求,为了减少电量消耗或网络流量消耗,
                // 注意设置合适的定位时间的间隔(最小间隔支持为1000ms),并且在合适时间调用stopLocation()方法来取消定位请求
                // 在定位结束后,在合适的生命周期调用onDestroy()方法
                // 在单次定位情况下,定位无论成功与否,都无需调用stopLocation()方法移除请求,定位sdk内部会移除
                //启动定位
                mlocationClient.startLocation();
            }
    
        }
    
    
    
        //高德地图数据监听改变
        @Override
        public void onLocationChanged(AMapLocation amapLocation) {
            if (amapLocation != null) {
                if (amapLocation.getErrorCode() == 0) {
                    //定位成功回调信息,设置相关消息
                    // amapLocation.getLocationType();//获取当前定位结果来源,如网络定位结果,详见定位类型表
                    lat= amapLocation.getLatitude();//获取纬度
                    lon =amapLocation.getLongitude();//获取经度
                    //amapLocation.getAccuracy();//获取精度信息
                        /*SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                        Date date = new Date(amapLocation.getTime());
                        df.format(date);//定位时间*/
                    //把经纬度转换成城市
                    if (this!=null){
    
                        GeocodeSearch geocoderSearch = new GeocodeSearch(this);
                        geocoderSearch.setOnGeocodeSearchListener(new GeocodeSearch.OnGeocodeSearchListener(){
    
                            @Override
                            public void onGeocodeSearched(GeocodeResult result, int rCode) {
                                // TODO Auto-generated method stub
    
                            }
    
                            @Override
                            public void onRegeocodeSearched(RegeocodeResult result, int rCode) {
                                //两个附近点的位置
                                String city  = result.getRegeocodeAddress().getCity();
                                editor.putString("lat", lat+"");
                                editor.putString("lon", lon+"");
                                editor.putString("city_tv",city);
                                editor.commit();
                                System.out.println("###############当前位置城市###############-->"+city);
                            }});
                        LatLonPoint lp = new LatLonPoint(lat,lon);
                        RegeocodeQuery query = new RegeocodeQuery(lp, 200,GeocodeSearch.AMAP);
                         geocoderSearch.getFromLocationAsyn(query);
    
    
                    }else {
                        //  return;
                    }
                } else {
                    //显示错误信息ErrCode是错误码,errInfo是错误信息,详见错误码表。
                    Log.e("AmapError","location Error, ErrCode:"
                            + amapLocation.getErrorCode() + ", errInfo:"
                            + amapLocation.getErrorInfo());
                }
            }
        }
    }

    在fragment中获取定位权限:  

    基本一样

    但是

    Fragment中运行时权限的特殊处理
    在Fragment中申请权限,不要使用ActivityCompat.requestPermissions, 直接使用Fragment的requestPermissions方法,否则会回调到Activity的 onRequestPermissionsResult
    父子fragment中子fragment获取权限:http://blog.csdn.net/qfanmingyiq/article/details/52561658

        

    最后找到的hongyang干货:

    MPermissions用法

    对外的接口和PermissionGen基本一致,因为申请只需要三个参数,抛弃了使用原本类库的单例的方式,直接一个几个静态方法,简单整洁暴力。

    贴一个用法:

    public class MainActivity extends AppCompatActivity
    {
    
        private Button mBtnSdcard;
        private static final int REQUECT_CODE_SDCARD = 2;
    
        @Override
        protected void onCreate(Bundle savedInstanceState)
        {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
    
            mBtnSdcard = (Button) findViewById(R.id.id_btn_sdcard);
            mBtnSdcard.setOnClickListener(new View.OnClickListener()
            {
                @Override
                public void onClick(View v)
                {
                    MPermissions.requestPermissions(MainActivity.this, REQUECT_CODE_SDCARD, Manifest.permission.WRITE_EXTERNAL_STORAGE);
                }
            });
        }
    
        @Override
        public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults)
        {
            MPermissions.onRequestPermissionsResult(this, requestCode, permissions, grantResults);
            super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        }
    
    
        @PermissionGrant(REQUECT_CODE_SDCARD)
        public void requestSdcardSuccess()
        {
            Toast.makeText(this, "GRANT ACCESS SDCARD!", Toast.LENGTH_SHORT).show();
        }
    
        @PermissionDenied(REQUECT_CODE_SDCARD)
        public void requestSdcardFailed()
        {
            Toast.makeText(this, "DENY ACCESS SDCARD!", Toast.LENGTH_SHORT).show();
        }
    }

    是不是简单明了~~对于onRequestPermissionsResult所有的Activity都是一致的,所以可以放到BaseActivity中去。此外,在Fragment中使用的方式一致,详见demo。

    详见库:https://github.com/hongyangAndroid/MPermissions.

    至于为什么不直接介绍MPermission的源码,因为主要涉及到Annotation Processor,所以以这种方式引出,后面考虑单篇博文介绍下我目前所会的编译时注解的相关做法以及API的使用。

  • 相关阅读:
    积水路面Wet Road Materials 2.3
    门控时钟问题
    饮料机问题
    Codeforces Round #340 (Div. 2) E. XOR and Favorite Number (莫队)
    Educational Codeforces Round 82 (Rated for Div. 2)部分题解
    Educational Codeforces Round 86 (Rated for Div. 2)部分题解
    Grakn Forces 2020部分题解
    2020 年百度之星·程序设计大赛
    POJ Nearest Common Ancestors (RMQ+树上dfs序求LCA)
    算法竞赛进阶指南 聚会 (LCA)
  • 原文地址:https://www.cnblogs.com/mrszhou/p/7105741.html
Copyright © 2011-2022 走看看