Understanding AsyncTask – Once and Forever

Motivation

Android modifies the user interface via one thread, the so called UI Thread. If you perform a long running operation directly on the UI Thread, for example downloading a file from the internet, the user interface of your application will “freeze” until the corresponding task is finished. When this happens it is very easy for the user to perceive your application as slow.

As a concrete example of a bad implementation and what happens when a long running operation is done on the UI Thread, I want to refer to one of my previous tutorials: Creating A Simple RSS Application in Android. Well, that application is working fine, and it does what it is supposed to do – parse an XML feed and display the headlines in a ListView. The “vulnerability” of that application is that the network access is done directly on the UI Thread which makes the application to “freeze” while the XML feed is downloaded (take a look at point number 5 to see).
When I created that tutorial I wanted to make it as simple as possible without dealing with more advanced topics like asynchronous tasks. The intent of tutorial was to show the working process with feeds on a high level. But I promise you, by the end of this article you will be able to fix it and have a Cool Rss App that runs smoothly! 🙂

To provide a good user experience all long running operations in an Android application should run asynchronously. To achieve this we will be using the AsyncTask class.

What does AsyncTask do?

AsyncTask enables proper and easy use of the UI thread. This class allows to perform background operations and publish results on the UI thread without having to manipulate threads and/or handlers. 

In order to use the AsyncTask class, you must extend it and override at least thedoInBackground() method.

The most common methods you will need to implement are these:

   1. onPreExecute() – called on the UI thread before the thread starts running. This method is usually used to setup the task, for example by displaying a progress bar.

   2. doInBackground(Params…) – this is the method that runs on the background thread. In this method you should put all the code you want the application to perform in background. Referring to our Simple RSS Aplication, you would put here the code that downloads the XML feed and does the parsing. The doInBackground() is called immediately after onPreExecute(). When it finishes, it sends the result to the onPostExecute().

   3. onProgressUpdate() – called when you invoke publishProgress() in the doInBackground().

   4. onPostExecute(Result) – called on the UI thread after the background thread finishes. It takes as parameter the result received from doInBackground().

AsyncTask is a generic class, it uses 3 types: AsyncTask<Params, Progress, Result>.

  1. Params – the input. what you pass to the AsyncTask
  2. Progress – if you have any updates, passed to onProgressUpdate()
  3. Result – the output. what returns doInBackground()

Once a task is created, it can be executed like this:
new DownloadTast().execute(url1, url2, urln);

Code Example

This is a simple skeleton of an AsyncTask implementation.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
public class AsyncTaskTestActivity extends Activity {
   @Override
   public void onCreate(Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);
      setContentView(R.layout.main);
      //Starting the task. Pass an url as the parameter.
      new PostTask().execute("http://feeds.pcworld.com/pcworld/latestnews");
   }
   // The definition of our task class
   private class PostTask extends AsyncTask<String, Integer, String> {
   @Override
   protected void onPreExecute() {
      super.onPreExecute();
      displayProgressBar("Downloading...");
   }
   @Override
   protected String doInBackground(String... params) {
      String url=params[0];
      // Dummy code
      for (int i = 0; i <= 100; i += 5) {
        try {
          Thread.sleep(50);
        } catch (InterruptedException e) {
          e.printStackTrace();
        }
         publishProgress(i);
      }
      return "All Done!";
   }
   @Override
   protected void onProgressUpdate(Integer... values) {
      super.onProgressUpdate(values);
      updateProgressBar(values[0]);
   }
   @Override
   protected void onPostExecute(String result) {
      super.onPostExecute(result);
      dismissProgressBar();
   }
   }
}

AsyncTasks are great for performing tasks in a separate thread, they have however one weakness. While the AsyncTask is in the middle of the work and the screen of device is rotated, you’ll notice that the application crashes. This happens because when rotating the device screen a configuration change occurs, which will trigger the Activity to restart. The AsyncTask reference to the Activity is invalid, an onPostExecute() will have no effect on the new Activity. How to handle this sort of issues is described in: Dealing with AsyncTask and Screen Orientation, which I highly recommend reading it if you are concerned to deliver stable Android applications.

How to build a server in Java: Allowing multiple users to connect

Servers with multiple connections

Right now, our server allows one user to connect and then just prints out what that user has sent to the server. Now that’s cool but it isn’t really very useful. Wouldn’t it be awesome if multiple connections would be allowed and we could actually chat? Well then, let’s do it!

If you haven’t already please check out Part 1 and Part 2 of this tutorial series.

So what do we need to add?

Well, in order to allow multiple connections to our server we need to add something called threads. Threads essentially are the things that allow multitasking on our computers and let us run more than one thing at a time. For example, you can listen to music while writing your essay, there is a thread running the music player and a thread running your word processor and then can perform their own functions separately. So, we can set up a thread for each person who connects to our server which will allow us to have tons of people to connect to our server. If you haven’t the slightest clue what a thread is then unfortunately this tutorial might not work out so well for you, try researching threads online (there are tons of tutorials out there about them) and then come back here.

Threads

Here is some code that will show you how we will be using threads to allow multiple connections:

1
2
3
Socket socket = sSocket.accept();
Thread socketThread = new ThreadClass(socket);
socketThread.start();

We will run this code every time someone connects and this will essentially place their connection in it’s own thread. This thread will handle everything for that one client. Unfortunately we can’t just say “RUN THE SOCKET” and it will suddenly magically receive connections from the client, print them out, and send them to all the other clients. So, we essentially create our own custom thread. Here is some code that will outline how this will work:

//We need to use "implements Runnable to tell Java that this is a thread
 class ClientThread implements Runnable {
 //This run method is what is executed when the thread starts
 public void run()
 {
 //Set up the PrintWriter and BufferedReader here
 while(true) {
 //Get info sent from client
 String clientInput = input.nextLine();
 //Here would would have a for loop that would send the
 //client's message to every other client connected.
 }
 }
 }
Okay, now that you know a little about threads and what we’re going to do with them lets redo the server code.

The New Server Code

import java.net.*;
import java.util.*;
import java.io.*;

public class Server
{
    public static void main(String[] args)
    {
        new Server();
    }

    public Server()
    {
        //We need a try-catch because lots of errors can be thrown
        try {
            ServerSocket sSocket = new ServerSocket(5000);
            System.out.println("Server started at: " + new Date());


            //Loop that runs server functions
            while(true) {
                //Wait for a client to connect
                Socket socket = sSocket.accept();



                //Create a new custom thread to handle the connection
                ClientThread cT = new ClientThread(socket);

                //Start the thread!
                new Thread(cT).start();

            }
        } catch(IOException exception) {
            System.out.println("Error: " + exception);
        }
    }

    //Here we create the ClientThread inner class and have it implement Runnable
    //This means that it can be used as a thread
    class ClientThread implements Runnable
    {
        Socket threadSocket;

        //This constructor will be passed the socket
        public ClientThread(Socket socket)
        {
            //Here we set the socket to a local variable so we can use it later
            threadSocket = socket;
        }

        public void run()
        {
            //All this should look familiar
            try {
                //Create the streams
                PrintWriter output = new PrintWriter(threadSocket.getOutputStream(), true);
                BufferedReader input = new BufferedReader(new InputStreamReader(threadSocket.getInputStream()));

                //Tell the client that he/she has connected
                output.println("You have connected at: " + new Date());

                while (true) {
                    //This will wait until a line of text has been sent
                    String chatInput = input.readLine();
                    System.out.println(chatInput);
                }
            } catch(IOException exception) {
                System.out.println("Error: " + exception);
            }
        }
    }
}

The Explanation

So for this new code you can see that we only have to change the server’s code. You can use the client code from the last tutorial to connect to the server. The main thing now is that, each time a socket connection is accepted, that socket is passed off to a thread which runs the socket. Once that socket has been passed off to the thread the server then waits for a new socket connection.

Defining and Starting a Thread

An application that creates an instance of Thread must provide the code that will run in that thread. There are two ways to do this:

  • Provide a Runnable object. The Runnable interface defines a single method, run, meant to contain the code executed in the thread. The Runnable object is passed to the Thread constructor, as in the HelloRunnable example:
    public class HelloRunnable implements Runnable {
    
        public void run() {
            System.out.println("Hello from a thread!");
        }
    
        public static void main(String args[]) {
            (new Thread(new HelloRunnable())).start();
        }
    
    }
    
  • Subclass Thread. The Thread class itself implements Runnable, though its run method does nothing. An application can subclassThread, providing its own implementation of run, as in the HelloThread example:
    public class HelloThread extends Thread {
    
        public void run() {
            System.out.println("Hello from a thread!");
        }
    
        public static void main(String args[]) {
            (new HelloThread()).start();
        }
    
    }
    

Notice that both examples invoke Thread.start in order to start the new thread.

Which of these idioms should you use? The first idiom, which employs a Runnable object, is more general, because the Runnable object can subclass a class other than Thread. The second idiom is easier to use in simple applications, but is limited by the fact that your task class must be a descendant of Thread. This lesson focuses on the first approach, which separates the Runnable task from the Thread object that executes the task. Not only is this approach more flexible, but it is applicable to the high-level thread management APIs covered later.

The Thread class defines a number of methods useful for thread management. These include static methods, which provide information about, or affect the status of, the thread invoking the method. The other methods are invoked from other threads involved in managing the thread and Thread object. We’ll examine some of these methods in the following sections.

What Is a Socket?

Normally, a server runs on a specific computer and has a socket that is bound to a specific port number. The server just waits, listening to the socket for a client to make a connection request.

On the client-side: The client knows the hostname of the machine on which the server is running and the port number on which the server is listening. To make a connection request, the client tries to rendezvous with the server on the server’s machine and port. The client also needs to identify itself to the server so it binds to a local port number that it will use during this connection. This is usually assigned by the system.

A client's connection requestIf everything goes well, the server accepts the connection. Upon acceptance, the server gets a new socket bound to the same local port and also has its remote endpoint set to the address and port of the client. It needs a new socket so that it can continue to listen to the original socket for connection requests while tending to the needs of the connected client.

The connection is madeOn the client side, if the connection is accepted, a socket is successfully created and the client can use the socket to communicate with the server.

The client and server can now communicate by writing to or reading from their sockets.


Definition:A socket is one endpoint of a two-way communication link between two programs running on the network. A socket is bound to a port number so that the TCP layer can identify the application that data is destined to be sent to.


An endpoint is a combination of an IP address and a port number. Every TCP connection can be uniquely identified by its two endpoints. That way you can have multiple connections between your host and the server.

The java.net package in the Java platform provides a class, Socket, that implements one side of a two-way connection between your Java program and another program on the network. The Socket class sits on top of a platform-dependent implementation, hiding the details of any particular system from your Java program. By using the java.net.Socket class instead of relying on native code, your Java programs can communicate over the network in a platform-independent fashion.

Additionally, java.net includes the ServerSocket class, which implements a socket that servers can use to listen for and accept connections to clients. This lesson shows you how to use the Socket and ServerSocket classes.

If you are trying to connect to the Web, the URL class and related classes (URLConnection, URLEncoder) are probably more appropriate than the socket classes. In fact, URLs are a relatively high-level connection to the Web and use sockets as part of the underlying implementation. See Working with URLs for information about connecting to the Web via URLs.

