Android ANR , Service is locking main GUI Activity - android

Im starting several background services that take a while to configure due to web services calls, etc...
However Im starting these services via AsyncTask in order to avoid locking the main thread & GUI, however the GUI stills becomes locked.
Im using AsyncTask to call start a BluetoothService in my Activity onCreate():
I only included relevant lines of code:
//asynchronously - start the bluetooth service
new BluetoothServiceStart().execute();
Then in the BluetoothServiceStart service class, Im using Callable & Future task to get bytes from a web service:
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
// stop the service when the notification bar is pressed
if (intent != null && ACTION_STOP_SERVICE.equals(intent.getAction())) {
Log.d(TAG, "Stopping bluetooth service...");
broadcastServiceState(false);
stopSelf();
return START_NOT_STICKY;
}
// in case of attempting to restart while already running
clearSubscriptions();
Util.logToast(this, TAG, "Bluetooth service starting", Toast.LENGTH_SHORT, Util.DEBUG);
setupNotification();
// find and load JSON config file
loadDevices();
}
/**
* Gets the UUIDs of devices to connect to from the bluetooth JSON file.
*/
private void loadDevices() {
devicesLoaded = false;
Byte [] bytesFromWebService = null;
InputStream is = null;
URL url = null;
try {
if (ConnectivityMonitoring.hasNetwork()) {
//lets get the path of rest service that has the config file
String address = NgfrApp.getContext().getResources().getString(R.string.address);
String configuration_restful_port = NgfrApp.getContext().getResources().getString(R.string.rest_port);
String client_name = NgfrApp.getContext().getResources().getString(R.string.client_name);
String protocol = NgfrApp.getContext().getResources().getString(R.string.protocol);
//construct bluetooth config path
String bluetooth_config_path = NgfrApp.getContext().getResources().getString(R.string.bluetooth_path);
url = new URL(protocol + "://" + address + ":" + configuration_restful_port + bluetooth_config_path + client_name);
//lets execute an FutureTask (async task with a result, that blocks until result is returned).
ExecutorService exService = Executors.newSingleThreadExecutor();
Log.i(TAG, "making call to URL:" + url.toString());
Future<byte []> future = exService.submit(new CallWebServiceAndGetBytes(url));
bytesFromWebService = Util.toObjects(future.get());
}
if (bytesFromWebService != null) {
devices = readDeviceConfigFromWebService(bytesFromWebService);
Log.i(TAG, "Loaded configuration from URL:" + url.toString());
} else {
// read in the device UUIDs from the file
is = Util.scanForJson(getString(R.string.file_path), getString(R.string.bt_config_file));
devices = Util.readJsonStream(is, localConfigReadFunc);
Log.i(TAG, "Read config file from PATH:" + getString(R.string.file_path)+getString(R.string.bt_config_file));
}
if (devices != null) {
if (devices.size() < 1)
Log.w(TAG, "No devices to load!");
devicesLoaded = true;
}
// devices successfully loaded
if (devices != null && devicesLoaded) {
Log.d(TAG, "" + devices.size() + " BLE device IDs retrieved");
Log.d(TAG, "Devices: " + devices.toString());
}
// failed to load devices or find the JSON file
else {
Log.e(TAG, "Unable to load devices! Creating empty list...");
devices = new ArrayList<>();
}
}
catch (MalformedURLException e1) {
e1.printStackTrace();
}
catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
catch (FileNotFoundException e) {
Log.d(TAG, "Unable to locate bluetooth config file: " + getString(R.string.bt_config_file));
} catch (IOException e) {
Log.d(TAG, "Error reading json file: " + e.getMessage());
}
}//end loadDevices
Im getting an ANR & later crash.
Android Thread dump:
"main#4817" prio=5 tid=0x2 nid=NA waiting
java.lang.Thread.State: WAITING
blocks main#4817
at java.lang.Object.wait(Object.java:-1)
at java.lang.Thread.parkFor$(Thread.java:2135)
- locked <0x1a72> (a java.lang.Object)
at sun.misc.Unsafe.park(Unsafe.java:358)
at java.util.concurrent.locks.LockSupport.park(LockSupport.java:190)
at java.util.concurrent.FutureTask.awaitDone(FutureTask.java:450)
at java.util.concurrent.FutureTask.get(FutureTask.java:192)
at ngfr.wams.controller.core.services.RuleEngineService.loadRules(RuleEngineService.java:358)
at ngfr.wams.controller.core.services.RuleEngineService.updateRules(RuleEngineService.java:462)
at ngfr.wams.controller.core.services.RuleEngineService.onCreate(RuleEngineService.java:131)
at android.app.ActivityThread.handleCreateService(ActivityThread.java:3542)
at android.app.ActivityThread.-wrap4(ActivityThread.java:-1)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1786)
at android.os.Handler.dispatchMessage(Handler.java:105)
at android.os.Looper.loop(Looper.java:164)
at android.app.ActivityThread.main(ActivityThread.java:6938)
at java.lang.reflect.Method.invoke(Method.java:-1)
at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:327)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1374)
The error line points to future.get().
I understand that the future.get() blocks, which is intended behavior in order to wait for the web service to return the bytes otherwise in low network connectivity/bandwidth situations the code will continue to execute and miss the network response & data.
The future.get() blocks the Service, however since the BluetoothService is started using BluetoothServiceStart AsyncTask, then why is the UI blocked???
Thanks

