GVKun编程网logo

说说Android6.0动态申请权限的那些坑(android 6.0动态申请权限)

14

如果您对说说Android6.0动态申请权限的那些坑感兴趣,那么本文将是一篇不错的选择,我们将为您详在本文中,您将会了解到关于说说Android6.0动态申请权限的那些坑的详细内容,我们还将为您解答a

如果您对说说Android6.0动态申请权限的那些坑感兴趣,那么本文将是一篇不错的选择,我们将为您详在本文中,您将会了解到关于说说Android6.0动态申请权限的那些坑的详细内容,我们还将为您解答android 6.0动态申请权限的相关问题,并且为您提供关于Android 6.0 以后申请权限控制相关问题、Android 6.0动态权限申请教程、Android registerForActivityResult动态申请权限案例详解、Android Socket 发送广播包的那些坑的有价值信息。

本文目录一览:

说说Android6.0动态申请权限的那些坑(android 6.0动态申请权限)

说说Android6.0动态申请权限的那些坑(android 6.0动态申请权限)

白天在做SDK23版本的适配,遇到了不少坑,现在抽空记下来,以此为戒。

  首先要知道哪些坑,就得先了解一些定义和基本使用方式。

那么先介绍一下动态申请的权限分组情况。

  下面的权限组是由谷歌官方定义的,目的是在申请权限时,只要用户允许同一权限组的任意一条权限,那么该组的其他权限也就默认是允许的。不过据高人介绍,在使用时最好是用到哪个权限就具体的请求该权限,因为保不齐哪天谷歌一高兴就把权限组换了甚至删了、

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

 

其实权限组的定义很简单,下面简单介绍下动态申请权限的步骤。

第一步,检查app拥有的权限。 

1 if(ContextCompat.checkSelfPermission(
2     mActivity,Manifest.permisson.READ_CONTACTS)
3     != PackageManager.PERMISSION_GRANTED) {
4     //当前Activity没有获得READ_CONTACTS权限时
5 }else{
6     否则已允许
7 }

第二步,申请权限。

ActivityCompat.requestPermissions(
3     new String[]{Manifest.permission.READ_CONTACTS},1)">4     REQUEST_CODE_PERMISSION_CONTACTS);

 

第三步,权限申请回调方法。

 1 @Override
 2 public void onRequestPermissionsResult(int requestCode,String permissions[],int[] grantResults) {
 3     switch (requestCode) {
 4         case REQUEST_CODE_PERMISSION_CONTACTS: {
 5             if (grantResults.length > 0 && grantResults[0] == 6                 用户已授权
 7             }  {
 8                 用户拒绝权限
 9             }
10             return;
11         }
12     }
13 }

 

如此三步,看上去很简单,可真要用起来可就没有那么简单了。下面就说说这里边的坑吧。

 

坑一、权限申请只能在Activity或者Fragment的上下文中,不能用getApplicationContext()。

  由于我们项目在应用初始化时要获取内存的存储路径并创建一系列文件缓存,这些操作都是写在Application的onCreate()中调用不同的Util工具类进行的,所以在Android6.0以上这么写就有点不太靠谱了。目前我的解决措施是在应用程序初始化时,先判断SDK版本,只对版本号小于23的app创建缓存文件,高于23的则在进入Activity之后再初始化。

 

坑二、权限申请时使用的请求码必须小于16。

  至于什么原因不太清楚,可能谷歌公司认为权限本来就不多,没必要将请求码弄得很大占用多余的内存吧。说到请求码,也就是上面代码中未定义的常量值REQUEST_CODE_PERMISSION_CONTACTS,如果你定义的这个值超过了15,运行时就会报安全异常,提示请求码必须小于16。

 

目前来说这两个坑就够我忙活一天的了,看来还是经验不足啊,以后还要多多吸取经验。

Android 6.0 以后申请权限控制相关问题

Android 6.0 以后申请权限控制相关问题

 

