Połączenie aplikacji mobilnej z zewnętrznym serwerem jest często czynnością niezbędną dla wielu usług, dlatego w niniejszym instruktażu zostanie przedstawione jak takie połączenie uzyskać przy wykorzystaniu podstawowych funkcji androida, servletów Javy oraz XML.
Poniżej został stworzony program który umożliwia wysłanie obiektu 'książki’ przez internet i otrzymaniu z powrotem jej zmodyfikowanej wersji. Zacznijmy więc od części serwerowej. Tworzymy prostą klasę odwzorowującą książkę:
public class Book {
private String title;
private String author;
private int pages;
private String ISBN;
private int rating;
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getAuthor() {
return author;
}
public void setAuthor(String author) {
this.author = author;
}
public int getPages() {
return pages;
}
public void setPages(int pages) {
this.pages = pages;
}
public String getISBN() {
return ISBN;
}
public void setISBN(String ISBN) {
this.ISBN = ISBN;
}
public int getRating() {
return rating;
}
public void setRating(int rating) {
this.rating = rating;
}
}
Oraz servlet który będzie obsługiwał zapytania przychodzące z androida:
@WebServlet(name = "androidServlet", urlPatterns = { "/index", "", "/androidServlet" })
public class androidServlet extends HttpServlet{
public androidServlet() {
super();
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
getDataFromAndroid(request,response);
}
public synchronized void getDataFromAndroid(HttpServletRequest request, HttpServletResponse response) {
try {
byte[] input = getInputData(request);
String recievedString = new String(input);
Book book = deserializeData(recievedString, Book.class);
Random rand = new Random();
book.setRating(rand.nextInt(10));
StringWriter bookWithRating = serializeData(book);
response.setStatus(HttpServletResponse.SC_OK);
OutputStreamWriter writer = new OutputStreamWriter(response.getOutputStream());
writer.write(bookWithRating.toString());
writer.flush();
writer.close();
} catch (IOException e) {
try {
response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
response.getWriter().print(e.getMessage());
response.getWriter().close();
} catch (IOException ioe) {
}
}
}
public synchronized byte[] getInputData(HttpServletRequest request) throws IOException {
int length = request.getContentLength();
byte[] input = new byte[length];
ServletInputStream sin = request.getInputStream();
int c, count = 0;
while ((c = sin.read(input, count, input.length - count)) != -1) {
count += c;
}
sin.close();
return input;
}
public synchronized <T> T deserializeData(String recievedString, Class<T> myclass) {
Serializer serializer = new Persister();
T readObjects = null;
try {
readObjects = serializer.read(myclass, recievedString);
} catch (Exception e) {
e.printStackTrace();
}
return readObjects;
}
public synchronized <T> StringWriter serializeData(T writeObjects){
Serializer serializer = new Persister();
StringWriter send = new StringWriter();
try {
serializer.write(writeObjects, send);
} catch (Exception e) {
e.printStackTrace();
}
return send;
}
}
Na samym początku metoda getInputData(request) pobiera wszystkie dane wejściowe wysłane przez androida w postaci bajowej. Następnie z tablicy bajtów tworzymy Stringa. Program w androidzie jest tak stworzony iż przesyłamy jedynie interesujący nasz xml dlatego możemy od razu otrzymane dane deserializować na obiekt książka. Metody serializacji i deserializacji zostały tak stworzone by w razie rozbudowy projektu możliwe było łatwe i szybkie serializowanie i deserializownie innych projektów. Następnie losujemy liczbę w zakresie od 0-10 i przypisujemy to jako ocene dopiero co otrzymanej książce. Na koniec ponownie serializujemy obiekt i przesyłamy go do androida.
Do serializacji i deserializacji obiektów na XML wykorzystuje bibliotekę simple dlatego jeśli chcemy uzyskać ten sam efekt nie zapomnijmy jej dodać w Mavenie
<dependency>
<groupId>org.simpleframework</groupId>
<artifactId>simple-xml</artifactId>
<version>2.7.1</version>
</dependency>
oraz gradle
compile 'org.simpleframework:2.7.1'
Niemniej nic nie stoi nam na przeszkodzie by korzystać np z JSONa.
Przejdźmy do części związanej z androidem. Tutaj również tworzymy identyczną klasę Book:
public class Book {
private String title;
private String author;
private int pages;
private String ISBN;
private int rating;
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getAuthor() {
return author;
}
public void setAuthor(String author) {
this.author = author;
}
public int getPages() {
return pages;
}
public void setPages(int pages) {
this.pages = pages;
}
public String getISBN() {
return ISBN;
}
public void setISBN(String ISBN) {
this.ISBN = ISBN;
}
public int getRating() {
return rating;
}
public void setRating(int rating) {
this.rating = rating;
}
}
Główną aktywność w której podajemy informacje o książce i w której otrzymamy informacje zwrotne:
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final EditText title = (EditText) findViewById(R.id.Title);
final EditText author = (EditText) findViewById(R.id.Author);
final EditText pages = (EditText) findViewById(R.id.Pages);
final EditText ISBN = (EditText) findViewById(R.id.ISBN);
final TextView rating = (TextView) findViewById(R.id.Rating);
Button send = (Button) findViewById(R.id.Send);
send.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
Book someBook = new Book();
someBook.setAuthor(author.getText().toString());
someBook.setTitle(title.getText().toString());
someBook.setPages(Integer.valueOf(pages.getText().toString()));
someBook.setAuthor(ISBN.getText().toString());
new sendUrlToServlet(MainActivity.this, someBook, rating).execute();
}
});
}
}
<LinearLayout xmlns:android="https://schemas.android.com/apk/res/android"
xmlns:tools="https://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context="com.example.android_connector.MainActivity" >
<EditText
android:id="@+id/Title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="Title" />
<EditText
android:id="@+id/Author"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="Author" />
<EditText
android:id="@+id/Pages"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="Pages" />
<EditText
android:id="@+id/ISBN"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="ISBN" />
<Button
android:id="@+id/Send"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Send" />
<TextView
android:id="@+id/Rating"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="30sp" />
</LinearLayout>
Oraz AsyncTask który wykonuję większość pracy:
class sendUrlToServlet extends AsyncTask<Integer, Void, Integer> {
private Context con;
private Book book;
private Book newBook;
private TextView resultTextView;
sendUrlToServlet(Context con,Book book, TextView resultTextView) {
this.con = con;
this.book = book;
this.resultTextView = resultTextView;
}
protected void onPreExecute() {
super.onPreExecute();
}
protected void onPostExecute(Integer result) {
resultTextView.setText("Book " + newBook.getTitle() + " acquired Rating of " + newBook.getRating());
}
@Override
protected Integer doInBackground(Integer... params) {
HttpURLConnection connection = null;
try {
URL url = new URL("https://android-baseconnection.rhcloud.com");
connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod("POST");
connection.setRequestProperty("Content-Type", "text/xml; charset=utf-8");
sendData(connection);
reciveData(connection);
} catch (Exception e) {
} finally {
try {
System.out.println(connection.getResponseCode());
} catch (IOException e) {
e.printStackTrace();
}
connection.disconnect();
}
return null;
}
void sendData(HttpURLConnection connection) throws Exception{
Serializer serializer = new Persister();
StringWriter send = new StringWriter();
serializer.write(book, send);
BufferedOutputStream out = new BufferedOutputStream(connection.getOutputStream());
out.write(send.toString().getBytes());
out.flush();
out.close();
}
void reciveData(HttpURLConnection connection) throws Exception {
BufferedReader in = new BufferedReader(new InputStreamReader(connection.getInputStream()));
String returnString = "";
StringBuilder allData = new StringBuilder("");
while ((returnString = in.readLine()) != null) {
allData.append(returnString);
}
in.close();
System.out.println(allData.toString());
Serializer serializer = new Persister();
newBook = serializer.read(Book.class,allData.toString());
}
}
W głównej klasie nie ma wielu niezwykłych funkcji pobieramy tam podstawowe informacje o książce przy kliknięciu zaś buttona uruchamiamy zadanie asynchroniczne odpowiedzialne za komunikacje z servletem. Tworzymy obiekt url który określa adres z jakim chcemy się połączyć oraz parametry połączenia. W przypadku wysyłania informacji serializujemy obiekt książka i przesyłamy go w postaci bajtowej przez strumień wyjściowy. Analogicznie postępujemy w przypadku otrzymywania danych z serwera.
Przykładowy wynik programu możemy obejrzeć poniżej dane o książce został wysłane na serwer a tam korzystając z losowej liczby książka ta została oceniona i z powrotem przesłana do naszej aplikacji: