Η δυνατότητα των συσκευών να είναι online και να επικοινωνούν με υπηρεσίες που τρέχουν στο τοπικό δίκτυο ή στο διαδίκτυο πολλαπλασιάζει τη δυναμική των mobile εφαρμογών. Ίσως αυτό ακούγεται προφανές, αλλά για όσους από εμάς ασχολούμαστε με embedded συστήματα περισσότερο από μια δεκαετία, εκτιμούμε πολύ περισσότερο τη δυνατότητα της άντλησης και εναπόθεσης της πληροφορίας την στιγμή που απαιτείται, χωρίς προσωρινή αποθήκευση, χωρίς τη μαρτυρική αναμονή της συσκευής να διασυνδεθεί στους server με σειριακό καλώδιο ή άλλα περίεργα πρωτόκολλα για να πάρει και να δώσει τα data που απαιτούνται. Δεν θα επεκταθούμε επ’ αυτού, γιατί στο παρόν σχεδιάσαμε να μιλήσουμε για ανταλλαγή αρχείων με το πρωτόκολλο ftp και πιο συγκεκριμένα για το πως αυτή επιτυγχάνεται μεταξύ android mobile app και ftp server.
Ξεκινήστε ανοίγοντας το IDE για android development της επιλογής σας, δημιουργήστε νέο project και ενσωματώστε μια βιβλιοθήκη ftp client, μια καλή είναι εδώ: http://commons.apache.org/proper/commons-net/download_net.cgi, εκτός φυσικά αν έχετε όρεξη να υλοποιήσετε το RFC 959!
Η ενσωμάτωση του jar στο android studio: copy-paste το jar στον φάκελο libs του project και δεξί κλικ, “add as library”, αρκούν για να κομπαϊλαριστεί το project. Εδώ να σημειώσουμε ότι αυτή η φαινομενικά απλή διαδικασία του android studio φαίνεται να έχει προκαλέσει πονοκέφαλο σε πολλούς καθώς ο φάκελος libs δεν είναι ορατός σε κάποια perspectives του studio. Αν είστε ένας από αυτούς ίσως το stackoverflow να σας βοηθήσει. Το eclipse, που ανήκει στις συμπάθειες του ιστολογίου (όχι όμως για το android development), έχει αντίστοιχη διαδικασία ενσωμάτωσης, δείτε την εδώ.
Μην ξεχάσετε να προσαρτήσετε “το δικαίωμα για internet” στο “μανιφέστο” του project σας.
<uses-permission android:name="android.permission.INTERNET"/>
Καιρός να δοκιμάσουμε τη βιβλιοθήκη. Φτιάξτε ένα activity, προσθέστε ένα ‘κουμπί’ και γράψτε στον click handler τα παρακάτω.
public class MyActivity extends Activity { protected void onCreate(Bundle b) { super.onCreate(b); setContentView(R.layout.content_layout_id); final Button button = (Button) findViewById(R.id.button_id); button.setOnClickListener(new View.OnClickListener() { public void onClick(View v) { FtpConnection c = new FtpConnection(); c.password = "ftp_password"; c.port = 21; c.server = "ftp.example.com"; c.username = "ftp_username"; FtpAction a = new FtpAction(); a.upload = true; a.connection = c; a.remoteFilename = "remotefilename.bin"; new FtpAsyncTask(MyActivity.this).execute(a); } }); } } }
Ουσιαστικά, αυτό που συμβαίνει στο κλικ του κουμπιού μας είναι η ενεργοποίηση ενός ασύγχρονου task που αναλαμβάνει την επικοινωνία με τον ftp server. Σαν παράμετρο στο ασύγχρονο task διαθέτουμε το object της custom κλάσης FtpAction που φέρει τις απαραίτητες πληροφορίες για την επικοινωνία με τον server.
Ο παραπάνω κώδικας συνδέει 3 νέες οντότητες: FtpConnection, FtpAction, FtpAsyncTask. Η FtpConnection φέρει τις πληροφορίες σύνδεσης στον ftp server, η FtpAtion περιγράφει τις ενέργειες που θα πραγματοποιηθούν στον ftp server και η FtpAsyncTask αναλαμβάνει την ασύγχρονη εκτέλεση των ενεργειών.
Για ποιό λόγο χρησιμοποιείται ασύγχρονο task; Ο λόγος είναι ότι δεν επιτρέπεται δικτυακές λειτουργίες να εκτελούνται στο κυρίως thread. Αντίθετα, με τη χρήση του AsyncTask η εφαρμογή εκτελεί τη δικτυακή επικοινωνία στο background, το UI εξακολουθεί να ανταποκρίνεται και, αν φυσικά υλοποιηθεί, η εφαρμογή ενημερώνει τον χρήστη για την πρόοδο του task. Ας δούμε την υλοποίηση της FtpAsyncTask:
import android.app.ProgressDialog; import android.content.Context; import android.os.AsyncTask; import android.widget.Toast; import org.apache.commons.net.ftp.FTP; import org.apache.commons.net.ftp.FTPClient; import java.io.ByteArrayInputStream; import java.io.InputStream; public class FtpAsyncTask extends AsyncTask<FtpAction, Void, Boolean> { private Exception exception; /** progress dialog to show user that the backup is processing. */ private ProgressDialog dialog; /** application context. */ private Context context; protected void onPreExecute() { this.dialog.setMessage("Εκκίνηση"); this.dialog.show(); } @Override protected void onPostExecute(final Boolean success) { if (dialog.isShowing()) { dialog.dismiss(); } if (success) { Toast.makeText(context, "OK", Toast.LENGTH_LONG).show(); } else { Toast.makeText(context, "Σφάλμα", Toast.LENGTH_LONG).show(); } } public FtpAsyncTask(Context context) { this.context = context; dialog = new ProgressDialog(context); } protected Boolean doInBackground(FtpAction... actions) { FTPClient ftp = new FTPClient(); FtpAction a = actions[0]; try { ftp.connect(a.connection.server, a.connection.port); ftp.login(a.connection.username, a.connection.password); boolean success = false; byte[] test = new byte[]{0, 1, 0, 1, 0, 1, 0, 1}; InputStream inputStream = new ByteArrayInputStream(test); ftp.setFileType(FTP.BINARY_FILE_TYPE); ftp.enterLocalPassiveMode(); success = ftp.storeFile(a.remoteFilename, inputStream); inputStream.close(); a.success = success; ftp.disconnect(); return success; } catch (Exception x) { this.exception = x; a.success = false; return false; } } }
Η FtpAsyncTask επεκτείνει την AsyncTask υλοποιώντας την doInBackground που επιτελεί τη λειτουργία διασύνδεσης και επικοινωνίας με τον ftp server. Στο παράδειγμα, αντί για κανονικό αρχείο, δημιουργούμε input stream των 8 byte και το ‘ανεβάζουμε’ στον ftp server με όνομα “test.bin”. Ακόμα, συμπεριλάβαμε διάλογο ενημέρωσης προόδου, χρήσιμο για να ενημερώνεται ο χρήστης για την έναρξη και τη λήξη της επικοινωνίας με τον server. Στην περίπτωση αποτυχίας εμφανίζεται διακριτικό Toast με το μήνυμα “Σφάλμα”. Η διασύνδεση με το UI Thread επιτρέπεται στις μεθόδους onPreExecute και onPostExecute, αρκεί φυσικά το object του AsynTask να γνωρίζει το context, αυτός είναι και ο λόγος που διατίθεται σαν παράμετρος στον constructor του FtpAsyncTask. Κατά τ’άλλα, για όσους από εσάς είστε βιαστικοί, μπορείτε να παραλείψετε τόσο τον constructor, όσο και τις onPreExecute και onPostExecute.
Αν και μάλλον προφανείς, οι κλάσεις FtpConnection και FtpAction παρουσιάζονται παρακάτω για την ακεραιότητα του παρόντος. Και πάλι για εσάς τους βιαστικούς σημειώνουμε ότι μπορείτε να τις παραλείψετε και να “καρφώσετε” τις τιμές που θέλετε απευθείας στην FtpAsyncTask!
public class FtpConnection { public String server; public int port; public String username; public String password; } public class FtpAction { public FtpConnection connection; public String remoteFilename; public boolean success = false; }
Αφήστε μια απάντηση