IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 移动开发 -> 关于Android读取SD卡存储的动态申请 -> 正文阅读

[移动开发]关于Android读取SD卡存储的动态申请

介绍

这篇文章主要关于我学习SD卡的动态获取权限时的一些问题。

Android的目录结构

参考自解析Android内部存储、外部存储的区别
图:目录结构
以我的手机为例,可以看到:
手机目录
与adb shell的ls命令相结合:
shell ls命令
可见,一般的手机用户(即非程序员用户)在手机的文件管理所能看到、操纵的文件都是手机的/storage/emulated/0目录下文件。

数据的主要存储方式

疯狂Android讲义–第四版中,主要提到的几种存储方式中:

  • 使用数据文件夹。即内存储
    • 使用SharedPreferences与Editor
      数据总保持在/data/data/< package name>/shared_prefs中
    • 使用openFileOutput和openFileInput
      数据总保持在/data/data/< package name>/files中
  • 读取SD卡上文件
    数据总保存在/storage/emulated/0目录下。
  • SQLite数据库
    嗯,存储的位置大概是比较随意。
    而这篇文章主要是关于SD卡的

疑惑

在学习教科书时,我发现课本的8.2.2节中,用Genymotion模拟器(google nexus 5,API 23)可以基本还原实验效果,但用我本人的真机(红米k30)以及Android Studio(Pixel_XL_API_30)自带的就不行了。
同时每次访问SD卡,都要来个:
在这里插入图片描述
你说这谁顶得住啊?
我搜了一下,说什么Android 6.0+需要动态申请。尤其是我发现,平时的文件也是放在所谓的SD卡目录下的,为什么其他应用就不用说什么动态申请?

原来的代码:MainActivity.java

package com.example.android.filetest;

import android.Manifest;
import android.app.Activity;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.os.Environment;
import android.util.Log;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;

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

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.RandomAccessFile;

/**
 * Description:<br>
 * 网站: <a href="http://www.crazyit.org">疯狂Java联盟</a><br>
 * Copyright (C), 2001-2020, Yeeku.H.Lee<br>
 * This program is protected by copyright laws.<br>
 * Program Name:<br>
 * Date:<br>
 *
 * @author Yeeku.H.Lee kongyeeku@163.com<br>
 * @version 1.0
 */
public class MainActivity extends Activity {
    private static final String FILE_NAME = "/crazy.bin";
    private EditText edit1;
    private TextView edit2;
    File f1;
    File f2;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        // 获取两个按钮
        Button read = findViewById(R.id.read);
        Button write = findViewById(R.id.write);
        // 获取两个文本框
        edit1 = findViewById(R.id.edit1);
        edit2 = findViewById(R.id.edit2);
        // 为write按钮绑定事件监听器
        write.setOnClickListener(view ->
                // 运行时请求获取写入SD卡的权限
                requestPermissions(new String[]
                        {Manifest.permission.WRITE_EXTERNAL_STORAGE}, 0x123)); // ①