It's a common mistake to assume, that a service is running on another thread than the starting activity. It will run on the main thread as well as stated here: Services
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.
The startService call will not change this behaviour, even if you call it in an AsyncTask. So if you want to reanimate your app, you should create a thread inside of your service, which is not blocking the service, thus not blocking the main thread.
Note that IntentServices offload the task to a worker thread but automatically stop when there is no further work to do.
Clients send requests through Context.startService(Intent) calls; the service is started as needed, handles each Intent in turn using a worker thread, and stops itself when it runs out of work.
Maybe this is not what you want with a bluetooth service.

Lifecycle methods always run on the man application loop, since the system creates the Service object for you. So, it's onStartCommand won't run on a background thread like you intend. If you want the Service to run on a background thread, use an IntentService.

Related

Android Open Accessory USB communication failing after sending large data packets

I have an android phone communicating with a Linux machine using AOA. The Linux machine is set up to initiate the connection, then wait for incoming data and echo it back to the phone unchanged. This works fine for small packets of data (less than 1024 bytes) from the phone. However, if I send exactly 1024 bytes, it appears to work from the Android end, but the computer never sees the packet, just any following ones that are smaller. If the phone attempts to send packets larger than 1024, these do get received by the computer, but the android phone will no longer be able to receive any packets from the computer. Further confusing the issue, this did work in the past, yet rolling back to earlier versions of the transmitting/receiving code on the phone doesn't seem to have any effect. The code on the computer has not been changed.
The android app checks for a USB accessory at start-up, and if one is found it starts a listener and sender thread. The sender thread waits on a blocking queue for outgoing packets, and sends them as soon as they are received. The listener thread continuously attempts to read from the input stream, which blocks until data is available. This is the code I use for setting up & running the threads:
private boolean openUSB(){
mUSBManager = (UsbManager) getSystemService(Context.USB_SERVICE);
mAccessory = mUSBManager.getAccessoryList();
if (mAccessory != null && mAccessory.length > 0) {
mParcelFileDescriptor = mUSBManager.openAccessory(mAccessory[0]);
mFileDescriptor = mParcelFileDescriptor.getFileDescriptor();
mListener = new Thread() {
public void run() {
listenerThread();
}
};
mListener.start();
mSender = new Thread() {
public void run() {
senderThread();
}
};
mSender.start();
displayText("Connected to USB accessory");
return true;
} else {
displayText("No USB accessory detected");
return false;
}
}
private void listenerThread(){
byte packet[] = new byte[SDR_PREFIX_SIZE+SDR_HEADER_SIZE+SDR_MAX_PAYLOAD+SDR_CRC_SIZE];
FileInputStream input = new FileInputStream(mFileDescriptor);
try {
ByteArrayOutputStream incoming = new ByteArrayOutputStream();
displayText("Listener Started");
while ( mFileDescriptor != null && input != null ) {
int read = input.read(packet,0,packet.length);
/* data in packet gets processed */
}
} catch ( Exception e) {
displayText("Listener Exception - "+e.getMessage(),true);
}
displayText("Listener Exited");
}
private void senderThread(){
displayText("sender started");
FileOutputStream output=new FileOutputStream(mFileDescriptor);
try {
byte data[] = mTransmitQueue.take();
while (data != null) {
displayText("Sending packet " + packet + ", "+data.length + " bytes");
output.write(data);
data = mTransmitQueue.take();
}
} catch ( Exception e) {
displayText("Sender Exception - "+e.getMessage(),true);
}
}
In the past, I had issues getting the listener and sender to work, until I found out that some of the intermediate objects that were used to create the file streams were being garbage-collected, yet were still needed. I now store all those intermediate objects to member variables (mUSBManager, mAccessory, mParcelFileDescriptor, mFileDescriptor) to give them persistence. I suspect that this issue is something similar, but I haven't been able to make any headway. I have been beating my head on this issue without any success, and really hope that others will have some insight on what is causing this.
I've found a work-around, expanding the buffer used for receiving data seems to fix the issue, even though the existing buffer was large enough for all packets. Increasing the buffer from 1524 to 2524 fixed the issue with incoming packets not being received. This is a kludgy solution, but it works.

