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的使用。

  • 相关阅读:
    hdu 1247 Hat’s Words (字典树)
    测试
    hdu 1285 确定比赛名次 (拓扑)
    hdu 3172 Virtual Friends (并查集)
    hdu 3635 Dragon Balls (并查集)
    [Shell学习笔记] read命令从键盘或文件中获取标准输入(转载)
    Shell脚本下条件测试(eq.ne.....)(转载)
    每天一个 linux命令(35):ln 命令(转载)
    ubuntu中 python升级 (转载)
    source命令用法(转载)
  • 原文地址:https://www.cnblogs.com/mrszhou/p/7105741.html
Copyright © 2011-2022 走看看