Java Android App Development Course, Creating a To-Do List App

This course will teach you how to develop a simple To-Do List app using Java. It is aimed at individuals with basic knowledge of Android application development and will cover various concepts such as classes, lists, and databases. By the end of the course, you will be able to complete a basic To-Do List app.

1. Setting Up the Android App Development Environment

To develop Android apps, you need to install an integrated development environment (IDE) called Android Studio. Android Studio is officially supported by Google and optimized for Android app development. Here are the basic steps to set up the development environment:

  1. Visit the official Android Studio website and download the latest version.
  2. Run the downloaded file to install it.
  3. Once the installation is complete, run Android Studio.
  4. Install the SDK (Software Development Kit).

2. Creating a Project

To create a new project in Android Studio, follow these steps:

  1. Run Android Studio and select “Start a new Android Studio project.”
  2. Select “Empty Activity” and click “Next.”
  3. Specify the project name and set the package name, storage location, etc. Select “Java” as the language.
  4. Click “Finish” to create the project.

3. Designing the UI

Now let’s design the UI. The basic UI components of the To-Do List app are as follows:

  • EditText for inputting tasks
  • Button to add tasks
  • ListView to display the list of tasks

3.1 Modifying the XML Layout File

Open the res/layout/activity_main.xml file in Android Studio and modify it as follows:

        <?xml version="1.0" encoding="utf-8"?>
        <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
            android:layout_width="match_parent"
            android:layout_height="match_parent">

            <EditText
                android:id="@+id/editTextTask"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:hint="Please enter a task" />

            <Button
                android:id="@+id/buttonAdd"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="Add"
                android:layout_below="@id/editTextTask" />

            <ListView
                android:id="@+id/listViewTasks"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_below="@id/buttonAdd" />
          
        </RelativeLayout>
    

4. Writing Java Code

After creating the layout, let’s write the Java code to add functionality. Open the MainActivity.java file, which is the main activity, and implement it as follows:

        package com.example.todolist;

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

        import androidx.appcompat.app.AppCompatActivity;

        import java.util.ArrayList;

        public class MainActivity extends AppCompatActivity {

            private EditText editTextTask;
            private Button buttonAdd;
            private ListView listViewTasks;
            private ArrayList<String> tasks;
            private ArrayAdapter<String> adapter;

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

                editTextTask = findViewById(R.id.editTextTask);
                buttonAdd = findViewById(R.id.buttonAdd);
                listViewTasks = findViewById(R.id.listViewTasks);

                tasks = new ArrayList<>();
                adapter = new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, tasks);
                listViewTasks.setAdapter(adapter);

                buttonAdd.setOnClickListener(new View.OnClickListener() {
                    @Override
                    public void onClick(View view) {
                        String task = editTextTask.getText().toString();
                        if (!task.isEmpty()) {
                            tasks.add(task);
                            adapter.notifyDataSetChanged();
                            editTextTask.setText("");
                        }
                    }
                });
            }
        }
    

5. Running the App

After writing the code, follow these steps to run the app:

  1. Click the “Run” button in the toolbar of Android Studio.
  2. Select an emulator or a physical device.
  3. Once the app runs successfully, enter a task in the input field and click the “Add” button to confirm that the task is added to the list.

6. Implementing Additional Features

The basic To-Do List app is now complete. However, you can enhance the app by implementing additional features. Here are some features you can add:

  • Functionality to delete items from the task list
  • Functionality to mark items as completed
  • Implementing a database so that data persists even after exiting the app

6.1 Implementing the Delete Functionality

You can add functionality to delete an item when long-clicking an item in the ListView. Please add the following code to MainActivity.java:

        listViewTasks.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() {
            @Override
            public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {
                tasks.remove(position);
                adapter.notifyDataSetChanged();
                return true;
            }
        });
    

6.2 Implementing the Completed Marking Feature

It is also useful to add a check mark to tasks when completed. Using RecyclerView makes it easier to manage a checklist, and you can add checkboxes for each item.

6.3 Integrating SQLite Database

To ensure that tasks persist even after exiting the app, you can use an SQLite database. You can create a SQLiteOpenHelper class to manage the database.

Conclusion

In this course, you learned how to develop an Android To-Do List app using Java. We covered not only how to create a basic app but also how to implement additional features. These basic components and concepts will serve as a foundation for developing more complex apps in the future. Continuous learning is essential in Android app development, so please try various projects.

Reference Materials

Java Android App Development Course, Fragment – A View that Works Like an Activity

In Android application development, various components are used to build the user interface (UI). Among these, the Fragment plays an important role alongside the Activity. In this course, we will explore the basic concepts, usage, advantages, and example code of fragments in depth, including how to apply fragments in practice.

