Android常见数据存储方式

安卓中大概分为五种数据存储方式

  1. SharedPrefences;
  2. SQLite数据库
  3. 文件存储
  4.  网络存储
  5.  ContentProvider

1、共享参数SharedPrefences

1.1共享参数的简介

SharedPrefences是Android的一个轻量级存储工具,它采用的存储结构是Key-Value的键值对方式类似于java中的Properties,SharedPrefences将Key-Value的键值对保存到配置文件当中,存储的介质时一个XML文件,以XML标记保存键值对。保存

保存共享参数键值对信息的文件路径为/data/data/应用包名/shared_prefs/文件名.xml。

例:

<?xml version='1.0' encoding='utf-8' standalone='yes' ?>
<map>
    <string name="state">学习中</string>
</map>

基于XML格式的特点,共享参数的只要应用场合:

① 简单且孤立的数据。若是复杂且相互关联的数据,则要保存到关系型数据库。

②文本形式的数据。若是二进制数据,则要保存至文件。

③需要持久化存储的数据。App的个性化配置信息、用户使用App的行为信息、临时需要保存的片段信息等。

1.2共享参数的使用

①使用getSharedPrefernces方法可以获得共享参数实例

 SharedPreferences share = getSharedPreferences("share",MODE_PRIVATE);
注:第一个参数表示参数的文件名,第二个参数表示操作模式,MODE_PRIVATE表示私有模式

②往共享参数存出数据,需要借助Editor类

 SharedPreferences.Editor editor = shared.edit();
 editor.putString("text",text);
 //提交编辑器修改
 editor.commit();

③从共享参数中读取数据

  SharedPreferences shared1 = getSharedPreferences("share",MODE_PRIVATE);
                String result = shared1.getString("text","");

案例代码:

xml部分:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".demo7"
    android:orientation="vertical">
    <EditText
        android:id="@+id/et_text"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="请输入一段悄悄话"/>
    <Button
        android:id="@+id/btn_submit"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="保存共享参数"/>
    <Button
        android:id="@+id/btn_view"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="显示共享参数"/>
    <TextView
        android:id="@+id/tv_result"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:textSize="20dp"
        android:textColor="@color/black">
    </TextView>

</LinearLayout>

java部分:

package com.example.aclass;

import androidx.appcompat.app.AppCompatActivity;

import android.content.SharedPreferences;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;

//共享参数SharedPreferences  存储数据

public class demo7 extends AppCompatActivity implements View.OnClickListener {


    private     EditText et_text;
    private     Button btn_submit;
    private     Button btn_view;
    private     TextView tv_result;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_demo7);

        //获取输入文本
        et_text = findViewById(R.id.et_text);
        //获取提交共享参数按钮
        btn_submit =findViewById(R.id.btn_submit);
        //获取读取共享参数按钮
        btn_view = findViewById(R.id.btn_view);
        //获取显示文本视图
        tv_result = findViewById(R.id.tv_result);
        //给提交按钮注册事件
        btn_submit.setOnClickListener(this);
        //给读取按钮注册事件
        btn_view.setOnClickListener(this);
    }

    @Override
    public void onClick(View view) {
        switch (view.getId()){
            case  R.id.btn_submit:
                //得到输入文本
                String text = et_text.getText().toString();
                //使用getSharedPrefernces方法可以获得共享参数实例
                //第一个参数表示参数的文件名,第二个参数表示操作模式,MODE_PRIVATE表示私有模式
                System.out.println(1);
                SharedPreferences shared = getSharedPreferences("share",MODE_PRIVATE);
                //往共享参数存出数据,需要借助Editor类
                SharedPreferences.Editor editor = shared.edit();
                editor.putString("text",text);
                //提交编辑器修改
                editor.commit();
                break;
            case R.id.btn_view:
                //也可以将共享参数声明为全局
                SharedPreferences shared1 = getSharedPreferences("share",MODE_PRIVATE);
                String result = shared1.getString("text","");
                //将结果显示到结果集中
                tv_result.setText(result);
                break;

        }
    }
}

2、SQLite数据库

待补充

3、文件存储

3.1 私有空间和公共存储空间

