一手遮天 Android - 存储: 内部存储,外部存储,权限请求,存储大小,获取 assets 中的数据,获取 res/raw 中的数据

项目地址 https://github.com/webabcd/AndroidDemo
作者 webabcd

一手遮天 Android - 存储: 内部存储,外部存储,权限请求,存储大小,获取 assets 中的数据,获取 res/raw 中的数据

示例如下:

/storage/StorageDemo3.java

/**
 * 内部存储,外部存储,权限请求,存储路径,存储大小,获取 assets 中的数据,获取 res/raw 中的数据
 *
 *
 * 从硬件上来说,包括机身存储和外部可移动存储
 * 从程序上来说,包括程序的内部存储和程序的外部存储(一般说的内部存储和外部存储就就是指的这个)
 *     程序的内部存储包括内部存储(肯定在机身存储)和扩展内部存储(可能在机身存储也可能在外部可移动存储)
 *     程序的外部存储可能在机身存储也可能在外部可移动存
 *     卸载程序后,内部存储的数据会被删除,外部存储的数据会被保留
 *
 *
 * 程序的内部存储(机身存储)路径为 /data/data/packagename/, 如果是 6.0 以上的系统(开启多用户的支持时)则为 /data/user/n/packagename/(其中的 n 为整数,代表不同的用户,如果就一个用户那么 n 就是 0)
 * 程序的扩展内部存储(机身存储)路径类似 /storage/emulated/0/Android/data/packagename/
 * 程序的扩展内部存储(外部可移动存储)路径类似 /storage/emulated/B3E4-1711/Android/data/packagename/(中间的那个 B3E4-1711 是挂载目录,以实际为准)
 * 程序的外部存储(机身存储)路径为 /storage/emulated/0/
 * 程序的外部存储(外部可移动存储)路径类似 /storage/B3E4-1711/(后面的那个 B3E4-1711 是挂载目录,以实际为准)
 *
 * 内部存储的根目录下有 files 目录和 cache 目录
 * files 目录,存放的数据系统不会主动删除,用户在设置中的“应用信息”中单击“清除数据”后会删除
 * files 目录路径类似 /data/data/packagename/files, /data/user/n/packagename/files, /storage/emulated/0/Android/data/packagename/files 等
 * cache 目录,存放的数据可能会被系统主动删除(比如系统认为存储空间不够的时候),用户在设置中的“应用信息”中单击“清除缓存”后会删除
 * cache 目录路径类似 /data/data/packagename/cache, /data/user/n/packagename/cache, /storage/emulated/0/Android/data/packagename/cache 等
 *
 * 另外
 * 1、内部存储的根目录下的 shared_prefs 目录(类似 /data/data/packagename/shared_prefs )用于保存 SharedPreferences 数据
 *    如果用户在设置中的“应用信息”中单击“清除数据”的话,会删除 shared_prefs 中的全部文件
 * 2、内部存储的根目录(类似 /data/data/packagename)下的文件,以及其内其他目录中的文件不会被系统主动删除,只有在程序卸载后会被删除
 *    如果你的文件不想被系统主动删除,又想程序卸载后会被删除,就可以在这里保存文件或新建目录保存文件
 *
 *
 * 要记住:
 * 1、在 android 6.0 或以上系统操作外部存储,需要动态申请权限
 * 2、关于 android 10.0, android 11.0 或以上系统, targetSdkVersion 版本之间的问题,请参见其他相关示例的说明
 *
 *
 * 本例演示
 * 1、如何动态申请存储权限(外部存储需要申请权限,内部存储不需要申请权限)
 * 2、如何获取各种存储的路径,以及如何获取存储的大小
 *    内部存储的路径通过 Context 中的方法来获得,外部存储的路径通过 Environment 中的方法获得
 * 3、如何获取 assets 中的数据(不会映射到 R.java 文件中,允许有子目录)
 * 4、如何获取 res/raw 中的数据(会映射到 R.java 文件中,不允许有子目录)
 *
 *
 * 注:
 * 关于 File 的 getPath(), getAbsolutePath(), getCanonicalPath() 的区别
 * 如果你定义 File 时用的是绝对路径,则这 3 个方法返回的数据是一样的
 * 如果你定义 File 时用的是相对路径,请看下面的说明
 * File file = new File(".\\test.txt");
 * file.getPath() - .\test.txt
 * file.getAbsolutePath() - c:\directory1\directory2\.\test.txt
 * file.getCanonicalPath() - c:\directory1\directory2\test.txt
 */

