内部存储

# 硬件 [RAM && ROM && SD Card]
  - RAM就是普通所谓的计算机内存,是和CPU配套用来运行操作系统和程序的
  - 我们用来存储文件的区域只能是ROM或SD Card
  - ROM里一般用于存储操作系统文件和app应用程序,相当于Windows的C盘  【读写速度快,价格高】
  - SD卡一般容量有几十G,可以存放一些大的文件,比如照片、视频、地图离线包等 【速度慢,价格低,容量高】

# 内部存储 Internal Storage
  ·概述
    - 安卓系统为每个app创建一个私有目录,这个目录只能被该app自己访问,别的app不能访问。
    - 通常内部存储就是在ROM上。
    - 通常把小型的、需要快速加载的数据存储在内部存储(即私有目录)下
  ·操作
    - 获取这个目录的位置:getFilesDir() 
      // 查看本app的私有目录
      File appDir = getFilesDir();
      Log.d(TAG, "本APP的私有目录为:" + appDir);
    - 文件读写
      File appDir = getFilesDir();
      File f = new File(appDir, "abc.txt");
      FileOutputStream fstream = new FileOutputStream(f);

# 外部存储 External Storage
  · 概述
    - 以前的安卓手机都是插一张SD卡作为外部存储,
      现在的安卓手机都内部自带了一块存储,不需要外插SD卡了。
    - 这个位置是所有app都能访问的。
    - 大型文件放在外部存储里(即SD卡)里
  · 数据目录
    - 数据目录获取 //··· /storage/emulated/0/Android/data/example.demo0902/files
      File appDataDir = getExternalFilesDir("");
    - 子目录获取 // ··· /storage/emulated/0/Android/data/example.demo0902/files/movie
      File movieDir = getExternalFilesDir("movie");
      movieDir.mkdirs();
  · 公共目录 
    - 在SD卡有一些公共目录,各app都会访问这些公共目录
    - // 公共目录, /storage/emulated/0
      File publicDir = Environment.getExternalStoragePublicDirectory("");
    - // 图片目录, /storage/emulated/0/Pictures
      File picDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES);
    - // 相机目录 (DCIM: Digital Camera In Memory )  ,/storage/emulated/0/DCIM
      File dcimDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM);
    - // 音乐目录, /storage/emulated/0/Music
      File musicDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_MUSIC);

# 添加权限
  · 概述:要读写 app自己的数据目录,并不需要添加权限。
         要读写外部存储的上其他位置,例如,访问DCIM下的文件,需要添加权限 。
  · 添加过程  
    - 声明权限 [在 AndroidManifest.xml里添加]
      <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
      <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
      -- 通常为了简单起见,我们只声明 WRITE_EXTERNAL_STORAGE 就够了,因为能WRITE就包含了READ的权限。
    - 申请权限 [应用启动时手工向用户申请批准]
      // 检查和申请权限
      public void checkPermissions(){}
      // 处理用户返回的结果
      public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults){}
   - 相关博客:

# 代码示例·内部存储

package example.demo0901;

import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import android.widget.EditText;
import android.widget.Toast;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;

public class MainActivity extends AppCompatActivity
{
    final String TAG = "测试";

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

        // 查看本app的私有目录
        File appDir = getFilesDir();
        Log.d(TAG, "本APP的私有目录为:" + appDir);