为了更规范地管理手机存储空间, Android 7.0 开始将存储卡划分为私有存储和公共存储两大部分,也就是分区存储方式,

系统给每个App 都分配了默认的私有存储空间。 App 在私有空间上读写文件无须任何授权。 但是若想在公共空间读写文件,则要在AndroidManifest.xml里面添加下述的权限配置。
<!-- 存储卡读写 -->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAG" />
安卓10以后还需要在application中添加
android:requestLegacyExternalStorage="true"
但是即使 App 声明了完整的存储卡操作权限,系统仍然默认禁止该App访问公共空间。打开手机的系统设置界面,进入到具体应用的管理页面,会发现该应用的存储访问权限被禁止了。

当然图示的禁止访问只是不让访问存储卡的公共空间, App 自身的私有空间依旧可以正常读写。这缘于Android把存储卡分成了两块区域,一块是所有应用均可访问的公共空间,另一块是只有应用自己才可访问的专享空间。虽然Android 给每个应用都分配了单独的安装目录,但是安装目录的空间很紧张,所以Android 在存储卡的 “Android/data” 目录下给每个应用又单独建了一个文件目录,用来保存应用自己需要处理的临时文件。这个目录只有当前应用才能够读写文件,其他应用是不允许读写的。由于私有空间本身已经加了访问权限控制,因此它不受系统禁止访问的影响,应用操作自己的文件目录自然不成问题。因为私有的文件目录只有属主应用才能访问,所以一旦属主应用被卸载,那么对应的目录也会被删掉。

3.2存储卡上读写文本文件

① 写一个文件读写的类  FileUtil:
package com.example.aclass.util;

import android.graphics.Bitmap;
import android.graphics.BitmapFactory;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;

public class FileUtil {
    //把字符串保存到指定的文本文件
    public static  void saveText(String path ,String txt){
        BufferedWriter os = null;
        try {
            os = new BufferedWriter(new FileWriter(path));
            os.write(txt);
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            if(os!=null){
                try {
                    os.close();

                }catch (Exception e){
                    e.printStackTrace();
                }
            }
        }

    }
    //从指定路径的文本文件中读取内容字符串
    public  static  String openText(String path){
        BufferedReader is = null;
        StringBuilder sb = new StringBuilder();
        try {
            is = new BufferedReader(new FileReader(path));
            String line =null;
            while ((line = is.readLine())!=null){
                sb.append(line);
            }
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            if(is!=null){
                try {
                    is.close();
                }catch (Exception e){
                    e.printStackTrace();

                }
            }
        }
        return sb.toString();
    }
    //把位图数据保存到指定路径的图片文件
    public static void saImage(String path, Bitmap bitmap) {
        FileOutputStream fos = null;
        try {
            fos = new FileOutputStream(path);
            //把位图数据压缩到文件输出流中
            bitmap.compress(Bitmap.CompressFormat.JPEG,100,fos);
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            if(fos != null){
                try {
                    fos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }


    }


    //从指定路径的图片文件读取位图数据
    public static Bitmap openImage(String path) {
        Bitmap bitmap = null;
        FileInputStream fis = null;
        try {
            fis =new FileInputStream(path);
            bitmap = BitmapFactory.decodeStream(fis);

        }catch (Exception e){
            e.printStackTrace();
        }finally {
            if(fis != null){
                try {
                    fis.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return bitmap;
    }
}

ToastUtil:

package com.example.aclass.util;

import android.content.Context;
import android.widget.Toast;

public class ToastUtil {

    public static void show(Context ctx, String desc) {
        Toast.makeText(ctx, desc, Toast.LENGTH_SHORT).show();
    }

}

②布局文件

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".demo8"
    android:orientation="vertical">
    <EditText
        android:id="@+id/et_text"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="请输入一段悄悄话"/>
    <Button
        android:id="@+id/btn_submit"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="保存"/>
    <Button
        android:id="@+id/btn_view"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="读取"/>
    <TextView
        android:id="@+id/tv_result"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:textSize="20dp"
        android:textColor="@color/black">
    </TextView>

</LinearLayout>

③ Java
1) 外部存储私有空间
 //外部存储的私有空间
directory = getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS).toString();
2) 外部存储公共空间
  directory = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).toString();

3)内部存储的私有空间