1. What is a Fragment?

A fragment is a module that makes up part of the UI. Fragments, which have several advantages, help to manage complex UIs more easily and have a lighter structure than activities. Fragments can be reused multiple times within an activity and provide the flexibility to be configured differently based on various screen sizes and orientations (portrait/landscape).

2. Features of Fragments

  • Reusability: The same fragment can be reused in multiple activities, reducing code duplication.
  • Modularity: Each part of the UI can be modularized into fragments, creating an easily manageable structure.
  • Dynamic UI: Fragments can be added or removed at runtime, allowing for dynamic management of the UI.
  • Lifecycle Management: Fragments have their own lifecycle, which helps in easily managing the state of the user interface.

3. Lifecycle of a Fragment

Fragments manage their lifecycle separately from activities, but they are dependent on the activity’s lifecycle. Here are the main lifecycle methods of a fragment:

  • onAttach: Called when the fragment is attached to the activity.
  • onCreate: Called when the fragment is first created in the lifecycle.
  • onCreateView: Called to create the UI for the fragment.
  • onActivityCreated: Called when the activity and fragment initialization is complete.
  • onStart: Called when the fragment is ready to be displayed on the screen.
  • onResume: Called when the fragment is ready to interact with the user.
  • onPause: Called when the interaction with the user is temporarily paused.
  • onStop: Called when the fragment is no longer visible on the screen.
  • onDestroyView: Called when the UI of the fragment is removed.
  • onDetach: Called when the fragment is detached from the activity.

4. Creating a Fragment

The process of creating a fragment is very simple. First, define the fragment class, then create an XML layout file to set up the UI. Let’s create a fragment in the example below.

4.1. Creating a Fragment Class


import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.fragment.app.Fragment;

public class MyFragment extends Fragment {
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        // Inflate the layout for this fragment
        return inflater.inflate(R.layout.fragment_my, container, false);
    }
}

4.2. Creating an XML Layout File


<?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">

    <TextView
        android:id="@+id/textView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Hello from MyFragment!" />

</LinearLayout>

5. Adding a Fragment to an Activity

After creating a fragment, you need to add it to the activity. You can add the fragment either dynamically or statically in the XML layout. The example below shows how to add a fragment dynamically.

5.1. Creating a Space for the Fragment in the Activity Layout


<FrameLayout
    android:id="@+id/fragment_container"
    android:layout_width="match_parent"
    android:layout_height="match_parent" /
>

5.2. Adding the Fragment in the Activity Class


import android.os.Bundle;
import androidx.appcompat.app.AppCompatActivity;
import androidx.fragment.app.FragmentTransaction;

public class MainActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        MyFragment myFragment = new MyFragment();
        FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
        transaction.add(R.id.fragment_container, myFragment);
        transaction.commit();
    }
}

6. Passing Data Between Fragments

You can use a Bundle to pass data between fragments. This method allows you to pass data when creating the fragment from the activity. The example below shows how to pass data to a fragment.

6.1. Passing Data to a Fragment


public static MyFragment newInstance(String data) {
    MyFragment fragment = new MyFragment();
    Bundle args = new Bundle();
    args.putString("key", data);
    fragment.setArguments(args);
    return fragment;
}

6.2. Receiving Data Inside the Fragment


@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    if (getArguments() != null) {
        String data = getArguments().getString("key");
        // Use the data to update the UI
    }
}

7. Managing Fragment Lifecycle

Managing the lifecycle of a fragment is very important. It’s necessary to update the UI or fetch data at the right moments. For example, you can run code to fetch data when the user is ready to view the fragment.


@Override
public void onStart() {
    super.onStart();
    // Fetch data when the fragment is visible on the screen
    loadData();
}

8. Adding Fragments to the Back Stack

The back stack feature helps users easily return to a previous fragment while navigating through fragments. You can activate this by using the `addToBackStack()` method when adding a fragment.


FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
transaction.add(R.id.fragment_container, myFragment);
transaction.addToBackStack(null);
transaction.commit();

9. Advantages and Disadvantages of Fragments

9.1. Advantages

  • Improved maintainability with a modular UI.
  • Dynamic UI can adapt to various screens.
  • Reusable, reducing code duplication.

9.2. Disadvantages

  • Need to manage lifecycle between fragments and activities carefully.
  • Creating a complex UI structure can make management more difficult.

10. Conclusion

Fragments are very useful components in Android application development. They focus on modularizing the UI, enhancing reusability, and providing an interesting user experience suitable for various screen sizes. Based on the contents covered in this tutorial, we encourage you to effectively utilize fragments in your Android applications.