Android client socket doesn't send data after some time

I'm developing an Android real-time-data app that sends data (floats and ints) to a server on the local subnet via a TCP socket. The problem I'm facing is that after sending some data simultaneously the socket doesn't send anymore data at all. I debugged the app and it shows that data is being sent but doesn't show up on the server. After this happens if I close the connection the server doesn't even get the notification that the connection has been terminated which it should according to my design model. Meanwhile I get an exception on the app saying it can not write to a broken pipe. This tells me that the problem is with the app because I also did test using a desktop app and I can send huge amounts of data to the server and it gets delivered.
And please keep in mind that the data size I'm talking about here is 252 bytes per packet.
Here's my class I'm using. (This runs in an AsyncTask object )
public class Network
{
private Socket handle;
public static enum TASK
{
TASK_CONNECT, TASK_SEND, TASK_CLOSE
}
public Network()
{
}
public String lastError = "";
public boolean Connect(String host, int port)
{
try
{
lastError = "Connecting to server.";
handle = new Socket(host, port);
handle.setTcpNoDelay(true); //
handle.setSendBufferSize(SIZE_OF_PACKET); ///==> These don't seem to help at all
handle.setKeepAlive(true); ///
return true;
}catch(IOException e)
{
lastError += e.getMessage() != null ? " "+ e.getMessage() : "";
return false;
}
}
private void err(String e){
System.err.println(e);
}
private boolean SendPacket(byte buffer[])
{
OutputStream oStream = null;
err("sending: " + buffer.length + " bytes");
try
{
lastError = "Obtaining output stream.";
oStream = handle.getOutputStream();
lastError = "Error sending data.";
oStream.write(buffer);
oStream.flush();
return true;
}catch(Exception e)
{
lastError += e.getMessage() != null ? " "+ e.getMessage() : "";
}
return false;
}
public void Close()
{
try{ handle.close(); handle = null; }catch(Exception e){} // swallow exception
}
}
I send my data in a loop depending on how many numbers I have. I tried a Google search but didn't find anything relevant. Has anyone experienced this before? It's making me mad now.
EDIT: Wireshark shows incoming "red" packets that don't reach the desktop app (server)
Look at this picture.
You can see the first few have Len > 0 the red ones have 0.
I think it's time Google interfaced the USB so we can use it. At least that'd would have been my first option.
Should you not be calling oStream.close() after you flush the stream, given that you never use it again?
Also, you say that this is being run in an AsyncTask object. Is it possible that multiple threads could be attempting to send packets at the same time? If so, you might need some form of synchronisation around the SendPacket method.
Ok. I solved the issue by using UDP instead. Thank you all.
But I still didn't find the source of the problem.