directory = getFilesDir().toString();

案例代码:

package com.example.aclass;

import androidx.appcompat.app.AppCompatActivity;

import android.content.SharedPreferences;
import android.os.Bundle;
import android.os.Environment;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;

import com.example.aclass.util.FileUtil;
import com.example.aclass.util.ToastUtil;

import java.io.File;

//共享参数SharedPreferences  存储数据

public class demo8 extends AppCompatActivity implements View.OnClickListener {


    private     EditText et_text;
    private     Button btn_submit;
    private     Button btn_view;
    private     TextView tv_result;
    private String path;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_demo8);

        //获取输入文本
        et_text = findViewById(R.id.et_text);
        //获取提交按钮
        btn_submit =findViewById(R.id.btn_submit);
        //获取读取按钮
        btn_view = findViewById(R.id.btn_view);
        //获取显示文本视图
        tv_result = findViewById(R.id.tv_result);
        //给提交按钮注册事件
        btn_submit.setOnClickListener(this);
        //给读取按钮注册事件
        btn_view.setOnClickListener(this);
    }

    @Override
    public void onClick(View view) {
        switch (view.getId()){
            case  R.id.btn_submit:
                String text = et_text.getText().toString();
                //保存的文件名称
                String fileName = System.currentTimeMillis() + ".txt";
                String directory = null;
                //外部存储的私有空间
                directory = getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS).toString();
                //外部存储的公共空间
                directory = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).toString();
                //内部存储的私有空间
                directory = getFilesDir().toString();

                //文件的完整路径
                path = directory+ File.separatorChar+fileName;
                System.out.println("保存路径为:"+path);
                FileUtil.saveText(path,text);
                ToastUtil.show(this,"保存成功");
                break;
            case R.id.btn_view:
                tv_result.setText(FileUtil.openText(path));
                break;

        }
    }
}

3.2存储卡上读写图片文件

① FileUtil,ToastUil使用上面写到的文件
②xml布局文件
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">
    <Button
        android:id="@+id/btn_save"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="保存"/>
    <Button
        android:id="@+id/btn_read"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="读取"/>
    <ImageView
        android:id="@+id/iv_content"
        android:layout_width="match_parent"
        android:layout_height="400dp"
        android:scaleType="fitCenter"/>

</LinearLayout>

③java代码

package com.example.aclass;

import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.net.Uri;
import android.os.Bundle;
import android.os.Environment;
import android.util.Log;
import android.view.View;
import android.widget.ImageView;

import androidx.appcompat.app.AppCompatActivity;

import com.example.aclass.util.FileUtil;
import com.example.aclass.util.ToastUtil;

import java.io.File;

public class ImageWriteActivity extends AppCompatActivity implements View.OnClickListener {

    private ImageView iv_content;
    private String path;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_image_write);
        iv_content = findViewById(R.id.iv_content);
        findViewById(R.id.btn_save).setOnClickListener(this);
        findViewById(R.id.btn_read).setOnClickListener(this);
    }

    @Override
    public void onClick(View view) {
      switch (view.getId()){
          case R.id.btn_save:
              String fileName = System.currentTimeMillis()+".jpeg";
              //获取当前APP的私有下载目录
              path = getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS).toString()+ File.separatorChar +fileName;
              Log.d("ning",path);
          //从指定的资源文件中获取位图对象
              Bitmap b1 = BitmapFactory.decodeResource(getResources(), R.drawable.yellow);
              FileUtil.saImage(path,b1);
              ToastUtil.show(this,"保存成功");
              break;
          case R.id.btn_read:
              //方式1
//              Bitmap b2 = FileUtil.openImage(path);
//              iv_content.setImageBitmap(b2);
              //方式2
//              Bitmap b2 = BitmapFactory.decodeFile(path);
//              iv_content.setImageBitmap(b2);
              //方式3
              iv_content.setImageURI(Uri.parse(path));

              break;
      }
    }
}

④ 示例

4、网络存储

待补充

5、ContentProvider

待补充

6、安卓权限

Android 系统为了防止某些 App 滥用权限,从 6.0 开始引入了运行时权限管理机制,允许 A
pp 在运行过程中动态检查是否拥有某项权限,一旦发现缺少某种必需的权限,则系统会
自动弹出小窗提示用户去开启该权限。

 6.1 运行时动态申请权限