11. References

Java Android App Development Course, Setting Permissions

When developing an Android app, setting permissions is essential to use specific features or data. In this tutorial, we will learn in detail about the importance and methods of permission settings in Android app development using Java.

1. The Importance of Permissions

Permissions are the rights that allow the app to access specific features. For example, when accessing the camera, microphone, location information, or storage, permissions must be requested. If permissions are not set, these features cannot be used, and they play an important role in protecting user data and enhancing security.

1.1 Types of Permissions

In Android, permissions can be divided into two main types:

  • Normal Permissions: Features that do not significantly affect the user and are automatically granted during app installation. E.g., internet access.
  • Dangerous Permissions: Features that access sensitive user information and require explicit user consent. E.g., location information, camera access.

2. How to Set Permissions

There are two steps to set permissions in an Android app. We will look at each method based on the type.

2.1 Declaring Permissions in the AndroidManifest.xml File

All permissions must first be declared in the AndroidManifest.xml file. This file defines the app’s basic information, including permission settings.

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
          package="com.example.myapp">

    <uses-permission android:name="android.permission.CAMERA"/>
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>

    <application
        ...
    >
        ...
    </application>

</manifest>

2.2 Requesting Runtime Permissions

Starting from Android 6.0 (API level 23), when using dangerous permissions, user approval must be requested at runtime. You can use the code below to request permissions.

import android.Manifest;
import android.content.pm.PackageManager;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.Toast;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;

public class MainActivity extends AppCompatActivity {

    private static final int CAMERA_REQUEST_CODE = 100;

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

        if (ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
            ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.CAMERA}, CAMERA_REQUEST_CODE);
        } else {
            // If permission has already been granted
            Toast.makeText(this, "Camera permission has been granted.", Toast.LENGTH_SHORT).show();
        }
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        if (requestCode == CAMERA_REQUEST_CODE) {
            if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                Toast.makeText(this, "Camera permission has been granted.", Toast.LENGTH_SHORT).show();
            } else {
                Toast.makeText(this, "Camera permission has been denied.", Toast.LENGTH_SHORT).show();
            }
        }
    }
}

In the above code, the ContextCompat.checkSelfPermission() method checks if the permission has already been granted, and ActivityCompat.requestPermissions() requests approval. When the user chooses to allow or deny, the result is handled in the onRequestPermissionsResult() method.

3. Permission Handling Example

In the example below, we will request camera permission and implement functionality to take a photo if the user grants the request.

import android.Manifest;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.graphics.Bitmap;
import android.net.Uri;
import android.os.Bundle;
import android.provider.MediaStore;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;

public class MainActivity extends AppCompatActivity {

    private static final int CAMERA_REQUEST_CODE = 100;
    private static final int IMAGE_CAPTURE_REQUEST = 1;
    private ImageView imageView;

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

        imageView = findViewById(R.id.imageView);
        Button button = findViewById(R.id.captureButton);

        button.setOnClickListener(v -> {
            if (ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
                ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.CAMERA}, CAMERA_REQUEST_CODE);
            } else {
                openCamera();
            }
        });
    }

    private void openCamera() {
        Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
        if (intent.resolveActivity(getPackageManager()) != null) {
            startActivityForResult(intent, IMAGE_CAPTURE_REQUEST);
        }
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        if (requestCode == IMAGE_CAPTURE_REQUEST && resultCode == RESULT_OK) {
            Bundle extras = data.getExtras();
            Bitmap imageBitmap = (Bitmap) extras.get("data");
            imageView.setImageBitmap(imageBitmap);
        }
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        if (requestCode == CAMERA_REQUEST_CODE) {
            if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                openCamera();
            } else {
                Toast.makeText(this, "Camera permission has been denied.", Toast.LENGTH_SHORT).show();
            }
        }
    }
}

In this example, the user is prompted to request camera permission, and if granted, the camera app is opened, allowing them to take a picture. The captured image is displayed in the ImageView.

4. Tips Related to Permissions

When using permissions, consider the following tips:

  • Request Minimum Permissions: Since users may not accept them, only request the permissions you need.
  • Provide a Clear Reason: It’s a good idea to provide a message that explains why the permission is needed, so users can understand the request.
  • Consider User Experience: To avoid disrupting the user’s flow during permission requests, ask for them at appropriate times.

5. Conclusion

In this tutorial, we discussed how to set permissions in Android apps. Since permissions are crucial in protecting user data, it is essential to set and request them correctly. Utilize what you learned in this tutorial when developing your apps in the future.

6. References

Java Android App Development Course, Compatibility with Phone Sizes

Introduction

