通过TCP / IP从Droid发送恒定速率数据流 [英] Sending a constant rate data stream from Droid via TCP/IP
问题描述
在我学习Java / Android开发的过程中,我遇到了很多障碍。主要是因为我对线程/进程之间的线程和通信并不是很了解。我正在尝试将IMU数据从Android设备流式传输到计算机上的python应用程序。每当传感器值发生变化时,传感器侦听器会将当前值保存到变量中,供网络处理程序访问。
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.
网络处理程序依次在定时器上运行,以33Hz的或多或少的固定速率发送值和当前时间戳(可能有点快?好吧,我愿意接受10Hz的慢速,但不要慢于此。无论如何,当我测试这个时,我可以在计算机界面上看到数据几乎没有以每秒30的稳定速度进入,而是出现激增,有时甚至根本不会出现,并且整体累积相当延迟(即值越晚,它们进入的延迟越多)。我知道网络中可能存在一些变化和一些滞后,但我至少会喜欢总体速度至少是正确的,即我发送的时间越长,情况就越差。
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.
考虑到设备都在一个普通的wifi网络上,而且我能够在没有任何延迟的情况下通过wifi传输1080p视频,我相信协议应该能够处理每30毫秒一个64字节的字符串没有麻烦。为了消除传感器读取器作为问题源,我做了一个最小的工作示例,它每30ms只发送一个字符串,没有任何传感器读数。我基本上从各种stackoverflow帖子中获取此代码,并对其进行修改,直到它或多或少做了我想要的。问题是网络接口在AsynchronousTask中运行,我不确定如何在启动后访问它。我的理论是,浪费资源为每个新数据包打开一个新套接字,但我不确定如何在后台打开套接字,然后在计时器上将值传递给它并告诉它发送。
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);
}
}
这是进行网络的类: / p>
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) {
}
}
使用Android 5.1在Moto G LTE(更新的第1代)上运行。任何提示都表示赞赏,谢谢!
Running on a Moto G LTE (updated 1st gen) with android 5.1. Any hints are appreciated, thanks!
推荐答案
您可以使用Thread。而不是AsyncTask并始终打开新连接。
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.
这篇关于通过TCP / IP从Droid发送恒定速率数据流的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!