Hello,
I have been having an issue with my app crashing randomly, because of a problem in the QtThread.
I am using Qt5.5 on QtCreator.
The app is meant to run for an extended period of time (days to weeks) and at times it runs fine for days, and at others it crashes and fails.
The error that is given by the application output is:
F/libc ( 3353): Fatal signal 11 (SIGSEGV), code 1, fault addr 0x4 in tid 3372 (QtThread)F/libc
Unfortunately there is no surrounding output of code to give me any hints as to where/why this is happening. Also, because the crash is so seemingly random, I have no idea whether or not I have "fixed" it until it decides to crash again (or not).
The app itself uses a bluetooth proximity sensor, and when it is triggered will show a selected screen. This screen could be a simple Calendar, whose data is read from Androids Calendar Provider.
The only thing I can think of that would be causing this is the Calendar updating in the background causing a crash, or perhaps a Timer being called from the wrong thread.
I have already investigated if timers are being jumbled through threads, and have read over KDABs explanation of how to access and use Android Java API from Qt on Android using JNI but since I am not using Intents or overriding any prewritten methods I am not entirely certain how/if this applies to my code.
How my calendar is set up is I am querying a Google Calendar through AndroidCalendarClient.java,
which is then called through an C++ AndroidCalendarClient class using QAndroidJNI,
the C++ AndroidCalendarClient functions are then called from a calendarController C++ class and the results stored in a model or QStringList, which can be loaded and used through QML.
(see below for source code bits)
I have also looked through the Android Calendar Provider documentation, and it says under querying a calendar that the example shown is simplified and in practice should be called on an asynchronous thread. However, I'm not sure how to do this and whether it is required since I am calling it through Qt C++ anyways?
This has gotten complicated, to summarize what I am asking is this:
Am I looking in the right direction for this problem, or is there anything else you can think of that could be causing this?
How would I make sure the Android and Qt threads are properly separated?
Do I need to query the calendar on a separate asynchronous thread?
Thank you very much, if there's anything else I can add or explain please let me know,
spludlow
Here are source code snippets showing the getCalendars() function (there is also a getEvents() function that works pretty much exactly the same way)
AndroidCalendarClient.java
import android.content.Context;
import android.content.ContentProvider;
import android.content.ContentResolver;
import android.content.ContentUris;
import android.content.ContentValues;
import android.database.Cursor;
import android.net.Uri;
import android.provider.CalendarContract;
import android.provider.CalendarContract.Calendars;
import android.provider.CalendarContract.Events;
import android.provider.CalendarContract.Instances;
import java.util.ArrayList;
import java.util.List;
import java.util.Calendar;
import java.util.Date;
public class AndroidCalendarClient {
//Projection array for Calendar. Creating indeices for this array instead of
//dynamic lookups improves performance
public static final String[] CALENDAR_PROJECTION = new String[] {
Calendars._ID, // 0
Calendars.ACCOUNT_NAME, // 1
Calendars.CALENDAR_DISPLAY_NAME, // 2
Calendars.OWNER_ACCOUNT // 3
};
//The indices for the projection array above
private static final int PROJECTION_ID_INDEX = 0;
private static final int PROJECTION_ACCOUNT_NAME_INDEX = 1;
private static final int PROJECTION_DISPLAY_NAME_INDEX = 2;
private static final int PROJECTION_OWNER_ACCOUNT_INDEX = 3;
public static String[] getCalendars(Context context) {
//System.out.println("AndroidCalendarClient.getCalendars() has been called");
List<String> calendarList = new ArrayList<String>();
try {
// ...
//run a query of the calendar
Cursor curCal = null;
ContentResolver cr = context.getContentResolver();
Uri uri = Calendars.CONTENT_URI;
//query all google calendars
String selection = "((" + Calendars.ACCOUNT_TYPE +" = ? ))";
String[] selectionArgs = new String[] {"com.google"};
// Submit the query and get a Cursor object back.
curCal = cr.query(uri, CALENDAR_PROJECTION, selection, selectionArgs, null);
//Use the cursor to step through the returned records
while (curCal.moveToNext()) {
long calID = 0;
String dispName = null;
//get the field values
calID = curCal.getLong(PROJECTION_ID_INDEX);
dispName = curCal.getString(PROJECTION_DISPLAY_NAME_INDEX);
//System.out.println("calID: "+calID+" displayName: "+dispName);
//use CalendarEvent to hold details, title-> name, startTime-> calID
calendarList.add(dispName);
}
curCal.close();
}catch (RuntimeException e) {
e.printStackTrace();
}
return calendarList.toArray(new String[0]);
}
}
import android.content.Context;
import android.content.ContentProvider;
import android.content.ContentResolver;
import android.content.ContentUris;
import android.content.ContentValues;
import android.database.Cursor;
import android.net.Uri;
import android.provider.CalendarContract;
import android.provider.CalendarContract.Calendars;
import android.provider.CalendarContract.Events;
import android.provider.CalendarContract.Instances;
import java.util.ArrayList;
import java.util.List;
import java.util.Calendar;
import java.util.Date;
public class AndroidCalendarClient {
//Projection array for Calendar. Creating indeices for this array instead of
//dynamic lookups improves performance
public static final String[] CALENDAR_PROJECTION = new String[] {
Calendars._ID, // 0
Calendars.ACCOUNT_NAME, // 1
Calendars.CALENDAR_DISPLAY_NAME, // 2
Calendars.OWNER_ACCOUNT // 3
};
//The indices for the projection array above
private static final int PROJECTION_ID_INDEX = 0;
private static final int PROJECTION_ACCOUNT_NAME_INDEX = 1;
private static final int PROJECTION_DISPLAY_NAME_INDEX = 2;
private static final int PROJECTION_OWNER_ACCOUNT_INDEX = 3;
public static String[] getCalendars(Context context) {
//System.out.println("AndroidCalendarClient.getCalendars() has been called");
List<String> calendarList = new ArrayList<String>();
try {
// ...
//run a query of the calendar
Cursor curCal = null;
ContentResolver cr = context.getContentResolver();
Uri uri = Calendars.CONTENT_URI;
//query all google calendars
String selection = "((" + Calendars.ACCOUNT_TYPE +" = ? ))";
String[] selectionArgs = new String[] {"com.google"};
// Submit the query and get a Cursor object back.
curCal = cr.query(uri, CALENDAR_PROJECTION, selection, selectionArgs, null);
//Use the cursor to step through the returned records
while (curCal.moveToNext()) {
long calID = 0;
String dispName = null;
//get the field values
calID = curCal.getLong(PROJECTION_ID_INDEX);
dispName = curCal.getString(PROJECTION_DISPLAY_NAME_INDEX);
//System.out.println("calID: "+calID+" displayName: "+dispName);
//use CalendarEvent to hold details, title-> name, startTime-> calID
calendarList.add(dispName);
}
curCal.close();
}catch (RuntimeException e) {
e.printStackTrace();
}
return calendarList.toArray(new String[0]);
}
}
To copy to clipboard, switch view to plain text mode
AndroidCalendarClient.cpp
#include "AndroidCalendarClient.h"
#include <QApplication>
#include <QDebug>
#include <QtAndroidExtras/QAndroidJniEnvironment>
#include <QtAndroidExtras/QAndroidJniObject>
#include <qpa/qplatformnativeinterface.h>
AndroidCalendarClient
::AndroidCalendarClient( QObject *parent
) : CalendarClient(parent),
m_context(0)
{
QPlatformNativeInterface
* const interface
= QApplication::platformNativeInterface();
Q_ASSERT_X(interface, "AndroidCalendarClient", "Null pointer to QPlatformNativeInterface!");
m_context = interface->nativeResourceForIntegration("QtActivity");
Q_ASSERT_X(m_context, "AndroidCalendarClient", "Null pointer to void!");
}
QList<QString> AndroidCalendarClient::getCalendar() {
QList<QString> calendars;
const QAndroidJniObject calObj = QAndroidJniObject::callStaticObjectMethod("org.qtproject.canassist.AndroidCalendarClient",
"getCalendars",
"(Landroid/content/Context;)[Ljava/lang/String;",
m_context);
if(calObj.isValid()) {
const jobjectArray calendarArray = calObj.object<jobjectArray>();
QAndroidJniEnvironment qjniEnv;
const int n = qjniEnv->GetArrayLength(calendarArray);
jclass clazz = 0;
for (int i = 0; i < n; i++){
const jobject jCalendar = qjniEnv->GetObjectArrayElement(calendarArray, i);
Q_ASSERT_X(jCalendar, "calendar", "Null jobject!");
if(clazz == 0){
clazz = qjniEnv->GetObjectClass(jCalendar);
Q_ASSERT_X(clazz, "calendar", "Null jclass!");
}
else
Q_ASSERT_X(qjniEnv->IsInstanceOf(jCalendar, clazz), "calendar", "jobject is not of the expected type!");
calendars.append(QAndroidJniObject(jCalendar).toString());
qjniEnv->DeleteLocalRef(jCalendar);
}
if (qjniEnv->ExceptionCheck()) {
qjniEnv->ExceptionDescribe();
Q_ASSERT(false);
}
}
return calendars;
}
#include "AndroidCalendarClient.h"
#include <QApplication>
#include <QDebug>
#include <QtAndroidExtras/QAndroidJniEnvironment>
#include <QtAndroidExtras/QAndroidJniObject>
#include <qpa/qplatformnativeinterface.h>
AndroidCalendarClient::AndroidCalendarClient( QObject *parent) :
CalendarClient(parent),
m_context(0)
{
QPlatformNativeInterface * const interface = QApplication::platformNativeInterface();
Q_ASSERT_X(interface, "AndroidCalendarClient", "Null pointer to QPlatformNativeInterface!");
m_context = interface->nativeResourceForIntegration("QtActivity");
Q_ASSERT_X(m_context, "AndroidCalendarClient", "Null pointer to void!");
}
QList<QString> AndroidCalendarClient::getCalendar() {
QList<QString> calendars;
const QAndroidJniObject calObj = QAndroidJniObject::callStaticObjectMethod("org.qtproject.canassist.AndroidCalendarClient",
"getCalendars",
"(Landroid/content/Context;)[Ljava/lang/String;",
m_context);
if(calObj.isValid()) {
const jobjectArray calendarArray = calObj.object<jobjectArray>();
QAndroidJniEnvironment qjniEnv;
const int n = qjniEnv->GetArrayLength(calendarArray);
jclass clazz = 0;
for (int i = 0; i < n; i++){
const jobject jCalendar = qjniEnv->GetObjectArrayElement(calendarArray, i);
Q_ASSERT_X(jCalendar, "calendar", "Null jobject!");
if(clazz == 0){
clazz = qjniEnv->GetObjectClass(jCalendar);
Q_ASSERT_X(clazz, "calendar", "Null jclass!");
}
else
Q_ASSERT_X(qjniEnv->IsInstanceOf(jCalendar, clazz), "calendar", "jobject is not of the expected type!");
calendars.append(QAndroidJniObject(jCalendar).toString());
qjniEnv->DeleteLocalRef(jCalendar);
}
if (qjniEnv->ExceptionCheck()) {
qjniEnv->ExceptionDescribe();
Q_ASSERT(false);
}
}
return calendars;
}
To copy to clipboard, switch view to plain text mode
calendarController.cpp
void CalendarController::load() {
qDebug() << "CalendarController::load()";
g_rootContext->setContextProperty("CalendarController", this);
QLOG_INFO() << "Loading Calendar. Valid: " <<g_rootContext->isValid();
}
void CalendarController::loadCalendarModel() {
Q_ASSERT_X(m_calendarClient, "loadCalendarModel", "Null pointer to CalendarClient!");
foreach
(QString calendar, m_calendarClient
->getCalendar
()) { model.append(calendar);
}
setCalendarModel(model);
}
void CalendarController
::setCalendarModel(QStringList model
) { if(m_calendarModel != model){
m_calendarModel = model;
emit calendarModelChanged();
}
}
void CalendarController::load() {
qDebug() << "CalendarController::load()";
g_rootContext->setContextProperty("CalendarController", this);
QLOG_INFO() << "Loading Calendar. Valid: " <<g_rootContext->isValid();
}
void CalendarController::loadCalendarModel() {
Q_ASSERT_X(m_calendarClient, "loadCalendarModel", "Null pointer to CalendarClient!");
QStringList model = QStringList()<<tr("No Calendar Selected");
foreach(QString calendar, m_calendarClient->getCalendar()) {
model.append(calendar);
}
setCalendarModel(model);
}
void CalendarController::setCalendarModel(QStringList model) {
if(m_calendarModel != model){
m_calendarModel = model;
emit calendarModelChanged();
}
}
To copy to clipboard, switch view to plain text mode
Bookmarks