Compatibility with various screen sizes and resolutions is an essential element in Android app development. As various mobile devices are released in the market, it has become important to ensure that the same app can be effectively used across different screen sizes. In this post, we will explore in-depth how to build such compatibility using Java.

1. Classification of Android Screen Sizes

Android provides the following classifications based on screen size.

  • Small: 3″ display (e.g., old phones)
  • Normal: 4″~5″ display (e.g., mid-sized smartphones)
  • Large: 7″~10″ display (e.g., tablets)
  • Xlarge: 10″ and above display (e.g., large tablets)

1.1 Screen Density

Android must consider the density of the user’s screen by default. Screen density is expressed in ‘dpi’ (dots per inch), with various densities existing as follows.

  • ldpi (low) – 120dpi
  • mdpi (medium) – 160dpi, the baseline density
  • hdpi (high) – 240dpi
  • xhdpi (extra-high) – 320dpi
  • xxhdpi (extra-extra-high) – 480dpi
  • xxxhdpi (extra-extra-extra-high) – 640dpi

2. Layout Design Principles

To create a compatible app, it is necessary to design a layout that can accommodate various screen sizes and densities. Here are some essential principles.

2.1 Use of Inclusive Layouts

Android uses flexible layouts such as ConstraintLayout to support various screen sizes. This layout allows views to reference each other’s positions through constraints.

2.2 Use of dp and sp

When setting the size of views and text, it is recommended to use dp (density-independent pixels) and sp (scale-independent pixels). These two units automatically adjust according to screen density. For instance, when setting textSize, sp should be used to allow size adjustments based on user preferences.

2.3 Use of Various Layout Resources

In Android, different layouts according to screen size and density can be defined separately through resource directories. For example, layouts can be separated into layout-mdpi, layout-hdpi, layout-xhdpi, etc., to set different layouts.

3. Code Example

Below is example code for a simple Android app designed to adapt to various screen sizes.

3.1 MainActivity.java

package com.example.compatibility;

import android.os.Bundle;
import androidx.appcompat.app.AppCompatActivity;

public class MainActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }
}
    

3.2 activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <TextView
        android:id="@+id/textView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Hello, Android!"
        android:textSize="18sp"
        android:layout_centerInParent="true"/>

</RelativeLayout>
    

3.3 Layout Example for Different Screen Sizes

The following is a layout example with a “More” button that changes based on different screen sizes. Here, the position of the button is adjusted according to the screen size.

layout-small/activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <Button
        android:id="@+id/button"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="More"
        android:layout_alignParentBottom="true"/>

</RelativeLayout>
    

layout-normal/activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <Button
        android:id="@+id/button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="More"
        android:layout_alignParentBottom="true"
        android:layout_centerHorizontal="true"/>

</RelativeLayout>
    

layout-large/activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <Button
        android:id="@+id/button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="More"
        android:layout_alignParentBottom="true"
        android:layout_centerHorizontal="true"
        android:padding="16dp"/>

</RelativeLayout>
    

4. Conclusion

In this post, we have examined how to ensure compatibility for Android app development using Java to cater to various screen sizes and resolutions. We hope that applying various techniques and principles to enhance app compatibility will help in developing apps that provide the best user experience.

5. References

Java Android App Development Course, Save to File

There are several ways to store data in Android app development, and one of them is to utilize the file system. In this tutorial, we will explain in detail how to save data to a file in an Android app using Java. The main contents include creating files, writing and reading data, as well as covering key concepts related to file I/O.

1. Overview of Android File System

Android provides several methods to store data in files. The commonly used file storage methods can be divided into two categories: internal storage and external storage.

  • Internal Storage: Stores data in a dedicated space within the device where the application is installed. This data cannot be accessed by other apps, and it will be deleted along with the app when the app is uninstalled.
  • External Storage: Stores data in external storage devices like SD cards. Users can access it directly, and data can be shared with other apps. External storage can further be divided into public and private storage.

2. Saving Files in Internal Storage

The process of saving files in internal storage is as follows.

2.1 Creating and Writing Files

First, we will open the ‘MainActivity.java’ file and write code to save data in internal storage.

public class MainActivity extends AppCompatActivity {
    private static final String FILENAME = "example_file.txt";
    private Button writeButton;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        
        writeButton = findViewById(R.id.write_button);
        writeButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                writeFileToInternalStorage("Hello, World!");
            }
        });
    }

    private void writeFileToInternalStorage(String data) {
        FileOutputStream fos;
        try {
            fos = openFileOutput(FILENAME, Context.MODE_PRIVATE);
            fos.write(data.getBytes());
            fos.close();
            Toast.makeText(this, "File saved successfully!", Toast.LENGTH_SHORT).show();
        } catch (IOException e) {
            e.printStackTrace();
            Toast.makeText(this, "File write failed!", Toast.LENGTH_SHORT).show();
        }
    }
}

