Summary: synchronisation of multiple SQLite databases with server side sequentially.
I'm working on an Android application that is bound to be modular: we have a launcher that fires off intents, based on user choice, to start different application modules (separate installable packages). As it is now, each module has its own SQLite database and that works fine. The task I'm assigned to requires making data synchronisation between handheld and server side (SQL Server 2008 R2) using MS Sync Framework 4.0 (currently October CTP). I had developed android lib according to Sync Framework specs and that works too.
The issue I have is that because of this loosely coupled design (and some other restrictions) I need to have a button in launcher, that forces all modules to synchronise their databases one-by-one (order doesn't matter for now).
My current approach is to have an abstract BroadcastReceiver & Service (I only now discovered IntentService) classes that are inherited in each modules. So in launcher I broadcast intent, each module picks it up using customized BroadcastReceiver and syncs its database using, again, customized Service... In parallel. I have checked ordered broadcasts, but as I have a service doing the actual work it doesn't really help. The only other way I can currently think of is to have a system-wide mutex and use it to lock sync call in every service.
This is my first Android related task so there probably is a better way to solve this, I wouldn't run from redesigning synchronisation part if that makes our teams future life a bit easier.
[EDIT] So it looks like Java doesn't support named mutexes.
[EDIT2] By modules (or separate installable packages) I meant different APK for each module. So when starting a module you actually start a new process for it.
Using 'synchronized' you can implement mutex in java as below. This is a very minimal example.
private final Object mutex = new Object();
...
synchronized (mutex) {
if (myCondition) {
try {
mutex.wait(n);
} catch (XYZException e) {
throw new PQRException(“Wait() issue!”, e);
}
}
}
...
At the end I had to implement global lock using server socket like this:
ServerSocket mServerSocket;
/**
* Simulates global locking using server socket
* #return If lock was successful
*/
private synchronized boolean lock() {
try {
Log.v(serviceName, "Trying to acquire a lock...");
// any port from 49152 through 65535 should work
mServerSocket = new ServerSocket(51515);
return true;
} catch (IOException ioe) {
return false;
}
}
/**
* Simulates global unlocking
*/
private synchronized void unlock() {
try {
Log.v(serviceName, "Releasing a lock.");
if (mServerSocket != null) {
if (!mServerSocket.isClosed()) {
mServerSocket.close();
}
mServerSocket = null;
}
} catch (IOException e) {
e.printStackTrace();
}
}
Related
We want to add a reporting feature to our existing application.
For this purpose we are sending Events in JSON via HTTPS to a server application.
We need to remember Event-objects that could not be send to the server (No internet, server not reachable...). We are considering to store the events in a SQLite database and discard all Events that are older than 24 hours to prevent flooding our storage.
Another option would be to write the JSON-objects to a file and concat each new event when it could not be send to the server. The problem with this solution is, that it would be hard for us to discard logs older than 24 hours.
We store the event sin a table with the columns:
| id | json | created_at |
Can anyone recommend best practices for this use case?
Currently we tend to use the sqlite solution but we are wondering if there are any caveats that we are not aware of.
If you don't mind using third-party lib i can recommend android-priority-jobqueue. You can easily achieve what you are trying to do. You can always create job and it will handle itself. You can set if it needs network, if it is persistent (saved into DB when no network) and even you can customize your own retry logic.
Here's little example.
public class PostTweetJob extends Job {
public static final int PRIORITY = 1;
private String text;
public PostTweetJob(String text) {
// This job requires network connectivity,
// and should be persisted in case the application exits before job is completed.
super(new Params(PRIORITY).requireNetwork().persist());
}
#Override
public void onAdded() {
// Job has been saved to disk.
// This is a good place to dispatch a UI event to indicate the job will eventually run.
}
#Override
public void onRun() throws Throwable {
// yours code here
}
#Override
protected RetryConstraint shouldReRunOnThrowable(Throwable throwable, int runCount,
int maxRunCount) {
// An error occurred in onRun.
return RetryConstraint.createExponentialBackoff(runCount, 1000);
}
}
And you call it like this.
jobManager.addJobInBackground(new PostTweetJob("It works"));
use JobService(Android 5+ - lollipop and above) and AlarmManager (for android sdk<21 - pre lollipop) with this solution u can schedule any task and it would be performed. JobService was developed rxactely for tjis purposes(schedule and perform different tasks) maybe you can try JobIntentService it is would work on kitkat(android 4+) devices
P.S.
In that case you didnt need any third party libs and other dependrncies like firebase/google play services(like for FirebaseDispatcher)
I'm making image processor app. I need to scan the phone for pictures and list them with their number of pixels. So that's gonna be a a large impact on performance and as I understood, I need to make it work on background thread.
So my question is, what is the best approach for this? I understand that IntentService may be the best solution, but I'm not sure how I will implement progress bar with it, and I need to return Picture objects and later update the UI on shuffle button. I'm doing update with Glide library so that's gonna go smooth.
Reading about Asynctasks, I stumbled about comments how it's bad and leads to leaks in memory and should avoid using it. rXJava is too complicated at the moment.
This is my code:
Main activity:
#OnClick(R.id.shuffle)
public void shuffleList() {
Collections.shuffle(listOfImageFiles);
recyclerViewAdapter = new PictureRecycleViewAdapter(listOfImageFiles, this);
recyclerView.swapAdapter(recyclerViewAdapter, false);
recyclerViewAdapter.notifyDataSetChanged();
}
#OnClick(R.id.scan)
public void processImages() {
//progress bar
listOfPictures = new ArrayList<>();
//Gets data from default camera roll directory. Note that some of the phone companies have different file paths. So instead of hardcoding string paths, I used this instead.
String path = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM).getPath();
File filePath = new File(path);
listOfImageFiles = scanPhotos(filePath);
// async?
for (File file : listOfImageFiles
) {
Bitmap bitmap = BitmapFactory.decodeFile(file.getPath());
//int is sufficient for most today's pixels. long would be overkill - 4 vs 8 bytes
int pixels = bitmap.getHeight() * bitmap.getWidth();
listOfPictures.add(new Picture(file.getPath(), pixels));
}
}
public List<File> scanPhotos(File directory) {
List<File> listOfPictures = new ArrayList<>();
try {
File[] files = directory.listFiles();
for (File file : files
) {
if (file.isDirectory() && !file.isHidden()) {
listOfPictures.addAll(scanPhotos(file));
} else {
if (file.getName().endsWith(".jpg") || file.getName().endsWith(".jpeg") || file.getName().endsWith(".png")) {
listOfPictures.add(file);
}
}
}
} catch (Exception e) {
Log.e(e.getMessage(), e.getMessage());
}
return listOfPictures;
}
IntentService
IntentService is definitely a valid approach. You can use Broadcasts to return your result to another component of the app, be it Activity or another Service, for example:
Start the IntentService - if you need some parameters, place them in the Extras of the service intent.
Your IntentService runs on the background thread until the computation is finished.
Upon finishing, send a broadcast with computation result placed in intent extras.
In your activity, register a BroadcastReceiver that will listen for your computation result broadcast.
Upon getting the broadcast in your Activity, retrieve the computation result from intent extras.
You might also implement broadcasts received by your Service for things like cancellation of the computation or updating the parameters.
One of the advantages of IntentService is that you can easily integrate it with the JobScheduler API to defer execution until certain system conditions are met.
Alternatives
You can use a bus library, such as https://github.com/greenrobot/EventBus to communicate between Activity and Service - the only problem is, EventBus won't work with remote services (running in a separate process).
Like you've mentioned, using RxJava with IO and computation schedulers is also a good idea.
AsyncTask is fine as long as you not tie it with a hard reference to an activity - don't implement it as an inner class of Activity and if you want to communicate the result back, do it through a WeakReference<T>
AsyncTask is fine, you just need to be careful with its implementation.
However, for longer running tasks there are better options. IntentService is a good option.
When it comes to a responsive UI when using an IntentService you could add two things.
Notifications
Create an ongoing notification that indicates that your App is working on something. This lets users know that their CPU cycles are being eaten by something in the background and they are less likely(?) to be confused and cranky about their device running slower.
Additionally, it gives your App more of an allowance for staying alive when Android is looking for background Apps to kill to release memory.
EventBus
You can make UI reporting extremely simple by using an EventBus library. I am personally a fan of greenbot/EventBus, but there are others.
Example
In your Activity:
#Subscribe(threadMode = ThreadMode.MAIN)
public void onProgressEvent(ProgressEvent event) {
mProgressBar.setProgress(event.value);
}
In your IntentService:
EventBus.getDefault().post(new ProgressEvent(5000));
I'm developing an app for a company and I need to integrate it with Google Drive. I can't use the native API because the company has files not created by the application that needs to be handled, I need the full drive scope, so the REST API is what I must use.
Here's the problem, the tutorials are not basic enough for me to get started since I only have a very basic understanding of JSON and REST.
The tutorial: https://developers.google.com/drive/web/integrate-create
As I understand it I need to create JSON in my Java code and then pass that through the example code?
JSON
{
"action":"create",
"folderId":"0ADK06pfg",
"userId":"103354693083460731603"
}
JAVA
public class State {
/**
* Action intended by the state.
*/
public String action;
/**
* IDs of files on which to take action.
*/
public Collection<String> ids;
/**
* Parent ID related to the given action.
*/
public String parentId;
/**
* Empty constructor required by Gson.
*/
public State() {}
/**
* Create a new State given its JSON representation.
*
* #param json Serialized representation of a State.
*/
public State(String json) {
GsonBuilder builder = new GsonBuilder();
Gson gson = builder.create();
State other = gson.fromJson(json, State.class);
this.action = other.action;
this.ids = other.ids;
this.parentId = other.parentId;
}
}
The problem is that I have no idea how to create JSON nor do I quite understand how to use the JSON when created to do things like create files and query for files.
If someone can get me as far as creating an empty file in a users root folder then I can probably take it from there, but I could really use a nudge in the right direction!
Assuming, that you're talking about an Android app, there is no need to create JSON. The Java REST Api is quite easy to use on Android. If the official docs and the examples do not suffice, you may look at this demo. It is a bit more complex that needs to be (in order to maintain compatibility with the GDAA version), but with a few simple steps, you may simplify it.
I certainly can't copy all the code here, but you can just snatch the REST class, supply a GooDrive account to setSelectedAccountName() (or omit the method and let the service handle it) and simplify the connect() / disconnect() methods. The connect() method (for compatibility with GDAA) should be) replaced by a try/catch construct like this:
com.google.api.services.drive.Drive mGOOSvc;
...
try {
mGOOSvc...execute();
} catch (UserRecoverableAuthIOException uraIOEx) {
// standard authorization failure - user fixable
} catch (GoogleAuthIOException gaIOEx) {
// usually PackageName / SHA1 mismatch in DevConsole
} catch (IOException e) {
// '404 not found' in FILE scope, still, consider connected
if (e instanceof GoogleJsonResponseException) {
if (404 == ((GoogleJsonResponseException) e).getStatusCode())
mConnected = true;
}
} catch (Exception e) {
// "the name must not be empty" indicates
// UNREGISTERED / EMPTY account in 'setSelectedAccountName()' ???
}
anywhere you find the '...execute()' method in the REST class in order to catch sudden loss of authorization, etc... (can happen anytime). Otherwise, I've been running this CRUD implementation for some time and never experienced problems.
One general note about the REST Api. Since it is 'network state dependent', I would recommend to disconnect it completely from your app's UI and run it in some type of sync service invoked when there is an active network (WIFI, cellular) traffic. See the network related episodes here.
Good Luck
My Question is: Can Android 4.3 (client) have active connections with multiple BLE devices (servers)? If so, how can I achieve it?
What I did so far
I try to evaluate what throughput you can achieve using BLE and Android 4.3 BLE API. In addition I also try to find out how many devices can be connected and active at the same time. I use a Nexus 7 (2013), Android 4.4 as master and TI CC2540 Keyfob as slaves.
I wrote a simple server software for the slaves, which transmits 10000 20Byte packets through BLE notifications. I based my Android App on the Application Accelerator from the Bluetooth SIG.
It works well for one device and I can achieve around 56 kBits payload throughput at a Connection Interval of 7.5 ms. To connect to multiple slaves I followed the advice of a Nordic Employee who wrote in the Nordic Developer Zone:
Yes it's possible to handle multiple slaves with a single app. You would need to handle each slave with one BluetoothGatt instance. You would also need specific BluetoothGattCallback for each slave you connect to.
So I tried that and it partly works. I can connect to multiple slaves. I can also register for notifications on multiple slaves. The problem begins when I start the test. I receive at first notifications from all slaves, but after a couple Connection Intervals just the notifications from one device come trough. After about 10 seconds the other slaves disconnect, because they seem to reach the connection time-out. Sometimes I receive right from the start of the test just notifications from one slave.
I also tried accessing the attribute over a read operation with the same result. After a couple of reads just the answers from one device came trough.
I am aware that there are a few similar questions on this forum: Does Android 4.3 support multiple BLE device connections?, Has native Android BLE GATT implementation synchronous nature? or Ble multiple connection. But none of this answers made it clear for me, if it is possible and how to do it.
I would be very grateful for advice.
I suspect everyone adding delays is just allowing the BLE system to complete the action you have asked before you submit another one. Android's BLE system has no form of queueing. If you do
BluetoothGatt g;
g.writeDescriptor(a);
g.writeDescriptor(b);
then the first write operation will immediately be overwritten with the second one. Yes it's really stupid and the documentation should probably actually mention this.
If you insert a wait, it allows the first operation to complete before doing the second. That is a huge ugly hack though. A better solution is to implement your own queue (like Google should have). Fortunately Nordic have released one for us.
https://github.com/NordicSemiconductor/puck-central-android/tree/master/PuckCentral/app/src/main/java/no/nordicsemi/puckcentral/bluetooth/gatt
Edit: By the way this is the universal behaviour for BLE APIs. WebBluetooth behaves the same way (but Javascript does make it easier to use), and I believe iOS's BLE API also behaves the same.
Re visting the bluetooth-lowenergy problem on android: I am still using delays.
The concept: after every major action that provokes the BluetoothGattCallback (e.g. conenction, service discovery, write, read) a dealy is needed. P.S. have a look at Google example on BLE API level 19 sample for connectivity to understand how Broadcasts should be sent and get some general understanding etc...
Firstly, scan (or scan) for BluetoothDevices, populate the connectionQueue with desired devices and call initConnection().
Have a look on the following example.
private Queue<BluetoothDevice> connectionQueue = new LinkedList<BluetoothDevice>();
public void initConnection(){
if(connectionThread == null){
connectionThread = new Thread(new Runnable() {
#Override
public void run() {
connectionLoop();
connectionThread.interrupt();
connectionThread = null;
}
});
connectionThread.start();
}
}
private void connectionLoop(){
while(!connectionQueue.isEmpty()){
connectionQueue.poll().connectGatt(context, false, bleInterface.mGattCallback);
try {
Thread.sleep(250);
} catch (InterruptedException e) {}
}
}
Now if all is good, you have made connections and BluetoothGattCallback.onConnectionStateChange(BluetoothGatt gatt, int status, int newState) has been called.
public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
switch(status){
case BluetoothGatt.GATT_SUCCESS:
if (newState == BluetoothProfile.STATE_CONNECTED) {
broadcastUpdate(BluetoothConstants.ACTION_GATT_CONNECTED, gatt);
}else if(newState == BluetoothProfile.STATE_DISCONNECTED){
broadcastUpdate(BluetoothConstants.ACTION_GATT_DISCONNECTED, gatt);
}
break;
}
}
protected void broadcastUpdate(String action, BluetoothGatt gatt) {
final Intent intent = new Intent(action);
intent.putExtra(BluetoothConstants.EXTRA_MAC, gatt.getDevice().getAddress());
sendBroadcast(intent);
}
P.S. sendBroadcast(intent) might need to be done like this:
Context context = activity.getBaseContext();
context.sendBroadcast(intent);
Then the broadcast is received by BroadcastReceiver.onReceive(...)
public BroadcastReceiver myUpdateReceiver = new BroadcastReceiver(){
#Override
public void onReceive(Context context, Intent intent) {
final String action = intent.getAction();
if(BluetoothConstants.ACTION_GATT_CONNECTED.equals(action)){
//Connection made, here you can make a decision: do you want to initiate service discovery.
// P.S. If you are working with multiple devices,
// make sure that you start the service discovery
// after all desired connections are made
}
....
}
}
After doing whatever you want in the broadcast receiver, here is how I continue:
private Queue<BluetoothGatt> serviceDiscoveryQueue = new LinkedList<BluetoothGatt>();
private void initServiceDiscovery(){
if(serviceDiscoveryThread == null){
serviceDiscoveryThread = new Thread(new Runnable() {
#Override
public void run() {
serviceDiscovery();
serviceDiscoveryThread.interrupt();
serviceDiscoveryThread = null;
}
});
serviceDiscoveryThread.start();
}
}
private void serviceDiscovery(){
while(!serviceDiscoveryQueue.isEmpty()){
serviceDiscoveryQueue.poll().discoverServices();
try {
Thread.sleep(250);
} catch (InterruptedException e){}
}
}
Again, after a successful service discovery, BluetoothGattCallback.onServicesDiscovered(...) is called. Again, I send an intent to the BroadcastReceiver (this time with different action String) and it is now that you can start reading, writing and enabling notifications/indications...
P.S. If you are working with multiple devices, make sure that you start the reading, writing etc... stuff after all devices have reported that their services have been discovered.
private Queue<BluetoothGattCharacteristic> characteristicReadQueue = new LinkedList<BluetoothGattCharacteristic>();
private void startThread(){
if(initialisationThread == null){
initialisationThread = new Thread(new Runnable() {
#Override
public void run() {
loopQueues();
initialisationThread.interrupt();
initialisationThread = null;
}
});
initialisationThread.start();
}
}
private void loopQueues() {
while(!characteristicReadQueue.isEmpty()){
readCharacteristic(characteristicReadQueue.poll());
try {
Thread.sleep(BluetoothConstants.DELAY);
} catch (InterruptedException e) {}
}
// A loop for starting indications and all other stuff goes here!
}
BluetoothGattCallback will have all your incoming data from the BLE sensor. A good practice is to send a broadcast with the data to your BroadcastReceiver and handle it over there.
I am developing an app with BLE features myself. The way I managed to connect to multiple devices and turn on notifications was to implement delays.
So I make a new thread (in order not to block UI thread) and in the new thread connect and turn on notifications.
For example, after BluetoothDevice.connectGatt(); call Thread.sleep();
And add the same delay for read/write and enable/dissable notifications.
EDIT
Use wait like this so that Android dindn't reaise ANR
public static boolean waitIdle() {
int i = 300;
i /= 10;
while (--i > 0) {
if (true)
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
return i > 0;
}
Unfortunately notifications in the current Android BLE stack are a bit buggy. There are some hardcoded limits and I've found some stability issues even with a single device. (I read at one point that you could only have 4 notifications... not sure if that's across all devices or per device. Trying to find the source for that info now.)
I would try switching to a polling loop (say, poll the items in question 1/sec) and seeing if you find your stability increases. I would also consider switching to a different slave device (say a HRM or the TI SensorTag) to see if there is perhaps an issue with the slave-side code (unless you can test that against iOS or another platform and confirm it isn't part of the issue).
Edit: Reference for notification limitation
Rain is right in his answer, you need delays for pretty much everything when you work with BLE in Android. I developed several apps with it and it is really necessary. By using them you avoid a lot of crashes.
In my case, I use delays after every read/write command. Doing so, you ensure you receive the response from the BLE device almost always. I do something like this: (of course everything is done in a separate thread to avoid to much work on the main thread)
readCharacteristic(myChar);
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
myChar.getValue();
or:
myChar.setValue(myByte);
writeCharacteristic(myChar);
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
This is really useful when you read/write several characteristics in a row... As Android is enough fast to execute the commands almost instantly, if you don't use a delay between them you may get errors or incoherent values...
Hope it helps even if it is not exactly the answer to your question.
I am developing an Android app for connecting to Tridion 2011 SP1 Core Service.
So far I have created Android WS Stubs from the core service wsdl using wsclient.
Imported those stubs, which allow access to all the core service methods.
I can now authenticate to Tridion via the Android application but as soon as I try to perform even the most basic of web service calls, such as getApiVersion(), I get the error:
ReflectionHelper*java.lang.NoSuchFieldException: GetApiVersionResult.
I was wondering has anyone else managed to create a java android app that communicates with the Core Service?
Interestingly enough, if I run the code as a java application, using wsimport stubs everything works a treat.
Any help appreciated. For reference here is a code snippet:
To connect to Tridion:
class TridionConnect extends AsyncTask<String, Integer, String> {
// Called to initiate the background activity
#Override
protected String doInBackground(String... statuses) {
try {
Authenticator.setDefault(new Authenticator() {
#Override
protected PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication("username", "password".toCharArray());
}
});
url = new URL("http://tridion-server/webservices/CoreService2011.svc?wsdl");
System.out.println(String.format("Get Service"));
service = new CoreService2011();
System.out.println(String.format("Get Client"));
client = service.getBasicHttp();
return "Authenticated To Tridion";
} catch (Exception e) {
Log.e("Authentication failure", e.toString());
e.printStackTrace();
return "Failed to authenticate";
}
}
// Called when there's a status to be updated
#Override
protected void onProgressUpdate(Integer... values) {
super.onProgressUpdate(values);
// Not used in this case
}
// Called once the background activity has completed
#Override
protected void onPostExecute(String result) { //
Toast.makeText(FullscreenActivity.this, result, Toast.LENGTH_LONG).show();
area.setText("Authenticated to Tridion OK");
}
}
To get the ApiVersion
client.getApiVersion();
UserData currentUser = client.getCurrentUser();
System.out.println(String.format("'%s' %s", currentUser.getTitle(), currentUser.getId()));
Frank,
It is not possible for a couple of reasons.
If you use wsimport to create the coreservice proxy it will use the javax library, which exists in the JRE. However Dalvik implements only a subset of the javax library which means this approach is impossible in the Android environment.
I then looked at Ksoap2 tools for creating the proxy. This seemed to work OK, in as much as it did create a proxy, however it did not match the coreservice so I was unable to authenticate. I didn't get any further with this approach beyond examining the JRE proxy v Ksoap2 proxy. They were quite different.
At this point I took a step back, had a cup of tea and re-engineered the approach.
I created a c# REST service to sit between the android app and the core service.
This approach seemed a bit complex, but it offers lots of advantages. Lots of the spade work can be done in the REST service, which will be much quicker than similar code on a tablet or phone.
Secondly the REST service sits on the same server as the CMS/CoreService so the comms is quicker and you can make the REST requests from the android app much lighter.
I have got the application to the point where you can authenticate to Tridion, select a publication, and components that is then rendered in a dynamic layout ready for update/save/publish.
The one big downside of this approach is that the REST service 'should' be stateless so superficially you have to authenticate to the coreservice for every request. Of course I don't do that, but you have to come up with some alternative approach Oauth, shared secret etc.
In initial tests this approach has seemed to be fairly slick on an android device.