        // 加载数据
        try
        {
            loadData();
            Toast.makeText(this, "已加载数据", Toast.LENGTH_SHORT).show();
        } catch (Exception e)
        {
            Log.w(TAG, "无法加载数据!");
        }
    }

    // 点击 '保存' 按钮
    public void doSave( View view )
    {
        String text = ((EditText)findViewById(R.id.id_edittext)).getText().toString();

        // 在私有目录下创建文件abc.txt,然后往里面写入一个字符串
        File appDir = getFilesDir();
        File f = new File(appDir, "abc.txt");

        FileOutputStream fstream = null;
        try{
            fstream = new FileOutputStream(f);
            fstream.write( text.getBytes("UTF-8"));//写入字符串
        }catch(Exception e){
        }
        finally {
            try{ fstream.close();} catch (Exception e2){}
        }

        Toast.makeText(this, "已保存", Toast.LENGTH_SHORT).show();//提示信息
    }

    // 读取私有目录下的abc.txt
    // 注:这里故意展示不同的写法,给大家的参考
    private void loadData () throws Exception
    {
        File f = new File( getFilesDir(), "abc.txt");
        FileInputStream fstream = null;
        try{
            fstream = new FileInputStream(f);
            byte[] data = new byte[1024]; // 当文件很大时,应分作多次读取, 这里是简易写法仅作演示
            int n = fstream.read(data);
            if(n> 0)
            {
                // 当读取的到byte[]转成String
                String str = new String(data,0, n, "UTF-8");
                ((EditText)findViewById(R.id.id_edittext)).setText(str);
                Log.d(TAG, "加载内容: " + str);
            }
        }finally
        {
            try{ fstream.close();}catch (Exception e){}
        }
    }
}
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
    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="example.demo0901.MainActivity">

    <EditText
        android:id="@+id/id_edittext"
        android:layout_width="368dp"
        android:layout_height="wrap_content"
        android:layout_marginLeft="8dp"
        android:layout_marginRight="8dp"
        android:layout_marginTop="8dp"
        android:ems="10"
        android:hint="请输入"
        android:inputType="textPersonName"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent"/>

    <Button
        android:id="@+id/button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginLeft="8dp"
        android:layout_marginTop="8dp"
        android:onClick="doSave"
        android:text="保存"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/id_edittext"/>
</android.support.constraint.ConstraintLayout>

# 代码示例·外部存储

package example.demo0902;

import android.Manifest;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.os.Environment;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import android.widget.EditText;
import android.widget.Toast;

import java.io.File;
import java.io.FileOutputStream;

public class MainActivity extends AppCompatActivity
{
    final String TAG = "测试 MainActivity";

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

        // 在SD卡上有一个app的数据目录, /storage/emulated/0/Android/data/example.demo0902/files
        File appDataDir = getExternalFilesDir("");
        Log.d(TAG, "外部数据目录为: " + appDataDir);

        // 公共目录, /storage/emulated/0
        File publicDir = Environment.getExternalStoragePublicDirectory("");
        // 图片目录, /storage/emulated/0/Pictures
        File picDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES);
        // 相机目录 (DCIM: Digital Camera In Memory )  ,/storage/emulated/0/DCIM
        File dcimDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM);
        // 音乐目录, /storage/emulated/0/Music
        File musicDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_MUSIC);
        Log.d(TAG, "图片目录为: " + picDir);
        Log.d(TAG, "相机目录为: " + dcimDir);
        Log.d(TAG, "音乐目录为: " + musicDir);
        Log.d(TAG, "公共目录为: " + publicDir);

        // 如果要读写外部内存上的其他位置,如DCIM下的文件,都是需要申请权限的
        // (1) 在AndroidManifest.xml里声明权限,
        // (2) 在这里调用checkPermissions()来请用户给予授权!
        // checkPermissions();
    }

    // 点击 '保存' 按钮
    public void doSave (View view)
    {
        String text = ((EditText)findViewById(R.id.id_edittext)).getText().toString();

        File f = new File(getExternalFilesDir(""), "abc.txt");
        FileOutputStream fstream = null;
        try{
            fstream = new FileOutputStream(f);
            fstream.write( text.getBytes("UTF-8"));
        }catch(Exception e)
        {
        }
        finally
        {
            try{ fstream.close();} catch (Exception e2){}
        }

        Toast.makeText(this, "已保存", Toast.LENGTH_SHORT).show();
    }


    // 检查和申请权限
    final int PERMISSION_REQ_CODE = 1;
    public void checkPermissions()
    {
        // 要申请的权限列表
        final String[] permissions = {
                Manifest.permission.WRITE_EXTERNAL_STORAGE
        };

        // 检查本应用是否有了 WRITE_EXTERNAL_STORAGE 权限
        if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE)
                != PackageManager.PERMISSION_GRANTED)
        {
            // 系统将弹出一个对话框,询问用户是否授权
            ActivityCompat.requestPermissions(this, permissions, PERMISSION_REQ_CODE);
        }
    }

    // 权限申请的结果
    // requestCode:请求码
    // permissions: 申请的N个权限
    // grantResults: 每个权限是否被授权
    @Override
    public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults)
    {

        if(requestCode == PERMISSION_REQ_CODE)
        {
            for(int i=0; i<permissions.length;i++)
            {
                if(grantResults[i] != PackageManager.PERMISSION_GRANTED)
                {
                    // 惨,用户没给我们授权...这意味着有此功能就不能用了
                }
            }
        }
    }

}