15.文件存储

1、数据持久化

数据持久化就是指将内存中的瞬时数据保存到存储设备中,保证手机在关机的情况下数据仍然不会丢失。保存在内存中的数据是处于瞬时状态的,而保存在存储设备中的数据是处于持久状态的。

Android系统提供了三种方式简单的数据持久化功能,即文件存储SharedPreferences存储SQLite数据库存储

2、文件存储

文件存储是Android中最基本的一种数据存储方式,它与Java中的文件存储类似,都是通过I/O流的形式把数据直接存储到文件中。

不同的是,Android中的文件存储分为 内部存储 和 外部存储 

1.内部存储

内部存储是指应用程序中的数据以文件方式存储到应用程序内部中。当创建的应用程序被卸载时,其内部存储文件也随之被删除。

(1)将数据存储到文件中

Context类中提供了一个openFileOutput()方法,可以用于将数据存储到指定的文件中。

这个方法接收两个参数,第一个参数是文件名,文件是默认存储到/data/data/<package name>/files/目录下的。

第二个参数是文件的操作模式,主要有两种模式,MODE_PRIVATE和MODE_APPEND。

其中MODE_PRIVATE是默认的操作模式,表示当指定同样文件名的时候,所写入的内容将会覆盖原文件中的内容,而MODE_APPEND则表示如果该文件已存在就往文件里面追加内容,不存在就创建新文件。

下面我们就编写一个完整的例子,来学习一下如何在Android项目中使用文件存储的技术。

首先创建一个FileStorageTest项目,并修改activity_main.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">

    <EditText
        android:id="@+id/edtData"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="请输入要保存的数据" />

    <Button
        android:id="@+id/btnWriteToApp"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="写入到内部文件" />
</LinearLayout>

在布局中添加一个EditText和一个Button,点击Button时,将EditText中的内容存储到文件当中。

修改MainActivity中的代码,如下所示:

import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;

import java.io.FileOutputStream;
import java.io.IOException;

