This is a follow-up question for Android APP connect to FTP server in Java and Android APP FTP uploading file implementation in Java. I am attempting to perform the download operation to specified FTP server in Android APP. The public class FTPconnection
has been implemented for performing the download details and due to AsyncTask was deprecated in API level 30, I am attempting to use java.util.concurrent
for FTP downloading file implementation.
The experimental implementation
Project name: FTPDownloadFile
FTPconnection
class implementationpackage com.example.ftpdownloadfile; import android.util.Log; import org.apache.commons.net.ftp.FTP; import org.apache.commons.net.ftp.FTPClient; import org.apache.commons.net.ftp.FTPReply; import java.io.BufferedOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStream; import java.net.SocketException; import java.net.UnknownHostException; public class FTPconnection { private native String getHostname(); private native String getUsername(); private native String getPassword(); private static String FTPHostName = ""; private static final int FTPPort = 21; private static String FTPUsername = ""; private static String FTPPassword = ""; public FTPconnection() { FTPHostName = getHostname(); FTPUsername = getUsername(); FTPPassword = getPassword(); } // Reference: https://stackoverflow.com/a/24605713/6667035 public Boolean downloadAndSaveFile(String filename, File localFile) throws IOException { String LOG_TAG = "FTPconnection_download"; FTPClient ftp = null; try { ftp = new FTPClient(); ftp.connect(FTPHostName, FTPPort); Log.d(LOG_TAG, "Connected. Reply: " + ftp.getReplyString()); ftp.login(FTPUsername, FTPPassword); Log.d(LOG_TAG, "Logged in"); ftp.setFileType(FTP.BINARY_FILE_TYPE); Log.d(LOG_TAG, "Downloading"); ftp.enterLocalPassiveMode(); OutputStream outputStream = null; boolean success = false; try { outputStream = new BufferedOutputStream(new FileOutputStream( localFile)); success = ftp.retrieveFile(filename, outputStream); } finally { if (outputStream != null) { outputStream.close(); } } return success; } finally { if (ftp != null) { ftp.logout(); ftp.disconnect(); } } } }
ftpdownloadfile.cpp
#include <jni.h> #include <string> extern "C" JNIEXPORT jstring JNICALL Java_com_example_ftpdownloadfile_FTPconnection_getHostname(JNIEnv *env, jobject thiz) { std::string output = "Hostname"; return env->NewStringUTF(output.c_str()); } extern "C" JNIEXPORT jstring JNICALL Java_com_example_ftpdownloadfile_FTPconnection_getUsername(JNIEnv *env, jobject thiz) { std::string output = "Username"; return env->NewStringUTF(output.c_str()); } extern "C" JNIEXPORT jstring JNICALL Java_com_example_ftpdownloadfile_FTPconnection_getPassword(JNIEnv *env, jobject thiz) { std::string output = "Password"; return env->NewStringUTF(output.c_str()); }
ExecutorService
for new threadExecutorService executor = Executors.newSingleThreadExecutor(); executor.submit(() -> { String threadName = Thread.currentThread().getName(); showToast(threadName, Toast.LENGTH_SHORT); FTPconnection ftpConnect = new FTPconnection(); final String LOG_TAG = "executor"; try { String filename = "test.txt"; Log.v(LOG_TAG,"Preparing to download " + filename); var result = ftpConnect.downloadAndSaveFile(filename, Paths.get(getApplicationInfo().dataDir + "/" + filename).toFile()); if (result == false) { Log.v(LOG_TAG,"Fail to download " + filename + "!"); } else { Log.v(LOG_TAG,"Download " + filename + " successfully!"); } } catch (Exception ex) { ex.printStackTrace(); } });
User permission setting
<uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.ftpdownloadfile"> <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/Theme.FTPDownloadFile"> <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> </activity> </application> <uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" /> </manifest>
build.gradle
plugins { id 'com.android.application' } android { compileSdk 31 defaultConfig { applicationId "com.example.ftpdownloadfile" minSdk 26 targetSdk 31 versionCode 1 versionName "1.0" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" externalNativeBuild { cmake { cppFlags '' } } } buildTypes { release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' } } compileOptions { sourceCompatibility JavaVersion.VERSION_11 targetCompatibility JavaVersion.VERSION_11 } externalNativeBuild { cmake { path file('src/main/cpp/CMakeLists.txt') version '3.18.1' } } } dependencies { implementation fileTree(include: ['*.jar'], dir: 'libs') // https://mvnrepository.com/artifact/commons-net/commons-net implementation group: 'commons-net', name: 'commons-net', version: '20030805.205232' implementation 'androidx.appcompat:appcompat:1.3.1' implementation 'com.google.android.material:material:1.4.0' implementation 'androidx.constraintlayout:constraintlayout:2.1.0' testImplementation 'junit:junit:4.13.2' androidTestImplementation 'androidx.test.ext:junit:1.1.3' androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0' }
Full Testing Code
MainActivity.java
implementation:
package com.example.ftpdownloadfile;
import androidx.annotation.StringRes;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import android.Manifest;
import android.content.Context;
import android.content.pm.PackageManager;
import android.os.AsyncTask;
import android.os.Bundle;
import android.util.Log;
import android.widget.Toast;
import org.apache.commons.net.ftp.FTPClient;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.nio.file.Paths;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class MainActivity extends AppCompatActivity {
static {
System.loadLibrary("ftpdownloadfile");
}
private static final int READ_EXTERNAL_STORAGE_CODE = 1;
private static final int WRITE_EXTERNAL_STORAGE_CODE = 2;
private static final int ACCESS_NETWORK_STATE_CODE = 3;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
checkPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE, WRITE_EXTERNAL_STORAGE_CODE);
checkPermission(Manifest.permission.READ_EXTERNAL_STORAGE, READ_EXTERNAL_STORAGE_CODE);
checkPermission(Manifest.permission.ACCESS_NETWORK_STATE, ACCESS_NETWORK_STATE_CODE);
downloadProcess("test.txt");
//mySleep(1000);
try {
BufferedReader fileReader = new BufferedReader(new FileReader(this.getApplicationInfo().dataDir + "/test.txt"));
String row;
while ((row = fileReader.readLine()) != null) {
showToast(row, Toast.LENGTH_SHORT);
}
fileReader.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
private void downloadProcess(final String filename) {
ExecutorService executor = Executors.newSingleThreadExecutor();
executor.submit(() -> {
String threadName = Thread.currentThread().getName();
showToast(threadName, Toast.LENGTH_SHORT);
FTPconnection ftpConnect = new FTPconnection();
final String LOG_TAG = "executor";
try {
Log.v(LOG_TAG,"Preparing to download " + filename);
var result = ftpConnect.downloadAndSaveFile(filename, Paths.get(getApplicationInfo().dataDir + "/" + filename).toFile());
if (result == false) {
Log.v(LOG_TAG,"Fail to download " + filename + "!");
} else {
Log.v(LOG_TAG,"Download " + Paths.get(getApplicationInfo().dataDir + "/" + filename) + " successfully!");
}
} catch (Exception ex) {
ex.printStackTrace();
}
});
}
private void mySleep(final long millis)
{
try {
Thread.sleep(millis);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
private void showToast(@StringRes int res, int duration) {
showToast(getResources().getString(res), duration);
return;
}
private void showToast(final String textInput, int duration)
{
Context context = getApplicationContext();
CharSequence text = textInput;
// Reference: https://stackoverflow.com/a/12897386/6667035
runOnUiThread(() -> Toast.makeText(context, text, duration).show());
}
// Function to check and request permission.
public void checkPermission(String permission, int requestCode)
{
if (ContextCompat.checkSelfPermission(MainActivity.this, permission) == PackageManager.PERMISSION_DENIED) {
// Requesting the permission
ActivityCompat.requestPermissions(MainActivity.this, new String[] { permission }, requestCode);
}
else {
if (BuildConfig.DEBUG) {
showToast("Permission already granted", Toast.LENGTH_SHORT);
}
}
}
}
All suggestions are welcome.
The summary information:
Which question it is a follow-up to?
What changes has been made in the code since last question?
I am attempting to perform the download operation to specified FTP server in Android APP in this post.
Why a new review is being asked for?
If there is any possible improvement, please let me know.