The above code is an example that saves the text “Hello, World!” in a file named ‘example_file.txt’ in internal storage when a simple button is clicked.

2.2 Reading Files

To read the saved file, add the following code.

private void readFileFromInternalStorage() {
    FileInputStream fis;
    try {
        fis = openFileInput(FILENAME);
        InputStreamReader isr = new InputStreamReader(fis);
        BufferedReader reader = new BufferedReader(isr);
        StringBuilder sb = new StringBuilder();
        String line;

        while ((line = reader.readLine()) != null) {
            sb.append(line);
        }

        fis.close();
        Toast.makeText(this, "Read from file: " + sb.toString(), Toast.LENGTH_SHORT).show();
    } catch (IOException e) {
        e.printStackTrace();
        Toast.makeText(this, "File read failed!", Toast.LENGTH_SHORT).show();
    }
}

Now it is possible to read from the file after writing to it. When the user clicks the button, the saved content is read and displayed as a Toast message on the screen.

3. Saving Files in External Storage

Saving files in external storage is slightly different from internal storage, and users can access the files. To save data in external storage, you must first request permissions.

3.1 Requesting Permissions

Add the following code to the AndroidManifest.xml file to grant access permissions for external storage.

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

Now, add the following code to ‘MainActivity.java’ for runtime permission requests.

private static final int REQUEST_EXTERNAL_STORAGE = 1;
private String[] permissions = {Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.READ_EXTERNAL_STORAGE};

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

    if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
        ActivityCompat.requestPermissions(this, permissions, REQUEST_EXTERNAL_STORAGE);
    }

    writeButton = findViewById(R.id.write_button);
    writeButton.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            writeFileToExternalStorage("Hello from External Storage!");
        }
    });
}

3.2 Creating and Writing Files

Now you can create files and write data to external storage. The code to write files to external storage is as follows.

private void writeFileToExternalStorage(String data) {
    File file = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS), "example_external_file.txt");
    FileOutputStream fos;
    try {
        fos = new FileOutputStream(file);
        fos.write(data.getBytes());
        fos.close();
        Toast.makeText(this, "External file saved successfully!", Toast.LENGTH_SHORT).show();
    } catch (IOException e) {
        e.printStackTrace();
        Toast.makeText(this, "External file write failed!", Toast.LENGTH_SHORT).show();
    }
}

The above code creates a file named ‘example_external_file.txt’ in the documents directory of external storage and fills it with the data “Hello from External Storage!”.

3.3 Reading Files

The code to read the saved file is as follows.

private void readFileFromExternalStorage() {
    File file = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS), "example_external_file.txt");
    FileInputStream fis;
    try {
        fis = new FileInputStream(file);
        InputStreamReader isr = new InputStreamReader(fis);
        BufferedReader reader = new BufferedReader(isr);
        StringBuilder sb = new StringBuilder();
        String line;

        while ((line = reader.readLine()) != null) {
            sb.append(line);
        }

        fis.close();
        Toast.makeText(this, "Read from external file: " + sb.toString(), Toast.LENGTH_SHORT).show();
    } catch (IOException e) {
        e.printStackTrace();
        Toast.makeText(this, "External file read failed!", Toast.LENGTH_SHORT).show();
    }
}

4. Additional File Handling Features

When dealing with file I/O, there are a few additional functions that can be implemented beyond simple reading and writing.

4.1 Checking File Existence

The code to check if a file exists is as follows.

private boolean fileExists(String fileName) {
    File file = new File(getFilesDir(), fileName);
    return file.exists();
}

4.2 Deleting Files

If you want to delete a file, use the code below.

private void deleteFileFromInternalStorage(String fileName) {
    File file = new File(getFilesDir(), fileName);
    if (file.delete()) {
        Toast.makeText(this, "File deleted successfully!", Toast.LENGTH_SHORT).show();
    } else {
        Toast.makeText(this, "File deletion failed!", Toast.LENGTH_SHORT).show();
    }
}

5. Conclusion

In this tutorial, we have learned in detail how to save data to a file in an Android app using Java. We broadly covered methods using internal and external storage, file creation and reading, and the aspects of permission requests.

The file system continues to play an important role in various applications that require data storage. I hope this tutorial enhances your understanding of file I/O and enables you to apply it in real application development.

In future tutorials, we will also cover more complex data storage methods, such as databases. I hope you develop the ability to learn and appropriately utilize various data storage methods along with the file system.

Thank you!