I am having trouble with the JobScheduling API on android, when my job fails, I want it to be rescheduled according to the previously set back off policy.
The creation of the job looks like that :
try {
mJobSchedulerServiceComponent = new ComponentName(this.context, JobSchedulerService.class);
JobInfo.Builder builder = new JobInfo.Builder(Constant.AUTO_UPLOAD_JOB_ID, mJobSchedulerServiceComponent );
builder.setPersisted(true);
builder.setRequiresCharging(true);
builder.setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY);
builder.setBackoffCriteria(10000, JobInfo.BACKOFF_POLICY_LINEAR);
builder.setOverrideDeadline(600 * 1000);
builder.setMinimumLatency(60 * 1000);
PersistableBundle bundle = new PersistableBundle();
bundle.putString("action", Constant.JOB_SCHEDULER_AUTO_UPLOAD);
bundle.putString("url", Constant.API_URL_SEND_DATA);
builder.setExtras(bundle);
if( mJobScheduler.schedule( builder.build() ) <= 0 ) {
//If something goes wrong
logger.e(TAG, "Create auto upload job failed");
}
} catch (Exception e) {
logger.e(TAG, "createAutoUploadJob error " + e);
}
The JobService handling the jobs looks like that :
public class JobSchedulerService extends JobService {
private Handler handler;
protected Config config;
protected Logger logger;
protected static final String TAG = "MDM-JobSchedulerService";
private volatile ServiceThread thread = null;
#Override
public void onCreate() {
super.onCreate();
logger = Logger.getInstance(this);
logger.d(TAG, "JobSchedulerService start");
thread = new ServiceThread(this);
logger.d(TAG, "Starting thread");
thread.lock.lock();
thread.start();
logger.d(TAG, "waiting for init_done");
try {
thread.init_done.await();
}catch (InterruptedException e){
logger.i(TAG, "Got exception "+e.toString());
}
logger.d(TAG, "init_done");
thread.lock.unlock();
}
#Override
public void onDestroy() {
super.onDestroy();
logger.i(TAG, "JobSchedulerService destroyed");
}
#Override
public boolean onStartJob(JobParameters params) {
/*
* You'll notice that in the following code snippet, the onStartJob(JobParameters params) method returns true.
* This is because you're going to use a Handler instance to control your operation, which means that it could
* take longer to finish than the onStartJob(JobParameters params) method. By returning true, you're letting
* the application know that you will manually call the jobFinished(JobParameters params, boolean needsRescheduled) method
* */
if (handler != null) {
handler.sendMessage(Message.obtain(handler, params.getJobId(), params));
}else{
logger.e(TAG, "handler is null on onStartJob");
}
return true;
}
#Override
public boolean onStopJob(JobParameters params) {
handler.removeMessages(params.getJobId());
return true;
}
private class ServiceThread extends Thread{
private Context context;
protected Database.Helper db;
public Lock lock;
public Condition init_done;
// MAIN TREAD
public ServiceThread(Context context){
setContext(context);
config = Config.getInstance(context);
logger = Logger.getInstance(context);
db = Database.Helper.getInstance(context);
encryptor = new Encryption(context);
lock = new ReentrantLock();
init_done = lock.newCondition();
}
public void setContext(Context context) {
this.context = context;
}
//SERVICE THREAD
#Override
public void run() {
logger.i(TAG, "Thread started");
lock.lock();
Looper.prepare();
handler = new Handler( new Handler.Callback() {
#Override
public boolean handleMessage( Message msg ) {
logger.d(TAG, "New message");
JobParameters job_params = (JobParameters) msg.obj;
boolean success = handleJob(job_params);
if(!success) {
logger.d(TAG, "Auto upload job will be rescheduled (because upload failed)");
}
jobFinished( job_params, !success ); // Auto reschedule on failure following backoff policy
return true;
}
} );
logger.i(TAG, "Thread init_done");
init_done.signal();
lock.unlock();
Looper.loop();
}
private boolean httpPost(String url, String json){
logger.i(TAG, "Http post to : " + url);
return false; // Force fail to test rescheduling
}
public boolean handleJob(JobParameters params){
//Thread
// Return true to tell job successful, so it will not be retyied, false will retry
PersistableBundle extras = params.getExtras();
String action = extras.getString("action");
String json;
Boolean success;
logger.d(TAG, "Action = "+action);
switch (action){
case Constant.JOB_SCHEDULER_AUTO_UPLOAD:
success = httpPost(extras.getString("url"), json);
if(success){
db.storeEvent(System.currentTimeMillis(), Constant.JOB_SCHEDULER_AUTO_UPLOAD, "auto upload success");
return true;
}
db.storeEvent(System.currentTimeMillis(), Constant.JOB_SCHEDULER_AUTO_UPLOAD, "auto upload failed");
return false;
}
return false;
}
}
}
The job is launched properly when conditions are met, howerver it is never launch after failing, here are some logs related to the previous code :
10-18 15:06:51.857 19261-19261/fr.myapp.mdm D/MDM-JobSchedulerService: JobSchedulerService start
10-18 15:06:51.859 19261-19261/fr.myapp.mdm D/MDM-JobSchedulerService: Starting thread
10-18 15:06:51.860 19261-19261/fr.myapp.mdm D/MDM-JobSchedulerService: waiting for init_done
10-18 15:06:51.862 19261-19653/fr.myapp.mdm I/MDM-JobSchedulerService: Thread started
10-18 15:06:51.862 19261-19653/fr.myapp.mdm I/MDM-JobSchedulerService: Thread init_done
10-18 15:06:51.863 19261-19261/fr.myapp.mdm D/MDM-JobSchedulerService: init_done
10-18 15:06:51.869 19261-19653/fr.myapp.mdm D/MDM-JobSchedulerService: New message
10-18 15:06:51.870 19261-19653/fr.myapp.mdm D/MDM-JobSchedulerService: Action = JOB_SCHEDULER_AUTO_UPLOAD
10-18 15:06:51.870 19261-19653/fr.myapp.mdm I/MDM-JobSchedulerService: Auto upload job starting
10-18 15:06:52.029 19261-19653/fr.myapp.mdm I/MDM-JobSchedulerService: Http post to : http://xxxx
10-18 15:06:52.037 19261-19653/fr.myapp.mdm D/MDM-JobSchedulerService: Auto upload job will be rescheduled (because upload failed)
10-18 15:06:52.047 19261-19261/fr.myapp.mdm I/MDM-JobSchedulerService: JobSchedulerService destroyed
Thanks for your help
Related
I have several problems with Qt Android services.
The service isnt starting immediately. It runs after 1-2 minutes delay.
The service stops after some time and the app crashes.
QTimer in the service doesn't work.
QTcpSocket client doesn't work.
( I added android.permission.INTERNET and still not works )
Im using the same main.cpp file for the Activity and Service.
and this is the main function of the service::
QTcpSocket _socket;
void onReadyRead()
{
QByteArray datas = _socket.readAll();
QString DataAsString = QString(datas);
Log::log("DATA: ");
Log::log(DataAsString);
if(DataAsString!="HELLO\n")
NotificationClient().setNotification(DataAsString + " BGN");
}
int main_service(int argc, char *argv[])
{
QAndroidService app(argc, argv);
_socket.connectToHost(QHostAddress("xx.xx.xx.xx"), 1234);
QObject::connect(&_socket, &QTcpSocket::readyRead, onReadyRead);
_socket.write(QByteArray("get_money()\r\n"));
QThread::msleep(1000);
Log::log("creating timer..");
QTimer timer;
QObject::connect(&timer, &QTimer::timeout, someFunction);
timer.start(500);
//MoneyWorker *mw = new MoneyWorker();
for(;;)
{
_socket.write(QByteArray("get_money()\r\n"));
Log::log("loopin..");
QThread::msleep(10000);
}
//NotificationClient().setNotification("The user is happy!");
return app.exec();
}
Im starting the service this way::
QJniObject::callStaticMethod<void>(
"org/qtproject/example/qtandroidservice/QtAndroidService",
"startQtAndroidService",
"(Landroid/content/Context;)V",
QNativeInterface::QAndroidApplication::context());
package org.qtproject.example.qtandroidservice;
import android.content.Context;
import android.content.Intent;
import android.util.Log;
import org.qtproject.qt.android.bindings.QtService;
public class QtAndroidService extends QtService
{
private static final String TAG = "QtAndroidService";
#Override
public void onCreate() {
super.onCreate();
Log.i(TAG, "Creating Service");
}
#Override
public void onDestroy() {
super.onDestroy();
Log.i(TAG, "Destroying Service");
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
int ret = super.onStartCommand(intent, flags, startId);
// Do some work
return ret;
}
public static void startQtAndroidService(Context context) {
context.startService(new Intent(context, QtAndroidService.class));
}
public static void stopQtAndroidService(Context context) {
context.stopService(new Intent(context, QtAndroidService.class));
}
public static void log(String message)
{
Log.i(TAG, message);
}
}
<service android:name="org.qtproject.example.qtandroidservice.QtAndroidService">
The goal is to show my debit card balance on my smart watch via notification so I can know when my money go low.
This is the log from 'adb logcat'
08-07 22:45:15.182 5342 5366 W ActivityManager: Timeout executing service: ServiceRecord{d2392a2 u0 org.qtproject.example.androidnotifier/org.qtproject.example.qtandroidservice.QtAndroidService}
08-07 22:45:15.531 5342 27607 E ActivityManager: Reason: executing service org.qtproject.example.androidnotifier/org.qtproject.example.qtandroidservice.QtAndroidService
08-07 22:47:35.784 5342 5366 W ActivityManager: Timeout executing service: ServiceRecord{33bcd86 u0 org.qtproject.example.androidnotifier/org.qtproject.example.qtandroidservice.QtAndroidService}
08-07 22:47:37.037 5342 29382 E ActivityManager: Reason: executing service org.qtproject.example.androidnotifier/org.qtproject.example.qtandroidservice.QtAndroidService
08-07 22:47:37.121 5342 7675 W ActivityManager: Scheduling restart of crashed service org.qtproject.example.androidnotifier/org.qtproject.example.qtandroidservice.QtAndroidService in 62676ms for start-requested
08-07 22:48:39.837 5342 5367 I ActivityManager: Start proc 29406:org.qtproject.example.androidnotifier/u0a477 for service {org.qtproject.example.androidnotifier/org.qtproject.example.qtandroidservice.QtAndroidService}
08-07 22:48:39.982 5342 7678 W ActivityManager: Stopping service due to app idle: u0a477 -1m34s200ms org.qtproject.example.androidnotifier/org.qtproject.example.qtandroidservice.QtAndroidService
08-07 22:48:40.198 29406 29428 I QtAndroidService: creating timer..
08-07 22:48:40.198 29406 29428 I QtAndroidService: loopin..
08-07 22:48:41.199 29406 29428 I QtAndroidService: loopin..
08-07 22:48:42.200 29406 29428 I QtAndroidService: loopin..
08-07 22:48:43.201 29406 29428 I QtAndroidService: loopin..
08-07 22:48:44.201 29406 29428 I QtAndroidService: loopin..
08-07 22:48:45.202 29406 29428 I QtAndroidService: loopin..
08-07 22:48:46.203 29406 29428 I QtAndroidService: loopin..
08-07 22:48:47.204 29406 29428 I QtAndroidService: loopin..
1.The service isnt starting immediately. It runs after 1-2 minutes delay.
see (2.)
The service stops after some time and the app crashes.
These problems was because I went this way -> "Service in the Same Process as QtActivity"
QTimer in the service doesn't work.
I haven't tried this when I separated the service from the activity.
QTcpSocket client doesn't work.
It seems I don't know how to use QTcpSocket properly because I tried it in simple x64 console application and can't do what I want.
I decided to go the Java way and do the task in java and I managed to succeed, now Im watching my money on my smart watch hehe :)
It uses node.js API with tcp server and puppeteer (headless chrome) that logs into my e-banking and gets my balance, listens to port and gives it on request.
/*
INCLUDES
*/
const config = require('config');
const puppeteer = require('puppeteer');
const Net = require('net');
const util = require("util");
const cron = require('node-cron');
/*
VARIABLES
*/
var kesh = "";
const hl = config.get('puppy.headless');
const server = new Net.Server();
const port = config.get('server.port');
get_kesh();
/*
CRON
*/
cron.schedule('0 10 * * *', () =>
{
get_kesh();
});
cron.schedule('0 13 * * *', () =>
{
get_kesh();
});
cron.schedule('0 16 * * *', () =>
{
get_kesh();
});
cron.schedule('0 19 * * *', () =>
{
get_kesh();
});
/*
SERVER
*/
server.listen(port, function()
{
console.log(`listen 0.0.0.0:${port}`);
});
/*
PUPETEER
*/
async function get_kesh()
{
console.log("updating kesh.. ");
const browser = await puppeteer.launch({headless: hl});
const page = await browser.newPage();
await page.setViewport({width: 1200, height: 720});
await page.goto('https://xxxxx.com', { waitUntil: 'networkidle0' }); // wait until page load
await page.type('#username', config.get('bank.user'));
await page.type('#password', config.get('bank.pass'));
// click and wait for navigation
await Promise.all([
page.click('button.btn.btn-primary.ng-scope'),
page.waitForNavigation({ waitUntil: 'networkidle0' }),
]);
const data = await page.evaluate(() => document.querySelector('div.acc-balance.row.ng-isolate-scope').querySelector('div#step0').querySelector('h4.blue-txt.ng-binding').textContent);
kesh = data.split(".")[0];
console.log(kesh);
console.log("\n");
browser.close();
}
/*
SERVER
*/
server.on('connection', function(socket)
{
console.log('A new connection has been established.');
var the_interval = 3 * 1000;
var hb = setInterval(function()
{
console.log("sending heartbeat to client..");
socket.write("HB\r\n");
}, the_interval);
socket.write("HELLO\r\n");
socket.on('data', function(chunk)
{
if(chunk.toString() == "get_kesh()\r\n")
{
console.log("sending kesh => ");
console.log(kesh);
console.log("BGN\n");
socket.write(kesh);
socket.write("\r\n");
}
});
socket.on('end', function()
{
clearInterval(hb);
console.log('Closing connection with the client');
});
socket.on('error', function(err)
{
console.log(`Error: ${err}`);
});
});
and this is my java service:
package org.qtproject.example.qtandroidservice;
import android.content.Context;
import android.content.Intent;
import android.util.Log;
import org.qtproject.qt.android.bindings.QtService;
import android.os.IBinder;
import android.widget.Toast;
import org.qtproject.example.tcpclient.Kesh;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.NotificationChannel;
//import android.app.Service;
public class QtAndroidService extends QtService
{
private Kesh k;
private static final String TAG = "QtAndroidService";
#Override
public void onCreate()
{
//super.onCreate();
Log.i(TAG, "Creating Service");
if(android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O)
{
String CHANNEL_ID = "my_channel_id";
NotificationChannel channel = new NotificationChannel(CHANNEL_ID,
"my_channel_title",
NotificationManager.IMPORTANCE_DEFAULT);
((NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE)).createNotificationChannel(channel);
Notification notification = new Notification.Builder(this, channel.getId())
.setContentTitle("service")
.setContentText("")
.build();
startForeground(1, notification);
}
}
#Override
public void onDestroy()
{
//super.onDestroy();
Log.i(TAG, "Destroying Service");
Toast.makeText(this, "service done", Toast.LENGTH_SHORT).show();
}
#Override
public int onStartCommand(Intent intent, int flags, int startId)
{
//int ret = super.onStartCommand(intent, flags, startId);
Log.i(TAG, "Starting Service");
k = new Kesh(this);
if(k.isConnected())
Log.i(TAG, "connected");
else
Log.i(TAG, "not connected");
Toast.makeText(this, "service started", Toast.LENGTH_SHORT).show();
//Toast.makeText(this, "Service Started", Toast.LENGTH_LONG).show();
new Thread()
{
public void run()
{
task();
}
}.start();
// If we get killed, after returning from here, restart
return START_STICKY;
//return ret;
}
#Override
public IBinder onBind(Intent intent)
{
// We don't provide binding, so return null
return null;
}
public static void startQtAndroidService(Context context)
{
if(android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O)
{
context.startForegroundService(new Intent(context, QtAndroidService.class));
}
else
{
context.startService(new Intent(context, QtAndroidService.class));
}
}
public static void stopQtAndroidService(Context context)
{
context.stopService(new Intent(context, QtAndroidService.class));
}
public static void log(String message)
{
Log.i(TAG, message);
}
private void task()
{
try
{
for(;;)
{
while(!k.hello);
Log.i(TAG, "polling..");
k.get_kesh();
Thread.sleep(10000);
//Thread.sleep(1800000);
//Thread.sleep(1800000);
}
}
catch (InterruptedException e)
{
// Restore interrupt status.
Thread.currentThread().interrupt();
}
}
}
this is my Kesh Class
package org.qtproject.example.tcpclient;
import android.os.AsyncTask;
import android.util.Log;
import android.content.Context;
import org.qtproject.example.androidnotifier.NotificationClient;
//import java.io.IOException;
import java.util.Timer;
import java.util.TimerTask;
import android.os.CountDownTimer;
public class Kesh
{
private TcpClient mTcpClient;
private static final String TAG = "QtAndroidService";
public String kesh;
public boolean hello = false;
private Context context;
private boolean tryToReconnect = true;
//private final Thread heartbeatThread;
private long heartbeatDelayMillis = 5000;
private boolean error;
private static final long TIMER_INTERVAL = 10000L;
private Timer mTimer;
private CountDownTimer waitTimer;
/**
* Constructor of the class.
*/
public Kesh(Context context)
{
this.context = context;
// connect
new ConnectTask().execute("");
waitTimer = new CountDownTimer(10000, 1000)
{
public void onTick(long millisUntilFinished)
{
Log.i(TAG, "Timer val: " +Long.toString(millisUntilFinished));
//called every 300 milliseconds, which could be used to
//send messages or some other action
}
public void onFinish()
{
Log.i(TAG, "Connection lost, reconnecting..");
Log.i(TAG, "Connection lost, reconnecting..");
Log.i(TAG, "Connection lost, reconnecting..");
Log.i(TAG, "Connection lost, reconnecting..");
Log.i(TAG, "Connection lost, reconnecting..");
reconnect();
waitTimer.start();
}
}.start();
}
public void restart_my_timer()
{
if(waitTimer != null)
{
waitTimer.cancel();
waitTimer.start();
}
}
public void notify_kesh()
{
NotificationClient.notify(this.context, this.kesh);
}
public void get_kesh()
{
//sends the message to the server
if (mTcpClient != null)
{
mTcpClient.sendMessage("get_kesh()\r\n");
}
}
protected void reconnect()
{
// disconnect
mTcpClient.stopClient();
//mTcpClient = null;
mTcpClient.run();
hello=false;
new ConnectTask().execute("");
}
public boolean isConnected()
{
if (mTcpClient != null)
{return true;
}
else
{return false;
}}
public class ConnectTask extends AsyncTask<String, String, TcpClient>
{
#Override
protected TcpClient doInBackground(String... message)
{
//we create a TCPClient object and
mTcpClient = new TcpClient(new TcpClient.OnMessageReceived()
{
#Override
//here the messageReceived method is implemented
public void messageReceived(String message)
{
Log.i(TAG, message);
if(message.equals("HB"))
{
//mTimer.cancel(); // Terminates this timer, discarding any currently scheduled tasks.
//mTimer.purge(); // Removes all cancelled tasks from this timer's task queue.
//start_my_timer();
restart_my_timer();
}
else if(!message.equals("HELLO"))
{
Kesh.this.kesh = message;
Kesh.this.notify_kesh();
}
else
{
Kesh.this.hello = true;
}
}
});
mTcpClient.run();
return null;
}
}
}
and my TcpClient class
package org.qtproject.example.tcpclient;
import android.util.Log;
import java.io.*;
import java.net.InetAddress;
import java.net.Socket;
/**
* Description
*
* #author Catalin Prata
* Date: 2/12/13
*/
public class TcpClient
{
public static final String SERVER_IP = "192.168.100.11"; //your computer IP address
public static final int SERVER_PORT = 9009;
// message to send to the server
private String mServerMessage;
// sends message received notifications
private OnMessageReceived mMessageListener = null;
// while this is true, the server will continue running
private boolean mRun = false;
// used to send messages
private PrintWriter mBufferOut;
// used to read messages from the server
private BufferedReader mBufferIn;
public Socket socket;
/**
* Constructor of the class. OnMessagedReceived listens for the messages received from server
*/
public TcpClient(OnMessageReceived listener)
{
mMessageListener = listener;
}
/**
* Sends the message entered by client to the server
*
* #param message text entered by client
*/
public void sendMessage(String message)
{
if (mBufferOut != null && !mBufferOut.checkError())
{
mBufferOut.print(message);
mBufferOut.flush();
}
}
// public boolean sendMessage(String message)
// {
// if(mBufferOut.checkError())
// {
// return true;
// }
// if (mBufferOut != null)
// {
// mBufferOut.print(message);
// mBufferOut.flush();
// }
// return false;
// }
/**
* Close the connection and release the members
*/
public void stopClient()
{
mRun = false;
}
public void run()
{
mRun = true;
try {
//here you must put your computer's IP address.
InetAddress serverAddr = InetAddress.getByName(SERVER_IP);
Log.e("QtAndroidService - TCP Client", "C: Connecting.../creating socket");
//create a socket to make the connection with the server
socket = new Socket(serverAddr, SERVER_PORT);
try {
//sends the message to the server
mBufferOut = new PrintWriter(new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())), true);
//receives the message which the server sends back
mBufferIn = new BufferedReader(new InputStreamReader(socket.getInputStream()));
//in this while the client listens for the messages sent by the server
while (mRun)
{
mServerMessage = mBufferIn.readLine();
if (mServerMessage != null && mMessageListener != null)
{
//call the method messageReceived from MyActivity class
mMessageListener.messageReceived(mServerMessage);
}
}
Log.e("RESPONSE FROM SERVER", "S: Received Message: '" + mServerMessage + "'");
} catch (Exception e) {
Log.e("TCP", "S: Error", e);
} finally {
//the socket must be closed. It is not possible to reconnect to this socket
// after it is closed, which means a new socket instance has to be created.
socket.close();
}
socket = null;
if (mBufferOut != null)
{
// mBufferOut.flush();
mBufferOut.close();
mBufferOut = null;
}
mBufferIn = null;
mMessageListener = null;
mServerMessage = null;
//socket = null;
} catch (Exception e) {
Log.e("TCP", "C: Error", e);
}
}
//Declare the interface. The method messageReceived(String message) will must be implemented in the MyActivity
//class at on asynckTask doInBackground
public interface OnMessageReceived {
public void messageReceived(String message);
}
}
I've a Service to manage my MQTT Client connection, the MQTT works fine, but the problem is when I restart Broker Server, the Android client not reconnect. A exception is triggered on onConnectionLost() callback.
Notes
I'm using Moquette Broker at same computer -> Moquette
I've two Android clients app, a using Service (the problematic) and other working on a Thread, without Service (this works fine, reconnect is ok).
I can't run the Android Client MQTT lib, because this I'm using the Eclipse Paho MQTT.
Yes, I make setAutomaticReconnect(true);
Problem
The Android app that use Service, to works forever, not reconnect to MQTT Broker.
Code
MQTTService.java
public class MQTTService extends Service implements MqttCallbackExtended {
boolean running;
private static final String TAG = "MQTTService";
public static final String ACTION_MQTT_CONNECTED = "ACTION_MQTT_CONNECTED";
public static final String ACTION_MQTT_DISCONNECTED = "ACTION_MQTT_DISCONNECTED";
public static final String ACTION_DATA_ARRIVED = "ACTION_DATA_ARRIVED";
// MQTT
MqttClient mqttClient;
final String serverURI = "tcp://"+ServidorServices.IP+":1883";
final String clientId = "Responsavel";
String topicoId;
Thread mqttStartThread;
public boolean subscribe(String topic) {
try {
Log.i(TAG,"Subscripe: " + topic);
mqttClient.subscribe(topic);
mqttClient.subscribe("LOCATION_REAL");
return true;
} catch (Exception e) {
e.printStackTrace();
}
return false;
}
// Life Cycle
#Override
public IBinder onBind(Intent intent) {
Log.d(TAG,"onBind()");
return null;
}
#Override
public void onCreate() {
Log.d(TAG,"onCreate()");
running = true;
topicoId = getSharedPreferences("myprefs",MODE_PRIVATE).getString("tag_id_aluno","0");
mqttStartThread = new MQTTStartThread(this);
if(topicoId.equals("0")) {
Log.i(TAG,"Error to subscribe");
return;
}
mqttStartThread.start();
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d(TAG,"onStartCommand()");
return super.onStartCommand(intent, flags, startId);
}
class MQTTStartThread extends Thread {
MqttCallbackExtended mqttCallbackExtended;
public MQTTStartThread(MqttCallbackExtended callbackExtended) {
this.mqttCallbackExtended = callbackExtended;
}
#Override
public void run() {
try {
mqttClient = new MqttClient(serverURI,clientId,new MemoryPersistence());
MqttConnectOptions options = new MqttConnectOptions();
options.setAutomaticReconnect(true);
options.setCleanSession(true);
mqttClient.setCallback(mqttCallbackExtended);
mqttClient.connect();
} catch (Exception e) {
Log.i(TAG,"Exception MQTT CONNECT: " + e.getMessage());
e.printStackTrace();
}
}
}
#Override
public void onDestroy() {
Log.d(TAG,"onDestroy()");
running = false;
if (mqttClient != null) {
try {
if (mqttClient.isConnected()) mqttClient.disconnect();
} catch (Exception e) {
e.printStackTrace();
}
}
}
#Override
public boolean onUnbind(Intent intent) {
Log.i(TAG,"onUnbind()");
return super.onUnbind(intent);
}
// Callbacks MQTT
#Override
public void connectComplete(boolean reconnect, String serverURI) {
Log.i(TAG,"connectComplete()");
if (topicoId == null) {
Log.i(TAG,"Erro ao ler ID da Tag");
return;
}
sendBroadcast(new Intent(ACTION_MQTT_CONNECTED));
subscribe(topicoId);
}
#Override
public void connectionLost(Throwable cause) {
Log.i(TAG,"connectionLost(): " + cause.getMessage());
cause.printStackTrace();
sendBroadcast(new Intent(ACTION_MQTT_DISCONNECTED));
}
#Override
public void messageArrived(String topic, MqttMessage message) throws Exception {
Log.i(TAG,"messageArrived() topic: " + topic);
if (topic.equals("LOCATION_REAL")) {
Log.i(TAG,"Data: " + new String(message.getPayload()));
} else {
Context context = MQTTService.this;
String data = new String(message.getPayload());
Intent intent = new Intent(context,MapsActivity.class);
intent.putExtra("location",data);
LatLng latLng = new LatLng(Double.valueOf(data.split("_")[0]),Double.valueOf(data.split("_")[1]));
String lugar = Utils.getAddressFromLatLng(latLng,getApplicationContext());
NotificationUtil.create(context,intent,"Embarque",lugar,1);
if (data.split("_").length < 3) {
return;
}
double latitude = Double.valueOf(data.split("_")[0]);
double longitude = Double.valueOf(data.split("_")[1]);
String horario = data.split(" ")[2];
Intent iMqttBroadcast = new Intent(ACTION_DATA_ARRIVED);
iMqttBroadcast.putExtra("topico",String.valueOf(topic));
iMqttBroadcast.putExtra("latitude",latitude);
iMqttBroadcast.putExtra("longitude",longitude);
iMqttBroadcast.putExtra("evento","Embarcou");
iMqttBroadcast.putExtra("horario",horario);
sendBroadcast(iMqttBroadcast);
}
}
#Override
public void deliveryComplete(IMqttDeliveryToken token) {
Log.i(TAG,"deliveryComplete()");
}
}
Exception Stacktrace
I/MQTTService: connectionLost(): Connection lost
W/System.err: Connection lost (32109) - java.io.EOFException
W/System.err: at org.eclipse.paho.client.mqttv3.internal.CommsReceiver.run(CommsReceiver.java:146)
W/System.err: at java.lang.Thread.run(Thread.java:818)
W/System.err: Caused by: java.io.EOFException
W/System.err: at java.io.DataInputStream.readByte(DataInputStream.java:77)
W/System.err: at org.eclipse.paho.client.mqttv3.internal.wire.MqttInputStream.readMqttWireMessage(MqttInputStream.java:65)
W/System.err: at org.eclipse.paho.client.mqttv3.internal.CommsReceiver.run(CommsReceiver.java:107)
W/System.err: ... 1 more
I think you forgot to include MqttConnectOptions with MqttClient object.
Please try like following
mqttClient.connect(options);
instead of
mqttClient.connect();
Hope it may help to resolve your re-connect issue.
As method description says.
options.setAutomaticReconnect(true);
The client will attempt to reconnect to the server. It will initially wait 1 second before it attempts to reconnect, for every failed reconnect attempt, the delay will doubleuntil it is at 2 minutes at which point the delay will stay at 2 minutes.
Another option would be you could manage retry interval in case of connection lost events.
My Android app uses JobService to perform my App Widget Refresh.
Today I received a single crash report of:-
Fatal Exception: java.lang.OutOfMemoryError: Could not allocate JNI Env
at java.lang.Thread.nativeCreate(Thread.java)
at java.lang.Thread.start(Thread.java:731)
at com.research.app_widget.WidgetUpdateJobService.onStartJob(SourceFile:29)
at android.app.job.JobService$JobHandler.handleMessage(JobService.java:143)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:154)
at android.app.ActivityThread.main(ActivityThread.java:6776)
at java.lang.reflect.Method.invoke(Method.java)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1496)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1386)
From the stack trace of this crash I have created over 500 instances of my HandlerThread
My onStartJob method resembles this:-
#Override
public boolean onStartJob(final JobParameters params) {
mServerHandlerThread = new HandlerThread("ServerApplication");
mServerHandlerThread.start();
mServerHandler = new Handler(mServerHandlerThread.getLooper(), new Handler.Callback() {
#Override
public boolean handleMessage(final Message msg) {
final int what = msg.what;
switch (what) {
case WHAT_WIDGET_REFRESH:
refreshWidget(params);
break;
default:
Log.e(TAG, "handleMessage: Unexpected WHAT = " + what);
}
return true;
}
});
mServerHandler.sendEmptyMessage(WHAT_WIDGET_REFRESH);
return true;
}
The refreshWidget(params) method is as follows:-
private void refreshWidget(final JobParameters params) {
final PersistableBundle persistableBundle = params.getExtras();
final int[] appWidgetIds = persistableBundle.getIntArray(AppWidgetManager.EXTRA_APPWIDGET_IDS);
if (appWidgetIds == null) {
Log.e(TAG, "refreshWidget: appWidgetIds array is null");
} else {
for (int appWidgetId : appWidgetIds) {
new WidgetRemoteViewsHelper().configureViews(this.getApplicationContext(), appWidgetId, isListEmpty(persistableBundle));
}
}
jobFinished(params, false);
}
What I don't understand is that theres an Android limit of starting 100 scheduledJobs, so how have I managed to start over 500 HandlerThreads?
What I would like to know is, is the following an acceptable solution?
e.g. reusing a single HandlerThread Looper instance?
private final HandlerThread mServerHandlerThread;
{
mServerHandlerThread = new HandlerThread("ServerApplication");
mServerHandlerThread.start();
}
#Override
public boolean onStartJob(final JobParameters params) {
mServerHandler = new Handler(mServerHandlerThread.getLooper(), new Handler.Callback() {
#Override
public boolean handleMessage(final Message msg) {
final int what = msg.what;
switch (what) {
case WHAT_WIDGET_REFRESH:
refreshWidget(params);
break;
default:
Log.e(TAG, "handleMessage: Unexpected WHAT = " + what);
}
return true;
}
});
mServerHandler.sendEmptyMessage(WHAT_WIDGET_REFRESH);
return true;
}
From my testing so far this approach seems to work as required. I just felt uneasy about reusing a single HandlerThread.
In my application I want use service for get request to server.
I should run this service for always and not stop it!
I write below code in service, but just show for 5 time and when receive to 5 step. then not show Toast!
But I want always getData() and show Toast.
Service class :
public class NotifyService extends Service {
private static final String TAG = "HelloService";
private boolean isRunning = false;
#Override
public void onCreate() {
Log.i(TAG, "Service onCreate");
isRunning = true;
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.i(TAG, "Service onStartCommand");
//Creating new thread for my service
//Always write your long running tasks in a separate thread, to avoid ANR
new Thread(new Runnable() {
#Override
public void run() {
//Your logic that service will perform will be placed here
//In this example we are just looping and waits for 5000 milliseconds in each loop.
for (int i = 0; i < 5; i++) {
try {
Thread.sleep(5000);
} catch (Exception e) {
}
if (isRunning) {
ExploreSendData sendData = new ExploreSendData();
sendData.setPageIndex(1);
sendData.setPageSize(10);
sendData.setShowFollows(false);
sendData.setShowMovies(true);
sendData.setShowNews(true);
sendData.setShowReplies(false);
sendData.setShowSeries(true);
sendData.setShowSuggestions(false);
InterfaceApi api = ApiClient.getClient().create(InterfaceApi.class);
Call<ExploreResponse> call = api.getExplore(new SharedPrefrencesHandler(NotifyService.this)
.getFromShared(SharedPrefrencesKeys.TOKEN.name()), sendData);
call.enqueue(new Callback<ExploreResponse>() {
#Override
public void onResponse(Call<ExploreResponse> call, Response<ExploreResponse> response) {
if (response.body().getData() != null && response.body().getStatusCode() != 401
&& response.body().getStatusCode() != 402) {
Toast.makeText(NotifyService.this, "Test Show message ever 5second", Toast.LENGTH_SHORT).show();
}
}
#Override
public void onFailure(Call<ExploreResponse> call, Throwable t) {
}
});
}
}
//Stop service once it finishes its task
stopSelf();
}
}).start();
return Service.START_STICKY;
}
#Override
public IBinder onBind(Intent arg0) {
Log.i(TAG, "Service onBind");
return null;
}
#Override
public void onDestroy() {
isRunning = false;
Log.i(TAG, "Service onDestroy");
}
}
I copy this service code from internet, but just show 5times. I want show always.
How can I edit my codes and fix it? Please help me. Thanks
The problem is not in the service, services start and continue living as long as the app is alive and android doesn't kill it. For an infinite loop replace the "for loop" with "While loop". The below loop doesn't end.
while (true) {
......
......
......
}
I need to call the Google activity recognition service through a service (not activity) and run it in the background, of course when the user starts the app, which has an activity (But the service does not called directly from activity).
Therefore I have created a service class (ActivitySensor) and another class (ActivityRecognitionScan).
When I install the app on my Galaxy Nexus S device, the service starts calling onCreate and onDestroy automatically. Even without doing anything in the GUI
It is very strange behaviour. Does anybody has the same experience or solution for it?
I mean I get something as follows in the debug console:
Activity-Logging --- onCreate
Activity-Logging --- onDestroy
Activity-Logging --- onCreate
Activity-Logging --- onDestroy
Activity-Logging --- onCreate
Activity-Logging --- onDestroy
...
Here are my two classes:
public class ActivitySensor extends IntentService {
private ActivityRecognitionScan myascan;
private Intent inIntent;
private static long ACTIVITY_LOG_INTERVAL = 30000L;
private static JsonEncodeDecode jsonencoder = new JsonEncodeDecode();
public ActivitySensor() {
super("ActivitySensor");
}
#Override
public void onCreate(){
super.onCreate();
Log.d("Activity-Logging", "--- onCreate");
try {
myascan = new ActivityRecognitionScan(getApplicationContext());
myascan.startActivityRecognitionScan();
} catch (Exception e) {
Log.e("[Activity-Logging]","----------Error:"+e.getLocalizedMessage());
e.printStackTrace();
}
}
#Override
public void readSensor() {
// Log.e("Activity-Logging", "ActivityRecognitionResult.hasResult: "+String.valueOf(ActivityRecognitionResult.hasResult(inIntent)));
if (ActivityRecognitionResult.hasResult(inIntent)) {
ActivityRecognitionResult result = ActivityRecognitionResult.extractResult(inIntent);
DetectedActivity activity = result.getMostProbableActivity();
final int type = activity.getType();
String strType = new String();
switch(type){
case DetectedActivity.IN_VEHICLE:
strType = "invehicle";
break;
case DetectedActivity.ON_BICYCLE:
strType ="onbicycle";
break;
case DetectedActivity.ON_FOOT:
strType = "onfoot";
break;
case DetectedActivity.STILL:
strType = "still";
break;
case DetectedActivity.TILTING:
strType ="tilting";
break;
case DetectedActivity.UNKNOWN:
strType ="unknown";
break;
}
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
Editor edt = prefs.edit();
String previousActv = prefs.getString("PREVIOUS_ACTIVIY","");
long previousDate = prefs.getLong("PREVIOUS_DATE", 0);
if (previousActv.length()==0){ // nothing was in the string and it is the first time just initialize
previousActv = strType;
previousDate = new Date().getTime();
// Log.e("-----FIRST TIME: type:", previousActv+" date:"+String.valueOf(previousDate));
edt.putString("PREVIOUS_ACTIVIY", strType);
edt.putLong("PREVIOUS_DATE", previousDate);
edt.commit();
}else {
if (!strType.equalsIgnoreCase(previousActv)){
Date readablePrevDate = new Date(previousDate);
Date nowDate = new Date();
String jsonstr = jsonencoder.EncodeActivity("Activity", readablePrevDate, nowDate, strType, activity.getConfidence());
// Log.e("[Activity-Logging] ----->",jsonstr);
edt.putString("PREVIOUS_ACTIVIY", strType);
edt.putLong("PREVIOUS_DATE", nowDate.getTime());
edt.commit();
DataAcquisitor.dataBuff.add(jsonstr);
}
}
}
}
#Override
protected void onHandleIntent(Intent intent) {
Log.d("Activity-Logging", "--- onHandleIntent"+ "---"+intent.getAction());
intent.putExtra("LOG_INTERVAL",ACTIVITY_LOG_INTERVAL );
intent.putExtra("STOP",false);
inIntent = intent;
readSensor();
}
#Override
public void onDestroy(){
Log.d("Activity-Logging", "--- onDestroy");
myascan.stopActivityRecognitionScan();
myascan=null;
//super.onDestroy();
}
}
This is the class that calls the Google Activity Recognition Service:
ActivityRecognitionScan implements GooglePlayServicesClient.ConnectionCallbacks, GooglePlayServicesClient.OnConnectionFailedListener {
private Context ctx;
private static final String TAG = "ActivityRecognition";
private static ActivityRecognitionClient actrecClient;
private static PendingIntent callbackIntent;
private long ACTIVITY_LOG_INTERVAL=30000;
public ActivityRecognitionScan(Context context) {
ctx=context;
}
public void startActivityRecognitionScan(){
int resp = GooglePlayServicesUtil.isGooglePlayServicesAvailable(ctx);
if(resp == ConnectionResult.SUCCESS){
actrecClient = new ActivityRecognitionClient(ctx, this, this);
if (!actrecClient.isConnected()){
actrecClient.connect();
} else{
Log.e("ActivityRecognitionScan"," ---Activity recognition client is already connected");
}
}else{
Log.e("[Activity-Logging]", "Google Play Service hasn't installed");
}
}
public void stopActivityRecognitionScan(){
try{
if (actrecClient.isConnected() || actrecClient.isConnecting() ){
actrecClient.removeActivityUpdates(callbackIntent);
actrecClient.disconnect();
}
} catch (Exception e){
e.printStackTrace();
}
}
#Override
public void onConnectionFailed(ConnectionResult result) {
Log.e("[ActivityRecognitionScan]", "Connection Failed");
}
#Override
public void onConnected(Bundle connectionHint) {
try{
Intent intent = new Intent(ctx, ActivitySensor.class);
Bundle bundle = intent.getExtras();
callbackIntent = PendingIntent.getService(ctx, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
long interval = 5000;
if ( null!= bundle && bundle.containsKey("LOG_INTERVAL") ){
interval = bundle.getLong("LOG_INTERVAL");
}
actrecClient.requestActivityUpdates(interval, callbackIntent);
actrecClient.disconnect();
}catch(Exception ex){
Log.e("[Activity-Logging]","Error in requesting Activity update "+ex.getMessage());
ex.printStackTrace();
}
}
#Override
public void onDisconnected() {
callbackIntent.cancel();
actrecClient = null;
Log.e("[ActivityRecognitionScan]","---onDisconnected");
}
}
IntentService automatically stops itself on completion of onHandleIntent as per the source code (see ServiceHandler.handleMessage()) as per the description of an IntentService:
Clients send requests through 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.
Use a Service if you want it to run continuously in the background.
You have 2 issues with your code that is causing the problem you are experiencing.
When activity is detected, the pending intent that is called calls (and creates, since it is an IntentService) ActivitySensor. The onCreate will connect another ActivityRecognitionClient, which is unnecessary. This causes another activity to be detected which causes your logging loop.
You should separate the creation of the ActivityRecognitionClient from the handling of the detected activity. You don't need to keep recreating it as subsequent detections will use the same PendingIntent. This will prevent the logging loop.