浅析Android位置权限以及数组寻找索引的坑

**一、**Android 危险权限,来自官方文档的坑

Android开发者都知道,Android 6.0 之前,权限申请只需要在 AndroidManifest.xml 文件中声明就可以。Android 6.0 开始,权限申请发生了变化,危险权限需要在应用中动态申请,之前写过一篇 Android 动态申请危险权限的笔记,详情参考: Android 6.0 动态申请危险权限。

先截个图,看看Android官方的说明:

img

再看危险权限的分组情况:

img

意思是,对危险权限进行了分组,同一组中,只要有有一个权限被授权了,同组中其它权限也就默认授权了。比如,我授权应用有读存储卡的权限之后,应用也就有了写存储卡的权限,事实上也确实如此。 然而问题来了,利用 GPS 获取位置信息的代码:

LocationManager locationManager = (LocationManager)getSystemService(Context.LOCATION_SERVICE);
if(locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER)){
 if(ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION)
  != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission
  (MainActivity.this, Manifest.permission.ACCESS_COARSE_LOCATION)
  != PackageManager.PERMISSION_GRANTED){
 // request permissions
 // ...
 // ...
 }else{  
 Location location = locationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER;
 if(location != null){ 
  double latitude = location.getLatitude(); 
  double longitude = location.getLongitude(); 
 } 
 }
}

通过官方的危险权限组,我们也能看到:Location 权限组里包含:ACCESS_FINE_LOCATION 和 ACCESS_COARSE_LOCATION 两个权限,按照上面的说明,两个权限只要有一个申请授权成功,即可成功获取经纬度。——然鹅,当成功申请了 ACCESS_COARSE_LOCATION 权限后,程序依然会崩,错误信息提示,需要获得 ACCESS_FINE_LOCATION 权限 。

二、Spinner 的 setSelection() 方法,源于自己想当然的坑

Android 中的下拉列表控件 spinner 有一个方法 setSelection(int position) ,显示第几项。此方法可能没有效果???总是显示第一项???

当在做两个spinner联动时,spinner2依据spinner1的选择填充数据,然后使用setSeletion(2)来设置默认项。结果发现:spinner2显示的总是第一项,但是实际选择的确实已经是position 2的位置 。 解决方法:

旧代码:

spinner.setAdapter(adapter);
  spinner.setSelection(2);

解决方案有二:

(1)

spinner.setAdapter(adapter);
  spinner.setSelection(2,true); //spinner会重新layout

(2) 推荐

 spinner.setAdapter(adapter);![](https://images2018.cnblogs.com/blog/758949/201807/758949-20180726224508174-131546620.jpg)

  adapter.notifyDataSetChanged(); //通知spinner刷新数据
 spinner.setSelection(2);

那么,这到底是什么原因造成的?我认为这是一个bug 。这种情况通常发生在重新填充数据之后,除此之外,使用setSelection(int position)都能得到正确的显示。

setSelection(int position, boolean animate)和setSelection(int position) 实现机制有较大区别,当调用前者时重新layout,立即触发onItemSelected函数,作用相当于用手直接点击。而后者设置了下次选择位置:setNextSelectedPositionInt(position); 然后请求Layout;,而requestLayout并非立即执行,仅仅是一个schedule。但是后者可能在重新装载数据然后Layout时丢失了某些状态。

然鹅,我要说的坑不是这样,我没有两个 Spinner 联动,我出现的问题是这样的,上代码

布局文件:

<?xml version="1.0" encoding="utf-8"? 
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
 xmlns:tools="http://schemas.android.com/tools"
 android:layout_width="match_parent"
 android:layout_height="match_parent"
 tools:context="com.example.sharpcj.helloworld.MainActivity" 

 <Spinner
  android:id="@+id/sp_test"
  android:layout_width="wrap_content"
  android:layout_height="wrap_content"
  android:layout_centerInParent="true" 

 </Spinner 

</RelativeLayout 

java 代码:

package com.example.sharpcj.helloworld;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.ArrayAdapter;
import android.widget.Spinner;

import java.util.Arrays;

public class MainActivity extends AppCompatActivity {

 private Spinner mSpTest;

 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);
  String[] strs = new String[20];
  for (int i = 0; i < strs.length; i++) {
   strs[i] = "第" + i + "项";
  }
  mSpTest = findViewById(R.id.sp_test);
  mSpTest.setAdapter(new ArrayAdapter< (this, android.R.layout.simple_list_item_1, strs));

  int index = Arrays.binarySearch(strs, "第11项");
  mSpTest.setSelection(index);
 }
}

运行结果:

img

what??? 为什么会这样呢? spinner 表示这个锅它不背,其实这个坑怪我自己想当然了,原因在于错误地使用了 Arrays.binarySearch(Object[] a , Object key) 这个方法,想当然地认为了返回值为查找到数组的 index。代码中, index 的实际值是 -2 。

网上找了一下资料:

binarySearch(int[] a, int key) 此方法的规则是这样的:

1、如果找到关键字,则返回值为关键字在数组中的位置索引,且索引从0开始

2、如果没有找到关键字,返回值为负的插入点值,所谓插入点值就是第一个比关键字大的元素在数组中的位置索引,而且这个位置索引从1开始。

而binarySearch(Object[] a, Object key) 最终调用方法的源码如下:

 // Like public version, but without range checks.
 private static int binarySearch0(Object[] a, int fromIndex, int toIndex,
          Object key) {
  int low = fromIndex;
  int high = toIndex - 1;

  while (low <= high) {
   int mid = (low + high)     1;
   @SuppressWarnings("rawtypes")
   Comparable midVal = (Comparable)a[mid];
   @SuppressWarnings("unchecked")
   int cmp = midVal.compareTo(key);

   if (cmp < 0)
    low = mid + 1;
   else if (cmp   0)
    high = mid - 1;
   else
    return mid; // key found
  }
  return -(low + 1); // key not found.
 }

关于 Object 类型,Oracle 大神写的这个二分法寻找索引的代码暂时没看懂。

更多Android进阶指南 可以扫码 解锁 《Android十大板块文档》

1.Android车载应用开发系统学习指南(附项目实战)

2.Android Framework学习指南,助力成为系统级开发高手

3.2023最新Android中高级面试题汇总+解析,告别零offer

4.企业级Android音视频开发学习路线+项目实战(附源码)

5.Android Jetpack从入门到精通,构建高质量UI界面

6.Flutter技术解析与实战,跨平台首要之选

7.Kotlin从入门到实战,全方面提升架构基础

8.高级Android插件化与组件化(含实战教程和源码)

9.Android 性能优化实战+360°全方面性能调优

10.Android零基础入门到精通,高手进阶之路

敲代码不易,关注一下吧。ღ( ´・ᴗ・` ) 🤔

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值