java.lang.SecurityException: Permission Denial: starting Intent { act=android.media.action.IMAGE_CAPTURE flg=0x3 cmp=com.android.camera/.sdk.activity.CaptureActivity clip={text/uri-list U:content://com.dgbiz.zfxworker.fileProvider/external_storage_root/PicturesJPEG_20181024_175317_.jpg} (has extras) } from ProcessRecord{6691ac3 29310:com.dgbiz.zfxworker/u0a125} (pid=29310, uid=10125) with revoked permission android.permission.CAMERA

 

首先介绍一下遇到这个问题的原因:

Android开发时,到6.0系统上之后,有的权限就得申请才能用了。

Android将权限分为正常权限 和 危险权限

Android系统权限分为几个保护级别。需要了解的两个最重要保护级别是 正常权限 和 危险权限:

(1)正常权限:

涵盖应用需要访问其沙盒外部数据或资源,但对用户隐私或其他应用操作风险很小的区域。

这些权限在应用安装时授予,运行时不再询问用户。例如: 网络访问、WIFI状态、音量设置等。

(2)危险权限:

涵盖应用需要涉及用户隐私信息的数据或资源,或者可能对用户存储的数据或其他应用的操作产生影响的区域。

例如: 读取通讯录、读写存储器数据、获取用户位置等。如果应用声明需要这些危险权限,则必须在运行时明确告诉用户,让用户手动授予。

 

如何解决:

RxPermission:6.0以后个人建议统一使用这个来注册权限,此工具的牛逼之处在于6.0之前版本会直接permission.granted赋值true,6.0之后版本会先注册再赋值,省去了我们很多工作。话不多说上代码

注册权限(上面代码是注册多个权限,下面是注册单个的)    不足之处欢迎指点

final boolean[] isPermissionType = {false, false};
        RxPermissions rxPermissions = new RxPermissions(activity);
        rxPermissions.requestEach(Manifest.permission.CAMERA,Manifest.permission.WRITE_EXTERNAL_STORAGE)
                .subscribe(new Observer<Permission>() {
                    @Override
                    public void onSubscribe(Disposable d) {}
                    @Override
                    public void onNext(Permission permission) {
                        if (permission.name.equals(Manifest.permission.CAMERA)){
                            isPermissionType[0] = permission.granted;
                        }
                        if (permission.name.equals(Manifest.permission.WRITE_EXTERNAL_STORAGE)){
                            isPermissionType[1] = permission.granted;
                        }
                        if(isPermissionType[0] && isPermissionType[1]){
                            Matisse.from(activity)
                                    .choose(MimeType.allOf())//图片类型
                                    .theme(R.style.Matisse_Dracula)
                                    .countable(false)//true:选中后显示数字;false:选中后显示对号
                                    .maxSelectable(1)//可选的最大数
                                    .capture(true)//选择照片时,是否显示拍照
                                    .captureStrategy(new CaptureStrategy(true, "com.dgbiz.zfxworker.fileProvider"))//参数1 true表示拍照存储在共有目录,false表示存储在私有目录;参数2与 AndroidManifest中authorities值相同,用于适配7.0系统 必须设置
                                    .imageEngine(new PicassoEngine())//图片加载引擎
                                    .forResult(REQUEST_CODE_CHOOSE);
                        }else{
                            ToastUtil.showText(activity,"权限请求失败",ToastUtil.TWO_SECOND);
                        }
                    }
                    @Override
                    public void onError(Throwable e) {}

                    @Override
                    public void onComplete() {}
                });
        rxPermissions.request(Manifest.permission.WRITE_EXTERNAL_STORAGE)
                .subscribe(new Observer<Boolean>() {
                    @Override
                    public void onSubscribe(Disposable d) {
                    }
                    @Override
                    public void onNext(Boolean aBoolean) {
                        if (aBoolean) {
                            Matisse.from(activity)
                                    .choose(MimeType.allOf())//图片类型
                                    .theme(R.style.Matisse_Dracula)
                                    .countable(false)//true:选中后显示数字;false:选中后显示对号
                                    .maxSelectable(1)//可选的最大数
                                    .capture(true)//选择照片时,是否显示拍照
                                    .captureStrategy(new CaptureStrategy(true, "com.dgbiz.zfxworker.fileProvider"))//参数1 true表示拍照存储在共有目录,false表示存储在私有目录;参数2与 AndroidManifest中authorities值相同,用于适配7.0系统 必须设置
                                    .imageEngine(new PicassoEngine())//图片加载引擎
                                    .forResult(REQUEST_CODE_CHOOSE);//
                        } else{
                            ToastUtil.showText(activity,"权限请求失败",ToastUtil.TWO_SECOND);
                        }
                    }
                    @Override
                    public void onError(Throwable e) {
                    }
                    @Override
                    public void onComplete() {
                    }
                })

 

Android 6.0动态权限申请教程

Android 6.0动态权限申请教程

PermissionManage

项目地址:https://github.com/why168/AndroidProjects/tree/master/PermissionManage

介绍

如果设备运行的是 Android 6.0(API 级别 23)或更高版本,并且应用的 targetSdkVersion 是 23 或更高版本,则应用在运行时向用户请求权限。

如果设备运行的是 Android 5.1(API 级别 22)或更低版本,并且应用的 targetSdkVersion 是 22 或更低版本,则系统会在用户安装应用时要求用户授予权限。

参考资料

https://developer.android.google.cn/guide/topics/security/permissions.html

https://github.com/lovedise/PermissionGen

https://github.com/tbruyelle/RxPermissions

以下是需要单独申请的权限,共分为9组,每组只要有一个权限申请成功了,就默认整组权限都可以使用了

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

以下是普通权限,只需要在AndroidManifest.xml中申请即可

 android.permission.ACCESS_LOCATION_EXTRA_COMMANDS
 android.permission.ACCESS_NETWORK_STATE
 android.permission.ACCESS_NOTIFICATION_POLICY
 android.permission.ACCESS_WIFI_STATE
 android.permission.ACCESS_WIMAX_STATE
 android.permission.BLUetoOTH
 android.permission.BLUetoOTH_ADMIN
 android.permission.broADCAST_STICKY
 android.permission.CHANGE_NETWORK_STATE
 android.permission.CHANGE_WIFI_MULTICAST_STATE
 android.permission.CHANGE_WIFI_STATE
 android.permission.CHANGE_WIMAX_STATE
 android.permission.disABLE_KEyguard
 android.permission.EXPAND_STATUS_BAR
 android.permission.FLASHLIGHT
 android.permission.GET_ACCOUNTS
 android.permission.GET_PACKAGE_SIZE
 android.permission.INTERNET
 android.permission.KILL_BACKGROUND_PROCESSES
 android.permission.MODIFY_AUdio_SETTINGS
 android.permission.NFC
 android.permission.READ_SYNC_SETTINGS
 android.permission.READ_SYNC_STATS
 android.permission.RECEIVE_BOOT_COMPLETED
 android.permission.REORDER_TASKS
 android.permission.REQUEST_INSTALL_PACKAGES
 android.permission.SET_TIME_ZONE
 android.permission.SET_WALLPAPER
 android.permission.SET_WALLPAPER_HINTS
 android.permission.SUBSCRIbed_FeedS_READ
 android.permission.TRANSMIT_IR
 android.permission.USE_FINGERPRINT
 android.permission.VIBRATE
 android.permission.WAKE_LOCK
 android.permission.WRITE_SYNC_SETTINGS
 com.android.alarm.permission.SET_ALARM
 com.android.launcher.permission.INSTALL_SHORTCUT
 com.android.launcher.permission.UNINSTALL_SHORTCUT

示例代码

/**
 * BaseActivity
 * 
 * @author Edwin.Wu
 * @version 2017/3/17$ 14:12$
 * @since JDK1.8
 */
public class BaseActivity extends AppCompatActivity {

 @Override
 protected void onCreate(@Nullable Bundle savedInstanceState) {
 super.onCreate(savedInstanceState);
 }
 /**
 * 为子类提供一个权限检查方法
 *
 * @param permissions
 * @return
 */
 public boolean hasPermission(String... permissions) {
 for (String permission : permissions) {
 if (ActivityCompat.checkSelfPermission(this,permission)
 != PackageManager.PERMISSION_GRANTED) {
 return false;
 }
 }
 return true;
 }
 /**
 * 权限请求申请
 *
 * @param requestCode 请求码
 * @param permissions 权限
 */
 public void requestPermission(int requestCode,String... permissions) {
 ActivityCompat.requestPermissions(this,permissions,requestCode);
 }
 @Override
 public void onRequestPermissionsResult(int requestCode,@NonNull String[] permissions,@NonNull int[] grantResults) {
 super.onRequestPermissionsResult(requestCode,grantResults);
 switch (requestCode) {
 case PermissionsConstans.WRITE_STORAGE_CODE:
 if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
 doSDCardPermission();
 } else {
 //Todo 提示用户权限未授予
 Toast.makeText(BaseActivity.this,"WRITE_EXTERNAL_STORAGE 权限未开启",Toast.LENGTH_SHORT).show();
 }
 break;
 case PermissionsConstans.CALL_PHONE_CODE:
 if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
 doCallPhone();
 } else {
 //Todo 提示用户权限未授予
 Toast.makeText(BaseActivity.this,"ACTION_CALL 权限未开启",Toast.LENGTH_SHORT).show();
 }
 break;
 default:
 break;
 }
 }
 /**
 * 默认的写SD权限处理
 */
 protected void doSDCardPermission() {
 //Todo
 }
 /**
 * 默认的打电话处理
 */
 protected void doCallPhone() {
 //Todo 
 }
}
/**
 * BaseActivity
 *
 * @author Edwin.Wu
 * @version 2017/3/17 11:59
 * @since JDK1.8
 */