Use Socket to connect to emulator

I was trying to use SocketServer to setup a server
int i =1, PORT = 6666;
ServerSocket server;
Socket client;
try {
server = new ServerSocket(6666);
for(;;){
client = server.accept();//fail in here
textView.setText("server accept..." + i);
//new MyHttpServer(client, i, PORT).start();
i++;
}
} catch (IOException e) {
// TODO Auto-generated catch block
//e.printStackTrace();
textView.setText("Fail...");
}
However, the app always stops in server.accept(). I have already add internet permission for this app. I don't know why?
Update:
I have found the reason. It is because thread cannot start from an Active instance. I was putted server.accept() in onStart without a thread. Now I open a new Runnable for it, then fixed
There could be multiple reasons why your application can not start the server. My initial guess would be that you are trying to run the code on an emulator or a device and you already have some other application listening on that port.
You must check/provide the logcat trace in order to get to the cause of the error.
I think so your app will wait for client with port 6666.
server.accept();// return socket
Above code will return socket if client is available.For more details and clearity you can refer these links::
http://www.happygeek.in/socket-programming-in-android
http://www.edumobile.org/android/android-development/socket-programming/

android bluetooth application unresponsive during phone call

I have an application which communicates with a bluetooth device via async task
if I receive a phone call and during the call I return to the app
the screen dims and the application is unresponsive
back button doesn't work... and no ANR dialog is shown
any ideas?
here is the code which handles the connection:
#Override
protected Object doInBackground(Object... params) {
//boolean protocolUpdated;
int read = 0; // The amount of bytes read from the socket.
byte[] buff = new byte[MessageHandler.BUFFERSIZE]; // The data buffer.
byte[] tmpSend = null; // Misc bytes arrays returned from ProtocolParser as answers to send after decoding calls.
in = null;
out = null;
try {
if (Float.parseFloat(version) > 2.2){
Method m = dev.getClass().getMethod("createRfcommSocket", new Class[] {int.class});
sock = (BluetoothSocket) m.invoke(dev, 1);
}
else sock = dev.createRfcommSocketToServiceRecord(UUID_RFCOMM_GENERIC); // UUID is constant for serial BT devices.
sock.connect(); // connect to the BT device. This is rather heavy, may take 3 secs.
sendMessage(MESSAGE_CONNECTION_ESTABLISHED);
in = sock.getInputStream();
out = sock.getOutputStream();
timer = new Timer();
startFinishTimer(); //initialize finish timer
while(read != -1) { // read = -1 means EOF.
do { // as long as there is anything to send in the send queue - send it.
tmpSend = parser.nextSend();
if(tmpSend != null){
String msg = parseMessage(tmpSend);
Log.d("Writing:",msg);
out.write(tmpSend);
}
} while(tmpSend != null);
read = in.read(buff); // read. This is a blocking call, to break this, interrupt the thread.
timer.cancel();
startFinishTimer(); //read is a blocking call so timer should be restarted only after read bytes.
parser.parse(buff,read); // parse the read message using the logic in the ProtocolParser derived class.
tmpSend = parser.getPool(); // if pool ack is required - send it.
if (tmpSend != null){
Log.d("Writing:",parseMessage(tmpSend));
out.write(tmpSend);
}
if (read != 0){
Log.d("Read:",parseMessage(buff));
tmpSend = parser.getAnswer(); // if answer is required (based on message) - send it.
if(tmpSend != null){
out.write(tmpSend);
}
}
else {
Exception e = new IOException();
throw e;
}
}
}catch (IOException e){
e.printStackTrace();
Log.d("Connection: ", "Bluetooth Connection CRASHED!");
sendMessage(MESSAGE_CONNECTION_LOST);
}catch (Exception e){
e.printStackTrace();
}
return null;
}
Actually there is not enough context to find your problem.
Make sure that you launch this task from Main thread in other case PostExecute will be attached to wrong thread, you could get a race.
Make sure that you don't send same message to multiple handlers in your code.
Message it's a linked list and your could get ANR in that case.
Get /data/anr/traces.txt to make sure that it's not ANR.
You could make sure by time in the beginning of the file.