Network Service Discovery / Bonjour – Client

Last post we set up a Android Network Service Discovery server that will receive text messages from our client. Now we are ready to create the client that we will use to create the text messages and send to the server.

When creating the project choose Sdk version 16, Jelly Bean 4.1.

or

Edit the Manifest first to avoid errors when creating the MainActivity class.

Replace minimum with android:minSdkVersion=”16″

and add

1
2
"true" android:name="android.permission.INTERNET"/>
    "android.permission.ACCESS_WIFI_STATE" />

This will all be done in one file for simplicity.

 import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.InetAddress;
import java.net.Socket;
 
import org.json.JSONException;
import org.json.JSONObject;
 
import android.app.Activity;
import android.content.Context;
import android.net.nsd.NsdManager;
import android.net.nsd.NsdServiceInfo;
import android.net.wifi.WifiManager;
import android.os.AsyncTask;
import android.os.Bundle;
import android.text.format.Formatter;
import android.util.Log;
import android.view.Menu;
import android.widget.Toast;
 
public class MainActivity extends Activity {
 
 private String SERVICE_NAME = "Client Device";
 private String SERVICE_TYPE = "_letstalk._tcp.";
 
 private InetAddress hostAddress;
 private int hostPort;
 private NsdManager mNsdManager;
 
 private int SocketServerPort = 6000;
 private static final String REQUEST_CONNECT_CLIENT = "request-connect-client";
 
 private static final String TAG = "NSDClient";
 
 @Override
 protected void onCreate(Bundle savedInstanceState) {
 super.onCreate(savedInstanceState);
 setContentView(R.layout.main);
 
 mNsdManager = (NsdManager) getSystemService(Context.NSD_SERVICE);
 mNsdManager.discoverServices(SERVICE_TYPE,
 NsdManager.PROTOCOL_DNS_SD, mDiscoveryListener);
 }
 
 NsdManager.DiscoveryListener mDiscoveryListener = new NsdManager.DiscoveryListener() {
 
 // Called as soon as service discovery begins.
 @Override
 public void onDiscoveryStarted(String regType) {
 Log.d(TAG, "Service discovery started");
 }
 
 @Override
 public void onServiceFound(NsdServiceInfo service) {
 // A service was found! Do something with it.
 Log.d(TAG, "Service discovery success : " + service);
 Log.d(TAG, "Host = "+ service.getServiceName());
 Log.d(TAG, "port = " + String.valueOf(service.getPort()));
 
 if (!service.getServiceType().equals(SERVICE_TYPE)) {
 // Service type is the string containing the protocol and
 // transport layer for this service.
 Log.d(TAG, "Unknown Service Type: " + service.getServiceType());
 } else if (service.getServiceName().equals(SERVICE_NAME)) {
 // The name of the service tells the user what they'd be
 // connecting to. It could be "Bob's Chat App".
 Log.d(TAG, "Same machine: " + SERVICE_NAME);
 } else {
 Log.d(TAG, "Diff Machine : " + service.getServiceName());
 // connect to the service and obtain serviceInfo
 mNsdManager.resolveService(service, mResolveListener);
 }
 }
 
 @Override
 public void onServiceLost(NsdServiceInfo service) {
 // When the network service is no longer available.
 // Internal bookkeeping code goes here.
 Log.e(TAG, "service lost" + service);
 }
 
 @Override
 public void onDiscoveryStopped(String serviceType) {
 Log.i(TAG, "Discovery stopped: " + serviceType);
 }
 
 @Override
 public void onStartDiscoveryFailed(String serviceType, int errorCode) {
 Log.e(TAG, "Discovery failed: Error code:" + errorCode);
 mNsdManager.stopServiceDiscovery(this);
 }
 
 @Override
 public void onStopDiscoveryFailed(String serviceType, int errorCode) {
 Log.e(TAG, "Discovery failed: Error code:" + errorCode);
 mNsdManager.stopServiceDiscovery(this);
 }
 };
 
 NsdManager.ResolveListener mResolveListener = new NsdManager.ResolveListener() {
 
 @Override
 public void onServiceResolved(NsdServiceInfo serviceInfo) {
 Log.d(TAG, "Resolve Succeeded. " + serviceInfo);
 
 if (serviceInfo.getServiceName().equals(SERVICE_NAME)) {
 Log.d(TAG, "Same IP.");
 return;
 }
 
 // Obtain port and IP
 hostPort = serviceInfo.getPort();
 hostAddress = serviceInfo.getHost();
 
 /* Once the client device resolves the service and obtains
 * server's ip address, connect to the server and send data
 */
 
 connectToHost();
 }
 
 
 @Override
 public void onResolveFailed(NsdServiceInfo serviceInfo, int errorCode) {
 // Called when the resolve fails. Use the error code to debug.
 Log.e(TAG, "Resolve failed " + errorCode);
 Log.e(TAG, "serivce = " + serviceInfo);
 }
 };
 
 private void connectToHost() {
 
 if (hostAddress == null) {
 Log.e(TAG, "Host Address is null");
 return;
 }
 
 String ipAddress = getLocalIpAddress();
 JSONObject jsonData = new JSONObject();
 
 try {
 jsonData.put("request", REQUEST_CONNECT_CLIENT);
 jsonData.put("ipAddress", ipAddress);
 } catch (JSONException e) {
 e.printStackTrace();
 Log.e(TAG, "can't put request");
 return;
 }
 
 new SocketServerTask().execute(jsonData);
 }
 
 private String getLocalIpAddress() {
 WifiManager wm = (WifiManager) getSystemService(WIFI_SERVICE);
 String ip = Formatter.formatIpAddress(wm.getConnectionInfo().getIpAddress());
 return ip;
 }
 
 private class SocketServerTask extends AsyncTask<JSONObject, Void, Void> {
 private JSONObject jsonData;
 private boolean success;
 
 @Override
 protected Void doInBackground(JSONObject... params) {
 Socket socket = null;
 DataInputStream dataInputStream = null;
 DataOutputStream dataOutputStream = null;
 jsonData = params[0];
 
 try {
 // Create a new Socket instance and connect to host
 socket = new Socket(hostAddress, SocketServerPort);
 
 dataOutputStream = new DataOutputStream(
 socket.getOutputStream());
 dataInputStream = new DataInputStream(socket.getInputStream());
 
 // transfer JSONObject as String to the server
 dataOutputStream.writeUTF(jsonData.toString());
 Log.i(TAG, "waiting for response from host");
 
 // Thread will wait till server replies
 String response = dataInputStream.readUTF();
 if (response != null && response.equals("Connection Accepted")) {
 success = true;
 } else {
 success = false; 
 }
 
 } catch (IOException e) {
 e.printStackTrace();
 success = false;
 } finally {
 
 // close socket
 if (socket != null) {
 try {
 Log.i(TAG, "closing the socket");
 socket.close();
 } catch (IOException e) {
 e.printStackTrace();
 }
 }
 
 // close input stream
 if (dataInputStream != null) {
 try {
 dataInputStream.close();
 } catch (IOException e) {
 e.printStackTrace();
 }
 }
 
 // close output stream
 if (dataOutputStream != null) {
 try {
 dataOutputStream.close();
 } catch (IOException e) {
 e.printStackTrace();
 }
 }
 }
 return null;
 }
 
 @Override
 protected void onPostExecute(Void result) {
 if (success) {
 Toast.makeText(MainActivity.this, "Connection Done", Toast.LENGTH_SHORT).show();
 } else {
 Toast.makeText(MainActivity.this, "Unable to connect", Toast.LENGTH_SHORT).show();
 }
 }
 }
 
 protected void onPuase() {
 if (mNsdManager != null) {
 mNsdManager.stopServiceDiscovery(mDiscoveryListener);
 }
 super.onPause();
 }
 
 @Override
 protected void onResume() {
 super.onResume();
 if (mNsdManager != null) {
 mNsdManager.discoverServices(
 SERVICE_TYPE, NsdManager.PROTOCOL_DNS_SD, mDiscoveryListener);
 }
 
 }
 
 @Override
 protected void onDestroy() {
 if (mNsdManager != null) {
 mNsdManager.stopServiceDiscovery(mDiscoveryListener);
 }
 super.onDestroy();
 }
 
}

SERVICE_NAME is a constant that we use to set the device name, the Google example doesn’t use these it picks the name for you which is the service name if more than one device has the same service name, which it will, the 2nd device has a (1) added to it and the 3rd would have (2) added to it and so on. Not sure if this is what made the Google version so flaky so I don’t use it, I give the server a name and the client a name, and use that.

SERVICE_TYPE is a custom name you make up (that must conform to a standard). This is the name of your app on the network, it doesn’t have to be the same name as your app, just every device that you want to see each other must use the same one. The service type specifies which protocol and transport layer the application uses. The syntax is “_[protocol]._[transportlayer].” You can name the protocol anything you want but leave the transportlayer the way it is.

Note: If you plan on publishing an app to the app store that uses NSD you should register your protocol to the International Assigned Numbers Authority (IANA). They manage a centralized, authoritative list of service types used by service discovery protocols such as NSD and Bonjour. If you intend to use a new service type, you should reserve it by filling out the IANA Ports and Service registration form.

REQUEST_CONNECT_CLIENT is a constant we use to tell the server what we want to do. At first this will be the only option, but later we will add a display message option.

Now with our tools laid out let’s walk through the logic.

We display our screen with setContentView(R.layout.main);

We create an instance of NsdManager called mNsdManager to use to discover and connect to our server.

We then use NsdManager to discover our service (and server) on the network using our SERVICE_TYPE, we define what type of protocol we are using (for NSD you use NsdManager.PROTOCOL_DNS_SD) and where to go after the service is successfully registered (a interface callback we create called mDiscoveryListener).

Next we define, initialize and implement the DiscoveryListener interface call back. We use this to get all the devices that have registered the service. Since it is an interface we have methods() we must implement (@Override) to receive the NsdServiceInfo for all the servers we discovered.

The methods are:

public void onDiscoveryStarted(String regType) {}
public void onServiceFound(NsdServiceInfo service) {}
public void onServiceLost(NsdServiceInfo service) {}
public void onDiscoveryStopped(String serviceType) {}
public void onStartDiscoveryFailed(String serviceType, int errorCode) {}
public void onStopDiscoveryFailed(String serviceType, int errorCode) {}

The second one is the only one we will really use, the rest are used for logging purposes.

@Override
public void onServiceFound(NsdServiceInfo service) {
    // A service was found! Do something with it.
    Log.d(TAG, "Service discovery success : " + service);
    Log.d(TAG, "Host = "+ service.getServiceName());
    Log.d(TAG, "port = " + String.valueOf(service.getPort()));

    if (!service.getServiceType().equals(SERVICE_TYPE)) {
        // Service type is the string containing the protocol and
        // transport layer for this service.
        Log.d(TAG, "Unknown Service Type: " + service.getServiceType());
    } else if (service.getServiceName().equals(SERVICE_NAME)) {
        // The name of the service tells the user what they'd be
        // connecting to. It could be "Bob's Chat App".
        Log.d(TAG, "Same machine: " + SERVICE_NAME);
    } else {
        Log.d(TAG, "Diff Machine : " + service.getServiceName());
        // connect to the service and obtain serviceInfo
        mNsdManager.resolveService(service, mResolveListener);
    }
}