在4中讲到App 若想访问存储卡的公共空间,就要在 AndroidManifest.xml里面添加下述的权限配置。
<!-- 存储卡读写 -->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAG" />
然而即使 App 声明了完整的存储卡操作权限,从 Android 7.0 开始,系统仍然默认禁止该 App 访问公共空间,必须到设置界面手动开启应用的存储卡权限才行。尽管此举是为用户隐私着想,可是人家咋知道要手工开权限呢?就算用户知道,去设置界面找到权限开关也颇费周折。为此Android 支持在 Java 代码中处理权限,处理过程分为3 个步骤,详述如下:
① 检查 App 是否开启了指定权限
权限检查需要调用 ContextCompat checkSelfPermission 方法,该方法的第一个参数为活动实例,第二个参数为待检查的权限名称,例如存储卡的写权限名为 Manifest.permission.WRITE_EXTERNAL_STORAGE。注意 checkSelfPermission 方法的返回值,当它为PackageManager.PERMISSION_GRANTED时表示已经授权,否则就是未获授权。
② 请求系统弹窗,以便用户选择是否开启权限
一旦发现某个权限尚未开启,就得弹窗提示用户手工开启,这个弹窗不是开发者自己写的提醒对话框,而是系统专门用于权限申请的对话框。调用ActivityCompat requestPermissions 方法,即可命令系统自动弹出权限申请窗口,该方法的第一个参数为活动实例,第二个参数为待申请的权限名称数组,第三个参数为本次操作的请求代码。
③ 判断用户的权限选择结果
然而上面第二步的 requestPermissions 方法没有返回值,那怎么判断用户到底选了开启权限还是拒绝权限呢?其实活动页面提供了权限选择的回调方法onRequestPermissionsResult ,如果当前页面请求弹出权限申请窗口,那么该页面的Java 代码必须重写 onRequestPermissionsResult 方法,并在该方法内部处理用户的权限选择结果。

6.2 具体使用步骤

①定义用户使用的权限
例如:
 public static final  String[] PERMISSION_CONTACTS = new String[]{
            //操作通讯录的读写权限
            Manifest.permission.READ_CONTACTS,
            Manifest.permission.WRITE_CONTACTS
    };
    public static final  String[] PERMISSION_SMS = new String[]{
            //操作短信的读写权限
            Manifest.permission.SEND_SMS,
            Manifest.permission.READ_SMS
    };

②定义权限标识符

 //标识请求的权限类型
    //通讯录读写的权限标识
    private  static  final  int REQUEST_CODE_CONTACTS = 1;
    //发送短信的权限标识
    private  static  final  int REQUEST_CODE_SMS = 2;
③定义一个获取权限的工具类PermissionUtil
传递的三个参数 :
          Activity act:指定activity
         String[] permissions:权限的类型
         int requestCode:权限的标识符    (不可以为负数也不能大于2的16次方即65536)
package com.example.aclass.util;

import android.app.Activity;
import android.content.pm.PackageManager;
import android.os.Build;

import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;

public class PermissionUtil {

    /*
    * Activity act:指定activity
    * String[] permissions:权限的类型
    * int requestCode:权限的标识符
    * */

