I have an issue leading to the requirement of needing to wait sequentially from going from one thing into the next. I presently do this by setting 3 runnables with different delays to allow for a sequential flow of data to appear on my bluetooth connection. However, whilst this does work I feel there must be a better / cleaner implementation of this. My present code is below.
My code works like this:
Write command 1
Wait till command is written
Write command 2
Wait till command is written
Write command 3
Wait till command is written
Please could you give me some suggestions as to how I can perform my write functions one after another in a better manner.
Handler h =new Handler() ;
h.postDelayed(new Runnable() {
public void run() {
Log.d(TAG, "Write 1");
mBluetoothLeService.writeCharacteristic(10);
}
}, 1000);
Handler h1 =new Handler() ;
final int Command_to_run = value;
h1.postDelayed(new Runnable() {
public void run() {
Log.d(TAG, "Write 2");
mBluetoothLeService.writeCharacteristic(Command_to_run);
}
}, 2000);
Handler h2 =new Handler() ;
h2.postDelayed(new Runnable() {
public void run() {
Log.d(TAG, "Write 3");
mBluetoothLeService.writeCharacteristic(20);
}
}, 3000);
Write code
public void writeCharacteristic(int Data) {
if (mBluetoothAdapter == null || mBluetoothGatt == null) {
Log.w(TAG, "BluetoothAdapter not initialized");
return;
}
byte[] value = intToByteArray(Data);
BluetoothGattService mCustomService =
mBluetoothGatt.getService(UUID.fromString("f3641400-00b0-4240-ba50-
05ca45bf8abc"));
if(mCustomService == null){
Log.w(TAG, "Custom BLE Service not found");
return;
}
/*get the read characteristic from the service*/
BluetoothGattCharacteristic characteristic =
mCustomService.getCharacteristic(UUID.fromString("f3641401-00b0-4240-
ba50-05ca45bf8abc"));
characteristic.setValue(value);
mBluetoothGatt.writeCharacteristic(characteristic);
}
I think mBluetoothLeService.writeCharacteristic(10); calls like these already blocks the thread so using them in order without the need of handlers can be your solution. I don't think that function is asynchronous, so if it returns true, you can write the next one. They are boolean functions so if it returns true you can switch to next one.
I've examined the source code and if it throws an exception inside it, it returns false. Otherwise, it returns if it was successful or not.
Side note: This behavior might differ on different API versions, the source code I've looked into was for API 29. Though, I believe the behavior would be the same, except you might need to wrap the mBluetoothLeService.writeCharacteristic(10); calls to a try-catch block.
I have to edit this since the answer is wrong, the boolean return value is not enough to determine whether the operation was successful. The operation is indeed asynchronous but there is a callback that you can use (this callback) to see if the write operation was successful, and then move on to the next one.
Check this answer for more information, and, if possible, remove the tick from this one please.
Android's BLE API is fully asynchronous and there are no blocking methods. The methods will return true if the operation was initiated successfully and false otherwise. In particular, false will be returned if there already is an operation ongoing.
When you call requestMtu, writeCharacteristic, readCharacteristic and so on, the corresponding callback onMtuChanged, onCharacteristicWrite, onCharacteristicRead will be called when the operation is complete. Note that this usually means a roundtrip to the remote device, which might take different amount of time to complete depending on how noisy the environment is and which connection parameters you have, so there is never a good idea to sleep or delay some fixed amount of time and assume the operation has then completed.
To make the code structure a bit better and avoiding the "callback hell", you could for example implement a (thread-safe) GATT queue which is later used by your application. This way you can just push what you want in the queue and let your GATT queue library handle the dirty stuff. Or you can use some Android BLE library that already does this.
See https://medium.com/#martijn.van.welie/making-android-ble-work-part-3-117d3a8aee23 for a more thorough discussion.
If the work you want to perform sequentially can be done asynchronously, you can consider the new WorkManager included in Android Jetpack. With WorkManager you can organize all your work very smartly, as per the documentation you can do it as follows:
WorkManager.getInstance(myContext)
// Candidates to run in parallel
.beginWith(listOf(filter1, filter2, filter3))
// Dependent work (only runs after all previous work in chain)
.then(compress)
.then(upload)
// Don't forget to enqueue()
.enqueue()
The library takes good care of the order of the execution for you. You can find more information on this matter here: https://developer.android.com/topic/libraries/architecture/workmanager/how-to/chain-work
Related
I am coding a discord bot in java, I use discord JDA, and the utilities dependency, I tried using the utilities one but I didn't get it to work, so I tried using just the normal JDA, this is what I did, but I need some way of telling the bot not to send the message in the new channel if the command wasn't ran.
public class NukeCommand extends ListenerAdapter {
#Override
public void onGuildMessageReceived(GuildMessageReceivedEvent event){
if (event.getMessage().getContentRaw().equalsIgnoreCase(".nuke")){
event.getChannel().createCopy().queue();
event.getChannel().delete().queue();
}
}
#Override
public void onTextChannelCreate(TextChannelCreateEvent createEvent){
createEvent.getChannel().sendMessage(":warning:Nuked channel:warning:\nhttps://imgur.com/a/93vq9R8").queue();
}
I am open for answers in both dependencies.
this is the effect I want: https://gyazo.com/e549fd8dda0ded62db19fb84e31d3a61
I have the same effect but it sends the message every time I create a text channel.
I want it to only send the message if the .nuke command was ran.
You said you already got it but I though I'd explain more about how it actually works and refine your answer.
ListenerAdapter's methods are called for every event that happens in the whole scope of the bot, for example if you have
class Adapter extends ListenerAdapter {
#Override
public void onMessageReceived(MessageReceivedEvent event){
/*This will be called for every message, everywhere, including the bot's private channel*/
}
}
So you need to filter those events inside the method's body, what you did on your answer was check if whoever sent the message has permissions to manage messages, which is not the one you actually need to delete channels, it is Permission.MANAGE_CHANNELS, you can find at the roles tab in your server.
Then you call createCopy() which basically creates a shallow copy of the channel's information
I don't think you actually wanted to do that.
Then you queue that action, (This is what actually executes it) it is put in a queue for asynchronous processing by JDA's threads, which will subsequently be sent to discord over the websocket connection.
That queue method can take a Consumer<TextChannel> as a parameter, what does that mean?
Basically a consumer represent an operation that takes a single parameter as input (In this case a TextChannel) and returns nothing. More about it here: https://docs.oracle.com/javase/8/docs/api/java/util/function/Consumer.html
The consumer you give in your answer is doing what you actually want to, sending the message, on the channel that was operated on by the previous queue, meaning that, createCopy() is completely useless, both TextChannel objects are the same, the appropriate would be:
event.getChannel().sendMessage(":warning:Nuked channel:warning:\nhttps://imgur.com/a/93vq9R8").queue();
After that, you delete the channel right away, which does not make that much sense, since most likely no one would even be able to see the nuked message.
For that, JDA provides another method for queueing tasks, this time with a delay:
queueAfter()
It takes a long as the value, and a TimeUnit object to specify what is the time unit of said value, for example:
event.getChannel().delete().queueAfter(10, TimeUnit.SECONDS);
This would queue the task to be executed in 10 seconds, and it does NOT stop the execution of your code, unlike the complete() method.
Alternatively to that, you could just use Thread.sleep() which takes a long value as input: Thread.sleep(10000) for 10 seconds (10000 milliseconds).
You can find a lot more information regarding JDA and start tips and here: https://github.com/DV8FromTheWorld/JDA#creating-the-jda-object
I found a solution you can pass a consumable or whatever its called into the .queue method, this code gets run whenever the channel is created.
what i did:
#Override
public void onGuildMessageReceived(GuildMessageReceivedEvent event){
if (event.getMember().hasPermission(Permission.MESSAGE_MANAGE)){
if (event.getMessage().getContentRaw().equalsIgnoreCase(".nuke")){
event.getChannel().createCopy().queue(channel->channel.sendMessage(":warning:Nuked channel:warning:\nhttps://imgur.com/a/93vq9R8").queue());
event.getChannel().delete().queue();
}
}
}
This seems to work (Without deleting the channel)
#Override
public void onMessageReceived(#NotNull MessageReceivedEvent event) {
String message = event.getMessage().getContentRaw();
if (message.toLowerCase().equals("$" + "clear")) {
for (int i = 0; i <= 1000; i++) {
TextChannel channel = (TextChannel) event.getChannel();
MessageHistory history = new MessageHistory(channel);
List<Message> msgs;
msgs = history.retrievePast(100).complete();
if (msgs.size() > 1) {
channel.deleteMessages(msgs).queue();
} else {
channel.sendMessage("Mensagens deletadas").queue();
return;
}
}
}`
So here's the situation: I'm implementing the caching of our webapp using vertx-redis (we were formerly using lettuce). Pretty simple mechanism, there is an anotation we use on endpoints which is responsible to invoke the redis-client (whatever implementation we are using) and, if there is cached info for the given key it should be used as response body and the request should be finished with no processing.
But there's this really annoying behavior with the vertx-redis implementation in which ending the request doesn't stop the processing. I make the request, get the quick response since there was cached info, but I can still see in the logs that the app keeps the processing going on, as if the request was still open. I believe that it's because I'm ending the response inside the handler for the Redis client call, like this:
client.get("key", onResponse -> {
if (onResponse.succeeded() && onResponse.result() != null) {
//ending request from here
}
});
I realize that I could maybe reproduce the behavior as it was before if I could do something like this:
String cachedInfo = client.get("key").map(onResponse -> onResponse.result());
// endResponse
But as we know, vertx-redis is a semantic API and every method returns the same instance of RedisClient. I also thought about doing something like this:
private String cachedInfo;
...
client.get("key", onResult -> {
if (onResponse.succeeded()) {
this.cachedInfo = onResponse.result();
}
});
if (cachedInfo != null) { // The value could be unset since the lambda is running in other thread
//end request
}
Really don't know what to do, is there a way to return the contents of the AsyncResult to a variable or maybe set it to a variable synchronously somehow? I've also been searching for ways to somehow stop the whole flow of the current request but couldn't find any satisfactory, non-aggressive solution so far, but I'm really open to this option either.
I have a Unity3D app with a Java plugin I've written in Android Studio for accessing a BluetoothLE server over on my Raspberry Pi 3 from my Android device, as well as an Objective-C plugin for XCode on the IOS side for CoreBluetooth. On the plugin side my Objective-C code works perfectly on IOS, but my Java crashes and burns on Android, and freezes the whole tablet, I'm not too surprised. Also notably, it's already taken me about 3x longer so far to code plugin support in Java. Maybe it's time to have a fresh set of eyes looking at it.
The difference is that CoreBluetooth's functions on IOS seem to read a GATT characteristic synchronously while Java requires asynchronous callbacks that don't seem to be working for me. It causes Unity and the entire tablet to hang until reboot.
Here is the part causing problems (it's two parts).
This first part is where I have Unity telling BluetoothLE to go start reading the pins (GetPins). I wanted it to basically act synchronously and wait until it's retrieved data so it can return a string like I've coded in the IOS version so I have it do a synchronized lock.
Basically Java is imposing an asynchronous coding model that's unnecessary on the IOS side (in fact, I'm not so sure there's an asynchronous way to code GATT characteristic reading on IOS CoreBluetooth, but I could be wrong), that would cause me to have to rewrite my entire plugin structure from Unity to support what Java's doing, which is going to be a lot more fiddly in the end and may break other things in the process.
public String GetPins() {
String returnValue = "";
if (mConnected) {
try {
if (getPinsCharacteristic == null) {
List<BluetoothGattService> services = mGatt.getServices();
if (mServiceRead == null) {
mGatt.discoverServices();
mServiceWrite = mGatt.getService(ServiceWriteUUID);
mServiceRead = mGatt.getService(ServiceReadUUID);
mServiceNotify = mGatt.getService(ServiceNotifyUUID);
}
getPinsCharacteristic = mServiceRead.getCharacteristic(PinsCharacteristic);
}
} catch(Exception ex) {
Log.i("MERRRRRP",String.format("Exception: (%s)",ex.toString()));
}
Log.i("MERRRRRP", "Reading Pins");
if (getPinsCharacteristic != null) {
try {
boolean success = mGatt.readCharacteristic(getPinsCharacteristic);
isReadingPins = true;
while(isReadingPins) {
synchronized (readLockPins) {
try {
readLockPins.wait(1);
}
catch(InterruptedException ex) {
ex.printStackTrace();
}
}
}
returnValue = new String(getPinsCharacteristic.getValue(), "UTF-8");
}
catch(Exception ex) {
returnValue = ex.toString();
}
}
}
return returnValue;
}
Now this is the part onCharacteristicRead where it's actually getting the result back, and should be unlocking the synchronized lock and I should get the value back. Instead the whole tablet is freezing.
#Override
public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
super.onCharacteristicRead(gatt, characteristic, status);
if (status == BluetoothGatt.GATT_SUCCESS) {
broadcastUpdate(ACTION_DATA_AVAILABLE, characteristic);
if (characteristic.equals(getPinsCharacteristic)) {
synchronized (readLockPins) {
isReadingPins = false;
}
}
}
}
Your first block of code acquiring lock in a loop and holding it for one second. You assume that your second block of code will execute and acquire the lock between those iterations in first block.
That assumption is wrong. The locks are not fair. If thread A is acquiring and releasing a lock in the loop, and thread B is trying to acquire that same lock, there is not guarantee, that thread B gets it in reasonable time (or maybe never).
The second potential problem (I cannot determine from the code you just posted) is that the variable isReadingPins in while(isReadingPins) MUST be volatile.
Variables not marked volatile do not trigger memory barrier operation, which results in the scenario where writing to the variable in one thread is not visible in another thread.
In your case, isReadingPins = false; in second code block will probably not be visible to the thread running the first code block, because value of the variable will in cache local to the CPU core running that code block, which will always be true, because the write to false is local to another CPU core and CPU core caches will not be synchronized due to absence of memory barrier. This is why the variable must be volatile.
In the end, this is pretty clumsy way of doing this. I don't know Android API, but in regular Java this would be solved using a promise/future or at least a simple Latch.
edit And another thing... if your read function fails, or returns something that is not GATT_SUCCESS, then isReadingPins is never set to false and your application just freezes. This is bad design.
I am using the new Couchbase Java Client API 2.1.1 and therefore JavaRx to access my Couchbase cluster.
When using asynchronous getAndLock on an already locked document, getAndLock fails with a TemporaryLockFailureException. In another SO question (rxjava: Can I use retry() but with delay?) I found out how to retry with delay.
Here is my adopted code:
CountDownLatchWithResultData<JsonDocument> resultCdl = new CountDownLatchWithResultData<>(1);
couchbaseBucket.async().getAndLock(key, LOCK_TIME).retryWhen((errorObserver) -> {
return errorObserver.flatMap((Throwable t) -> {
if (t instanceof TemporaryLockFailureException) {
return Observable.timer(RETRY_DELAY_MS, TimeUnit.MILLISECONDS);
}
return Observable.error(t);
});
}).subscribe(new Subscriber<JsonDocument>() {
#Override
public void onCompleted() {
resultCdl.countDown();
}
#Override
public void onError(Throwable e) {
resultCdl.countDown();
}
#Override
public void onNext(JsonDocument t) {
resultCdl.setInformation(t);
}
});
........
resultCdl.await();
if (resultCdl.getInformation() == null) {
//do stuff
} else ....
(CountDownLatchWithResultData simply extends a normal CountDownLatch and adds two methods to store some information before the count has reached 0 and retrieve it afterwards)
So basically I'd like this code to
try to get the lock infinitely once every RETRY_DELAY_MS milliseconds if a TemporaryLockFailureException occured and then call onNext
or to fail completely on other exceptions
or to directly call onNext if there is no exception at all
The problem now is that when retrying, it only retries once and the JsonDocument from resultCdl.getInformation() is always null in this case even though the document exists. It seems onNext is never called.
If there is no exception, the code works fine.
So apparently I am doing something wrong here but I have no clue as to where the problem might be. Does returning Observable.timer imply that with this new Obervable also the previously associated retryWhen is executed again? Is it the CountDownLatch with count 1 getting in the way?
This one is subtle. Up to version 2.2.0, the Observables from the SDK are in the "hot" category. In effect that means that even if no subscription is made, they start emitting. They will also emit the same data to every newcoming Subscriber, so in effect they cache the data.
So what you retry does is resubscribe to an Observable that will always emit the same thing (in this case an error). I suspect it comes out of the retry loop just because the lock maximum duration is LOCK_TIME...
Try wrapping the call to asyncBucket.getAndLock inside an Observable.defer (or migrate to the 2.2.x SDK if that's something you could do, see release and migration notes starting from 2.2.0).
Edit
This question has gone through a few iterations by now, so feel free to look through the revisions to see some background information on the history and things tried.
I'm using a CompletionService together with an ExecutorService and a Callable, to concurrently call the a number of functions on a few different webservices through CXF generated code.. These services all contribute different information towards a single set of information I'm using for my project. The services however can fail to respond for a prolonged period of time without throwing an exception, prolonging the wait for the combined set of information.
To counter this I'm running all the service calls concurrently, and after a few minutes would like to terminate any of the calls that have not yet finished, and preferably log which ones weren't done yet either from within the callable or by throwing an detailed Exception.
Here's some highly simplified code to illustrate what I'm doing already:
private Callable<List<Feature>> getXXXFeatures(final WiwsPortType port,
final String accessionCode) {
return new Callable<List<Feature>>() {
#Override
public List<Feature> call() throws Exception {
List<Feature> features = new ArrayList<Feature>();
//getXXXFeatures are methods of the WS Proxy
//that can take anywhere from second to never to return
for (RawFeature raw : port.getXXXFeatures(accessionCode)) {
Feature ft = convertFeature(raw);
features.add(ft);
}
if (Thread.currentThread().isInterrupted())
log.error("XXX was interrupted");
return features;
}
};
}
And the code that concurrently starts the WS calls:
WiwsPortType port = new Wiws().getWiws();
List<Future<List<Feature>>> ftList = new ArrayList<Future<List<Feature>>>();
//Counting wrapper around CompletionService,
//so I could implement ccs.hasRemaining()
CountingCompletionService<List<Feature>> ccs =
new CountingCompletionService<List<Feature>>(threadpool);
ftList.add(ccs.submit(getXXXFeatures(port, accessionCode)));
ftList.add(ccs.submit(getYYYFeatures(port accessionCode)));
ftList.add(ccs.submit(getZZZFeatures(port, accessionCode)));
List<Feature> allFeatures = new ArrayList<Feature>();
while (ccs.hasRemaining()) {
//Low for testing, eventually a little more lenient
Future<List<Feature>> polled = ccs.poll(5, TimeUnit.SECONDS);
if (polled != null)
allFeatures.addAll(polled.get());
else {
//Still jobs remaining, but unresponsive: Cancel them all
int jobsCanceled = 0;
for (Future<List<Feature>> job : ftList)
if (job.cancel(true))
jobsCanceled++;
log.error("Canceled {} feature jobs because they took too long",
jobsCanceled);
break;
}
}
The problem I'm having with this code is that the Callables aren't actually canceled when waiting for port.getXXXFeatures(...) to return, but somehow keep running. As you can see from the if (Thread.currentThread().isInterrupted()) log.error("XXX was interrupted"); statements the interrupted flag is set after port.getFeatures returns, this is only available after the Webservice call completes normally, instead of it having been interrupted when I called Cancel.
Can anyone tell me what I am doing wrong and how I can stop the running CXF Webservice call after a given time period, and register this information in my application?
Best regards, Tim
Edit 3 New answer.
I see these options:
Post your problem on the Apache CXF as feature request
Fix ACXF yourself and expose some features.
Look for options for asynchronous WS call support within the Apache CXF
Consider switching to a different WS provider (JAX-WS?)
Do your WS call yourself using RESTful API if the service supports it (e.g. plain HTTP request with parameters)
For über experts only: use true threads/thread group and kill the threads with unorthodox methods.
The CXF docs have some instructions for setting the read timeout on the HTTPURLConnection:
http://cwiki.apache.org/CXF20DOC/client-http-transport-including-ssl-support.html
That would probably meet your needs. If the server doesn't respond in time, an exception is raised and the callable would get the exception. (except there is a bug where is MAY hang instead. I cannot remember if that was fixed for 2.2.2 or if it's just in the SNAPSHOTS right now.)