I have a pretty simple Qt program I'm deploying on Android 4.4.2 (Nexus 5 phone) via QtCreator 3.0.1 (Qt 5.2.1).
This program creates a dummy file and then proposes to open it using user's default file editor via a Java SDK call. The file can be opened either by clicking a button or selecting a menu item.
Why one crashs but not the other when it actually executes the same code?
From button:
Then I press F10 and it continues.
From menu:
Then I press F10 and it crashs (my program ends, but Android's file viewer remains opened):
Qt application log reports:
F/libc ( 6949): Fatal signal 11 (SIGSEGV) at 0x00000001 (code=1), thread 6971 (ample.MenuCrash)
"org.qtproject.example.MenuCrash" est mort.
Here is the code:
pro file:
QT += core gui androidextras widgets
TARGET = MenuCrash
TEMPLATE = app
SOURCES += main.cpp\
mainwindow.cpp
HEADERS += mainwindow.h
CONFIG += mobility
MOBILITY =
main.cpp:
#include "mainwindow.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}
mainwindow.h:
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = 0);
~MainWindow();
public slots:
void openFile();
private:
QString m_fileName;
};
#endif // MAINWINDOW_H
mainwindow.cpp:
#include "mainwindow.h"
#include <fstream>
#include <QMenuBar>
#include <QMenu>
#include <QVBoxLayout>
#include <QAndroidJniObject>
#include <QPushButton>
#include <QMessageBox>
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
{
m_fileName = "/sdcard/Download/test_file.txt";
std::fstream file;
// create the file
file.open( m_fileName.toStdString().c_str(), std::ios_base::out );
if ( file.is_open() )
{
// Write something in the file
file << "Hello!!!" << std::endl;
file.close();
QWidget* parent = new QWidget( this );
QVBoxLayout* layout = new QVBoxLayout( parent );
setCentralWidget( parent );
QMenuBar* pBar = menuBar();
QMenu* pMenu = pBar->addMenu( "" );
QPushButton* button = new QPushButton( "Open file", this );
layout->addWidget( button );
QObject::connect( button, SIGNAL(clicked()), this, SLOT(openFile()) );
pMenu->addAction( "Open file now", this, SLOT(openFile()) );
QAction* action = pMenu->addAction( "Open file later" );
QObject::connect( action, SIGNAL(triggered()), this, SLOT(openFile()), Qt::QueuedConnection );
}
else
{
QMessageBox::critical(this,"Error","Unable to create file");
}
}
MainWindow::~MainWindow()
{
}
void MainWindow::openFile()
{
/* Translate this java code:
Intent intent = new Intent();
intent.setAction(android.content.Intent.ACTION_VIEW);
File file = new File( m_fileName );
intent.setDataAndType(Uri.fromFile(file), "text/plain");
myActivity.startActivity(intent);
*/
QAndroidJniObject activity =
QAndroidJniObject::callStaticObjectMethod("org/qtproject/qt5/android/QtNative",
"activity",
"()Landroid/app/Activity;");
if ( activity.isValid() ) //activity is valid
{
QAndroidJniObject intent("android/content/Intent","()V");
if ( intent.isValid() )
{
QAndroidJniObject name = QAndroidJniObject::fromString(m_fileName);
QAndroidJniObject type = QAndroidJniObject::fromString("text/plain");
QAndroidJniObject action = QAndroidJniObject::fromString("android.intent.action.VIEW");
if ( type.isValid() && name.isValid() && action.isValid() )
{
QAndroidJniObject file( "java/io/File","(Ljava/lang/String;)V",name.object<jobject>());
if ( file.isValid() )
{
QAndroidJniObject uri = QAndroidJniObject::callStaticObjectMethod("android/net/Uri", "fromFile", "(Ljava/io/File;)Landroid/net/Uri;", file.object<jobject>());
if ( uri.isValid() )
{
intent.callObjectMethod("setDataAndType","(Landroid/net/Uri;Ljava/lang/String;)Landroid/content/Intent;",uri.object<jobject>(),type.object<jobject>());
intent.callObjectMethod("setAction","(Ljava/lang/String;)Landroid/content/Intent;",action.object<jobject>());
if ( intent.isValid() )
{
activity.callObjectMethod("startActivity","(Landroid/content/Intent;)V",intent.object<jobject>());
}
}
}
}
}
}
}
Note:
Qt::QueuedConnection
, same crash when using the menuQAction::triggered()
to QPushButton::click()
(pMenu->addAction( "Open file now", button, SLOT(click()) );
), same crash when using the menuQPushButton::clicked()
to QAction::trigger()
(QObject::connect( button, SIGNAL(clicked()), action, SLOT(trigger()) );
), it does not crash when clicking the button. So it seems the QAction can be triggered....so now I try stackoverflow....;-)
Just created a Qt bug report: https://bugreports.qt-project.org/browse/QTBUG-41395
Christian from Qt team fixed this...
See https://bugreports.qt-project.org/browse/QTBUG-41395
The fix is to replace
activity.callObjectMethod("startActivity","(Landroid/content/Intent;)V",intent.object<jobject>());
by
activity.callMethod<void>("startActivity","(Landroid/content/Intent;)V",intent.object<jobject>());
As startActivity
is a void method...