[JAVA] * Android * About saving / reading files to external storage and permissions

Introduction

Hello. Continuing from Last time, I will explain the storage operation of Android. This time, the content is to save / read the file to the external storage, but I will try to realize it in a way as close as possible to the previous saving / reading of the internal storage.

As I mentioned last time, all the articles about Android commentary are slightly different, and I think that some people may get lost, so I try to write a standard method.

Premise

The development environment is as follows. *Android Studio 4.0.1 *targetSdkVersion 28 *Google Nexus 5x

Save / read files to external storage

This time, the file is saved in the application-specific area (/ sdcard / Android / data / package name / files /) of the external storage. When it comes to saving files to external storage, it is standard to save them to this location.

Starting with Android 6.0 (API level 23), users will check Permission after launching the app, not when installing the app. Permission is required to read and write external storage.

Regarding Permission

In Android application development, Permission has two protection levels, called Normal and Dangerous, respectively. Since the protection level is determined for each Permission, it is necessary to implement the processing required for Normal and the processing required for Dangerous according to the required Permission.

Processing required for Normal Permission

All you need to do for Normal Permission is to describe the permission in AndroidManifest.xml. For example, Normal Permission is required to connect to the Internet with an Android app.

AndroidManifest.xml


<uses-permission android:name="android.permission.INTERNET" />

Just add one line like the one above and you will be able to connect to the Internet.

Processing required for Dangerous Permission

Dangerous Permission processing is required for reading and writing external storage this time. First, describe the permission in AndroidManifest.xml.

AndroidManifest.xml


<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

Next, you need to implement the permission check. When the app is opened, the user can display a dialog regarding permission / non-permission of Permission. Please refer to the sample program because the processing will be long.

Saving files to external storage

First, get the path to the folder you want to save in the external storage, and create the path including the file name.

this.fileName = "test.txt";
this.path = getExternalFilesDir(Environment.DIRECTORY_DOCUMENTS).toString();
this.file = this.path + "/" + this.fileName;

As a result, the character string "/sdcard/Android/data/package name/files/Documents/test.txt" is assigned to the file variable.

To check whether the external storage is currently readable and writable, write the following before the read / write process.

String state = Environment.getExternalStorageState();
if (Environment.MEDIA_MOUNTED.equals(state)){
    //File read / write processing
}

Next is the process of saving the file. When saving a file to the internal storage, the path to the internal storage was automatically specified by using the `openFileOutput ()` method for the instance of FileOutputStream. However, this time I don't use openFileOutput ()` `` because I have to specify the path of the external storage by myself.

FileOutputStream fileOutputStream = new FileOutputStream(file, true);

** First argument ** "/path/file name"

** Second argument ** trueIf is set, it will be written to the end of the file. falseIf is specified, the file will be overwritten from the beginning.

After that, convert the character string you want to write to the file to a byte type array and write it to the stream using `write ()`.

fileOutputStream.write(str.getBytes());

Read a file from external storage

When reading a file, you get an instance of FileInputStream. This will allow you to open the stream.

FileInputStream fileInputStream = new FileInputStream(file);

Subsequent processing is the same as Last time.

Sample code

AndroidManifest.xml


<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.keita.myapplication">
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

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

</manifest>

activity_main.xml


<?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=".MainActivity">


    <Button
        android:id="@+id/button1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="50dp"
        android:layout_marginTop="100dp"
        android:text="Save File"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <Button
        android:id="@+id/button2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="100dp"
        android:layout_marginTop="100dp"
        android:text="Read File"
        app:layout_constraintStart_toEndOf="@+id/button1"
        app:layout_constraintTop_toTopOf="parent" />

    <TextView
        android:id="@+id/textView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="120dp"
        android:text=""
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/editText" />

    <EditText
        android:id="@+id/editText"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="30dp"
        android:ems="10"
        android:inputType="textPersonName"
        android:text="Name"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

</android.support.constraint.ConstraintLayout>

MainActivity.java


package com.example.keita.myapplication;

import android.Manifest;
import android.content.pm.PackageManager;
import android.os.Environment;
import android.support.annotation.NonNull;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;

public class MainActivity extends AppCompatActivity {
    private String file;
    private String path;
    public String state;
    private String fileName;
    private TextView textView;
    private Button button1;
    private Button button2;
    private EditText editText;
    private final String[] PERMISSIONS = {
            Manifest.permission.WRITE_EXTERNAL_STORAGE
    };
    private final int REQUEST_PERMISSION = 1000;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        this.textView = findViewById(R.id.textView);
        this.editText = findViewById(R.id.editText);
        this.button1 = findViewById(R.id.button1);
        this.button2 = findViewById(R.id.button2);
        this.fileName = "test.txt";
        this.path = getExternalFilesDir(Environment.DIRECTORY_DOCUMENTS).toString();
        this.file = this.path + "/" + this.fileName;

        checkPermission();
    }

    private void checkPermission(){
        if (isGranted()){
            setEvent();
        }
        else {
            requestPermissions(PERMISSIONS, REQUEST_PERMISSION);
        }
    }


    private boolean isGranted(){
        for (int i = 0; i < PERMISSIONS.length; i++){
            //The first time is PERMISSION_DENIED returns
            if (checkSelfPermission(PERMISSIONS[i]) != PackageManager.PERMISSION_GRANTED) {
                //Returns true if the request is rejected once. Returns false for the first time or when "Do not show again" is selected.
                if (shouldShowRequestPermissionRationale(PERMISSIONS[i])) {
                    Toast.makeText(this, "Permission required to run the app", Toast.LENGTH_LONG).show();
                }
                return false;
            }
        }
        return true;
    }


    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        if (requestCode == REQUEST_PERMISSION){
            checkPermission();
        }
    }


    private void setEvent(){
        button1.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                String text = editText.getText().toString();
                state = Environment.getExternalStorageState();
                if (Environment.MEDIA_MOUNTED.equals(state)){
                    saveFile(file, text);
                }
            }
        });

        button2.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                state = Environment.getExternalStorageState();
                if (Environment.MEDIA_MOUNTED.equals(state)){
                    String str = readFile(file);
                    textView.setText(str);
                }
            }
        });
    }


    private void saveFile(String file, String str){
        try {
            FileOutputStream fileOutputStream = new FileOutputStream(file, true);
            fileOutputStream.write(str.getBytes());
        } catch (IOException e) {
            e.printStackTrace();
        }
    }


    private String readFile(String file){
        String text = null;
        try {
            FileInputStream fileInputStream = new FileInputStream(file);
            BufferedReader reader = new BufferedReader(new InputStreamReader(fileInputStream, "UTF-8"));
            String lineBuffer;
            while (true){
                lineBuffer = reader.readLine();
                if (lineBuffer != null){
                    text += lineBuffer;
                }
                else {
                    break;
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return text;
    }
}

Recommended Posts

* Android * About saving / reading files to external storage and permissions
* Android * Saving / loading files to internal storage
* Android * Saving / loading images to external storage
Zip and upload multiple files to Firebase Storage on Android.
I tried to chew C # (reading and writing files)
Reading and writing Java basic files
[Java] Reading and writing files with OpenCSV
[Java] How to output and write files!
Java to C and C to Java in Android Studio
Reading and writing gzip files in Java
About uploading files from on-premises environment to GCS bucket and setting minimum privilege