When any service is found (even if it is not ours we show all the NsdServiceInfo that we discovered and then we point out the service name (which is “Server Device” for what we are looking for) and port number for the service, even if it is not ours (if you have printers on the network or webcams you will see those here too).

Now we start parsing the NsdServiceInfo to find what we want. First we check the SERVICE_TYPE if it is NOT (notice the ! at the beginning of the if statement) _letstalk._tcp. then we log it and keep discovering.

If the SERVICE_TYPE IS _letstalk._tcp. we check the SERVICE_NAME. If the SERVICE_NAME is the same as ours, Client Device, we know we have discovered ourself, so we keep looking.

If the SERVICE_NAME is different than we have found another device that we can connect to. In our case it should be the server. This is where you would create a List if you wanted to make a list of several devices you could connect to, but for simplicity sake if we find our server we immediately resolve the service so we can connect.

To resolve the service we use our instance of NsdManager to start the resolveService() method which takes two parameters the first is the NsdServiceInfo of the device we are wanting to resolve(get IPAddress for), and the second parameter is where to send that information when we get it, which is the ResolveListener interface call back.

Now we define, initialize and implement the ResolveListener interface call back. It has two methods we must implement.

public void onServiceResolved(NsdServiceInfo serviceInfo) {}
public void onResolveFailed(NsdServiceInfo serviceInfo, int errorCode) {}

onServiceResolved(NsdServiceInfo serviceInfo) is the one we care about, it gives us the NsdServiceInfo that includes the server IP address this time.

First we double check to make sure we didn’t get our own device’s address by checking the SERVICE_NAME, if we did we return, which goes back to resolving the next device we found.

If we did get the information for our server, we set our local variables that we will use to connect to the server, hostPort is the server port number and hostAddress is the servers IP address.

1
2
3
// Obtain port and IP
hostPort = serviceInfo.getPort();
hostAddress = serviceInfo.getHost();

We then run a custom method(), connectToHost();


private void connectToHost() {

    if (hostAddress == null) {
        Log.e(TAG, "Host Address is null");
        return;
    }

    String ipAddress = getLocalIpAddress();
    JSONObject jsonData = new JSONObject();

    try {
        jsonData.put("request", REQUEST_CONNECT_CLIENT);
        jsonData.put("ipAddress", ipAddress);
    } catch (JSONException e) {
        e.printStackTrace();
        Log.e(TAG, "can't put request");
        return;
    }

    new SocketServerTask().execute(jsonData);
}

We double check to make sure we have a IP address to connect to, if we don’t we jump out of the connectToHost() method so we don’t try to connect and crash.

If we do have a IP Address we run another custom method(), getLocalIpAddress(); Let’s jump there and then come back.

1
2
3
4
5
private String getLocalIpAddress() {
    WifiManager wm = (WifiManager) getSystemService(WIFI_SERVICE);
    String ip = Formatter.formatIpAddress(wm.getConnectionInfo().getIpAddress());
    return ip;
}

First of all when you get any kind of Wifi information you are going to need request permissions from the user which means we will need a permission in the Manifest file.

1
"android.permission.ACCESS_WIFI_STATE" />

Then in the getLocalIpAddress() method we initialize our WifiManager which we will use to get our local IP address, wm.getConnectionInfo().getIpAddress().

Then we take that IP address that is not in a format we can use and run it through the Formatter class which contains a method() that turns the IP Address we retrieved into a String that we can use, Formatter.formatIpAddress().

And we send it back to the connectToHost() method and save it as ipAddress.

Now we are back in the connectToHost() method, we create a JSONObject that we can use to send data to the server.

There are lots of things we could do wrong, so we surround our actions with a try and catch and then start to put data in our JSONObject (jsonData). We use (key, value) pairs to do this so the command ends up looking like this:

1
2
jsonData.put("request", REQUEST_CONNECT_CLIENT);
jsonData.put("ipAddress", ipAddress);

The request “key” contains our REQUEST_CONNECT_CLIENT “value” which is request-connect-client. This will tell the server that we want to connect.

We also send a ipAddress “key” that contains our ipAddress “value” which is the IP address of our device.

We catch our JSONObject errors and print out our stack trace (e.printStackTrace();) if there were any, and jump out of our try with return;

But for fun’s sake, let’s say it worked and then we create a new custom Thread called SocketServerTask() and run it with our jsonData (.execute(jsonData);).

1
new SocketServerTask().execute(jsonData);

makes this:


private class SocketServerTask extends AsyncTask<JSONObject, Void, Void> {
    private JSONObject jsonData;
    private boolean success;

    @Override
    protected Void doInBackground(JSONObject... params) {
        Socket socket = null;
        DataInputStream dataInputStream = null;
        DataOutputStream dataOutputStream = null;
        jsonData = params[0];

        try {
            // Create a new Socket instance and connect to host
            socket = new Socket(hostAddress, SocketServerPort);

            dataOutputStream = new DataOutputStream(
                    socket.getOutputStream());
            dataInputStream = new DataInputStream(socket.getInputStream());

            // transfer JSONObject as String to the server
            dataOutputStream.writeUTF(jsonData.toString());
            Log.i(TAG, "waiting for response from host");

            // Thread will wait till server replies
            String response = dataInputStream.readUTF();
            if (response != null && response.equals("Connection Accepted")) {
                success = true;
            } else {
                success = false;
            }

        } catch (IOException e) {
            e.printStackTrace();
            success = false;
        } finally {

            // close socket
            if (socket != null) {
                try {
                    Log.i(TAG, "closing the socket");
                    socket.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }

            // close input stream
            if (dataInputStream != null) {
                try {
                    dataInputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }

            // close output stream
            if (dataOutputStream != null) {
                try {
                    dataOutputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return null;
    }

    @Override
    protected void onPostExecute(Void result) {
        if (success) {
            Toast.makeText(MainActivity.this, "Connection Done", Toast.LENGTH_SHORT).show();
        } else {
            Toast.makeText(MainActivity.this, "Unable to connect", Toast.LENGTH_SHORT).show();
        }
    }
}

As you noticed probably quite quickly we are using a AsyncTask class to send the data. AsyncTask is not a bare bones Thread it is actually a “helper” class that uses a Thread to do it’s work

Some detailed, but very good documentation from googles AsyncTask web page.

AsyncTask enables proper and easy use of the UI thread. This class allows to perform background operations and publish results on the UI thread without having to manipulate threads and/or handlers.

AsyncTask is designed to be a helper class around Thread and Handler and does not constitute a generic threading framework. AsyncTasks should ideally be used for short operations (a few seconds at the most.) If you need to keep threads running for long periods of time, it is highly recommended you use the various APIs provided by the java.util.concurrent package such as Executor, ThreadPoolExecutor and FutureTask.

An asynchronous task is defined by a computation that runs on a background thread and whose result is published on the UI thread. An asynchronous task is defined by 3 generic types, called Params, Progress and Result, and 4 steps, called onPreExecute, doInBackground, onProgressUpdate and onPostExecute.

So in summary, AsyncTask uses a Thread and Handler, so you don’t have to, and posts it’s results to the UI Thread. It is also best for short tasks, like sending data, not listening for connections, like the server does.

private class SocketServerTask extends AsyncTask<JSONObject, Void, Void> {

The three parameters are used like this:

JSONObject is the type of Object we are passing in.
The first Void is the type of value you want to pass back to calculate the progress bar.
The second Void is the type of value you want to send back to the UI Thread to display.

Since we are using the AsyncTask to only do work, because what we are doing is going to happen so fast, we won’t be returning any values for the progress bar, and we don’t need to update the UI Thread because we will know it’s complete when the song starts playing.

We use the boolean value (true or false) success in multiple methods() in the SocketServerTask class so we define the variable here so we can get to it. We set it in our doInBackground() method and we retrieve it in our onPostExecute() method.

When we run the .execute() method we are actually running:

protected Void doInBackground(JSONObject... params) {}

protected means that it can not be called from outside the SocketServerTask class.

Void means we will not be passing any values to the onPostExecute() method. And also means we will be returning null from the doInBackground() method.

JSONObject… The … means array, so the doInBackground() method can take several JSONObjects in an array format.

params is the JSONObject (jsonData) we passed in from the connectToHost() method in the .execute(jsonData); command.

Now we do the same think like we did in our Server Thread:

We initialize our Socket, used to communicate with the server, and set it’s value to null. You may wonder why we initialize it here instead of farther down when we give it an actual value. When we are done using the socket we are going to try to close it from the finally {} section and if we don’t initialize it here we won’t be able to “see” it.

We initialize our DataInputStream, that receives incoming data, and set it to null.

We initialize our DataOutputStream, used to send data to clients, and set it to null.

We create a JSONObject jsonData and set it to the value in index 0 (the first value), which was passed in the params array of JSONObjects.

protected Void doInBackground(JSONObject... params) {}

Now we are about to do some pretty complicated stuff, Android makes it pretty easy, but if everything is not set up just right, it could easily fail, so we use a try and catch.

We create a new socket to talk to the server using the server IP Address we acquired and a port that we manually agreed upon.

Once we have the socket we can use it to create a DataInputStream and a DataOutputStream.

Now we can send data to the server.

dataOutputStream.writeUTF(jsonData.toString());

We take our dataOutputStream and write to the socket in a UTF-8 format which is a way to format text similar to the .xml files used for our layout objects, you will notice in the main.xml file at the beginning:

We convert our jsonData Object to a String, not exactly sure what it would look like, but it will contain two key/value pairs something like request request-connect-client ipAddress 192.168.1.98

then we run

String response = dataInputStream.readUTF();

Which blocks the Thread (which would cause our UI Thread to crash if we were running it there) and we wait until the server responds. Once we have a response, since we know it is only returning one string, not a JSONObject or any other object we can just compare the response to a text string.

if (response != null && response.equals(“Connection Accepted”)) {

We could have used a constant like:

final String CONNECTION_VALUE = “Connection Accepted” and then we could check if:

if (response != null && response.equals(CONNECTION_VALUE)) {

but that is a little more than we need at this point.

If the response was “Connection Accepted” then everything was successful and we can set the boolean value success to true, if the response didn’t equal “Connection Accepted” then we set the boolean value success to false.

Then we have our catch statement if something went wrong while performing I/O Input/Output and we can print our stackTrace if needed.

Finally which means after the Thread has performed it’s task we can try to close our socket, DataInputStream and our DataOutputStream. If we run into issues we catch the IOExceptions and print out our stackTrace for debugging.

After all that we have to return something because doInBackground() must have a return value, we are able to set it to Void like we did in this instance as a work around, but we still have to return something so we return null; which doesn’t go anywhere. Since we are returning Void the parameter we take in on the onPostExecute() method needs to be Void so it doesn’t expect a real value.

1
2
3
4
5
6
7
8
@Override
        protected void onPostExecute(Void result) {
            if (success) {
                Toast.makeText(MainActivity.this, "Connection Done", Toast.LENGTH_SHORT).show();
            } else {
                Toast.makeText(MainActivity.this, "Unable to connect", Toast.LENGTH_SHORT).show();
            }
        }

Then we check our success variable and Toast a message to the screen accordingly.

For a better example of the onPostExecute() method let’s say that we are returning a Bitmap from the doInBackground() method.

This:

private class SocketServerTask extends AsyncTask<JSONObject, Void, Void> {

Would become this:

private class SocketServerTask extends AsyncTask<JSONObject, Void, Bitmap> {

This:

protected Void doInBackground(JSONObject... params) {

Would become this:

protected Bitmap doInBackground(JSONObject... params) {

This:

return null;

Would become this:

return bitmap; //bitmap being a Bitmap Object

This:

protected void onPostExecute(Void result) {

Would become this:

protected void onPostExecute(Bitmap result) {

To finish out this project we define the onPause(), onResume(), onDestroy() methods. These are all protected so you can only run them from this class. They all make sure mNsdManager has been created because if you try to stop a service when it doesn’t exist the program will crash.

onPause() will stop service discovery because if the app is not running there is no need to waste resources discovering things we won’t use (memory leak). Then the default onPause() method will run.

onResume() will continue discovering services, and reconnect to our server in this case, on resuming, after the default onResume() method runs.

onDestroy() will stop service discovery because if the app is not running there is no need to waste resources discovering things we won’t use (memory leak). Then the default onDestroy() method will run.

Here is the main.xml file

    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:gravity="center"
    android:orientation="vertical" >

Now you can compile and run the server on one device and the client on the other, make sure both devices are on the same network. When you run the client you should see Toasts appear on both devices.

For my next post I will add the ability to send text from the client to the server to this project.

Network Service Discovery / Bonjour – Server

Before we get to the heart of this app, I will do a couple little apps that help you understand how to use NSD a little easier, Google’s example (nsdchat) is a full blown app with both the server and the client in the same one, along with fragments, classes buried in classes so the server can use the same logic as the client, it uses a Runnable Thread class which doesn’t handle reconnections (when orientations changes for instance) and for the most part it doesn’t work (flaky at best) and no documentation. You would think you would click on advertise on the server and detect and connect on the client, but that usually doesn’t work, you have to make both clients servers and then you can usually chat back and forth.

This little side app will strip out all that extra stuff and give you 2 apps (one client app and one server app).

You will start the server app and it will instantly start advertising itself.

You will start the client app and it will instantly detect the server and resolve it’s connection info and connect to the server.

Then you will be able to pass messages to the server which will display them.

Then we will dive into our first Apple code, Requires a Mac computer of some sort, which allows you to run Xcode, and you also need a Apple Developer License which you can get for free or they are $99 a year. The Apple code will allow us to act as a server and receive messages from the Android client.

This is what the MP3 player app will do also when we return to it, because all it does is send the song db number to the server which receives the number and plays the song it belongs to.

First, to use NSD you need to use a minimum of OS version of 16, JellyBean 4.1. This tutorial was tested on a JellyBean ASUS T700, KitKat Samsung S3, and a KitKat Samsung Note 3. The only issue seems to be the Note 3’s WIFI radio seems to drop connection more than it should, luckily NSD reconnects for you.

When creating the project choose Sdk version 16, Jelly Bean 4.1.

or

Edit the Manifest first to avoid errors when creating the MainActivity class.

Replace minimum with android:minSdkVersion=”16″

and add

<uses-permission android:required="true" android:name="android.permission.INTERNET"/>
 <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />

android.permission.INTERNET – let’s the user know that our application uses network sockets.

android.permission.ACCESS_WIFI_STATE – let’s the user know we are going to extract the WIFI IP Address.

Android NSD Server

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;
 
import org.json.JSONException;
import org.json.JSONObject;
 
import android.app.Activity;
import android.content.Context;
import android.net.nsd.NsdManager;
import android.net.nsd.NsdManager.RegistrationListener;
import android.net.nsd.NsdServiceInfo;
import android.os.Bundle;
import android.util.Log;
import android.view.Menu;
import android.widget.*;
 
public class MainActivity extends Activity {
 
 private String SERVICE_NAME = "Server Device";
 private String SERVICE_TYPE = "_letstalk._tcp.";
 private static final String REQUEST_CONNECT_CLIENT = "request-connect-client";
 private SocketServerThread socketServerThread;
 private NsdManager mNsdManager;
 
 private int SocketServerPort = 6000;
 
 private List<String> clientIPs;
 
 private static final String TAG = "NSDServer";
 
 public void showToast(final String toast){
 MainActivity.this.runOnUiThread(new Runnable(){
 public void run(){
 Toast.makeText(MainActivity.this,toast,Toast.LENGTH_LONG).show();
 }
 });
 }
 
 @Override
 public void onCreate(Bundle savedInstanceState) {
 super.onCreate(savedInstanceState);
 setContentView(R.layout.main);
 
 mNsdManager = (NsdManager) getSystemService(Context.NSD_SERVICE);
 registerService(9000);
 
 clientIPs = new ArrayList<String>();
 socketServerThread = new SocketServerThread();
 socketServerThread.start();
 }
 
 public void registerService(int port) {
 NsdServiceInfo serviceInfo = new NsdServiceInfo();
 serviceInfo.setServiceName(SERVICE_NAME);
 serviceInfo.setServiceType(SERVICE_TYPE);
 serviceInfo.setPort(port);
 
 mNsdManager.registerService(serviceInfo,NsdManager.PROTOCOL_DNS_SD,mRegistrationListener);
 }
 
 RegistrationListener mRegistrationListener = new NsdManager.RegistrationListener() {
 
 @Override
 public void onServiceRegistered(NsdServiceInfo NsdServiceInfo) {
 String mServiceName = NsdServiceInfo.getServiceName();
 SERVICE_NAME = mServiceName;
 Log.d(TAG, "Registered name : " + mServiceName);
 }
 
 @Override
 public void onRegistrationFailed(NsdServiceInfo serviceInfo,
 int errorCode) {
 // Registration failed! Put debugging code here to determine
 // why.
 }
 
 @Override
 public void onServiceUnregistered(NsdServiceInfo serviceInfo) {
 // Service has been unregistered. This only happens when you
 // call
 // NsdManager.unregisterService() and pass in this listener.
 Log.d(TAG,
 "Service Unregistered : " + serviceInfo.getServiceName());
 }
 
 @Override
 public void onUnregistrationFailed(NsdServiceInfo serviceInfo,
 int errorCode) {
 // Unregistration failed. Put debugging code here to determine
 // why.
 }
 };
 
 private class SocketServerThread extends Thread {
 
 @Override
 public void run() {
 
 Socket socket = null;
 ServerSocket serverSocket = null;
 DataInputStream dataInputStream = null;
 DataOutputStream dataOutputStream = null;
 
 try { 
 Log.i(TAG, "Creating server socket"); 
 serverSocket = new ServerSocket(SocketServerPort);
 
 while (true) {
 socket = serverSocket.accept();
 dataInputStream = new DataInputStream(
 socket.getInputStream());
 dataOutputStream = new DataOutputStream(
 socket.getOutputStream());
 
 String messageFromClient, messageToClient, request;
 
 //If no message sent from client, this code will block the Thread
 messageFromClient = dataInputStream.readUTF();
 
 final JSONObject jsondata;
 
 try {
 jsondata = new JSONObject(messageFromClient);
 request = jsondata.getString("request");
 
 if (request.equals(REQUEST_CONNECT_CLIENT)) {
 String clientIPAddress = jsondata.getString("ipAddress");
 
 // Add client IP to a list
 clientIPs.add(clientIPAddress);
 showToast("Accepted");
 
 messageToClient = "Connection Accepted";
 
 
// Important command makes client able to send message
 dataOutputStream.writeUTF(messageToClient);
// ****** Paste here Bonus 1
 
// ****** Paste here Bonus 1
 } else {
 // There might be other queries, but as of now nothing.
 dataOutputStream.flush();
 }
 
 } catch (JSONException e) {
 e.printStackTrace();
 Log.e(TAG, "Unable to get request");
 dataOutputStream.flush();
 }
 }
 
 } catch (IOException e) {
 e.printStackTrace();
 } finally {
 if (socket != null) {
 try {
 socket.close();
 } catch (IOException e) {
 e.printStackTrace();
 }
 } 
 
 if (dataInputStream != null) {
 try {
 dataInputStream.close();
 } catch (IOException e) {
 e.printStackTrace();
 }
 }
 
 if (dataOutputStream != null) {
 try {
 dataOutputStream.close();
 } catch (IOException e) {
 e.printStackTrace();
 }
 }
 }
 
 } 
 
 }
 
 protected void onPuase() {
 if (mNsdManager != null) {
 mNsdManager.unregisterService(mRegistrationListener);
 }
 super.onPause();
 }
 
 @Override
 protected void onResume() {
 super.onResume();
 if (mNsdManager != null) {
 registerService(9000);
 }
 
 }
 
 @Override
 protected void onDestroy() {
 if (mNsdManager != null) {
 mNsdManager.unregisterService(mRegistrationListener);
 }
 super.onDestroy();
 }
 
}

SERVICE_NAME is a constant that we use to set the device name. If more than one device has the same service name, the 2nd device has a (1) added to it and the 3rd would have (2) added to it and so on. Not sure if this is what made the nsdChat version so flaky so I don’t use it, I give the server a name and the client a name, and use that.

SERVICE_TYPE is a custom name you make up (that must conform to a standard). This is the name of your app on the network, it doesn’t have to be the same name as your app, just every device that you want to see each other must use the same one. The service type specifies which protocol and transport layer the application uses. The syntax is “_[protocol]._[transportlayer].” You can name the protocol anything you want but leave the transportlayer the way it is.

Note: If you plan on publishing an app to the app store that uses NSD you should register your protocol to the International Assigned Numbers Authority (IANA). They manage a centralized, authoritative list of service types used by service discovery protocols such as NSD and Bonjour. If you intend to use a new service type, you should reserve it by filling out the IANA Ports and Service registration form.

REQUEST_CONNECT_CLIENT is a static constant we use to detect what the client wants to do. At first this will be the only option, but later we will add a display message option. Static means that you can access the constant value from other classes without having to instantiate the class. A class is like blue prints on how to build a house, if you use the blue prints to instantiate the house, you build the house. A static value is like a equation on how to figure square footage that happens to also be written on the blue prints. Just because the equation is written on the blue prints it doesn’t mean you have to build the house to use them.

Now we layout our tools we want to use.

SocketServerThread is a custom class that we use to add functionality to the Thread class. The functionality that we add listen’s for incoming connections and receive’s data from the client.

Threads allow us to perform work simultaneously along with the main Thread. The main Thread is what the app display runs on and what Android watches to detect misbehaving apps that it needs to shut down to keep the phone performing in a user friendly way. The server will be waiting for a connection to be requested and established and it can not do this on the main Thread or Android will shut it down.

NsdManager is what we use to make the server discoverable by other devices. The client devices use NsdManager to discover us and then resolve (connect) to us.

List clientIPs is a list of Strings that will hold the IP Addresses of all the connected clients. This is not required to make the app work, and will not be used by me, but might be helpful to you if you need to know that information.

A TAG file is used for logging, it helps catch your eye when looking at logcat while your app is running to make sure it is making it through all steps of your program. This was one of the few concepts that I had to use logging for, especially with the nsdchat app, but that was before I found the showToast method on stackOverflow, which I show you next.

public void showToast(final String toast) {
    MainActivity.this.runOnUiThread(new Runnable() {
        public void run() {
            Toast.makeText(MainActivity.this, "Accepted", Toast.LENGTH_LONG).show();
        }
    });
}

This is one of the best tools ever! Especially if you are debugging on your phone. This allows you to Toast whatever you want to the screen from anywhere in the app. If you remember earlier when I was discussing Context I said you had to have access to the main screen to be able to see a Toast, and you can’t always get Context. But with this tool you don’t need to. You can pass any string you like to this method() but for this app I just Toast a static message of Accepted.

Now with our tools laid out let’s walk through the logic.

We display our screen with setContentView(R.layout.main); – Displayed at the bottom.

We create an instance of NsdManager called mNsdManager to use to make our server discoverable.

We run a custom method registerService().

This method initializes an instance of NsdServiceInfo that we use to assign our service name, service type, and service port.

We then use NsdManager to register our service (server) on the network using our NSDServiceInfo (which will be passed to the client during discovery and resolve, we define what type of protocol we are using (for NSD you use NsdManager.PROTOCOL_DNS_SD) and where to go after the service is successfully registered (a interface we create for callbacks called mRegistrationListener).

Next we define, initialize and implement the RegistrationListener interface. We use this to tell if we successfully registered the service. Since it is an interface we have methods() we must implement (@Override) to receive service status change call backs.

public void onServiceRegistered(NsdServiceInfo NsdServiceInfo) {

Is executed when a the service is successfully registered. We use the NsdServiceInfo we receive to Log the service name that was registered.(This would be helpful if you were registering multiple different services).

public void onRegistrationFailed(NsdServiceInfo serviceInfo,
int errorCode) {

Is executed when the registration fails, you could log the error if you need to, but we don’t in this example.

public void onServiceUnregistered(NsdServiceInfo serviceInfo) {

This is run if the client is turned off.

public void onUnregistrationFailed(NsdServiceInfo serviceInfo,
int errorCode) {

This is run if registration fails.

Next we define our custom background Thread that we use to accept connections, receive and send data.

We initialize our Socket, used to communicate with the clients, and set it’s value to null.

We initialize our ServerSocket, used to accept connections, and set it to null.

We initialize our DataInputStream, that receives incoming data, and set it to null.

We initialize our DataOutputStream, used to send data to clients, and set it to null.

We use a try and catch because a port may not be available or configured wrong.

We then create our ServerSocket that we use to listen for incoming connections. Which is a pre-defined integer variable (SocketServerPort = 6000).

while(true){ – is known as an infinite loop, while remains true as long as the app is running.

Now comes the main reason why we use a Thread.

socket = serverSocket.accept();

This command sits and waits till a connection comes in, if this were on the main thread it would cause the program to time out and crash. It’s called a blocking method.

When a connection does come in you can not use the same socket to talk with the client, because the ServerSocket is always waiting and listening for just incoming calls, so when a new connection is made a new socket is created and assigned to the socket variable

We can then use this socket to get a InputStream and OutputStream for the socket.

Then we wait for the client to send some data.

messageFromClient = dataInputStream.readUTF();

This is another blocking method that doesn’t allow the code to continue until data is received.

The data that the client sends us in this app is in a JSONObject format which is similar to a HashMap format, or a string value pair. It is essentially a variable name with a value. You can check for the existence of the variable, and if it exists get the value. The variable is the name for the value.

Since the client will be sending a JSONObject we create one to store the data passed in.

Since the data passed in might not end up being a JSONObject we surround it with a try and catch to debug issues if there are any.

We initialize the JSONObject by putting the data passed in from the client in it.

We then get the value (which is a String) out of the variable request.

If the value stored in request equals the constant stored in REQUEST_CONNECT_CLIENT, which is “request-connect-client”

Then we get the value stored in ipAddress (which is a String).

Then we take that value and add it to our List of client IP Adresses (which we do nothing with).

We then display a toast so we can tell the server received our message from the client.(to help with debugging)

We then create a message to send back to the client “Connection Accepted”.

We then send the message using writeUTF to send data out the OutputStream using the socket.

dataOutputStream.writeUTF(messageToClient);

if (request.equals(REQUEST_CONNECT_CLIENT)) is FALSE.

dataOutputStream.flush();

Which removes all data from the OutputStream, this will be useful for our MP3 player because we will be listening for a certain amount of data (20 characters) and if there are data remnants left in the pipe this could really mess up our data flow.

First we catch our last try statement which tries to pull data out of the JSONObject so we catch a JSONException. If there is an JSONException we do a printStackTrace() and flush any data that might have made it in the pipe.

Next we catch a IOException in case we have issues creating the Socket, InputStream, or OutputStream.

After making it through the Thread and only after making it through the Thread do we try to close the socket, close the OutputStream, and close the InputStream. (if they have been used( i.e. !=null))

Finally if another application is opened over this app.

protected void onPuase() {

If NsdManager is being used, we can stop advertising it’s existence.

mNsdManager.unregisterService(mRegistrationListener);

If the app is re-opened.

protected void onResume() {

and NsdManager is still available we can re-advertise our service.

registerService(9000);

Or if our app is completely removed from memory by the system, we can stop advertising it’s existence.

mNsdManager.unregisterService(mRegistrationListener);

That’s everything you need all in one file.

Except for the layout/main.xml file.

"http://schemas.android.com/apk/res/android"
 android:layout_width="fill_parent"
 android:layout_height="fill_parent"
 android:gravity="center"
 android:orientation="vertical" >

For this example we are connecting a Android client to a Android server using Network Service Discovery. We have set up the server. In my next post we will set up the client.

Android thread

Android modifies the user interface and handles input events from one single user interface thread. This thread is also called the main thread.

To provide a good user experience all potentially slow running operations in an Android application should run asynchronously

The Handler class can be used to register to a thread and provides a simple channel to send data to this thread.

To use a handler you have to subclass it and override the handleMessage() method to process messages.

Your thread can post messages via the sendMessage(Message) method or via thesendEmptyMessage() method to the Handler object.

Every app has its own special thread that runs UI objects such as View objects; this thread is called the UI thread. Only objects running on the UI thread have access to other objects on that thread. Because tasks that you run on a thread from a thread pool aren’trunning on your UI thread, they don’t have access to UI objects. To move data from a background thread to the UI thread, use a Handler that’s running on the UI thread.

Android color transpanrency

 

Original white: #FFFFFF

50% of white: #80FFFFFF.

Follow the table below

  • 100% — FF
  • 95% — F2
  • 90% — E6
  • 85% — D9
  • 80% — CC
  • 75% — BF
  • 70% — B3
  • 65% — A6
  • 60% — 99
  • 55% — 8C
  • 50% — 80
  • 45% — 73
  • 40% — 66
  • 35% — 59
  • 30% — 4D
  • 25% — 40
  • 20% — 33
  • 15% — 26
  • 10% — 1A
  • 5% — 0D
  • 0% — 00

RecyclerView

A better way to display collections in Android

Android now offers the new RecyclerView widget, a new and improved view group for displaying collections. This guide explains how to use and customize RecyclerView in Xamarin.Android applications.

Overview

Many apps need to display collections of the same type (such as messages, contacts, images, or songs); often, this collection is too large to fit on the screen, so the collection is presented in a small window that can smoothly scroll through all items in the collection. RecyclerView is an Android widget that displays a collection of items in a list or a grid, enabling the user to scroll through the collection. The following is a screenshot of an example app that uses RecyclerView to display email inbox contents in a vertical scrolling list:

RecyclerView offers two compelling features:

  • It has a flexible architecture that lets you modify its behavior by plugging in your preferred components.
  • It is efficient with large collections because it reuses item views and requires the use of view holders to cache view references.

This guide explains how to use RecyclerView in Xamarin.Android applications; it explains how to add the RecyclerView package to your Xamarin.Android project, and it describes how RecyclerView functions in a typical application. Real code examples are provided to show you how to integrate RecyclerView into your application, how to implement item-view click, and how to refresh RecyclerView when its underlying data changes. This guide assumes that you are familiar with Xamarin.Android development.

Requirements

Although RecyclerView is often associated with Android 5.0 Lollipop, it is offered as a support library – RecyclerView works with apps that target API level 7 (Android 2.1) and later. The following is required to use RecyclerViewin Xamarin-based applications:

  • Xamarin.Android – Xamarin.Android 4.20 or later must be installed and configured with either Visual Studio or Xamarin Studio. If you are using Xamarin Studio, version 5.5.4 or later is required.
  • Your app project must include theXamarin.Android.Support.v7.RecyclerView package. For more information about installing NuGet packages, see Walkthrough: Including a NuGet in your project.

Introducing RecyclerView

RecyclerView can be thought of as a replacement for the ListView andGridView widgets in Android. Like its predecessors, RecyclerView is designed to display a large data set in a small window, but RecyclerViewoffers more layout options and is better optimized for displaying large collections. If you are familiar with ListView, there are several important differences between ListView and RecyclerView:

  • RecyclerView is slightly more complex to use: you have to write more code to use RecyclerView compared to ListView.
  • RecyclerView does not provide a predefined adapter; you must implement the adapter code that accesses your data source. However, Android includes several predefined adapters that work with ListViewand GridView.
  • RecyclerView does not offer an item-click event when a user taps an item; instead, item-click events are handled by helper classes. By contrast, ListView offers an item-click event.
  • RecyclerView enhances performance by recycling views and by enforcing the view-holder pattern, which eliminates unnecessary layout resource lookups. Use of the view-holder pattern is optional in ListView.
  • RecyclerView is based on a modular design that makes it easier to customize. For example, you can plug in a different layout policy without significant code changes to your app. By contrast, ListView is relatively monolithic in structure.
  • RecyclerView includes built-in animations for item add and remove.ListView animations require some additional effort on the part of the app developer.

To understand how RecyclerView works in a typical application, we’ll explore the RecyclerViewer sample app, a simple code example that usesRecyclerView to display a large collection of photos:

This app uses CardView to implement each photograph item in theRecyclerView layout. Because of RecyclerView‘s performance advantages, this sample app is able to quickly scroll through a large collection of photos smoothly and without noticeable delays.

RecyclerView Helper Classes

RecyclerView handles some tasks internally (such as the scrolling and recycling of views), but it is essentially a manager that coordinates helper classes to display a collection. RecyclerView delegates tasks to the following helper classes:

  • Adapter – Inflates item layouts (instantiates the contents of a layout file) and binds data to views that are displayed within a RecyclerView. The adapter also reports item-click events.
  • LayoutManager – Measures and positions item views within aRecyclerView and manages the policy for view recycling.
  • ViewHolder – Looks up and stores view references. The view holder also helps with detecting item-view clicks.
  • ItemDecoration – Allows your app to add special drawing and layout offsets to specific views for drawing dividers between items, highlights, and visual grouping boundaries.
  • ItemAnimator – Defines the animations that take place during item actions or as changes are made to the adapter.

The relationship between the RecyclerView, LayoutManager, and Adapterclasses is depicted in the following diagram.

As this figure illustrates, the LayoutManager can be thought of as the intermediary between the Adapter and the RecyclerView. The LayoutManagermakes calls into Adapter methods on behalf of the RecyclerView. For example, the LayoutManager calls an Adapter method when it is time to create a new view for a particular item position in the RecyclerView. TheAdapter inflates the layout for that item and creates a ViewHolder instance (not shown) to cache references to the views at that position. When theLayoutManager calls the Adapter to bind a particular item to the data set, theAdapter locates the data for that item, retrieves it from the data set, and copies it to the associated item view.

When using RecyclerView in your app, creating derived types of the following classes is required:

  • RecyclerView.Adapter – Provides a binding from your app’s data set (which is specific to your app) to item views that are displayed within the RecyclerView. The adapter knows how to associate each item-view position in the RecyclerView to a specific location in the data source. In addition, the adapter handles the layout of the contents within each individual item view and creates the view holder for each view. The adapter also reports item-click events that are detected by the item view.
  • RecyclerView.ViewHolder – Caches references to the views in your item layout file so that resource lookups are not repeated unnecessarily. The view holder also arranges for item-click events to be forwarded to the adapter when a user taps the view-holder’s associated item view.
  • RecyclerView.LayoutManager – Positions items within the RecyclerView. You can use one of several predefined layout managers or you can implement your own custom layout manager. RecyclerView delegates the layout policy to the layout manager, so you can plug in a different layout manager without having to make significant changes to your app.

Also, you can optionally extend the following classes to change the look and feel of RecyclerView in your app:

  • RecyclerView.ItemDecoration
  • RecyclerView.ItemAnimator

If you do not extend ItemDecoration and ItemAnimator, RecyclerView uses default implementations. This guide does not explain how to create customItemDecoration and ItemAnimator classes; for more information about these classes, see RecyclerView.ItemDecoration and RecyclerView.ItemAnimator.

How View Recycling Works

RecyclerView does not allocate an item view for every item in your data source. Instead, it allocates only the number of item views that fit on the screen and it reuses those item layouts as the user scrolls. When the view first scrolls out of sight, it goes through the recycling process illustrated in the following figure:

  1. When a view scrolls out of sight and is no longer displayed, it becomes a scrap view.
  2. The scrap view is placed in a pool and becomes a recycle view. This pool is a cache of views that display the same type of data.
  3. When a new item is to be displayed, a view is taken from the recycle pool for reuse. Because this view must be re-bound by the adapter before being displayed, it is called a dirty view.
  4. The dirty view is recycled: the adapter locates the data for the next item to be displayed and copies this data to the views for this item. References for these views are retrieved from the view holder associated with the recycled view.
  5. The recycled view is added to the list of items in the RecyclerView that are about to go on-screen.
  6. The recycled view goes on-screen as the user scrolls the RecyclerViewto the next item in the list. Meanwhile, another view scrolls out of sight and is recycled according to the above steps.

In addition to item-view reuse, RecyclerView also uses another efficiency optimization: view holders. A view holder is a simple class that caches view references. Each time the adapter inflates an item-layout file, it also creates a corresponding view holder. The view holder uses FindViewById to get references to the views inside the inflated item-layout file. These references are used to load new data into the views every time the layout is recycled to show new data.

A Basic RecyclerView Example

In the following code examples, we’ll look at the implementation of theRecyclerViewer sample app to understand how RecyclerView works in a real application. We’ll examine the adapter and view holder implementations to see how they support RecyclerView. In addition, we’ll see how to specify the layout manager, and we’ll modify the app to use a different layout manager.

An Example Data Source

In this example, we use a “photo album” data source (represented by thePhotoAlbum class) to supply RecyclerView with item content. PhotoAlbum is a collection of photos with captions; when you instantiate it, you get a ready-made collection of 32 photos:

PhotoAlbum mPhotoAlbum = new PhotoAlbum();

Each photo instance in PhotoAlbum exposes properties that allow you to read its image resource ID, PhotoID, and its caption string, Caption. The collection of photos is organized such that each photo can be accessed by an indexer. For example, the following lines of code access the image resource ID and caption for the tenth photo in the collection:

int imageId = mPhotoAlbum[9].ImageId;
string caption = mPhotoAlbum[9].Caption;

PhotoAlbum also provides a RandomSwap method that you can call to swap the first photo in the collection with a randomly-chosen photo elsewhere in the collection:

mPhotoAlbum.RandomSwap();

Because the implementation details of PhotoAlbum are not relevant to understanding RecyclerView, the PhotoAlbum source code is not presented here. The source code to PhotoAlbum is available in the file PhotoAlbum.cs in the RecyclerViewer sample app.

Initialization

Before we implement the layout manager, view holder, and adapter, we need some preliminary code to initialize the application. The layout file,Main.axml, consists of a single RecyclerView within a LinearLayout:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent">
    <android.support.v7.widget.RecyclerView
        android:id="@+id/recyclerView"
        android:scrollbars="vertical"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent" />
</LinearLayout>

Note that you must use the fully-qualified name,android.support.v7.widget.RecyclerView, because RecyclerView is packaged in a support library. The OnCreate method of our MainActivitymust initialize this layout, instantiate the adapter, and prepare the underlying data source:

public class MainActivity : Activity
{
    RecyclerView mRecyclerView;
    RecyclerView.LayoutManager mLayoutManager;
    PhotoAlbumAdapter mAdapter;
    PhotoAlbum mPhotoAlbum;

    protected override void OnCreate (Bundle bundle)
    {
        base.OnCreate (bundle);

        // Prepare the data source:
        mPhotoAlbum = new PhotoAlbum();

        // Instantiate the adapter and pass in its data source:
        mAdapter = new PhotoAlbumAdapter (mPhotoAlbum);

        // Set our view from the "main" layout resource:
        SetContentView (Resource.Layout.Main);

        // Get our RecyclerView layout:
        mRecyclerView = FindViewById<RecyclerView> (Resource.Id.recyclerView);

        // Plug the adapter into the RecyclerView:
        mRecyclerView.SetAdapter (mAdapter);

This code instantiates the PhotoAlbum data source and passes it to our adapter, PhotoAlbumAdapter (which is defined later in this guide). Note that it is considered a best practice to pass the data source as a parameter to the constructor of the adapter. To plug our adapter into the RecyclerViewinstance, we call the RecyclerView SetAdapter method as shown above.

The Layout Manager

The layout manager is responsible for positioning items in the RecyclerViewdisplay; it determines the presentation type (a list or a grid), the orientation (whether items are displayed vertically or horizontally), and which direction items should be displayed (in normal order or in reverse order). The layout manager is also responsible for calculating the size and position of each item in the RecycleView display.

The layout manager has an additional purpose: it determines the policy for when to recycle item views that are no longer visible to the user. Because the layout manager is aware of which views are visible (and which are not), it is in the best position to decide when a view can be recycled. To recycle a view, the layout manager typically makes calls to the adapter to replace the contents of a recycled view with different data, as described previously inHow View Recycling Works.

We can extend RecyclerView.LayoutManager to create our own layout manager, or we can use a predefined layout manager. RecyclerViewprovides the following predefined layout managers:

  • LinearLayoutManager – Arranges items in a column that can be scrolled vertically, or in a row that can be scrolled horizontally.
  • GridLayoutManager – Displays items in a grid.
  • StaggeredGridLayoutManager – Displays items in a staggered grid, where some items have different heights and widths.

To specify the layout manager, we instantiate our chosen layout manager and pass it to the SetLayoutManager method as shown in the next code example. Note that we must specify the layout manager – RecyclerViewdoes not select a predefined layout manager by default. The following snippet is an example of how to instantiate the LinearLayoutManager and plug it into the RecyclerView instance:

mLayoutManager = new LinearLayoutManager (this);
mRecyclerView.SetLayoutManager (mLayoutManager);

This code resides in the main activity’s OnCreate method. The constructor to the layout manager requires a context; typically, you supply the entireMainActivity by passing this as shown.

In the example photo-viewing app, the predefind LinearLayoutManager is used to lay out each CardView in a vertical scrolling arrangement. Alternatively, we could plug in a custom layout manager that displays twoCardView items side-by-side, implementing a page-turning animation effect to traverse through the collection of photos. Later in this guide, we’ll provide an example of how to modify the layout by swapping in a different layout manager.

For more information about the layout manager, see theRecyclerView.LayoutManager class reference.

The View Holder

The view holder is a class that we define for caching our view references. The adapter uses these view references to bind each view to its content. Every item in the RecyclerView has an associated view holder instance that caches the view references for that item. To create a view holder, we use the following steps to define a class to hold our exact set of views per item:

  1. Subclass RecyclerView.ViewHolder.
  2. Implement a constructor that looks up and stores the view references.
  3. Implement properties that the adapter can use to access these references.

For example, in the RecyclerViewer sample app, the view holder class is called PhotoViewHolder. Each PhotoViewHolder instance holds references to the ImageView and TextView of an associated row item, which is laid out in aCardView as diagrammed here:

PhotoViewHolder derives from RecyclerView.ViewHolder and contains properties to store references to the ImageView and TextView shown in the above layout. PhotoViewHolder consists of two properties and one constructor:

public class PhotoViewHolder : RecyclerView.ViewHolder
{
    public ImageView Image { get; private set; }
    public TextView Caption { get; private set; }

    public PhotoViewHolder (View itemView) : base (itemView)
    {
        // Locate and cache view references:
        Image = itemView.FindViewById<ImageView> (Resource.Id.imageView);
        Caption = itemView.FindViewById<TextView> (Resource.Id.textView);
    }
}

In this code example, the PhotoViewHolder constructor is passed a reference to the parent item view (the CardView) that PhotoViewHolder wraps. Note that we always forward the parent item view to the base constructor. ThePhotoViewHolder constructor calls FindViewById on the parent item view to locate each of its child view references, ImageView and TextView, storing the results in the Image and Caption properties, respectively. The adapter later retrieves view references from these properties when it updates thisCardView‘s child views with new data.

For more information about RecyclerView.ViewHolder, see theRecyclerView.ViewHolder class reference.

The Adapter

Most of the “heavy-lifting” of our RecyclerView integration code takes place in the adapter. RecyclerView requires that we provide an adapter derived from RecyclerView.Adapter to access our data source and populate each item with content from the data source. Because the data source is app-specific, we must implement adapter functionality that understands how to access our data. The adapter extracts information from the data source and loads it into each item in the RecyclerView collection.

The following drawing illustrates how the sample app adapter maps content in the data source (the photo album) through view holders to individual views within each CardView row item in the RecyclerView:

In the example photo-viewing app, the adapter loads each RecyclerViewrow with data for a particular photograph. For a given photograph at row position P, for example, the adapter locates the associated data at position Pwithin the data source and copies this data to the row item at position P in the RecyclerView collection. The adapter uses the view holder to lookup the references for the ImageView and TextView at that position so it doesn’t have to repeatedly call FindViewById for those views as the user scrolls through the photograph collection and reuses views.

In the example photo-viewer app, we derive from RecyclerView.Adapter to create PhotoAlbumAdapter:

public class PhotoAlbumAdapter : RecyclerView.Adapter
{
    public PhotoAlbum mPhotoAlbum;

    public PhotoAlbumAdapter (PhotoAlbum photoAlbum)
    {
        mPhotoAlbum = photoAlbum;
    }
    ...
}

The mPhotoAlbum member contains the data source (the photo album) that is passed into the constructor; the constructor copies the photo album into this member variable. Next, we must override the followingRecyclerView.Adapter methods:

  • OnCreateViewHolder – Instantiates the item layout file and view holder.
  • OnBindViewHolder – Loads the data at the specified position into the views whose references are stored in the given view holder.
  • ItemCount – Returns the number of items in the data source.

The layout manager calls these methods while it is positioning items within the RecyclerView. In the next few sections, we’ll look at the actual implementation of these methods in the example photo-viewing app.

OnCreateViewHolder

The layout manager calls OnCreateViewHolder when the RecyclerView needs a new view holder to represent an item. OnCreateViewHolder inflates the item view from the view’s layout file and wraps the view in a newPhotoViewHolder instance. The PhotoViewHolder constructor locates and stores references to child views in the layout as described previously in The View Holder.

In the example photo-viewing app, each row item is represented by aCardView that contains an ImageView (for the photo) and a TextView (for the caption). This layout resides in the file PhotoCardView.axml:

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:card_view="http://schemas.android.com/apk/res-auto"
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content">
    <android.support.v7.widget.CardView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        card_view:cardElevation="4dp"
        card_view:cardUseCompatPadding="true"
        card_view:cardCornerRadius="5dp">
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="vertical"
            android:padding="8dp">
            <ImageView
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:id="@+id/imageView"
                android:scaleType="centerCrop" />
            <TextView
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:textAppearance="?android:attr/textAppearanceMedium"
                android:textColor="#333333"
                android:text="Caption"
                android:id="@+id/textView"
                android:layout_gravity="center_horizontal"
                android:layout_marginLeft="4dp" />
        </LinearLayout>
    </android.support.v7.widget.CardView>
</FrameLayout>

This layout represents a single row item in the RecyclerView.OnBindViewHolder (described below) copies data from the data source into the ImageView and TextView of this layout. OnCreateViewHolder inflates this layout for a given photo location in the RecyclerView and instantiates a newPhotoViewHolder instance (which locates and caches references to theImageView and TextView child views in the associated CardView layout):

public override RecyclerView.ViewHolder
    OnCreateViewHolder (ViewGroup parent, int viewType)
{
    // Inflate the CardView for the photo:
    View itemView = LayoutInflater.From (parent.Context).
                Inflate (Resource.Layout.PhotoCardView, parent, false);

    // Create a ViewHolder to hold view references inside the CardView:
    PhotoViewHolder vh = new PhotoViewHolder (itemView);
    return vh;
}

The resulting view holder instance, vh, is returned back to the caller (the layout manager).

OnBindViewHolder

When the layout manager is ready to display a particular view in theRecyclerView‘s visible screen area, it calls the adapter’s OnBindViewHoldermethod to fill the item at the specified row position with content from the data source. In the photo-viewing app, OnBindViewHolder gets the photo information for the specified row position (the photo’s image resource and the string for the photo’s caption) and copies this data to the associated views. Views are located via references stored in the view holder object (which is passed in through the holder parameter):

public override void
    OnBindViewHolder (RecyclerView.ViewHolder holder, int position)
{
    PhotoViewHolder vh = holder as PhotoViewHolder;

    // Load the photo image resource from the photo album:
    vh.Image.SetImageResource (mPhotoAlbum[position].PhotoID);

    // Load the photo caption from the photo album:
    vh.Caption.Text = mPhotoAlbum[position].Caption;
}

The passed-in view holder object must first be cast into the derived view holder type (in this case, PhotoViewHolder) before it is used. The adapter loads the image resource into the view referenced by the view holder’sImage property, and it copies the caption text into the view referenced by the view holder’s Caption property. This binds the associated view with its data.

Notice that OnBindViewHolder is the code that deals directly with the structure of the data. In this case, OnBindViewHolder understands how to map the RecyclerView item position to its associated data item in the data source. The mapping is trivial in this case because the position can be used as an array index into the photo album; however, more complex data sources may require extra code to establish such a mapping.

ItemCount

The ItemCount method returns the number of items in the data collection. In the example photo viewer app, the item count is the number of photos in the photo album:

public override int ItemCount
{
    get { return mPhotoAlbum.NumPhotos; }
}

For more information about RecyclerView.Adapter, see theRecyclerView.Adapter class reference.

Putting it All Together

The resulting RecyclerView implementation for the example photo app consists of MainActivity initialization code, the view holder, and the adapter. MainActivity creates the mRecyclerView instance, instantiates the data source and the adapter, and plugs in the layout manager and adapter:

public class MainActivity : Activity
{
    RecyclerView mRecyclerView;
    RecyclerView.LayoutManager mLayoutManager;
    PhotoAlbumAdapter mAdapter;
    PhotoAlbum mPhotoAlbum;

    protected override void OnCreate (Bundle bundle)
    {
        base.OnCreate (bundle);
        mPhotoAlbum = new PhotoAlbum();
        SetContentView (Resource.Layout.Main);
        mRecyclerView = FindViewById<RecyclerView> (Resource.Id.recyclerView);

        // Plug in the linear layout manager:
        mLayoutManager = new LinearLayoutManager (this);
        mRecyclerView.SetLayoutManager (mLayoutManager);

        // Plug in my adapter:
        mAdapter = new PhotoAlbumAdapter (mPhotoAlbum);
        mRecyclerView.SetAdapter (mAdapter);
    }
}

PhotoViewHolder locates and caches the view references:

public class PhotoViewHolder : RecyclerView.ViewHolder
{
    public ImageView Image { get; private set; }
    public TextView Caption { get; private set; }

    public PhotoViewHolder (View itemView) : base (itemView)
    {
        // Locate and cache view references:
        Image = itemView.FindViewById<ImageView> (Resource.Id.imageView);
        Caption = itemView.FindViewById<TextView> (Resource.Id.textView);
    }
}

PhotoAlbumAdapter implements the three required method overrides:

public class PhotoAlbumAdapter : RecyclerView.Adapter
{
    public PhotoAlbum mPhotoAlbum;
    public PhotoAlbumAdapter (PhotoAlbum photoAlbum)
    {
        mPhotoAlbum = photoAlbum;
    }

    public override RecyclerView.ViewHolder
        OnCreateViewHolder (ViewGroup parent, int viewType)
    {
        View itemView = LayoutInflater.From (parent.Context).
                    Inflate (Resource.Layout.PhotoCardView, parent, false);
        PhotoViewHolder vh = new PhotoViewHolder (itemView);
        return vh;
    }

    public override void
        OnBindViewHolder (RecyclerView.ViewHolder holder, int position)
    {
        PhotoViewHolder vh = holder as PhotoViewHolder;
        vh.Image.SetImageResource (mPhotoAlbum[position].PhotoID);
        vh.Caption.Text = mPhotoAlbum[position].Caption;
    }

    public override int ItemCount
    {
        get { return mPhotoAlbum.NumPhotos; }
    }
}

When this code is compiled and run, it creates the basic photo viewing app as shown in the following screenshots:

This basic app does not respond to item-touch events, nor does it handle changes in the underlying data. Later in this guide, we’ll add this functionality to the sample app.

Changing the LayoutManager

Because of RecyclerView‘s flexibility, it’s easy to modify the app to use a different layout manager. In this example, we’ll modify it to display with a grid layout that scrolls horizontally rather than with a vertical linear layout. To do this, we modify the layout manager instantiation to use theGridLayoutManager as follows:

mLayoutManager = new GridLayoutManager(this, 2, GridLayoutManager.Horizontal, false);

This code change replaces the vertical LinearLayoutManager with aGridLayoutManager that presents a grid made up of two rows that scroll in the horizontal direction. When we compile and run the app again, we see that the photographs are displayed in a grid and that scrolling is horizontal rather than vertical:

By changing only one line of code, we were able to modify the photo-viewing app to use a different layout with different behavior. Notice that neither the adapter code nor the layout XML had to be modified in order to change the layout policy.

Extending the Basic Example

The basic example described in the previous sections actually doesn’t do much – it simply scrolls and displays a fixed list of photograph items. In real-world applications, users expect to be able to interact with the app by tapping on items in the display. Also, the underlying data source often changes (or is changed by the app), and the contents of the display must be consistent with these changes. In the following sections, we’ll learn how to handle item-click events and update RecyclerView when the underlying data source changes.

Handling Item-Click Events

When a user touches an item in the RecyclerView, an item-click event is generated to notify our app as to which item was touched. This event is not generated by RecyclerView – instead, the item view (which is wrapped in the view holder) detects touches and reports these touches as click events.

To illustrate how to handle item-click events, we’ll modify the sample photo-viewing app to report which photograph had been touched by the user. When an item-click event occurs in the sample app, the following sequence takes place:

  1. The photograph’s CardView detects the item-click event and notifies the adapter.
  2. The adapter forwards the event (with item position information) to the activity’s item-click handler.
  3. The activity’s item-click handler responds to the item-click event.

First, let’s add an event handler member called ItemClick to ourPhotoAlbumAdapter class definition:

public event EventHandler<int> ItemClick;

Next, we’ll create an item-click event handler method in our MainActivity. This handler briefly displays a toast that indicates which photograph item was touched:

void OnItemClick (object sender, int position)
{
    int photoNum = position + 1;
    Toast.MakeText(this, "This is photo number " + photoNum, ToastLength.Short).Show();
}

Next, let’s add a line of code to register our OnItemClick handler withPhotoAlbumAdapter. A good place to do this is immediately afterPhotoAlbumAdapter is created (in the main activity’s OnCreate method):

mAdapter = new PhotoAlbumAdapter (mPhotoAlbum);
mAdapter.ItemClick += OnItemClick;

PhotoAlbumAdapter will now call OnItemClick when it receives an item-click event. Next, let’s create a handler in the adapter that raises this ItemClickevent. We’ll add the following method, OnClick, immediately after the adapter’s ItemCount method:

void OnClick (int position)
{
    if (ItemClick != null)
        ItemClick (this, position);
}

This OnClick method is the adapter’s listener for item-click events from item views. Before we can register this listener with an item view (via the item view’s view holder), we have to modify the PhotoViewHolder constructor to accept this method as an additional argument, and register OnClick with the item view Click event. Here’s the modified PhotoViewHolder constructor:

public PhotoViewHolder (View itemView, Action<int> listener)
    : base (itemView)
{
    Image = itemView.FindViewById<ImageView> (Resource.Id.imageView);
    Caption = itemView.FindViewById<TextView> (Resource.Id.textView);

    itemView.Click += (sender, e) => listener (base.Position);
}

The itemView parameter contains a reference to the CardView that was touched by the user. Note that the view holder base class knows the position of the item (CardView) that it represents (via the Positionproperty), and this position is passed to the adapter’s OnClick method when an item-click event takes place. Let’s modify the adapter’sOnCreateViewHolder method to pass the adapter’s OnClick method to the view-holder’s constructor:

PhotoViewHolder vh = new PhotoViewHolder (itemView, OnClick);

Now when we build and run the sample photo-viewing app, tapping a photo in the display will cause a toast to appear that reports which photograph was touched:

This example demonstrates just one approach for implementing event handlers with RecyclerView. Another approach that could be used here is to place events on the view holder and have the adapter subscribe to these events. If the sample photo app provided a photo editing capability, separate events would be required for the ImageView and the TextViewwithin each CardView: touches on the TextView would launch an EditViewdialog that lets the user edit the caption, and touches on the ImageViewwould launch a photo touchup tool that lets the user crop or rotate the photo. Depending on the needs of your app, you must design the best approach for handling and responding to touch events.

Notifying RecyclerView of Data Changes

RecyclerView does not automatically update its display when the contents of its data source changes; the adapter must notify RecyclerView when there is a change in the data set. The data set can change in many ways; for example, the contents within an item can change or the overall structure of the data may be altered. RecyclerView.Adapter provides a number of methods that you can call so that RecyclerView responds to data changes in the most efficient manner:

  • NotifyItemChanged – Signals that the item at the specified position has changed.
  • NotifyItemRangeChanged – Signals that the items in the specified range of positions have changed.
  • NotifyItemInserted – Signals that the item in the specified position has been newly inserted.
  • NotifyItemRangeInserted – Signals that the items in the specified range of positions have been newly inserted.
  • NotifyItemRemoved – Signals that the item in the specified position has been removed.
  • NotifyItemRangeRemoved – Signals that the items in the specified range of positions have been removed.
  • NotifyDataSetChanged – Signals that the data set has changed (forces a full update).

If you know exactly how your data set has changed, you can call the appropriate methods above to refresh RecyclerView in the most efficient manner. If you do not know exactly how your data set has changed, you can call NotifyDataSetChanged, which is far less efficient because RecyclerViewmust refresh all the views that are visible to the user. For more information about these methods, see RecyclerView.Adapter.

To demonstrate how RecyclerView can be updated when the data set changes, we’ll modify the sample photo-viewing app to randomly pick a photo in the data source and swap it with the first photo. First, let’s add aRandom Pick button to the example photo app’s Main.axml layout:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent">
    <Button
        android:id="@+id/randPickButton"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:gravity="center_horizontal"
        android:textAppearance="?android:attr/textAppearanceLarge"
        android:text="Random Pick" />
    <android.support.v7.widget.RecyclerView
        android:id="@+id/recyclerView"
        android:scrollbars="vertical"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent" />
</LinearLayout>

Next, let’s add code at the end of the main activity’s OnCreate method to locate the Random Pick button in the layout and attach a handler to it:

Button randomPickBtn = FindViewById<Button>(Resource.Id.randPickButton);

randomPickBtn.Click += delegate
{
    if (mPhotoAlbum != null)
    {
        // Randomly swap a photo with the first photo:
        int idx = mPhotoAlbum.RandomSwap();
    }
};

This handler calls the photo album’s RandomSwap method when the Random Pick button is tapped. The RandomSwap method randomly swaps a photo with the first photo in the data source, then returns the index of the randomly-swapped photo. When we compile and run the sample app with this code, tapping the Random Pick button does not result in a display change because the RecyclerView is not aware of the change to the data source.

To keep RecyclerView updated after the data source changes, we must modify the Random Pick click handler to call the adapter’sNotifyItemChanged method for each item in the collection that has changed (in this case, two items have changed: the first photo and the swapped photo). This causes RecyclerView to update its display so that it is consistent with the new state of the data source:

Button randomPickBtn = FindViewById<Button>(Resource.Id.randPickButton);

randomPickBtn.Click += delegate
{
    if (mPhotoAlbum != null)
    {
        int idx = mPhotoAlbum.RandomSwap();

        // First photo has changed:
        mAdapter.NotifyItemChanged(0);

        // Swapped photo has changed:
        mAdapter.NotifyItemChanged(idx);
    }
};

Now, when the Random Pick button is tapped, RecyclerView updates the display to show that a photo further down in the collection has been swapped with the first photo in the collection:

Of course, we could call NotifyDataSetChanged instead of making the two calls to NotifyItemChanged, but doing so would force RecyclerView to refresh the entire collection even though only two items in the collection had changed. Calling NotifyItemChanged is significantly more efficient than calling NotifyDataSetChanged.

Summary

This guide introduced the Android RecyclerView widget; it explained how to add the RecyclerView support library to Xamarin.Android projects, howRecyclerView recycles views, how it enforces the view-holder pattern for efficiency, and how the various helper classes that make up RecyclerViewcollaborate to display collections. It provided example code to demonstrate how RecyclerView is integrated into an application, it demonstrated how to tailor RecyclerView‘s layout policy by plugging in different layout managers, and it explained how to handle item click events and notify RecyclerView of data source changes.

For more information about RecyclerView, see the RecyclerView class reference.

Performance Tips for Android’s ListView

I’ve been messing around with Android-based code for a few months now while hacking on Native Firefox for Android and Pattrn. I noticed that the performance tips for ListViews are a bit scattered in different sources. This post is an attempt to summarize the ones I found most useful.

I’m assuming you’re already familiar with ListViews and understand the framework around AdapterViews. I’ve added some Android source code pointers for the curious readers willing to understand things a bit deeper.

How it works

ListView is designed for scalability and performance. In practice, this essentially means:

  1. It tries to do as few view inflations as possible.
  2. It only paints and lays out children that are (or are about to become) visible on screencode.

The reason for 1 is simple: layout inflations are expensive operationscode. Although layout files are compiled into binary form for more efficient parsingcode, inflations still involve going through a tree of special XML blockscode and instantiating all respective views. ListView solves this problem by recyclingcode non-visible views—called “ScrapViews” in Android’s source code—as you pan around. This means that developers can simply update the contents of recycled viewscode instead of inflating the layout of every single row—more on that later.

In order to implement 2, ListView uses the view recycler to keep adding recycled views below or above the current viewport and moving active views to a recyclable pool as they move off-screencode while scrolling. This wayListView only needs to keep enough views in memory to fill its allocated space in the layout and some additional recyclable views—even when your adapter has hundreds of items. It will fill the space with rows in different ways—from top, from bottom, etc—depending on how the viewport changedcode. The image below visually summarizes what happens when you pan a ListViewdown.

With this framework in mind, let’s move on to the tips. As you’ve seen above,ListView dynamically inflates and recycles tons of views when scrolling so it’s key to make your adapter’s getView() as lightweight as possible. All tips resolve around making getView() faster in one way or another.

View recycling

Every time ListView needs to show a new row on screen, it will call thegetView() method from its adapter. As you know, getView() takes three arguments arguments: the row position, a convertView, and the parentViewGroup.

The convertView argument is essentially a “ScrapView” as described earlier. It will have a non-null value when ListView is asking you recycle the row layout. So, when convertView is not null, you should simply update its contents instead of inflating a new row layout. The getView() code in your adapter would look a bit like:

public View getView(int position, View convertView, ViewGroup parent) {
    if (convertView == null) {
        convertView = mInflater.inflate(R.layout.your_layout, null);
    }

    TextView text = (TextView) convertView.findViewById(R.id.text);
    text.setText("Position " + position);

    return convertView;
}

View Holder pattern

Finding an inner view inside an inflated layout is among the most common operations in Android. This is usually done through a View method calledfindViewById(). This method will recursively go through the view tree looking for a child with a given IDcode. Using findViewById() on static UI layouts is totally fine but, as you’ve seen, ListView calls the adapter’s getView() very frequently when scrolling. findViewById() might perceivably hit scrolling performance in ListViews—especially if your row layout is non-trivial.

The View Holder pattern is about reducing the number of findViewById() calls in the adapter’s getView(). In practice, the View Holder is a lightweight inner class that holds direct references to all inner views from a row. You store it as a tag in the row’s view after inflating it. This way you’ll only have to usefindViewById() when you first create the layout. Here’s the previous code sample with View Holder pattern applied:

public View getView(int position, View convertView, ViewGroup parent) {
    ViewHolder holder;

    if (convertView == null) {
        convertView = mInflater.inflate(R.layout.your_layout, null);

        holder = new ViewHolder();
        holder.text = (TextView) convertView.findViewById(R.id.text);

        convertView.setTag(holder);
    } else {
        holder = convertView.getTag();
    }

    holder.text.setText("Position " + position);

    return convertView;
}

private static class ViewHolder {
    public TextView text;
}

Async loading

Very often Android apps show richer content in each ListView row such as images. Using drawable resources in your adapter’s getView() is usually fine as Android caches those internallycode. But you might want to show more dynamic content—coming from local disk or internet—such as thumbnails, profile pictures, etc. In that case, you probably don’t want to load them directly in your adapter’s getView() because, well, you should never ever block UI thread with IO. Doing so means that scrolling your ListView would look anything but smooth.

What you want to do is running all per-row IO or any heavy CPU-bound routine asynchronously in a separate thread. The trick here is to do that and still comply with ListView‘s recycling behaviour. For instance, if you run anAsyncTask to load a profile picture in the adapter’s getView(), the view you’re loading the image for might be recycled for another position before theAsyncTask finishes. So, you need a mechanism to know if the view hasn’t been recycled once you’re done with the async operation.

One simple way to achieve this is to attach some piece of information to the view that identifies which row is associated with it. Then you can check if the target row for the view is still the same when the async operation finishes. There are many ways of achieving this. Here is just a simplistic sketch of one way you could do it:

public View getView(int position, View convertView,
        ViewGroup parent) {
    ViewHolder holder;

    ...

    holder.position = position;

    new ThumbnailTask(position, holder)
            .executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, null);

    return convertView;
}

private static class ThumbnailTask extends AsyncTask {
    private int mPosition;
    private ViewHolder mHolder;

    public ThumbnailTask(int position, ViewHolder holder) {
        mPosition = position;
        mHolder = holder;
    }

    @Override
    protected Cursor doInBackground(Void... arg0) {
        // Download bitmap here
    }

    @Override
    protected void onPostExecute(Bitmap bitmap) {
        if (mHolder.position == mPosition) {
            mHolder.thumbnail.setImageBitmap(bitmap);
        }
    }
}

private static class ViewHolder {
    public ImageView thumbnail;
    public int position;
}

Interaction awareness

Asynchronously loading heavier assets for each row is an important step to towards a performant ListView. But if you blindly start an asynchronous operation on every getView() call while scrolling, you’d be wasting a lot of resources as most of the results would be discarded due to rows being recycled very often.

We need to add interaction awareness to your ListView adapter so that it doesn’t trigger any asynchronous operation per row after, say, a fling gesture on the ListView—which means that the scrolling is so fast that it doesn’t make sense to even start any asynchronous operation. Once scrolling stops, or is about to stop, is when you want to start actually showing the heavy content for each row.

I won’t post a code sample for this—as it involves too much code to post here—but the classic Shelves app by Romain Guy has a pretty good example. It basically triggers the async book cover loading once the GridView stops scrolling among other things. You can also balance interaction awareness with an in-memory cache so that you show cached content even while scrolling. You got the idea.