        read.setOnClickListener(view ->
                requestPermissions(new String[]
                        {Manifest.permission.WRITE_EXTERNAL_STORAGE}, 0x456));
    }

    private String read() {
//        for (int i = 0; i < 10; i++) {
//            System.out.println(f2.canRead() + ", " + f2.canWrite());
//        }
        // 如果手机插入了SD卡,而且应用程序具有访问SD卡的权限
        if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
            System.out.println(Environment.getExternalStorageState());
            // 获取SD卡对应的存储目录
            File sdCardDir = Environment.getExternalStorageDirectory();
            try (
                    // 获取指定文件对应的输入流
                    FileInputStream fis = new FileInputStream(sdCardDir.getCanonicalPath() + FILE_NAME);
                    // 将指定输入流包装成BufferedReader
                    BufferedReader br = new BufferedReader(new InputStreamReader(fis))
            ) {
                StringBuilder sb = new StringBuilder();
                String line = null;
                // 循环读取文件内容
                while ((line = br.readLine()) != null) {
                    sb.append(line);
                }
                return sb.toString();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return null;
    }

    private void write(String content) {
        // 获取SD卡的目录
        File sdCardDir = Environment.getExternalStorageDirectory();
        try {

            System.out.println(sdCardDir.getCanonicalPath() + FILE_NAME);
            File targetFile = new File(sdCardDir.getCanonicalPath() + FILE_NAME);
            System.out.println("file is exist: " + targetFile.exists());
            // 以指定文件创建 RandomAccessFile对象
            RandomAccessFile raf = new RandomAccessFile(targetFile, "rw");
            raf.seek(targetFile.length());
            // 将文件记录指针移动到最后
            // 输出文件内容
            raf.write(content.getBytes());
            // 关闭RandomAccessFile
            raf.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void onRequestPermissionsResult(int requestCode,
                                           String[] permissions, int[] grantResults) {
        if (requestCode == 0x123) {
            // 如果用户同意授权访问
            if (grantResults != null && grantResults[0] ==
                    PackageManager.PERMISSION_GRANTED) {
                write(edit1.getText().toString());
                edit1.setText("");
            } else {
                // 提示用户必须允许写入SD卡的权限
                Toast.makeText(this, R.string.writesd_tip, Toast.LENGTH_LONG)
                        .show();
            }
        }
        if (requestCode == 0x456) {
            // 如果用户同意授权访问
            if (grantResults != null && grantResults[0] ==
                    PackageManager.PERMISSION_GRANTED) {
                // 读取指定文件中的内容,并显示出来
                edit2.setText(read());
            } else {
                // 提示用户必须允许写入SD卡的权限
                Toast.makeText(this, R.string.writesd_tip, Toast.LENGTH_LONG)
                        .show();
            }
        }
    }
}

我觉得说可能是教材没说,就找了找,最终找了个参考:Android无法在SD卡写入文件(Android6.0+需要动态申请
解决了。

修改后代码:

package com.example.android.sdtest_my_1;

import android.Manifest;
import android.app.Activity;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.os.Environment;
import android.util.Log;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;

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

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.RandomAccessFile;

/**
 * Description:<br>
 * 网站: <a href="http://www.crazyit.org">疯狂Java联盟</a><br>
 * Copyright (C), 2001-2020, Yeeku.H.Lee<br>
 * This program is protected by copyright laws.<br>
 * Program Name:<br>
 * Date:<br>
 *
 * @author Yeeku.H.Lee kongyeeku@163.com<br>
 * @version 1.0
 */
public class MainActivity extends Activity {
    private static final String FILE_NAME = "/crazy.bin";
    private EditText edit1;
    private TextView edit2;
    File f1;
    File f2;
    //动态申请sd卡读写权限
    private static final int REQUEST_EXTERNAL_STORAGE = 1;
    private static String[] PERMISSIONS_STORAGE = {
            "android.permission.READ_EXTERNAL_STORAGE",
            "android.permission.WRITE_EXTERNAL_STORAGE" };


    public static void verifyStoragePermissions(Activity activity) {

        try {
            //检测是否有写的权限
            int permission = ActivityCompat.checkSelfPermission(activity,
                    "android.permission.WRITE_EXTERNAL_STORAGE");
            if (permission != PackageManager.PERMISSION_GRANTED) {
                // 没有写的权限,去申请写的权限,会弹出对话框
                ActivityCompat.requestPermissions(activity, PERMISSIONS_STORAGE,REQUEST_EXTERNAL_STORAGE);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        // 获取两个按钮
        Button read = findViewById(R.id.read);
        Button write = findViewById(R.id.write);
        // 获取两个文本框
        edit1 = findViewById(R.id.edit1);
        edit2 = findViewById(R.id.edit2);
        // 为write按钮绑定事件监听器
        write.setOnClickListener(view ->
                write());
        read.setOnClickListener(view ->
                read());
        verifyStoragePermissions(this);
    }

    private void read() {
//        for (int i = 0; i < 10; i++) {
//            System.out.println(f2.canRead() + ", " + f2.canWrite());
//        }
        // 如果手机插入了SD卡,而且应用程序具有访问SD卡的权限
        if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
            System.out.println(Environment.getExternalStorageState());
            // 获取SD卡对应的存储目录
            File sdCardDir = Environment.getExternalStorageDirectory();
            try (
                    // 获取指定文件对应的输入流
                    FileInputStream fis = new FileInputStream(sdCardDir.getCanonicalPath() + FILE_NAME);
                    // 将指定输入流包装成BufferedReader
                    BufferedReader br = new BufferedReader(new InputStreamReader(fis))
            ) {
                StringBuilder sb = new StringBuilder();
                String line = null;
                // 循环读取文件内容
                while ((line = br.readLine()) != null) {
                    sb.append(line);
                }
                edit2.setText(sb.toString());
            } catch (IOException e) {
                e.printStackTrace();
            }
        } else {
            edit2.setText(null);
        }
    }

    private void write() {
        String content = edit1.getText().toString();
        edit1.setText("");
        // 获取SD卡的目录
        File sdCardDir = Environment.getExternalStorageDirectory();
        try {
            System.out.println(sdCardDir.getCanonicalPath() + FILE_NAME);   // /storage/emulated/0/crazy.bin
            File targetFile = new File(sdCardDir.getCanonicalPath() + FILE_NAME);
            System.out.println("file is exist: " + targetFile.exists());
            // 以指定文件创建 RandomAccessFile对象
            RandomAccessFile raf = new RandomAccessFile(targetFile, "rw");
            raf.seek(targetFile.length());
            // 将文件记录指针移动到最后
            // 输出文件内容
            raf.write(content.getBytes());
            // 关闭RandomAccessFile
            raf.close();
            System.out.println("ok, writedc");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

}

一次申请,永久ok……吧。

作者:Andrew Detmer(当然是假名了。)
原文链接

  移动开发 最新文章
Vue3装载axios和element-ui
android adb cmd
【xcode】Xcode常用快捷键与技巧
Android开发中的线程池使用
Java 和 Android 的 Base64
Android 测试文字编码格式
微信小程序支付
安卓权限记录
知乎之自动养号
【Android Jetpack】DataStore
上一篇文章      下一篇文章      查看所有文章
加:2021-12-13 12:56:35  更:2021-12-13 12:57:37 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 7:18:05-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码