package com.webabcd.androiddemo.storage;

import android.Manifest;
import android.content.pm.PackageManager;
import android.content.res.AssetManager;
import android.os.Build;
import android.os.Environment;
import androidx.annotation.NonNull;
import androidx.core.app.ActivityCompat;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;

import com.webabcd.androiddemo.R;

import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.text.DecimalFormat;

public class StorageDemo3 extends AppCompatActivity {

    private final String LOG_TAG = "StorageDemo3";

    private TextView mTextView1;
    private Button mButton1;
    private Button mButton2;
    private Button mButton3;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_storage_storagedemo3);

        mTextView1 = findViewById(R.id.textView1);
        mButton1 = findViewById(R.id.button1);
        mButton2 = findViewById(R.id.button2);
        mButton3 = findViewById(R.id.button3);

        sample1();
        sample2();
        sample3();
    }


    // 演示如何动态申请存储权限(外部存储需要申请权限,内部存储不需要申请权限)
    private void sample1() {
        // 在 6.0 或以上系统操作外部存储,需要动态申请权限
        // 需要先在 AndroidManifest.xml 中做如下配置
        // <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
        // <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

        String[] storagePermissions = { Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE };
        // 检查是否有指定的权限
        if (ActivityCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
            // 动态申请指定的权限(前提是先要在 AndroidManifest.xml 做好权限的配置)
            // 申请结果通过回调 @Override onRequestPermissionsResult() 获取
            ActivityCompat.requestPermissions(this, storagePermissions, 1); // 最后的参数就是一个标识,以便在申请结果的回调中知道是谁申请的
        }
    }
    // 权限动态申请的结果的回调
    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);

        // requestCode - 就是通过 requestPermissions() 申请权限时的最后一个参数
        // permissions - 申请权限的名称列表
        // grantResults - 申请权限的结果列表
        //     -1 代表拒绝,对应的常量是 PackageManager.PERMISSION_DENIED
        //     0 代表允许,对应的常量是 PackageManager.PERMISSION_GRANTED

        for (int i = 0; i < permissions.length; i++) {
            Log.i(LOG_TAG, "申请的权限为:" + permissions[i] + ",申请结果:" + grantResults[i]);
        }
    }


    // 演示如何获取各种存储的路径,以及如何获取存储的大小
    private void sample2() {
        try {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
                // 内部存储根目录(类似 /data/data/packagename)
                mTextView1.append("getDataDir(): " + getDataDir().getCanonicalPath());
                mTextView1.append("\n");
            }
            // 内部存储的 files 目录(类似 /data/data/packagename/files)
            mTextView1.append("getFilesDir(): " + getFilesDir().getCanonicalPath());
            mTextView1.append("\n");
            // 内部存储的 cache 目录(类似 /data/data/packagename/cache)
            mTextView1.append("getCacheDir(): " + getCacheDir().getCanonicalPath());
            mTextView1.append("\n");
            // 扩展内部存储的 files 目录(类似 /storage/emulated/0/Android/data/packagename/files)
            mTextView1.append("getExternalFilesDir(): " + getExternalFilesDir("").getCanonicalPath());
            mTextView1.append("\n");
            // 扩展内部存储的 cache 目录(类似 /storage/emulated/0/Android/data/packagename/cache)
            mTextView1.append("getExternalCacheDir(): " + getExternalCacheDir().getCanonicalPath());
            mTextView1.append("\n");

            // 外部存储目录(类似 /storage/emulated/0),在这里操作是需要动态申请权限的
            mTextView1.append("getExternalStorageDirectory(): " + Environment.getExternalStorageDirectory().getCanonicalPath());
            mTextView1.append("\n");

            // getExternalFilesDirs(Environment.MEDIA_MOUNTED) - 获取多个挂载点的扩展内部存储目录
            // 比如既有机身存储又有外部可移动存储的时候,会获取到 2 条记录,类似如下
            // /storage/emulated/0/Android/data/packagename/files/mounted(其中的 /storage/emulated/0 就是机身存储中的扩展内部存储路径)
            // /storage/B3E4-1711/Android/data/packagename/files/mounted(其中的 /storage/B3E4-1711 就是可移动存储中的扩展内部存储路径)
            File[] fileList = getExternalFilesDirs(Environment.MEDIA_MOUNTED);
            for (int i = 0; i < fileList.length; i++) {
                mTextView1.append("getExternalFilesDirs(): " + fileList[i].getCanonicalPath());
                mTextView1.append("\n");
            }
        } catch (Exception ex) {
            mTextView1.setText(ex.toString());
        }

        // 获取存储的大小
        mButton1.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                DecimalFormat decimalFormat = new DecimalFormat(",###");

                File file = new File(getFilesDir(), "");
                mTextView1.setText(String.format("getTotalSpace():%s, getUsableSpace():%s, getFreeSpace:%s",
                        decimalFormat.format(file.getTotalSpace()), // 存储的总空间的大小(单位:字节)
                        decimalFormat.format(file.getUsableSpace()), // 存储的可用空间的大小(单位:字节)
                        decimalFormat.format(file.getFreeSpace()))); // 存储的剩余空间的大小(单位:字节),不一定都可用
            }
        });
    }


    // 演示如何获取 assets 中的数据,以及如何获取 res/raw 中的数据
    private void sample3() {
        // 获取 assets 中的数据(不会映射到 R.java 文件中,允许有子目录)
        mButton2.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                try {
                    AssetManager assetManager =  getAssets();
                    InputStream inputStream = assetManager.open("text_storage_storagedemo3.txt");
                    BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8));
                    StringBuilder sb = new StringBuilder();
                    String line = null;
                    while ((line = bufferedReader.readLine()) != null) {
                        sb.append(line + "\n");
                    }

                    bufferedReader.close();
                    inputStream.close();

                    mTextView1.setText(sb.toString());
                } catch (IOException ex) {
                    mTextView1.append(ex.toString());
                }
            }
        });

        // 获取 res/raw 中的数据(会映射到 R.java 文件中,不允许有子目录)
        mButton3.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                try {
                    InputStream inputStream = getResources().openRawResource(R.raw.text_storage_storagedemo3);
                    BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8));
                    StringBuilder sb = new StringBuilder();
                    String line = null;
                    while ((line = bufferedReader.readLine()) != null) {
                        sb.append(line + "\n");
                    }

                    bufferedReader.close();
                    inputStream.close();

                    mTextView1.setText(sb.toString());
                } catch (IOException ex) {
                    mTextView1.append(ex.toString());
                }
            }
        });
    }
}

/layout/activity_storage_storagedemo3.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/button1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="获取存储的总大小和可用大小" />

    <Button
        android:id="@+id/button2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textAllCaps="false"
        android:text="获取 assets 中的文件" />

    <Button
        android:id="@+id/button3"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textAllCaps="false"
        android:text="获取 res/raw 中的文件" />

    <TextView
        android:id="@+id/textView1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />

</LinearLayout>

项目地址 https://github.com/webabcd/AndroidDemo
作者 webabcd

posted @ 2021-06-02 09:08  webabcd  阅读(412)  评论(0编辑  收藏  举报