    // 检查多个权限。返回true表示已完全启用权限,返回false表示未完全启用权限
    public static boolean checkPermission(Activity act, String[] permissions, int requestCode) {
        // Android 6.0 之后开始采用动态权限管理
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            /*通过看源码
             public static final int PERMISSION_DENIED = -1;
             public static final int PERMISSION_GRANTED = 0;

             0代表全部授权  -1表示未授权
            *
            * */
            int check = PackageManager.PERMISSION_GRANTED;
            for (String permission : permissions) {
                //判断权限是否授权
                check = ContextCompat.checkSelfPermission(act, permission);
                if (check != PackageManager.PERMISSION_GRANTED) {
                    break;
                }
            }
            // 未开启该权限,则请求系统弹窗,好让用户选择是否立即开启权限
            if (check != PackageManager.PERMISSION_GRANTED) {
                //弹窗提示要获取权限
                ActivityCompat.requestPermissions(act, permissions, requestCode);
                return false;
            }
        }
        return true;
    }

    // 检查权限结果数组,返回true表示都已经获得授权。返回false表示至少有一个未获得授权
    public static boolean checkGrant(int[] grantResults) {
        if (grantResults != null) {
            // 遍历权限结果数组中的每条选择结果
            for (int grant : grantResults) {
                // 未获得授权
                if (grant != PackageManager.PERMISSION_GRANTED) {
                    return false;
                }
            }
            return true;
        }
        return false;
    }
}
注意到上面代码有判断安卓版本号,只有系统版本大于 Android 6.0 (版本代号为 M ),才执行后续的权限校验操作。这是因为从Android 6.0 开始引入了运行时权限机制,在 Android 6.0 之前,只要 App 在AndroidManifest.xml中添加了权限配置,则系统会自动给 App 开启相关权限;但在 Android 6.0 之后, 即便事先添加了权限配置,系统也不会自动开启权限,而要开发者在App 运行时判断权限的开关情况,再据此动态申请未获授权的权限。
④获取权限逻辑操作
两种形式:
懒汉式:当需要权限是才进行权限的判断
饿汉式:一进入activity就 进行权限的判断
代码:
懒汉式:
package com.example.aclass;

import android.Manifest;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.provider.Settings;
import android.util.Log;
import android.view.View;

import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;

import com.example.aclass.util.PermissionUtil;
import com.example.aclass.util.ToastUtil;

public class PermissionLazyActivity extends AppCompatActivity implements View.OnClickListener {


    //定义用户需要的权限
    public static final  String[] PERMISSION_CONTACTS = new String[]{
            //操作通讯录的读写权限
            Manifest.permission.READ_CONTACTS,
            Manifest.permission.WRITE_CONTACTS
    };
    public static final  String[] PERMISSION_SMS = new String[]{
            //操作短信的读写权限
            Manifest.permission.SEND_SMS,
            Manifest.permission.READ_SMS
    };

    //标识请求的权限类型
    //通讯录读写的权限标识
    private  static  final  int REQUEST_CODE_CONTACTS = 1;
    //发送短信的权限标识
    private  static  final  int REQUEST_CODE_SMS = 2;


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_permission_lazy);
        findViewById(R.id.btn_contact).setOnClickListener(this);
        findViewById(R.id.btn_sms).setOnClickListener(this);
    }

    @Override
    public void onClick(View view) {
        switch (view.getId()){
            case R.id.btn_contact:
                PermissionUtil.checkPermission(this,PERMISSION_CONTACTS,REQUEST_CODE_CONTACTS);
                break;
            case R.id.btn_sms:
                PermissionUtil.checkPermission(this,PERMISSION_SMS,REQUEST_CODE_SMS);
                break;
        }
    }


    //判断权限是否获取到了
    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        switch (requestCode){
            case REQUEST_CODE_CONTACTS:
                 if(PermissionUtil.checkGrant(grantResults)){
                     Log.d("ning","通讯录权限获取成功");
                 }else {
                     ToastUtil.show(this,"获取通讯录读写权限失败!");
                     jumpToSetting();
                 }
                break;
            case REQUEST_CODE_SMS:
                if(PermissionUtil.checkGrant(grantResults)){
                    Log.d("ning","收发短信权限获取成功");
                }else {
                    ToastUtil.show(this,"获取收发短信权限失败!");
                    jumpToSetting();
                }
                break;

        }
    }

    //跳转到应用设置界面
    private  void  jumpToSetting(){
        Intent intent = new Intent();
        //进入应用详情的设置界面
        intent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
        //指定那个应用  通过包名
        intent.setData(Uri.fromParts("package",getPackageName(),null));
        //使用新的任务栈运行
        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        startActivity(intent);
    }
}

饿汉式:

package com.example.aclass;

import android.Manifest;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Bundle;
import android.provider.Settings;
import android.util.Log;
import android.view.View;

import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;

import com.example.aclass.util.PermissionUtil;
import com.example.aclass.util.ToastUtil;

public class PermissionHungryActivity extends AppCompatActivity implements View.OnClickListener {

