The following code is taken from here
Can someone please explain in deep how this works?
What is this handler and what does it do?
How is the while condition implemented?.
How does the loop proceed?
I have very crude understanding how the code works , it would very much benefit me if you could help me out. Thank you.
{
final Handler handler = new Handler();
final byte delimiter = 10; //This is the ASCII code for a newline character
stopWorker = false;
readBufferPosition = 0;
readBuffer = new byte[1024];
workerThread = new Thread(new Runnable()
{
public void run()
{
while(!Thread.currentThread().isInterrupted() && !stopWorker)
{
try
{
int bytesAvailable = mmInputStream.available();
if(bytesAvailable > 0)
{
byte[] packetBytes = new byte[bytesAvailable];
mmInputStream.read(packetBytes);
for(int i=0;i<bytesAvailable;i++)
{
byte b = packetBytes[i];
if(b == delimiter)
{
byte[] encodedBytes = new byte[readBufferPosition];
System.arraycopy(readBuffer, 0, encodedBytes, 0, encodedBytes.length);
final String data = new String(encodedBytes, "US-ASCII");
readBufferPosition = 0;
handler.post(new Runnable()
{
public void run()
{
myLabel.setText(data);
}
});
}
else
{
readBuffer[readBufferPosition++] = b;
}
}
}
}
catch (IOException ex)
{
stopWorker = true;
}
}
}
});
workerThread.start();
}
void sendData() throws IOException
{
String msg = myTextbox.getText().toString();
msg += "\n";
mmOutputStream.write(msg.getBytes());
myLabel.setText("Data Sent");
}
Be sure to know what a thread is, or I can clarify it.
The code you provide, as you should know, is defining your workerThread. All that is written in the run method will be run in another thread, once you call start() on that thread. All the the variables defined before the run method are define in the main thread. So is the Handler.
A Handler aims at linking 2 threads. You define it in one thread (here the main thread before the run method), and you use it in another thread (in the run method) to execute some action in thread where it was defined. SO in your code, you call
handler.post(new Runnable()
{
public void run()
{
myLabel.setText(data);
}
});
from the workerThread, so
myLabel.setText(data);
will be executed from the main thread (the thread where was instantiated your handler).
Why is it doing so? Because .setText() can't be called from another thread than the main thread as it is drawing something.
The while loop checks that the thread hasn't been interrupted (by Android or something else) and that your boolean stopWorker hasn't been modified, so that there was no Exception thrown while reading from your inputStream
try{}catch(){} is just a way to manage exceptions.
.available() method gives you the number of bytes you can read from the inpuStream by calling the .read() method. If there are some available bytes (so if you phone received something from the connected device), then it reads it.
To read method works this way: you pas a byte array as argument, and it will get the available bytes from the input stream and put them into the byte array.
Then it processes the bytes received...
But for me it's not an efficient solution, because the thread will loop very fast, whereas .read() is a blocking method, so all the part checking that checking that there are some bytes available is useless, and even inefficient. .read() would make the thread sleep until new bytes are available, and thus releasing resources. Maybe there is another reason why the code is doing so but I don't see it.
Hope that's clear.
Related
I'm developing an app to communicate with a PCB board via bluetooth.
I receive a string from the PCB board to my app every 50ms. This string has the next structure:
start_byte(1byte)/battery _level(1byte)/speed(1byte)/mode(1byte)
So I'll receive a string like this (I'll put it in hex):
80464B11
each 50ms.
This is the code. First this is the ConnectedThread which listens for the communication and that sends the received message to the mainActivity:
public void run() {
byte[] buffer = new byte[1024];
int readed;
while (true) {
try {
readed = inputStream.read(buffer);
if (readed > 0) {
final byte[] temp = new byte [readed];
System.arraycopy(buffer, 0, temp, 0, readed);
activity.runOnUiThread(new Runnable() {
#Override
public void run() {
/*Sends message to UI*/
connectionListener.msgRead(temp);
}
});
}
} catch (IOException e) {
...
break;
}
}
Then in MainActivity I operate with the received string to extract from it each value.
#Override
public void msgRead(byte[] buffer) {
String income = byteArrayToHex(buffer);
...
Here the next step would be to check for the start_byte and after this, get the other values.
But here comes my doubt. This string will be received each 50ms, so I'll be receiving something like this:
80464B1180464B1180464B1180464B1180464B1180464B1180464B1180464B1180464B11...
So, what I do to check for the start_byte is this:
String start_byte = income.substring(0, 2);
And then, if that matches with the start_byte value, I extract the rest of the values:
if (start_byte.equals("80")) {
...
Is my approach correct to face this? Won't the buffer overflow? How can I correctly check for the start_byte to the get the other values?
maybe it is usefull to just use the read() function. This function is blocking until one byte has been read. So you can make something like this:
int[] yourArray = new int[4];
for(int i = 0; i < 4; i++)
{
yourArray[i] = inputStream.read();
}
so now your string is devived in 4 int's stored in a array.
maybe this helps you out in some sort of way
I have faced the problem this way. I've created a Queue in the ConnectedThread. Each time I receive a byte[] I put it into the Queue.
LinkedList<Byte> dataQueue = new LinkedList<Byte>();
int i = 0;
while (i< temp.length) {
dataQueue.add(temp[i]);
i++;
}
Then, when I want to get them I do:
byte readed_byte = dataQueue.pop();
This way I get a byte from the head of the queue each time I do pop().
I am facing some problems trying to call main thread in my background thread.
Based on this post: Running code in main thread from another thread
The solution should be:
private void runOnMainThread() {
new Handler(Looper.getMainLooper()).post(new Runnable() {
#Override
public void run() {
// Do something
ottoBus.post(new MyObjectEvent(mMyObject));
// End do something
mMyObject = null;
}
});
}
However my background thread is still being able to read Bluetooth socket data between "Do something" and "End do something"
What am I missing here? Is it possible to lock the background thread, while "Do something" is being executed?
My code for reading socket data is following:
InputStream stream = null;
InputStreamReader reader = null;
BufferedReader bufferedReader = null;
String data = "";
try {
stream = mSocket.getInputStream();
byte[] bytes = new byte[20];
int numRead = 0;
while ((numRead = stream.read(bytes)) >= 0) {
String s = new String(bytes, 0, numRead);
if (mMyObject != null) {
fillData(s); // Can cause NPE
} else {
mMyObject = new MyObject();
fillData(s);
}
// This should be synchronised call
runOnMainThread();
Thanks.
You will need to use a Java pattern called wait/notify. Simply put: it defines two threads,
a producer and a consumer, so that the consumer, after initiating the producer, stops and waits until the producer thread has completed.
It goes like this:
static final object uiActionMonitor = new Object();
transient boolean uiCompleted;
void network_thread_run() {
int numRead = 0;
while ((numRead = stream.read(bytes)) >= 0) {
String s = new String(bytes, 0, numRead);
// This should be synchronised call
uiCompleted = false;
runOnMainThread();
synchronized(uiActionMonitor) { //<---------- wait for UI to complete
while (!uiCompleted) {
uiActionMonitor.wait();
}
}
}
And the UI code:
private void runOnMainThread() {
new Handler(Looper.getMainLooper()).post(new Runnable() {
#Override
public void run() {
// Do something
// End do something
uiCompleted = true;
synchronized(uiActionMonitor) { //<---------- release networking thread
uiActionMonitor.notifyAll();
}
}
});
}
Copy the synchronization logic exactly as is. This is where many developers get it wrong.
I must admit I fail to see why you need to block your networking thread while the UI thread is handling your message...
I find CountDownLatch to be the simplest way to accomplish this sort of thing. Here's a reusable method for running Runnables on the main thread and blocking on their completion:
private static final Handler mainHandler = new Handler(Looper.getMainLooper());
private static void runOnMainThreadBlocking(Runnable runnable) throws InterruptedException {
CountDownLatch completionSignal = new CountDownLatch(1);
mainHandler.post(new Runnable() {
#Override public void run() {
runnable.run();
completionSignal.countDown();
}
});
completionSignal.await();
}
I think you need to use locks or synchronized blocs. You can take a look into the java concurency documentation and more specificaly this and this part.
This way you can guaranty that on portion of the code won't be executed muliple times in parallel.
I'm using the FileWriter and it works fine except for these messages in the logcat when I write largish files of various sizes upto about 3MB.
I had a look at the FileUtils.java source and the write function doesn't use the getThreadPool() interface (the reader does).
As a test I thought I'd adapt the filewriter to use the runnable interface and was able to get the code to compile and execute - unfortunately the logcat messages still show up...
The blocking times I get are anything between 25ms and 1200ms so far. I haven't run any serious comparison tests to determine if this change makes any real difference - I was just looking for the absence of logcat messages.
Would these changes as below make any real difference?
Are these message something I should worry about?
My java is pretty basic - but here are the changes I made - following the reader implementation.
else if (action.equals("write")) {
this.write(args.getString(0), args.getString(1), args.getInt(2), args.getBoolean(3), callbackContext);
}
/* this is the original code
else if (action.equals("write")) {
long fileSize = this.write(args.getString(0), args.getString(1), args.getInt(2), args.getBoolean(3));
callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.OK, fileSize));
}
*/
And in the write function as below...
public void write(String filename, final String data, final int offset, final boolean isBinary, final CallbackContext callbackContext) throws FileNotFoundException, IOException, NoModificationAllowedException {
if (filename.startsWith("content://")) {
throw new NoModificationAllowedException("Couldn't write to file given its content URI");
}
final String fname = FileHelper.getRealPath(filename, cordova);
this.cordova.getThreadPool().execute(new Runnable() {
public void run() {
Log.d(LOG_TAG, "Starting write");
try {
boolean append = false;
byte[] rawData;
if (isBinary) {
rawData = Base64.decode(data, Base64.DEFAULT);
} else {
rawData = data.getBytes();
}
ByteArrayInputStream in = new ByteArrayInputStream(rawData);
FileOutputStream out = new FileOutputStream(fname, append);
byte buff[] = new byte[rawData.length];
in.read(buff, 0, buff.length);
out.write(buff, 0, rawData.length);
out.flush();
out.close();
Log.d(LOG_TAG, "Ending write");
callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.OK, rawData.length));
} catch (IOException e) {
Log.d(LOG_TAG, e.getLocalizedMessage());
callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.IO_EXCEPTION, NOT_READABLE_ERR));
}
}
});
}
Yes, these messages are important and you should use background thread for complicated tasks, such as file writing. The reason of this problem is that these tasks are blocking cordova and you could experience for example UI lags.
If your next actions are dependent on this task being completed, I recommend you using callback method.
I have written a program that constantly reads from the Bluetooth via SPP and prints the contents in the stream to a edittext box. I have the following thread:
myTimer = new Timer();
myTimer.schedule(new TimerTask(){
#Override
public void run(){
TimerMethod();
}
},0,1000);
private void TimerMethod(){this.runOnUiThread(startReading);}
private Runnable startReading = new Runnable(){
public void run(){
EditText _txtArea = (EditText) findViewById(R.id._txtArea);
try{
inStream = btSocket.getInputStream();
}catch (IOException e3) {
_txtArea.append("inStream establishment Failed!");
}
Now the msg's incoming can be of any size and I want to keep reading until there isn't anything remaining to be read. I tried an implementation where i did something like this:
byte[] msgIn = new byte[15];
inStream.read(msgIn, 0, 15);
int len = msgIn.length;
for (int i=0; i<len; i++){
out = new Character ((char) msgIn[i]).toString();
_txtArea.append(out);
But that limits the read to 15 bytes and the code doesn't seem very effecient. If anyone is wondering why i have the following line out = new Character ((char) msgIn[i]).toString(); it's because the data coming in is in ASCII i am converting it to a char. Also using this method after reading all of the contents when there is nothing else to read the program hangs. Does anyone know a way i can keep reading until all of the data has been read?
I figured it out, for those who are interested it is because the stream should be closed before a read so that inputStream.read() will be able to reach -1 after all data has been sent
I have implemented a bluetooth connection using the now-classic Google Bluetooth Chat code. However, I have a question which I just cannot seem to wrap my brain around.
The reading of the input stream goes something like this:
public void run() {
byte[] buffer = new byte[1024]; // buffer store for the stream
int bytes; // bytes returned from read()
// Keep listening to the InputStream until an exception occurs
while (true) {
try {
// Read from the InputStream
bytes = mmInStream.read(buffer);
// Send the obtained bytes to the UI Activity
mHandler.obtainMessage(MESSAGE_READ, bytes, -1, buffer)
.sendToTarget();
} catch (IOException e) {
break;
}
}
}
Now, that's fine if I was just printing out the characters I was receiving as in the original example. However, suppose I wanted to transfer an image file. I don't know the size of the file, so I cannot count the bytes received or anything like that. In my tests, I don't seem to be ever receiving a "-1" from the input stream, which appears to be the "norm" for reading from input streams. So how can I know that I have reached the end of the file that was being sent?
Thank you for your help and your time.
It seems Android bluetooth input streams never return -1.
I guess setup a simple protocol by sending file size in the first place and EOF signals at last will help.
No it does not. Android sends -1 only when the Socket is closed as far as I know. So a workaround could be to do a reconnect, but I was trying that for hours and did not get it working, since I do not understand this "special" Code here (copied from a Stackoverflow Thread) for setting up the socket:
BluetoothSocket tmp = null;
Log.d(TAG, "New Connection initialized");
Method m;
try {
m = device.getClass().getMethod("createRfcommSocket",
new Class[] { int.class });
tmp = (BluetoothSocket) m.invoke(device, 1);
} catch (Exception e) {
e.printStackTrace();
}
mmSocket = tmp;
This Socket only works, when my App is started for the first filetransfer. If I want to "Reconnect" with a completely new instantiated Object (and a new Socket created with that Code), the program freezes on the blocking method mmSocket.connect(). It seems like the Method never comes to an ending. This is driving me nuts...
Try
while ((bytes = mmInStream.read(buffer) != -1)
and see if that helps.
Try this:
public void run() {
byte[] buffer;
ArrayList<Integer> arr_byte = new ArrayList<Integer>();
while (true) {
try {
int data = mmInStream.read();
if(mmInStream.available()>0) {
arr_byte.add(data);
} else {
arr_byte.add(data);
buffer = new byte[arr_byte.size()];
for(int i = 0 ; i < arr_byte.size() ; i++) {
buffer[i] = arr_byte.get(i).byteValue();
}
Log.e("INPUT",new String(buffer));
mHandler.obtainMessage(MESSAGE_READ, bytes, -1, buffer)
.sendToTarget();
arr_byte = new ArrayList<Integer>();
}
} catch (IOException e) {
break;
}
}
}