public class MainActivity extends AppCompatActivity implements View.OnClickListener {
    private EditText edtData;
    private Button btnWriteToApp;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        edtData = (EditText) findViewById(R.id.edtData);
        btnWriteToApp = (Button) findViewById(R.id.btnWriteToApp);
        btnWriteToApp.setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.btnWriteToApp:
                writeToApp(edtData.getText().toString());
                break;
        }
    }

    public void writeToApp(String data) {
        FileOutputStream out = null;
        try {
            out = openFileOutput("myFile", MODE_PRIVATE);
            out.write(data.getBytes());
            out.flush(); // 清空缓冲区的数据流
            out.close(); // 关闭输出流
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

我们通过使用MainActivity去实现监听器接口的方式为Button添加监听事件,在onCreate()方法中获取了EditText和Button的实例,为Button控件添加监听器,在onClick()方法中我们获取了EditText中输入的内容,并调用writeToApp()方法把输入的内容存储到内部文件中,文件命名为myFile。

writeToApp()方法接收一个String类型的参数,用于传入要保存的字符串数据,在该方法内,通过openFileOutput()方法获得一个FileOutputStream对象(文件输出流),然后使用它的write()方法将文本内容写入到文件中,但是write()方法需要一个字节数组参数,我们通过字符串的getBytes()方法可以将字符串转化为一个字节数组。

最后,一定要关闭文件输出流。现在重新运行一下程序,并在EditText中输入一些内容,点击按钮保存,如图所示。

    

如何才能证实数据确实已经保存成功了呢?我们可以借助Device File Explorer来查看一下。

在这里进入到/data/data/com.sdbi.filepersistencetest/files/目录下,可以看到生成了一个myFile文件,将文件保存到桌面或着直接双击打开,查看里面的内容如图所示。

如果在保存文件或者双击打开时出现如下错误: 

There were errors downloading files and/or directories: secure_mkdirs failed: Operation not permitted

我们可以到D:\Android\sdk\platform-tools目录下,使用命令行执行如下命令:

adb root 

再去保存或者打开就可以了。

这样就证实了,在EditText中输入的内容确实已经成功保存到文件中了。

不过只是成功将数据保存下来还不够,我们还要学习一下,如何从文件中读取数据。

(2)从文件中读取数据

类似于将数据存储到文件中,Context类中还提供了一个openFileInput()方法,用于从文件中读取数据。

这个方法只接收一个参数,即要读取的文件名,然后系统会自动到/data/data/<package name>/files/目录下去读取这个文件,并返回一个FileInputStream对象(文件输入流),得到了这个对象之后再通过Java流的方式就可以将数据读取出来了。

下面,我们在项目中完成数据的读取代码。

修改activity_main.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">

    <EditText
        android:id="@+id/edtData"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="请输入要保存的数据" />

    <Button
        android:id="@+id/btnWriteToApp"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="写入到内部文件" />

    <Button
        android:id="@+id/btnReadFromApp"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="从内部文件读取" />

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

</LinearLayout>

在布局文件中增加两个控件:一个Button和一个TextView,分别用于读取文件和显示读取的数据的。

修改MainActivity中的代码,如下所示:

import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;
import android.text.TextUtils;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

public class MainActivity extends AppCompatActivity implements View.OnClickListener {
    private EditText edtData;
    private Button btnWriteToApp, btnReadFromApp;
    private TextView tvDisplay;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        edtData = (EditText) findViewById(R.id.edtData);
        btnWriteToApp = (Button) findViewById(R.id.btnWriteToApp);
        btnWriteToApp.setOnClickListener(this);
        btnReadFromApp = (Button) findViewById(R.id.btnReadFromApp);
        btnReadFromApp.setOnClickListener(this);
        tvDisplay = (TextView) findViewById(R.id.tvDisplay);
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.btnWriteToApp:
                writeToApp(edtData.getText().toString());
                break;
            case R.id.btnReadFromApp:
                String strData = readFromApp();
                if (!TextUtils.isEmpty(strData)) { // 判空处理
                    tvDisplay.setText(strData);
                }
                break;
        }
    }

    public void writeToApp(String data) {
        FileOutputStream out = null;
        try {
            out = openFileOutput("myFile", MODE_PRIVATE);
            out.write(data.getBytes());
            out.flush(); // 清空缓冲区的数据流
            out.close(); // 关闭输出流
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public String readFromApp() {
        FileInputStream in = null;
        StringBuffer buffer = new StringBuffer("");
        try {
            in = openFileInput("myFile");
            byte[] temp = new byte[1024];
            int len = 0;
            while ((len = in.read(temp)) > 0) {
                buffer.append(new String(temp, 0, len));
            }
            in.close(); // 关闭输入流
        } catch (IOException e) {
            e.printStackTrace();
        }
        return buffer.toString();
    }
}

在这段代码中我们自己定义了一个方法:readFromApp(),专门用于从内部文件中读取数据,在该方法中,首先通过openFileInput()方法获取到了一个FileInputStream对象in(文件输入流),然后创建一个长度为1024的字节数组类型缓冲区temp,循环将文件的内容读取到缓冲区temp中,再将字节数组缓冲区中的数据转换成字符串添加到StringBuffer中,最后将StringBuffer中的数据转换为字符串返回给该方法。

注意:在循环读取输入流对象后一定要关闭文件输入流。然后,只需在onCreate()方法中调用readFromApp()方法来读取文件中存储的文本内容,如果读到的内容不为空,就调用TextView的setText()方法将内容填充到TextView里。

上述代码在对字符串进行非空判断的时候使用了TextUtils.isEmpty()方法,这是一个非常好用的静态方法,它可以一次性进行两种空值的判断。

当传入的字符串等于null或者等于空字符串的时候,这个方法都会返回true,从而使得我们不需要单独去判断这两种空值,再使用逻辑运算符连接起来了。

重新运行一下程序,点击“从内部文件读取”按钮,可以将原先保存过的数据读取出来,如图所示。

  

2.外部存储

外部存储就是指将文件存储到一些外部设备上,例如SD卡或者设备内嵌的存储卡,属于永久性的存储方式。

通常位于storage/sdcard文件夹,也有可能是mnt/sdcard文件夹,这个不同厂商生产的手机路径可能会不一样。

外部存储的文件可以被其他应用程序共享使用,当外部存储设备连接到计算机时,这些文件可以被浏览、修改和删除,因此这种方式是不安全的。

由于外部存储设备可能被移除、丢失或者处于其他状态,因此在使用外部设备之前必须使用Environment类的静态方法getExternalStorageState()来确认外部设备是否可用,当外部设备可用并且具有读写权限时,那么就可以通过FileOutputStream、FileInputStream对象来读写外部设备中的文件。

(1)将数据存储到外部设备的文件中

在前面程序的基础上增加一个writeToSdcard()方法用于将EditText输入的文本信息保存到外部存储设备上。代码如下所示:

public void writeToSdcard(String inputText) {
    String state = Environment.getExternalStorageState();
    if (state.equals(Environment.MEDIA_MOUNTED)) { // String“mounted”:安装好的
        File dir = Environment.getExternalStorageDirectory();
        File file = new File(dir, "myData"); // 在dir目录下构建一个新文件myData
        Log.d("MainActivity", "path = " + dir.getPath()); //或 dir.toString()
        Log.d("MainActivity", "file_name = " + file.getPath()); //或 file.toString()
        FileOutputStream out = null;
        try {
            out = new FileOutputStream(file);
            out.write(inputText.getBytes());
            out.flush();
            out.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

在上述代码中,我们使用Environment类的getExternalStorageState()静态方法来判断SD卡是否存在可用。

使用getExternalStorageDirectory()静态方法来获取SD卡根目录的路径,这样可以避免由于手机厂商不同而导致SD路径不同的问题。

另外,对于File对象(路径和文件)我们可以调用其getPath()或者toString()方法来查看路径内容。

(2)从外部设备的文件中读取数据

增加一个readFromSdcard()方法读取外部设备上的文件。

public String readFromSdcard() {
    String state = Environment.getExternalStorageState();
    StringBuffer buffer = new StringBuffer("");
    if (state.equals(Environment.MEDIA_MOUNTED)) {
        File dir = Environment.getExternalStorageDirectory();
        File file = new File(dir, "myData");
        FileInputStream in = null;
        try {
            in = new FileInputStream(file);
            byte[] temp = new byte[1024];
            int len = 0;
            while ((len = in.read(temp)) > 0) {
                Log.d("MainActivity", "len = " + len);
                buffer.append(new String(temp, 0, len));
            }
            in.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    return buffer.toString();
}

在外部文件中写入数据和读取数据的方法完成后,我们在布局文件activity_main.xml中增加两个按钮,分别用于“写入到外部文件”和“从外部文件读取”,并且在MainActivity.java中给它们添加监听器。

修改activity_main.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">

    <EditText
        android:id="@+id/edtData"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="请输入要保存的数据" />

    <Button
        android:id="@+id/btnWriteToApp"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="写入到内部文件" />

    <Button
        android:id="@+id/btnReadFromApp"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="从内部文件读取" />

    <Button
        android:id="@+id/btnWriteToSdcard"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="写入到外部文件" />

    <Button
        android:id="@+id/btnReadFromSdcard"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="从外部文件读取" />

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

</LinearLayout>

修改MainActivity.java文件代码如下:

import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;
import android.os.Environment;
import android.text.TextUtils;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;

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

public class MainActivity extends AppCompatActivity implements View.OnClickListener {
    private EditText edtData;
    private Button btnWriteToApp, btnReadFromApp, btnWriteToSdcard, btnReadFromSdcard;
    private TextView tvDisplay;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        edtData = (EditText) findViewById(R.id.edtData);
        btnWriteToApp = (Button) findViewById(R.id.btnWriteToApp);
        btnWriteToApp.setOnClickListener(this);
        btnReadFromApp = (Button) findViewById(R.id.btnReadFromApp);
        btnReadFromApp.setOnClickListener(this);
        tvDisplay = (TextView) findViewById(R.id.tvDisplay);
        btnWriteToSdcard = (Button) findViewById(R.id.btnWriteToSdcard);
        btnWriteToSdcard.setOnClickListener(this);
        btnReadFromSdcard = (Button) findViewById(R.id.btnReadFromSdcard);
        btnReadFromSdcard.setOnClickListener(this);
        tvDisplay = (TextView) findViewById(R.id.tvDisplay);
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.btnWriteToApp:
                writeToApp(edtData.getText().toString());
                break;
            case R.id.btnReadFromApp:
                String strData = readFromApp();
                if (!TextUtils.isEmpty(strData)) { // 判空处理
                    tvDisplay.setText(strData);
                }
                break;
            case R.id.btnWriteToSdcard:
                writeToSdcard(edtData.getText().toString());
                break;
            case R.id.btnReadFromSdcard:
                strData = readFromSdcard();
                if (!TextUtils.isEmpty(strData)) {
                    tvDisplay.setText(strData);
                }
                break;
        }
    }

    public void writeToApp(String data) {
        FileOutputStream out = null;
        try {
            out = openFileOutput("myFile", MODE_PRIVATE);
            out.write(data.getBytes());
            out.flush(); // 清空缓冲区的数据流
            out.close(); // 关闭输出流
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public String readFromApp() {
        FileInputStream in = null;
        StringBuffer buffer = new StringBuffer("");
        try {
            in = openFileInput("myFile");
            byte[] temp = new byte[1024];
            int len = 0;
            while ((len = in.read(temp)) > 0) {
                buffer.append(new String(temp, 0, len));
            }
            in.close(); // 关闭输入流
        } catch (IOException e) {
            e.printStackTrace();
        }
        return buffer.toString();
    }

    public void writeToSdcard(String inputText) {
        String state = Environment.getExternalStorageState();
        if (state.equals(Environment.MEDIA_MOUNTED)) { // String“mounted”:安装好的
            File dir = Environment.getExternalStorageDirectory();
            File file = new File(dir, "myData"); // 在dir目录下构建一个新文件myData
            Log.d("MainActivity", "path = " + dir.getPath()); //或 dir.toString()
            Log.d("MainActivity", "file_name = " + file.getPath()); //或 file.toString()
            FileOutputStream out = null;
            try {
                out = new FileOutputStream(file);
                out.write(inputText.getBytes());
                out.flush();
                out.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    public String readFromSdcard() {
        String state = Environment.getExternalStorageState();
        StringBuffer buffer = new StringBuffer("");
        if (state.equals(Environment.MEDIA_MOUNTED)) {
            File dir = Environment.getExternalStorageDirectory();
            File file = new File(dir, "myData");
            FileInputStream in = null;
            try {
                in = new FileInputStream(file);
                byte[] temp = new byte[1024];
                int len = 0;
                while ((len = in.read(temp)) > 0) {
                    Log.d("MainActivity", "len = " + len);
                    buffer.append(new String(temp, 0, len));
                }
                in.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return buffer.toString();
    }
}

需要注意的是:

① Android系统为了保证应用程序的安全性做了相应的规定,由于操作SD卡中的数据属于系统中比较关键的信息,因此需要在清单文件的<manifest>节点中添加SD卡的读写权限,示例代码如下:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools">

    <!--添加SD卡的读写权限-->
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />

    <application
        android:allowBackup="true"
        android:dataExtractionRules="@xml/data_extraction_rules"
        android:fullBackupContent="@xml/backup_rules"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/Theme.FileStorageTest"
        tools:targetApi="31">
        <activity
            android:name=".MainActivity"
            android:exported="true">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>

            <meta-data
                android:name="android.app.lib_name"
                android:value="" />
        </activity>
    </application>

</manifest>

② 在Android6.0(API 23)之后,APP需要动态获取权限,这也是为了用户数据更加安全。

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

import android.Manifest;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.os.Environment;
import android.text.TextUtils;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.lang.reflect.Array;
import java.util.Arrays;

public class MainActivity extends AppCompatActivity implements View.OnClickListener {
    private EditText edtData;
    private Button btnWriteToApp, btnReadFromApp, btnWriteToSdcard, btnReadFromSdcard;
    private TextView tvDisplay;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        edtData = (EditText) findViewById(R.id.edtData);
        btnWriteToApp = (Button) findViewById(R.id.btnWriteToApp);
        btnWriteToApp.setOnClickListener(this);
        btnReadFromApp = (Button) findViewById(R.id.btnReadFromApp);
        btnReadFromApp.setOnClickListener(this);
        tvDisplay = (TextView) findViewById(R.id.tvDisplay);
        btnWriteToSdcard = (Button) findViewById(R.id.btnWriteToSdcard);
        btnWriteToSdcard.setOnClickListener(this);
        btnReadFromSdcard = (Button) findViewById(R.id.btnReadFromSdcard);
        btnReadFromSdcard.setOnClickListener(this);
        tvDisplay = (TextView) findViewById(R.id.tvDisplay);

        int permission1 = ActivityCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE);
        int permission2 = ActivityCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE);
        // 常量PackageManager.PERMISSION_GRANTED=0,表示已授权
        if (permission1 != PackageManager.PERMISSION_GRANTED || permission2 != PackageManager.PERMISSION_GRANTED) {
            // 动态请求权限
            ActivityCompat.requestPermissions(this,
                    new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.READ_EXTERNAL_STORAGE},
                    1); // 第3个参数是一个表示请求的Code,要求>=0,作为回调方法onRequestPermissionsResult()的第1个参数
        }
    }

    // 授权成功的回调方法,用不着可以不重写
    // 第1个参数是一个表示请求的Code,ActivityCompat.requestPermissions()方法的第3个参数
    // 第2个参数是请求的权限数组
    // 第3个参数是授权结果,0表示已授权
    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        // 第3个参数是授权结果,0表示已授权
        super.onRequestPermissionsResult(requestCode,
                new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.READ_EXTERNAL_STORAGE},
                grantResults);
        Log.d("MainActivity", "onRequestPermissionsResult: requestCode = " + requestCode
                + ", permissions = " + Arrays.toString(permissions)
                + ", grantResults = " + Arrays.toString(grantResults));
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.btnWriteToApp:
                writeToApp(edtData.getText().toString());
                break;
            case R.id.btnReadFromApp:
                String strData = readFromApp();
                if (!TextUtils.isEmpty(strData)) { // 判空处理
                    tvDisplay.setText(strData);
                }
                break;
            case R.id.btnWriteToSdcard:
                writeToSdcard(edtData.getText().toString());
                break;
            case R.id.btnReadFromSdcard:
                strData = readFromSdcard();
                if (!TextUtils.isEmpty(strData)) {
                    tvDisplay.setText(strData);
                }
                break;
        }
    }

    public void writeToApp(String data) {
        FileOutputStream out = null;
        try {
            out = openFileOutput("myFile", MODE_PRIVATE);
            out.write(data.getBytes());
            out.flush(); // 清空缓冲区的数据流
            out.close(); // 关闭输出流
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public String readFromApp() {
        FileInputStream in = null;
        StringBuffer buffer = new StringBuffer("");
        try {
            in = openFileInput("myFile");
            byte[] temp = new byte[1024];
            int len = 0;
            while ((len = in.read(temp)) > 0) {
                buffer.append(new String(temp, 0, len));
            }
            in.close(); // 关闭输入流
        } catch (IOException e) {
            e.printStackTrace();
        }
        return buffer.toString();
    }

    public void writeToSdcard(String inputText) {
        String state = Environment.getExternalStorageState();
        if (state.equals(Environment.MEDIA_MOUNTED)) { // String“mounted”:安装好的
            File dir = Environment.getExternalStorageDirectory();
            File file = new File(dir, "myData"); // 在dir目录下构建一个新文件myData
            Log.d("MainActivity", "path = " + dir.getPath()); //或 dir.toString()
            Log.d("MainActivity", "file_name = " + file.getPath()); //或 file.toString()
            FileOutputStream out = null;
            try {
                out = new FileOutputStream(file);
                out.write(inputText.getBytes());
                out.flush();
                out.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    public String readFromSdcard() {
        String state = Environment.getExternalStorageState();
        StringBuffer buffer = new StringBuffer("");
        if (state.equals(Environment.MEDIA_MOUNTED)) {
            File dir = Environment.getExternalStorageDirectory();
            File file = new File(dir, "myData");
            FileInputStream in = null;
            try {
                in = new FileInputStream(file);
                byte[] temp = new byte[1024];
                int len = 0;
                while ((len = in.read(temp)) > 0) {
                    Log.d("MainActivity", "len = " + len);
                    buffer.append(new String(temp, 0, len));
                }
                in.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return buffer.toString();
    }
}

运行程序,提示请求权限,如图所示。

保存数据,我们可以看到在storage/sdcard文件夹生成一个myData文件。

输出日志如下。

 

posted @ 2022-10-07 20:07  熊猫Panda先生  阅读(950)  评论(0编辑  收藏  举报