Implementing Android File Chooser: A Comprehensive Guide from Intent to Custom Libraries

Nov 24, 2025 · Programming · 7 views · 7.8

Keywords: Android File Chooser | Intent File Selection | aFileChooser Library | Storage Access Framework | Uri Path Conversion | File Upload Functionality

Abstract: This article provides an in-depth exploration of Android file chooser implementation methods, covering core concepts such as using system Intent for file selection, handling return results, and parsing file paths. By analyzing the best practice solution of the aFileChooser library, it explains in detail how to avoid dependency on external file managers, offering complete code examples and implementation logic. Combined with official Android documentation, it introduces advanced usage of Storage Access Framework, including file creation, opening, directory access scenarios, providing developers with comprehensive file selection solutions.

Introduction

In Android application development, file upload functionality is a common requirement, and file chooser is the core component to achieve this functionality. Many developers face a critical question: is it necessary to force users to install specific file managers? The answer is no. This article details how to build robust file selection functionality without relying on external file managers.

System Intent File Selection

The Android system provides standard file selection mechanisms through Intent, enabling cross-application file selection. This method doesn't require users to install specific file managers, as the system automatically displays available file selection applications.

The core implementation code is as follows:

private static final int FILE_SELECT_CODE = 0;

private void showFileChooser() {
    Intent intent = new Intent(Intent.ACTION_GET_CONTENT); 
    intent.setType("*/*"); 
    intent.addCategory(Intent.CATEGORY_OPENABLE);

    try {
        startActivityForResult(
                Intent.createChooser(intent, "Select a File to Upload"),
                FILE_SELECT_CODE);
    } catch (android.content.ActivityNotFoundException ex) {
        Toast.makeText(this, "Please install a File Manager.", 
                Toast.LENGTH_SHORT).show();
    }
}

This code creates a file selection Intent, sets the type to all files ("*/*"), and specifies to select only openable files. Through the Intent.createChooser() method, the system displays a chooser allowing users to select which application to use for file selection.

Handling Selection Results

After users complete file selection, the return results need to be processed in the onActivityResult method:

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    switch (requestCode) {
        case FILE_SELECT_CODE:
        if (resultCode == RESULT_OK) {
            Uri uri = data.getData();
            Log.d(TAG, "File Uri: " + uri.toString());
            String path = FileUtils.getPath(this, uri);
            Log.d(TAG, "File Path: " + path);
        }
        break;
    }
    super.onActivityResult(requestCode, resultCode, data);
}

The key here is to obtain the file's Uri and then convert it to the actual file path. Due to Android's security mechanisms, directly using Uri might not access file content, requiring appropriate conversion.

Uri to Path Conversion

Converting file Uri to actual path is a critical step in the file selection process:

public static String getPath(Context context, Uri uri) throws URISyntaxException {
    if ("content".equalsIgnoreCase(uri.getScheme())) {
        String[] projection = { "_data" };
        Cursor cursor = null;

        try {
            cursor = context.getContentResolver().query(uri, projection, null, null, null);
            int column_index = cursor.getColumnIndexOrThrow("_data");
            if (cursor.moveToFirst()) {
                return cursor.getString(column_index);
            }
        } catch (Exception e) {
            // Handle exception
        } finally {
            if (cursor != null) {
                cursor.close();
            }
        }
    }
    else if ("file".equalsIgnoreCase(uri.getScheme())) {
        return uri.getPath();
    }
    return null;
}

This method handles two Uri schemes: content scheme and file scheme. For content scheme, it queries the actual file path through ContentResolver; for file scheme, it directly obtains the path.

aFileChooser Library Solution

To simplify the file selection process, developers created the aFileChooser open-source library. The main advantages of this library include:

Using the aFileChooser library, developers can implement complete file selection functionality with just a few lines of code, significantly reducing development complexity.

Storage Access Framework Advanced Usage

