I have a UDP Server/Client running in background with Service, these are my operations:
I launch the app
The Service starts
I close the app but the Service is running in background (this is
what I want)
I send udp message and the phone receives correctly and answer me
I don't send messages for about 5 minutes
I send message, my phone doesn't answer me
I try to send another message again, my phone now answer
How could it happen? My App seems to sleep and wake up when I send the first message but it could answer only the second or sometimes I need 4-5 messages before get an answer, maybe is this latency or other?
If I flood my App it always will answer me correctly, but if I don't send messages for an amount of time it will cause the problem.
I want my App answer me everytime, even if the app is closed or the phone is locked.
This is my code:
public class UDPListenerService extends Service {
DatagramSocket socket;
private Boolean shouldRestartSocketListen = true;
Thread UDPBroadcastThread;
private void listenAndWaitAndThrowIntent() throws Exception {
try {
byte[] receiveData = new byte[1024];
byte[] sendData = new byte[1024];
DatagramSocket serverSocket = new DatagramSocket(9876);
while (true) {
DatagramPacket receivePacket = new DatagramPacket(receiveData, receiveData.length);
serverSocket.receive(receivePacket);
String sentence = new String( receivePacket.getData());
System.out.println("RECEIVED: " + sentence);
InetAddress IPAddress = receivePacket.getAddress();
int port = receivePacket.getPort();
String capitalizedSentence = sentence.toUpperCase();
sendData = capitalizedSentence.getBytes();
DatagramPacket sendPacket = new DatagramPacket(sendData, sendData.length, IPAddress, port);
serverSocket.send(sendPacket);
receiveData = new byte[1024];
}
} catch (Exception e) {
}
}
void startListenForUDPBroadcast() {
UDPBroadcastThread = new Thread(new Runnable() {
public void run() {
try {
while (shouldRestartSocketListen) {
listenAndWaitAndThrowIntent();
}
//if (!shouldListenForUDPBroadcast) throw new ThreadDeath();
} catch (Exception e) {
Log.i("UDP", "no longer listening for UDP broadcasts cause of error " + e.getMessage());
}
}
});
UDPBroadcastThread.start();
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
shouldRestartSocketListen = true;
startListenForUDPBroadcast();
Log.i("UDP", "Service started");
return START_STICKY;
}
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public void onCreate() {
}
#Override
public void onDestroy() {
stopListen();
}
void stopListen() {
shouldRestartSocketListen = false;
socket.close();
}
}
The reason your app stops is because the service is put to sleep by the system. Maybe you need use the bindService() method in the calling activity.
Check https://developer.android.com/guide/components/services.html for more info about the methods and the lifecycles of services.
Related
I am working with images to send on the device, that works fine when I am using Async Task it works well but the problem is when I do it via Service it doesn't work and I get this error, "Unable to start Service with intent caused by "NetworkOnMainThreadException"". I am actually passing the byte[] from MainActivity to Service
I need to use service because OnReceive method of BroadcastReceiver cannot respond to Async task.
Thank you!
In the Mainfest.xml
<service android:name=".SendImageClientService"/>
MainActivity
private BroadcastReceiver wifiStateReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
int wifiStateExtra = intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE,
WifiManager.WIFI_STATE_UNKNOWN);
if(wifiStateExtra==WifiManager.WIFI_STATE_ENABLED){
sendingDrawableImage();
}else if(wifiStateExtra==WifiManager.WIFI_STATE_DISABLED){
Toast.makeText(context, "Please Check Your Internet Connection", Toast.LENGTH_SHORT).show();
}
}
};
private void sendingDrawableImage() {
drawable = (BitmapDrawable) imageView.getDrawable();
bitmap = drawable.getBitmap();
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, byteArrayOutputStream);
byte[] array = byteArrayOutputStream.toByteArray();
Intent serviceIntent=new Intent(this,SendImageClientService.class);
serviceIntent.putExtra("byte",array);
this.startService(serviceIntent);
}
#Override
protected void onStart() {
super.onStart();
IntentFilter intentFilter = new
IntentFilter(WifiManager.WIFI_STATE_CHANGED_ACTION);
registerReceiver(wifiStateReceiver, intentFilter);
}
Service class
public class SendImageClientService extends Service {
Handler handler = new Handler(Looper.getMainLooper());
#Nullable
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
try {
byte[] bytesss=intent.getByteArrayExtra("byte");
Socket socket = new Socket("ip_address_here", 8888);
OutputStream out = socket.getOutputStream();
DataOutputStream dataOutputStream = new DataOutputStream(out);
dataOutputStream.write(bytesss);
handler.post(new Runnable() {
#Override
public void run() {
Toast.makeText(SendImageClientService.this, "Image sent", Toast.LENGTH_SHORT).show();
}
});
dataOutputStream.close();
out.close();
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
return START_STICKY;
}
#Override
public void onDestroy() {
super.onDestroy();
}
}
the documentation says:
Caution: A service runs in the main thread of its hosting process; the
service does not create its own thread and does not run in a separate
process unless you specify otherwise. If your service is going to
perform any CPU-intensive work or blocking operations, such as MP3
playback or networking, you should create a new thread within the
service to complete that work. By using a separate thread, you can
reduce the risk of Application Not Responding (ANR) errors, and the
application's main thread can remain dedicated to user interaction
with your activities.
https://developer.android.com/guide/components/services
so, you need to create new thread in your service for a long operation.
Also, you need to know, that Services are using for a long background tasks and IntentServices are using for a short background tasks. But IntentService work in separate thread.
You can use something like this:
new Handler().post(runnable)
where runnable is your long operation action (like internet action or database action). Handler is a specific class for working with thread in android.
Edit.
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
try {
handler.post(new Runnable() {
#Override
public void run() {
byte[] bytesss=intent.getByteArrayExtra("byte");
Socket socket = new Socket("ip_address_here", 8888);
OutputStream out = socket.getOutputStream();
DataOutputStream dataOutputStream = new DataOutputStream(out);
dataOutputStream.write(bytesss);
dataOutputStream.close();
out.close();
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
});
return START_STICKY;
}
OVERVIEW
I'm using Android Studio to make an app that on a button press sends a string to a UDP listener in Node-Red running on my laptop, Node-Red filters anything that comes in and function nodes do their thing. This app will work inside a LAN not over the internet.
So far I have made a new project with an empty activity and my activity_main.xml has the button. There is no need for the user to input a string/text so the button press code will have the "string" and Node-Red listener IP and port hard coded.
There is also no need to receive a reply from the laptop/node-red side so the button press should be a fire and forget hard coded message hence UDP and not a TCP socket.
QUESTION
What code is required for the MainActivity to send the string when the button is pressed to the UDP listener in Node-Red?
I have spent a long time scouring the internet for answers and tried many code examples however they have not worked. A lot of the research I've seen is people with UDP receive problems, however I cannot understand their code for sending UDP.
I finally worked it out with a ridiculous amount of trial and error... please see the code below if anyone else ever gets the same problem:
//On button press the message is sent via UDP
findViewById(R.id.btSendMessage).setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
int txnumber = Integer.parseInt(((TextView) findViewById(R.id.transmitnumber)).getText().toString());
String data = ((TextView) findViewById(R.id.texttosend)).getText().toString();
int port = Integer.valueOf(((TextView) findViewById(R.id.serverport)).getText().toString());
String address = ((TextView) findViewById(R.id.serverip)).getText().toString();
SendData(txnumber, data, port, address);
}
});
private DatagramSocket UDPSocket;
private InetAddress address;
private int port;
public void Theaddress(InetAddress address) {
try {
this.UDPSocket = new DatagramSocket();
this.address = address;
} catch (SocketException e) {
e.printStackTrace();
}
}
public void SendInstruction(final byte[] data, final int port) {
new Thread() {
#Override
public void run() {
try {
DatagramPacket packet = new DatagramPacket(data, data.length, address, port);
UDPSocket.send(packet);
DatagramPacket packetreponse = null;
UDPSocket.receive(packetreponse);
DisplayData(packetreponse);
} catch (Exception e) {
e.printStackTrace();
}
}
}.start();
}
public void SendData(final int nbRepet, final String Sdata , final int port, final String address) {
new Thread() {
#Override
public void run() {
try {
Theaddress(InetAddress.getByName(address));
for (int i = 0; i < nbRepet; i++) {
byte[] data = Sdata.getBytes();
SendInstruction(data,port);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}.start();
}
public void ReceiveData(final int portNum) {
new Thread() {
#Override
public void run() {
try {
final int tally = 1024;
final byte[] buffer = new byte[tally];
DatagramSocket socketReceive = new DatagramSocket(portNum);
while (true) {
DatagramPacket data = new DatagramPacket(buffer, buffer.length);
socketReceive.receive(data);
DisplayData(data);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}.start();
}
public void DisplayData(DatagramPacket data) {
System.out.println(data);
}
I am creating an application that will monitor movements in a particular Android device (client) and report such instances to another Android device (server). Also, under specific conditions, the client will take a picture and transmit the image to the server.
I am using WiFi direct to setup the connection between the two devices. After that I am using socket connections as explained in the WiFi Direct Demo. I am using port 8988 to send the motion sensor events and I am using port 8987 to send the images capture.
On the server side, I am using two different instances of the same Async Task with serversocket connecting to different ports to listen for the incoming messages. Everything works fine as long as only the motion sensor events are being sent across. The first image capture is also being sent/received correctly. However, after that the server doesn't receive any additional messages. I tried having two different Async Task classes to avoid having two instances of the same class but that didn't work as well. I also tried having one as an Async Task and another as an Intent Service but even that doesn't work.
This is IntentService I am using to send the messages across to the server.
public class MessageSender extends IntentService {
public static final String EXTRAS_TIMEOUT = "timeout";
public static final String EXTRAS_ADDRESS = "go_host";
public static final String EXTRAS_PORT = "go_port";
public static final String EXTRAS_DATA = "data";
private Handler handler;
public MessageSender(String name) {
super(name);
}
public MessageSender() {
super("MessageTransferService");
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
handler = new Handler();
return super.onStartCommand(intent, flags, startId);
}
#Override
protected void onHandleIntent(Intent intent) {
String host = intent.getExtras().getString(EXTRAS_ADDRESS);
Socket socket = new Socket();
int port = intent.getExtras().getInt(EXTRAS_PORT);
byte[] data = intent.getExtras().getByteArray(EXTRAS_DATA);
int timeout = intent.getExtras().getInt(EXTRAS_TIMEOUT);
try {
socket.bind(null);
socket.connect((new InetSocketAddress(host, port)), timeout);
OutputStream stream = socket.getOutputStream();
stream.write(data);
} catch (final IOException e) {
handler.post(new Runnable() {
#Override
public void run() {
Toast.makeText(getApplicationContext(), "Exception has occurred: " + e.getMessage(),
Toast.LENGTH_SHORT).show();
}
});
} finally {
if (socket != null) {
if (socket.isConnected()) {
try {
socket.close();
/*handler.post(new Runnable() {
#Override
public void run() {
Toast.makeText(getApplicationContext(), "Socket Connection closed now..",
Toast.LENGTH_SHORT).show();
}
});*/
} catch (IOException e) {
// Give up
e.printStackTrace();
}
}
}
}
}
}
This is Async Task on the server that starts listeners on two ports (8987 and 8988) to receiver the information of motion sensor events and images.
public class MessageReceiver extends AsyncTask<Void, Void, String> {
private Context context;
private int port;
private Bitmap mBitmap;
public MessageReceiver(Context context, int port) {
this.context = context;
this.port = port;
}
#Override
protected String doInBackground(Void... params) {
try {
ServerSocket serverSocket = new ServerSocket(port);
Socket client = serverSocket.accept();
InputStream inputstream = client.getInputStream();
String returnString = "";
if (port == MainActivity.PORT_SENSOR_COMM) {
// do something
} else if (port == MainActivity.PORT_IMAGE_COMM) {
//do something
}
serverSocket.close();
return returnString;
} catch (Exception e) {
return "Exception Occurred:" + e.getMessage();
}
}
#Override
protected void onPostExecute(String result) {
boolean startNewTask = true;
if (port == MainActivity.PORT_SENSOR_COMM) {
//do something
} else if (port == MainActivity.PORT_IMAGE_COMM) {
//do something
}
//doing this to start listening for new messages again
new MessageReceiver(context, port).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}
#Override
protected void onPreExecute() {
}
}
I am now wondering whether Android WiFiDirect allows parallel communication between two devices on different ports. Searched the docs but could'nt find much help. What I am doing wrong? What is the correct method to accomplish what I am trying to do? Any help would be greatly appreciated. Thanks for looking.
I'm trying to launch service and then open socket to have connection with server.
On button click I create new Thread and then start service.
Thread t = new Thread(){
public void run(){
mIntent= new Intent(MainActivity.this, ConnectonService.class);
mIntent.putExtra("KEY1", "Value used by the service");
context.startService(mIntent);
}
};
t.start();
Then on service, I try to open socket and have connection with server
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
//TODO do something useful
try {
InetAddress serverAddr = InetAddress.getByName(SERVER_IP);
socket = new Socket(serverAddr, SERVERPORT);
Scanner scanner = new Scanner(socket.getInputStream());
message = scanner.nextLine();
} catch (IOException e) {
e.printStackTrace();
}
return Service.START_NOT_STICKY;
}
But when I call it, I have error
08-30 08:56:49.268: E/AndroidRuntime(3751): java.lang.RuntimeException: Unable to start service com.example.testofconnection.ConnectonService#40ef02a8 with Intent { cmp=com.example.testofconnection/.ConnectonService (has extras) }: android.os.NetworkOnMainThreadException*
I think problem is that service is on main thread, but I can't find how should I start service on new (independend) thread to keep connection alive?
You can use IntentService for this. Just launch it normally with an Intent from the main thread. onHandleIntent() method gets executed in background thread. Put your socket-code in there. Here is an example code.
public class MyIntentService extends IntentService {
public MyIntentService() {
super("MyIntentService");
}
#Override
protected void onHandleIntent(Intent intent) {
// this method is called in background thread
}
#Override
public IBinder onBind(Intent intent) {
return null;
}
}
In your activity you start the service as following.
startService(new Intent(this, MyIntentService.class));
If you need a long-lasting service, you can create a normal service and start a thread there. Here is an example. Make sure you launch it as "foreground" service. This will allow service to run longer without been killed by Android.
public class MyAsyncService extends Service {
private AtomicBoolean working = new AtomicBoolean(true)
private Runnable runnable = new Runnable() {
#Override
public void run() {
while(working.get()) {
// put your socket-code here
...
}
}
}
#Override
public void onCreate() {
// start new thread and you your work there
new Thread(runnable).start();
// prepare a notification for user and start service foreground
Notification notification = ...
// this will ensure your service won't be killed by Android
startForeground(R.id.notification, notification);
}
#Override
public onDestroy() {
working.set(false)
}
}
Move this code to your thread:
try {
InetAddress serverAddr = InetAddress.getByName(SERVER_IP);
socket = new Socket(serverAddr, SERVERPORT);
Scanner scanner = new Scanner(socket.getInputStream());
message = scanner.nextLine();
} catch (IOException e) {
e.printStackTrace();
}
Just as an example (I'm not sure it this fits to your task):
Thread t = new Thread(){
public void run(){
try {
InetAddress serverAddr = InetAddress.getByName(SERVER_IP);
socket = new Socket(serverAddr, SERVERPORT);
Scanner scanner = new Scanner(socket.getInputStream());
message = scanner.nextLine();
} catch (IOException e) {
e.printStackTrace();
}
mIntent= new Intent(MainActivity.this, ConnectonService.class);
mIntent.putExtra("KEY1", "Value used by the service");
context.startService(mIntent);
}
};
t.start();
You should know that a service is running on the UI thread, so you got this error. Check this nice site for more information about various threading approaches in Android.
I'm developing an Android application.
This application will have a server to start a DatagramSocket as a server. It will wait for incoming message. When the socket get a message I will process it.
To start a UDP Server socket I'm going to use a Local Service. This service will have a worker thread where I'm going to listen to incoming messages.
This is my unfinished Local Service implementation:
public class UDPSocketBackgroundService extends Service
{
private static final String TAG = "UDPSocketBackgroundService";
private ThreadGroup myThreads = new ThreadGroup("UDPSocketServiceWorker");
private Handler mServiceHandler;
#Override
public void onCreate()
{
super.onCreate();
Log.v(TAG, "in onCreate()");
}
#Override
public IBinder onBind(Intent arg0)
{
try
{
new Thread(myThreads, new UDPServerThread("X", 8888)).start();
}
catch (IOException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
return null;
}
}
And this is my also unfinished Worker Thread implementation:
public class UDPServerThread extends Thread
{
private static final int MESSAGE_SIZE = 256;
protected DatagramSocket socket = null;
protected boolean end = false;
public UDPServerThread(String serverName, int port) throws IOException
{
super(serverName);
socket = new DatagramSocket(port);
}
public void run()
{
while (!end)
{
try
{
byte[] buf = new byte[MESSAGE_SIZE];
// Wait an incoming message.
DatagramPacket packet = new DatagramPacket(buf, buf.length);
socket.receive(packet);
// TODO: Notify Service with packet received
}
catch (IOException e)
{
// TODO Mensaje de error.
e.printStackTrace();
}
}
}
}
Those classes have their own file (they are on different files).
Here:
socket.receive(packet);
//TODO: Notify Service with packet received
How can I notify service that we have received a packet? I want to send to service that packet also.
Here there is an example on how to communicate from Main thread to worker thread. But, I don't need that, I'm looking for an example on how to communicate from worker thread to service.
I've found this example, but I don't understand it very well because on that example both classes are declare it on the same file.
As you can see, I'm a newbie on Android development.
If you know a better approach, please tell me.
When you create the UDPServerThread, you could pass in a reference to the UDPSocketBackgroundService and then call a method on it (processPacket() for example) when packets are received. This processPacket() method will need to use some sort of synchronization.
Here's a small code excerpt of the related functions:
public class UDPSocketBackgroundService extends Service
{
....
#Override
public IBinder onBind(Intent arg0)
{
try
{
new Thread(myThreads, new UDPServerThread(this, "X", 8888)).start();
// Notice we're passing in a ref to this ^^^
}
...
}
public void processPacket(DatagramPacket packet)
{
// Do what you need to do here, with proper synchronization
}
}
public class UDPServerThread extends Thread
{
private static final int MESSAGE_SIZE = 256;
protected DatagramSocket socket = null;
protected boolean end = false;
protected UDPSocketBackgroundService = null;
public UDPServerThread(UDPSocketBackgroundService service, String serverName, int port) throws IOException
{
super(serverName);
this.service = service;
socket = new DatagramSocket(port);
}
...
public void run()
{
while (!end)
{
try
{
byte[] buf = new byte[MESSAGE_SIZE];
// Wait an incoming message.
DatagramPacket packet = new DatagramPacket(buf, buf.length);
socket.receive(packet);
service.processPacket(packet);
}
...
}
...
}
}
Notice that going this approach, the UDPSocketBackgroundService is now "tightly coupled" with the UDPServerThread. Once you get this working, you may consider refactoring it with a more elegant design where there is less coupling, but for now this should get you going :)