    public static final  String[] PERMISSIONS = new String[]{
            //操作通讯录的读写权限
            Manifest.permission.READ_CONTACTS,
            Manifest.permission.WRITE_CONTACTS,
            //操作短信的读写权限
            Manifest.permission.SEND_SMS,
            Manifest.permission.READ_SMS
    };
    private  static  final  int REQUEST_CODE_ALL = 1;
    private  static  final  int REQUEST_CODE_CONTACTS = 2;
    private  static  final  int REQUEST_CODE_SMS = 3;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_permission_lazy);
        findViewById(R.id.btn_contact).setOnClickListener(this);
        findViewById(R.id.btn_sms).setOnClickListener(this);
        PermissionUtil.checkPermission(this,PERMISSIONS,REQUEST_CODE_ALL);
    }

    @Override
    public void onClick(View view) {
        switch (view.getId()){
            case R.id.btn_contact:
                PermissionUtil.checkPermission(this,new String[]{PERMISSIONS[0],PERMISSIONS[1]},REQUEST_CODE_CONTACTS);
                break;
            case R.id.btn_sms:
                PermissionUtil.checkPermission(this,new String[]{PERMISSIONS[2],PERMISSIONS[3]},REQUEST_CODE_SMS);
                break;
        }
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        switch (requestCode){
            case REQUEST_CODE_ALL:
                if(PermissionUtil.checkGrant(grantResults)){
                    Log.d("ning","所有权限获取成功");
                }else {
                  //部分权限获取失败
                    for(int i = 0 ; i<grantResults.length;i++){
                        if(grantResults[i] != PackageManager.PERMISSION_GRANTED){
                            //判断是什么权限没有获取成功
                            switch (permissions[i]){
                                case Manifest.permission.READ_CONTACTS:
                                case Manifest.permission.WRITE_CONTACTS:
                                    ToastUtil.show(this,"获取通讯录读写权限失败!");
                                    jumpToSetting();
                                    return;
                                case Manifest.permission.SEND_SMS:
                                case Manifest.permission.READ_SMS:
                                    ToastUtil.show(this,"获取收发短信权限失败!");
                                    jumpToSetting();
                                   return;
                            }
                        }
                    }
                }
                break;
            case REQUEST_CODE_CONTACTS:
                 if(PermissionUtil.checkGrant(grantResults)){
                     Log.d("ning","通讯录权限获取成功");
                 }else {
                     ToastUtil.show(this,"获取通讯录读写权限失败!");
                     jumpToSetting();
                 }
                break;
            case REQUEST_CODE_SMS:
                if(PermissionUtil.checkGrant(grantResults)){
                    Log.d("ning","收发短信权限获取成功");
                }else {
                    ToastUtil.show(this,"获取收发短信权限失败!");
                    jumpToSetting();
                }
                break;

        }
    }

    //跳转到应用设置界面
    private  void  jumpToSetting(){
        Intent intent = new Intent();
        intent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
        intent.setData(Uri.fromParts("package",getPackageName(),null));
        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        startActivity(intent);
    }
}

⑤ 布局文件

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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=".PermissionLazyActivity"
    android:orientation="vertical">

    <Button
        android:id="@+id/btn_contact"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="读写通讯录"/>

    <Button
        android:id="@+id/btn_sms"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="收发短信"/>


</LinearLayout>

点击弹窗上的 始终允许 ,表示同意赋予存储卡读写权限,然后系统自动给 App 开启了存储卡权限,并执
行后续处理逻辑,也就是跳到了 FileWriteActivity 页面,在该页面即可访问公共空间的文件了。但在
Android 10 系统中,即使授权通过, App 仍然无法访问公共空间,这是因为 Android 10 默认开启沙箱模
式,不允许直接使用公共空间的文件路径,此时要修改 AndroidManifest.xml ,给 application 节点添加
如下的 requestLegacyExternalStorage 属性:
android:requestLegacyExternalStorage="true"
Android 11 开始,为了让应用升级时也能正常访问公共空间,还得修改 AndroidManifest.xml ,给
application 节点添加如下的 preserveLegacyExternalStorage 属性,表示暂时关闭沙箱模式:
android:preserveLegacyExternalStorage="true"

附:其他权限

  • 24
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值