Android 4.4 and above introduced Storage Access Framework (SAF), providing more powerful file access capabilities. SAF supports three main scenarios:

Creating New Files

Using ACTION_CREATE_DOCUMENT Intent to create new files:

private static final int CREATE_FILE = 1;

private void createFile(Uri pickerInitialUri) {
    Intent intent = new Intent(Intent.ACTION_CREATE_DOCUMENT);
    intent.addCategory(Intent.CATEGORY_OPENABLE);
    intent.setType("application/pdf");
    intent.putExtra(Intent.EXTRA_TITLE, "invoice.pdf");
    intent.putExtra(DocumentsContract.EXTRA_INITIAL_URI, pickerInitialUri);
    startActivityForResult(intent, CREATE_FILE);
}

Opening Existing Files

Using ACTION_OPEN_DOCUMENT Intent to open existing files:

private static final int PICK_PDF_FILE = 2;

private void openFile(Uri pickerInitialUri) {
    Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
    intent.addCategory(Intent.CATEGORY_OPENABLE);
    intent.setType("application/pdf");
    intent.putExtra(DocumentsContract.EXTRA_INITIAL_URI, pickerInitialUri);
    startActivityForResult(intent, PICK_PDF_FILE);
}

Directory Access

Using ACTION_OPEN_DOCUMENT_TREE Intent to access entire directories:

public void openDirectory(Uri uriToLoad) {
    Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE);
    intent.putExtra(DocumentsContract.EXTRA_INITIAL_URI, uriToLoad);
    startActivityForResult(intent, your-request-code);
}

File Operations and Metadata

After obtaining file selection results, various file operations can be performed:

@Override
public void onActivityResult(int requestCode, int resultCode, Intent resultData) {
    if (requestCode == your-request-code && resultCode == Activity.RESULT_OK) {
        Uri uri = null;
        if (resultData != null) {
            uri = resultData.getData();
            // Perform file operations
        }
    }
}

File metadata information can also be retrieved:

public void dumpImageMetaData(Uri uri) {
    Cursor cursor = getActivity().getContentResolver()
            .query(uri, null, null, null, null, null);
    try {
        if (cursor != null && cursor.moveToFirst()) {
            String displayName = cursor.getString(
                cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME));
            int sizeIndex = cursor.getColumnIndex(OpenableColumns.SIZE);
            String size = null;
            if (!cursor.isNull(sizeIndex)) {
                size = cursor.getString(sizeIndex);
            } else {
                size = "Unknown";
            }
        }
    } finally {
        cursor.close();
    }
}

Persistent Permission Management

To maintain access to selected files after device restart, persistent permissions need to be obtained:

final int takeFlags = intent.getFlags() & 
    (Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
getContentResolver().takePersistableUriPermission(uri, takeFlags);

Virtual File Handling

Android 7.0 and above support virtual files, requiring special handling:

private boolean isVirtualFile(Uri uri) {
    if (!DocumentsContract.isDocumentUri(this, uri)) {
        return false;
    }
    Cursor cursor = getContentResolver().query(
        uri, new String[] { DocumentsContract.Document.COLUMN_FLAGS }, null, null, null);
    int flags = 0;
    if (cursor.moveToFirst()) {
        flags = cursor.getInt(0);
    }
    cursor.close();
    return (flags & DocumentsContract.Document.FLAG_VIRTUAL_DOCUMENT) != 0;
}

Best Practice Recommendations

Based on practical development experience, the following best practices are recommended:

Conclusion

There are multiple solutions for implementing Android file choosers, ranging from simple system Intent to fully-featured custom libraries. Developers should choose appropriate solutions based on specific requirements, avoiding unnecessary dependencies and complexity. By properly utilizing Storage Access Framework and third-party libraries, powerful yet user-friendly file selection functionality can be built.

Copyright Notice: All rights in this article are reserved by the operators of DevGex. Reasonable sharing and citation are welcome; any reproduction, excerpting, or re-publication without prior permission is prohibited.