public class PermissionActivity extends BaseActivity {
 @Override
 protected void onCreate(Bundle savedInstanceState) {
 super.onCreate(savedInstanceState);
 setContentView(R.layout.activity_permission);
 }
 public void callPhone(View view) {
 if (hasPermission(android.Manifest.permission.CALL_PHONE)) {
 doCallPhone();
 } else {
 requestPermission(PermissionsConstans.CALL_PHONE_CODE,android.Manifest.permission.CALL_PHONE);
 }
 }
 public void sdCardPermission(View view) {
 if (hasPermission(android.Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
 doSDCardPermission();
 } else {
 requestPermission(PermissionsConstans.WRITE_STORAGE_CODE,android.Manifest.permission.WRITE_EXTERNAL_STORAGE);
 }
 }
 @Override
 protected void doCallPhone() {
 Intent intent = new Intent(Intent.ACTION_CALL);
 intent.setData(Uri.parse("tel:" + "10000"));
 startActivity(intent);
 }
 @Override
 protected void doSDCardPermission() {
 OkHttpClient okHttpClient = new OkHttpClient();
 Request request = new Request
 .Builder()
 .url("http://img.mmjpg.com/2015/350/3.jpg")
 .get()
 .build();
 Call call = okHttpClient.newCall(request);
 call.enqueue(new Callback() {
 @Override
 public void onFailure(Call call,IOException e) {
 }
 @Override
 public void onResponse(Call call,Response response) throws IOException {
 ResponseBody body = response.body();
 InputStream is = body.byteStream();
 int len = 0;
 byte[] bytes = new byte[2048];
 String path = Environment.getExternalStorageDirectory().getAbsolutePath() + "/" + System.currentTimeMillis() + ".jpg";
 File file = new File(path);
 FileOutputStream fos = new FileOutputStream(file);
 while ((len = is.read(bytes)) != -1) {
  fos.write(bytes,len);
  fos.flush();
 }
 Toast.makeText(PermissionActivity.this,"SDCard写入成功",Toast.LENGTH_SHORT).show();
 Log.e("Edwin","SDCard写入成功");
 }
 });
 }
}
/**
 * PermissionGen框架
 *
 * @author Edwin.Wu
 * @version 2017/3/17 23:19
 * @since JDK1.8
 */
public class PermissionGenActivity extends AppCompatActivity {
 @Override
 protected void onCreate(Bundle savedInstanceState) {
 super.onCreate(savedInstanceState);
 setContentView(R.layout.activity_permission_gen);
 }
 public void openCamera(View view) {
 PermissionGen.needPermission(this,200,Manifest.permission.CAMERA);
 }
 public void openGroup(View view) {
 PermissionGen
 .with(this)
 .addRequestCode(100)
 .permissions(
Manifest.permission.READ_CONTACTS,Manifest.permission.RECEIVE_SMS,Manifest.permission.CAMERA,Manifest.permission.WRITE_CONTACTS)
 .request();
 }
 @PermissionSuccess(requestCode = 100)
 public void openGroupSuccess() {
 Toast.makeText(this,"Group Permission Success",Toast.LENGTH_SHORT).show();
 startActivity(new Intent(this,ContactActivity.class));
 }
 @PermissionFail(requestCode = 100)
 private void openGroupFail() {
 Toast.makeText(this,"Group permission is not granted",Toast.LENGTH_SHORT).show();
 }
 @PermissionSuccess(requestCode = 200)
 public void openCameraSuccess() {
 Toast.makeText(this,"Camera permission Success",Toast.LENGTH_SHORT).show();
 }
 @PermissionFail(requestCode = 200)
 public void openCameraFail() {
 Toast.makeText(this,"Camera permission is not granted",Toast.LENGTH_SHORT).show();
 }
 @Override
 public void onRequestPermissionsResult(int requestCode,String[] permissions,int[] grantResults) {
 PermissionGen.onRequestPermissionsResult(this,requestCode,grantResults);
 }
}
/**
 * RxPermissionGen框架
 *
 * @author Edwin.Wu
 * @version 2017/3/19 14:17
 * @since JDK1.8
 */
public class RxPermissionActivity extends AppCompatActivity {
 private static final String TAG = "RxPermissionsSample";
 private Camera camera;
 private SurfaceView surfaceView;
 @Override
 protected void onCreate(Bundle savedInstanceState) {
 super.onCreate(savedInstanceState);
 RxPermissions rxPermissions = new RxPermissions(this);
 rxPermissions.setLogging(true);
 setContentView(R.layout.activity_rx_permission);
 surfaceView = (SurfaceView) findViewById(R.id.surfaceView);
 RxView.clicks(findViewById(R.id.enableCamera))
 // Ask for permissions when button is clicked
 .compose(rxPermissions.ensureEach(Manifest.permission.CAMERA))
 .subscribe(new Action1<Permission>() {
  @Override
  public void call(Permission permission) {
   Log.i(TAG,"Permission result " + permission);
   if (permission.granted) {
   releaseCamera();
   camera = Camera.open(0);
   try {
camera.setPreviewdisplay(surfaceView.getHolder());
camera.startPreview();
   } catch (IOException e) {
   Log.e(TAG,"Error while trying to display the camera preview",e);
   }
   } else if (permission.shouldShowRequestPermissionRationale) {
   // Denied permission without ask never again
   Toast.makeText(RxPermissionActivity.this,"Denied permission without ask never again",Toast.LENGTH_SHORT).show();
   } else {
   // Denied permission with ask never again
   // Need to go to the settings
   Toast.makeText(RxPermissionActivity.this,"Permission denied,can't enable the camera",Toast.LENGTH_SHORT).show();
   }
  }
  },new Action1<Throwable>() {
  @Override
  public void call(Throwable t) {
  Log.e(TAG,"onError",t);
  }
  },new Action0() {
  @Override
  public void call() {
  Log.i(TAG,"OnComplete");
  }
  });
 }
 @Override
 protected void onStop() {
 super.onStop();
 releaseCamera();
 }
 private void releaseCamera() {
 if (camera != null) {
 camera.release();
 camera = null;
 }
 }
}

以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,同时也希望多多支持编程小技巧!

Android registerForActivityResult动态申请权限案例详解

Android registerForActivityResult动态申请权限案例详解

前言

这几天在做一个小工具app,结果在fragment里面动态申请权限提示原有的申请方法已经弃用,还画了很明显的删除线。。。这叫一个强迫症的我怎么受得了。赶紧网上找资料也找不出什么结果,看了官方文档才发现了有registerForActivityResult这么一个神奇好用的函数,可以代替我们现有的startActivityForResult和权限申请函数。
那么下面就分两种情况来讲一下如何使用这个函数动态申请权限。

一、申请单个权限

首先,我们需要定义一个launcher:

ActivityResultLauncher<String> launcher = registerForActivityResult(new ActivityResultContracts.RequestPermission(),
			result -> {
				if (result.equals(true)) {
					//权限获取到之后的动作
				} else {
					//权限没有获取到的动作
				}
			});

上面代码里的ActivityResultContracts.RequestPermission()就是告诉系统你要获取权限,result是返回结果
可以看出只要权限获取到了,这里result返回就是true,直接进行判断即可。
当然,只定义了这个launcher系统是不会执行的,因为这只是一个注册函数,还要在你需要执行的地方加入如下的代码:

button.setOnClickListener(view -> {
	launcher.launch(Manifest.permission.READ_EXTERNAL_STORAGE);
});

完成后当你点击这个button的时候,就会跳出申请读存储权限的提示。

二、获取多个权限

这里我以同时获取拍照和存储两个权限为例,三个及以上的原理类似。
和获取单个权限一样,获取多个权限也需要定义一个launcher:

ActivityResultLauncher<String[]> launcher = registerForActivityResult(new ActivityResultContracts.RequestMultiplePermissions(),
			result -> {
				if (result.get(Manifest.permission.CAMERA) != null
						&& result.get(Manifest.permission.READ_EXTERNAL_STORAGE) != null) {
					if (Objects.requireNonNull(result.get(Manifest.permission.CAMERA)).equals(true)
							&& Objects.requireNonNull(result.get(Manifest.permission.READ_EXTERNAL_STORAGE)).equals(true)) {
						//权限全部获取到之后的动作
					} else {
						//有权限没有获取到的动作
					}
				}
			});

和单个权限不同的是,多个权限传入的方法是ActivityResultContracts.RequestMultiplePermissions()。ActivityResultLauncher里面带的是一个string数组。返回值result的类型是map(样例如下表)。

申请的权限(String) 权限的申请结果(boolean)
Manifest.permission.CAMERA true/false
Manifest.permission.READ_EXTERNAL_STORAGE true/false

所以直接使用map里的get方法就可以获取每个权限对应的结果。我以为就这样结束了,结果编译器在我get方法这里画了黄黄的底色,一看提示说可能导致空指针异常。所以我就多加了Objects.requireNonNull,警告马上消失。
最后一步同样不能少,在需要获取权限的地方执行一下launch方法:

launcher.launch(new String[]{Manifest.permission.CAMERA, Manifest.permission.READ_EXTERNAL_STORAGE})

三、总结

registerForActivityResult真的是个很好用的函数,要不是提示我之前的被弃用了我可能一直都不知道,不光可以代替获取权限方法,startActivityForResult方法也可以替换为这个函数。

到此这篇关于Android registerForActivityResult动态申请权限案例详解的文章就介绍到这了,更多相关Android registerForActivityResult动态申请权限内容请搜索以前的文章或继续浏览下面的相关文章希望大家以后多多支持!

您可能感兴趣的文章:
  • android开发权限询问的示例代码
  • Android申请相机权限和读写权限实例
  • Android11文件管理权限申请详细介绍
  • Android运行时权限终极方案(PermissionX)
  • Android权限询问的实例详解

Android Socket 发送广播包的那些坑

Android Socket 发送广播包的那些坑

Socket广播包经常被用于局域网内的两台设备之间互相发现和消息传递,在Android应用开发过程中,也经常会遇到这样的需求,例如:两台Android设备之间、Android与手环等智能硬件之间、Android与Windows电脑之间等等。


本文主要介绍在Android中使用Socket开发广播包程序时需要注意的编程事项,以及解决方法。


首先给出一段Android发送广播包的示例代码:

DatagramSocket socket = new DatagramSocket(8000);
socket.setBroadcast(true);
InetAddress addr = InetAddress.getByName("255.255.255.255");
byte[] buffer = "Hello World".getBytes();
DatagramPacket packet = new DatagramPacket(buffer,buffer.length);
packet.setAddress(addr);
packet.setPort(8086);
socket.send(packet);

下面分析其中需要注意的地方:


1. 不要在主线程中发送广播包


当然,这个做Android开发的人应该都知道,不能在UI线程中执行任何网络访问相关的操作,由于广播包的发送也属于网络操作,因此必须放到单独的线程中执行。


2. 广播地址不建议使用“255.255.255.255”


上述代码中,广播包的目标地址设置为了“255.255.255.255”,其实,这并不是一种推荐的做法。


“255.255.255.255” 是一种受限的广播地址,常用于在计算机不知道自己IP地址的时候发送,比如设备启动时向DHCP服务器索要地址等等,一般情况下,路由器不会转发目标为受限广播地址的广播包。


而且,有些路由器/Wi-Fi热点不支持该广播地址(例如:用Android手机做Wi-Fi热点的时候),因此在程序中会出现“ENETUNREACH (Network is unreachable)”的异常,因此,为了保证程序成功发送广播包,建议使用直接广播地址,例如:当前IP地址是 192.168.1.100,子网掩码是 255.255.255.0 的情况下,广播地址为:192.168.1.255,(具体的推算方法这里就不展开了,可以参考计算机网络相关书籍)。


那么,如何得到本网段的直接广播地址呢,下面是stackoverflow上面有位大牛分享的代码:

public static InetAddress getBroadcastAddress(Context context) throws UnknownHostException {
    WifiManager wifi = (WifiManager)context.getSystemService(Context.WIFI_SERVICE);
    DhcpInfo dhcp = wifi.getDhcpInfo();
    if(dhcp==null) {
        return InetAddress.getByName("255.255.255.255");
    }
    int broadcast = (dhcp.ipAddress & dhcp.netmask) | ~dhcp.netmask;
    byte[] quads = new byte[4];
    for (int k = 0; k < 4; k++)
        quads[k] = (byte) ((broadcast >> k * 8) & 0xFF);
    return InetAddress.getByAddress(quads);
}

直接使用该函数即可得到正确的“广播地址”,通过setAddress函数设置到DatagramPacket对象中即可。


3. Android设置为Wi-Fi热点时的广播地址


这是个比较大的坑,当Android设备被设置为Wi-Fi热点的时候,上面的函数得到的地址是"0.0.0.0",因此,我们需要探究当Android设备被设置为Wi-Fi热点的时候,它的IP地址究竟是多少?


有人研究了Android底层源码发现,当Android设备被设置为Wi-Fi热点的时候,其IP地址是hardcode写死在源码中的,地址是:“192.168.43.1”,对应的广播地址是:"192.168.43.255"


为此,我们需要写个函数来判断一下当前Android手机是否处于Wi-Fi热点模式下,如果是,则应该使用上面给出的这个广播地址,这里给出代码示例:

protected static Boolean isWifiApEnabled(Context context) {
    try {
        WifiManager manager = (WifiManager)context.getSystemService(Context.WIFI_SERVICE);  
        Method method = manager.getClass().getMethod("isWifiApEnabled");
        return (Boolean)method.invoke(manager);
    }
    catch (NoSuchMethodException e) {
        e.printStackTrace();
    }
    catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e)  {
        e.printStackTrace();
}
    return false;
}

Android SDK并没有开放判断是否处于热点模式的API,因此,我们需要通过反射的方式来得到,另外,注意添加权限: 

<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />

4. 小结


本文涉及到的代码被封装到了一个Broadcaster.java的文件中,可以在博文最后的附件中下载,也可以从下面的地址下载:


https://github.com/Jhuster/Android/blob/master/Socket/Broadcaster.java


关于Android Socket发送广播包的那些坑就总结到这里了,有任何疑问或者建议欢迎留言或者来信lujun.hust@gmail.com交流,或者关注我的新浪微博 @卢_俊 获取最新的文章和资讯。


本文出自 “对影成三人” 博客,请务必保留此出处http://ticktick.blog.51cto.com/823160/1707858

关于说说Android6.0动态申请权限的那些坑android 6.0动态申请权限的介绍已经告一段落,感谢您的耐心阅读,如果想了解更多关于Android 6.0 以后申请权限控制相关问题、Android 6.0动态权限申请教程、Android registerForActivityResult动态申请权限案例详解、Android Socket 发送广播包的那些坑的相关信息,请在本站寻找。

本文标签: