Keywords: Android Development | PDF Download | HttpURLConnection | File Storage | Intent Invocation
Abstract: This paper provides an in-depth exploration of the complete technical process for downloading PDF files from URLs and opening them with external readers in Android applications. By analyzing a common issue where downloaded files become corrupted, it reveals a critical configuration error in HttpURLConnection—incorrectly setting the setDoOutput(true) method. The article offers detailed problem analysis, corrected complete code implementation covering asynchronous downloading, file storage, permission management, and Intent invocation. Additionally, it discusses modern API alternatives and security considerations based on Android development best practices, providing reliable technical references for developers.
Technical Background and Problem Description
In Android application development, implementing the functionality to download PDF files from remote servers and open them with third-party readers on local devices is a common requirement. Developers typically need to handle multiple technical aspects including network requests, file storage, permission management, and inter-app communication. However, even when code appears to execute correctly, downloaded files may become corrupted due to improper configuration, preventing external readers from opening them properly.
Problem Analysis and Core Error
Through detailed analysis of the original code, the core issue was identified in the HttpURLConnection configuration within the FileDownloader class. The original code incorrectly called the urlConnection.setDoOutput(true) method. This method is typically used for POST requests, indicating that the connection will be used for output data. Setting this flag in GET requests causes abnormal connection behavior, potentially affecting the correct reading of input streams and resulting in corrupted downloaded file content.
The correction is simple yet crucial: remove both the setRequestMethod("GET") and setDoOutput(true) lines. In fact, HttpURLConnection defaults to the GET method, and for download operations, output mode doesn't need to be set. The correct configuration should allow the connection to focus on reading input streams.
Complete Technical Implementation
The following presents the corrected complete implementation, covering all key components:
1. Asynchronous Download Task
private class DownloadFile extends AsyncTask<String, Void, Void>{
@Override
protected Void doInBackground(String... strings) {
String fileUrl = strings[0];
String fileName = strings[1];
// Create storage directory
String extStorageDirectory = Environment.getExternalStorageDirectory().toString();
File folder = new File(extStorageDirectory, "testthreepdf");
folder.mkdir();
File pdfFile = new File(folder, fileName);
try {
pdfFile.createNewFile();
} catch (IOException e) {
e.printStackTrace();
}
// Call downloader
FileDownloader.downloadFile(fileUrl, pdfFile);
return null;
}
}
2. File Downloader Implementation
public class FileDownloader {
private static final int MEGABYTE = 1024 * 1024;
public static void downloadFile(String fileUrl, File directory) {
try {
URL url = new URL(fileUrl);
HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
// Critical correction: remove setDoOutput(true)
urlConnection.connect();
InputStream inputStream = urlConnection.getInputStream();
FileOutputStream fileOutputStream = new FileOutputStream(directory);
byte[] buffer = new byte[MEGABYTE];
int bufferLength;
while ((bufferLength = inputStream.read(buffer)) > 0) {
fileOutputStream.write(buffer, 0, bufferLength);
}
fileOutputStream.close();
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
3. File Opening and Intent Invocation
public void openPdfFile(String fileName) {
File pdfFile = new File(
Environment.getExternalStorageDirectory() +
"/testthreepdf/" + fileName
);
// Check if file exists
if (!pdfFile.exists()) {
Toast.makeText(this, "File does not exist", Toast.LENGTH_SHORT).show();
return;
}
Uri path;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
// Android 7.0+ requires FileProvider
path = FileProvider.getUriForFile(
this,
getPackageName() + ".provider",
pdfFile
);
} else {
path = Uri.fromFile(pdfFile);
}
Intent pdfIntent = new Intent(Intent.ACTION_VIEW);
pdfIntent.setDataAndType(path, "application/pdf");
pdfIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
// Add temporary read permission
pdfIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
try {
startActivity(pdfIntent);
} catch (ActivityNotFoundException e) {
Toast.makeText(this, "No PDF reader available", Toast.LENGTH_SHORT).show();
}
}
Permission Configuration and Security Considerations
Necessary permissions must be configured in AndroidManifest.xml:
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
For Android 6.0+, runtime storage permission requests are also required. Additionally, to address file sharing restrictions in Android 7.0+, using FileProvider for secure file sharing is recommended.
Modern API Alternatives
With the evolution of Android development technologies, consider the following modern alternatives:
- Use OkHttp: Provides cleaner APIs and better performance
- Use WorkManager: Handles background download tasks with guaranteed completion
- Use DownloadManager: System-level download service suitable for large files
Conclusion and Best Practices
This paper provides a detailed analysis of the complete technical process for downloading and opening PDF files in Android, focusing on solving file corruption issues caused by improper HttpURLConnection configuration. Key points include: correct network connection configuration, proper file I/O handling, and adaptation to permission and file sharing mechanisms across different Android versions. In practical development, developers should always implement error handling, add progress feedback, consider network state changes, and follow the principle of least privilege to ensure application security and stability.