onStartCommand after service process is killed when started with START_STICKY

I have been reading the Android documentation and I am wondering if anyone can shed some light on what happens to a service instance when a service started with START_STICKY has it's process killed. I am assuming that the local state data (instance variables) are also lost. Does Android do anything to help re-populate the local state when it recreates the service?
I had some data that was sent to the service in an intent. In onStateCommand(), I would populate the service's instance data based on what was in the intent. From what I have read in the Android docs, the intent passed to onStartCommand() will be null when the service has been killed and restarted (with START_STICKY). Does this mean that I lose both the intent and the service's member data when the service is recreated?
When a process is killed and recreated, it goes through the entire lifecycle again (starting at onCreate). Depending on how it was killed and how you save data it may or may not be available to you.
As for getting the intent back, there's a flag for START_REDELIVER_INTENT that will redeliver the intent.
I recently came across this same problem. Service provides no built in means of saving state and the last intent may not be enough to get the service back to its previous state. My solution was to have the activity persist state and pass that state to the service via startService(). The service then just fires events at the activity, like:
here's an update
something died and here's the exception
I've been killed, please restart me with any necessary state
This approach cleaned up my design a lot, and both service and activity are resilient to being killed.
Android will not re-populate 'lost' data values when it re-starts your service so your code needs to cater for this eventuality.
My approach is to use all non-primitive state variables and to leave them as null. That way I can test for null and take appropriate steps to initialise them as and when.
I have taken to storing lightweight data that I want to persist across application restarts in the application's preferences.
use Internal Storage for Saving object or its field individually.
public void writeToInternalStorage(String fileName,String userName)
{
try{
String endOfLine = System.getProperty("line.separator");
StringBuffer buffer = new StringBuffer();
FileOutputStream fos = openFileOutput(fileName, Context.MODE_PRIVATE); //// MODE_PRIVATE will create the file (or replace a file of the same name) and make it private to your application. Other modes available are: MODE_APPEND, MODE_WORLD_READABLE, and MODE_WORLD_WRITEABLE.
buffer.append(userName.toString() + endOfLine);
fos.write(buffer.toString().getBytes());
Log.v(TAG, "writeFileToInternalStorage complete.. " + buffer.toString());
// writer.write(userName);
fos.close();
}
catch(Exception e)
{
Log.v(TAG, "Error: " + e.getMessage());
ExceptionNotificationMessage("writeToInternalStorage() Error: " + e.getMessage());
}
}
public String readFromInternalStorage(String fileName)
{
try{
File file = this.getFileStreamPath(fileName);
if(file.exists() == true)
{
Log.v(TAG, "readFileFromInternalStorage File found...");
FileInputStream fis = openFileInput(fileName);
StringBuilder buffer = new StringBuilder();
int ch;
while( (ch = fis.read()) != -1){
buffer.append((char)ch);
}
Log.v(TAG, "readFileFromInternalStorage complete.. " + buffer.toString());
fis.close();
return buffer.toString();
}
}
catch(Exception e)
{
Log.v(TAG, "Error: " + e.getMessage());
ExceptionNotificationMessage("readFromInternalStorage() Error: " + e.getMessage());
}
return "";
}

Categories

Resources