Sending a constant rate data stream from Droid via TCP/IP
Question: in my quest to learn Java / Android development, I’m running into lots of
roadblocks. Mainly because I don’t really know much about threading and
communication between threads/processes. I’m trying to stream the IMU data
from an android device to a python application on a computer. Whenever the
sensor values change, a sensor listener saves the current values into a
variable for the network handler to access.

The network handler in turn is supposed to run on a timer, sending the values
and a current timestamp at a more or less fixed rate of 33Hz (perhaps a bit
fast? well, I’d be willing to accept as slow as 10Hz, but no slower than
that). Anyway, when I tested this, I could see on the computer interface that
the data isn’t nearly coming in at a steady pace of 30 per second, but rather
comes in surges, sometimes not coming at all for a second, and overall
accumulating quite the delay (ie. the later the values are, the more delayed
they come in). I understand there might be some variability in the network and
some lags, but I would at least like the overall pace to at least be correct,
ie that it doesn’t get worse and worse the longer I’m sending.

Considering the devices are both on a common wifi network, and I’m capable of
streaming 1080p video without any lags over wifi, I’m fairly confident that
the protocol should be able to handle a 64 Byte string every 30ms without
troubles. To eliminate the sensor reader as an problem source, I made a
minimum working example that simply sends a string every 30ms, without any
sensor reading. I basically got this code from various stackoverflow posts,
and modified it until it more or less did what I wanted. The problem is that
the network interface runs in an AsynchronousTask, for which I am uncertain
how to access it once it has been started. My theory is that it’s wasting
resources to open a new socket for every new data packet, but I’m unsure how
to open the socket once in the background and then pass the values to it on a
timer and tell it to send.

Here’s the basic activity I made to test this:

    
    
    package com.jamesdoesntlikejava.motionlearning15;
    
    import android.support.v7.app.ActionBarActivity;
    import android.os.Bundle;
    import android.view.Menu;
    import android.view.MenuItem;
    import android.widget.Toast;
    import java.util.Timer;
    import java.util.TimerTask;
    
    
    public class SendValuesActivity extends ActionBarActivity {
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_send_values);
            // creating timer task, timer
            final Timer timer = new Timer();
            TimerTask taskNew = new TimerTask() {
                @Override
                public void run() {
                    int counter = 0;
                    int numsteps = 333;
                    String params[] = new String[2];
                    if (counter < numsteps) {
                        params[0] = "192.168.1.33";
                        long currentTime = System.currentTimeMillis();
                        params[1] = Long.toString(currentTime)+"blablabla";
                        new ServerCommunicationTask().execute(params);
                        counter++;
                    } else  {
                        timer.cancel();
                        timer.purge();
                    }
                }
            };
            // scheduling the task at fixed rate delay
            Toast.makeText(this, "Sending Values in 1s...", Toast.LENGTH_SHORT).show();
            timer.scheduleAtFixedRate(taskNew,1000,30);
        }
    
    
        @Override
        public boolean onCreateOptionsMenu(Menu menu) {
            // Inflate the menu; this adds items to the action bar if it is present.
            getMenuInflater().inflate(R.menu.menu_send_values, menu);
            return true;
        }
    
        @Override
        public boolean onOptionsItemSelected(MenuItem item) {
            // Handle action bar item clicks here. The action bar will
            // automatically handle clicks on the Home/Up button, so long
            // as you specify a parent activity in AndroidManifest.xml.
            int id = item.getItemId();
    
            //noinspection SimplifiableIfStatement
            if (id == R.id.action_settings) {
                return true;
            }
    
            return super.onOptionsItemSelected(item);
        }
    }
    

And this is the class for doing the networking:

    
    
    package com.jamesdoesntlikejava.motionlearning15;
    
    import android.os.AsyncTask;
    import java.io.BufferedWriter;
    import java.io.IOException;
    import java.io.OutputStreamWriter;
    import java.net.Socket;
    import java.net.UnknownHostException;
    
    public class ServerCommunicationTask extends AsyncTask<String, Void, String> {
    
        public final static int TCP_SERVER_PORT = 13337;
        // params are 0: the target IP and 1: the message to send.
        @Override
        protected String doInBackground(String[] params) {
    
            String TCP_SERVER_IP = params[0];
            try {
                Socket s = new Socket(TCP_SERVER_IP, TCP_SERVER_PORT);
                BufferedWriter out = new BufferedWriter(new OutputStreamWriter(s.getOutputStream()));
                //send output msg
                String outMsg = params[1];
                out.write(outMsg);
                out.flush();
                //close connection
                s.close();
            } catch (UnknownHostException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }
            return "success";
        }
    
        @Override
        protected void onPostExecute(String response) {
        }
    }
    

Running on a Moto G LTE (updated 1st gen) with android 5.1. Any hints are
appreciated, thanks!


Answer: Instead of AsyncTask and always opening new connection, you can use Thread.

    
    
    import java.io.BufferedWriter;
    import java.io.IOException;
    import java.io.OutputStreamWriter;
    import java.net.Socket;
    import java.net.UnknownHostException;
    import java.util.ArrayList;
    
    public class ServerCommunicationThread extends Thread {
    
        public final static int TCP_SERVER_PORT = 13337;
    
        private ArrayList<String> mMessages = new ArrayList<>();
        private String mServer;
    
        private boolean mRun = true;
    
        public ServerCommunicationThread(String server) {
            this.mServer = server;
        }
    
        @Override
        public void run() {
    
            while (mRun) {
                Socket s = null;
                try {
                    s = new Socket(mServer, TCP_SERVER_PORT);
                    BufferedWriter out = new BufferedWriter(new OutputStreamWriter(s.getOutputStream()));
    
                    while (mRun) {
                        String message;
    
                        // Wait for message
                        synchronized (mMessages) {
                            while (mMessages.isEmpty()) {
                                try {
                                    mMessages.wait();
                                } catch (InterruptedException e) {
                                    e.printStackTrace();
                                }
                            }
                            // Get message and remove from the list
                            message = mMessages.get(0);
                            mMessages.remove(0);
                        }
    
                        //send output msg
                        String outMsg = message;
                        out.write(outMsg);
                        out.flush();
                    }
    
                } catch (UnknownHostException e) {
                    e.printStackTrace();
                } catch (IOException e) {
                    e.printStackTrace();
                } finally {
                    //close connection
                    if (s != null) {
                        try {
                            s.close();
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        }
    
        public void send(String message) {
            synchronized (mMessages) {
                mMessages.add(message);
                mMessages.notify();
            }
        }
    
        public void close() {
            mRun = false;
        }
    }
    

You can keep the thread running with connection opened and send message when
needed.

    
    
    ServerCommunicationThread thread = new ServerCommunicationThread("192.168.1.33");
    thread.start();
    ...
    thread.send("blablabla");
    ...
    thread.send("blablabla");
    ...
    thread.close();
    

Please note that this code is not tested.

