Dev Days, 2014 - Qt Developer Days · PDF filep.21 Copy missing Android files copy all files...
Transcript of Dev Days, 2014 - Qt Developer Days · PDF filep.21 Copy missing Android files copy all files...
p.1
ExtendingyourQtAndroidapplicationusingJNI
DevDays,2014
PresentedbyBogDanVatra
MaterialbasedonQt5.3,createdonNovember13,2014
p.2
ExtendingyourapplicationusingJNI
ExtendingyourapplicationusingJNI
ExtendingyourapplicationusingJNI
p.3
ExtendingyourapplicationusingJNI
JNICrashCourse
JNICrashCourse
p.4
WhatisJNIandwhydoyouneedit?
JNIistheJavaNativeInterfaceJavaNativeInterface.Itisneededtodocallsto/fromJavaworldfrom/tonative(C/C++)world.
ItisimpossibleforQttoimplementallAndroidfeatures,sotousethemyou'llneedJNItoaccessthem.
JNICrashCourse
p.5
MissingQtAPIs
listeningforAndroidO.S.notifications:
sdcardnotifications:badremoval,eject,mounted,unmounted,etc.
networknotifications:networkup/down.
batterylevelandchargingstate.
etc.
accessingAndroidO.S.features:
telephony(Initiatecalls,MMS,SMS,etc.)
contacts
speech(TTSandSpeechRecognizer)
systemaccounts
systempreferences
NFC
USB
printing(WARNING:needsAPI-19+)
createownAndroidActivitiesandServices
JNICrashCourse
p.6
JNICrashCourse
Usecase1:callaJavafunctionfromC/C++
Usecase1:callaJavafunctionfromC/C++
p.7
CreatetheJavafunction
1 //javafileandroid/src/com/kdab/training/MyJavaClass.java2 packagecom.kdab.training;34 publicclassMyJavaClass5 {6 //thismethodwillbecalledfromC/C++7 publicstaticintfibonacci(intn)8 {9 if(n<2)10 returnn;11 returnfibonacci(n-1)+fibonacci(n-2);12 }13 }
Demoandroid/JNIIntroDemoandroid/JNIIntro
Usecase1:callaJavafunctionfromC/C++
p.8
CallaJavamethodfromC/C++,theQtway
Let'sseewhatfibonaccifibonaccifunctioncalllookslikeusingtheQtandroidextrasmodule.
1 #Changestoyour.profile2 #....3 QT+=androidextras4 #....
1 //C++code2 #include<QAndroidJniObject>3 intfibonacci(intn)4 {5 returnQAndroidJniObject::callStaticMethod<jint>6 ("com/kdab/training/MyJavaClass"//javaclassname7 ,"fibonacci"//methodname8 ,"(I)I"//signature9 ,n);10 }
Yes,that'sallfolks!
Usecase1:callaJavafunctionfromC/C++
p.9
JNICrashCourse
Usecase2:callbackaC/C++functionfromJava
Usecase2:callbackaC/C++functionfromJava
p.10
Yourjob
declareanativemethodinJavausingnativekeyword(seeslide11)
registernativemethodinC/C++(seeslide12,slide15)
dotheactualcall(seeslide11)
Usecase2:callbackaC/C++functionfromJava
p.11
DeclareandinvoketheJavanativemethods
1 //javafileandroid/src/com/kdab/training/MyJavaClass.java2 packagecom.kdab.training;34 classMyJavaNatives5 {6 //declarethenativemethod7 publicstaticnativevoidsendFibonaciResult(intn);8 }910 publicclassMyJavaClass11 {12 //thismethodwillbecalledfromC/C++13 publicstaticintfibonacci(intn)14 {15 if(n<2)16 returnn;17 returnfibonacci(n-1)+fibonacci(n-2);18 }1920 publicstaticvoidcompute_fibonacci(intn)21 {22 //callbackthenativemethodwiththecomputedresult.23 MyJavaNatives.sendFibonaciResult(fibonacci(n));24 }25 }
Usecase2:callbackaC/C++functionfromJava
p.12
C/C++registernativemethods
RegisteringfunctionsusingJava_Fully_Qualified_Class_Name_MethodNameJava_Fully_Qualified_Class_Name_MethodNametemplate.
1 //C++code2 #include<jni.h>34 #ifdef__cplusplus5 extern"C"{6 #endif78 JNIEXPORTvoidJNICALL9 Java_com_kdab_training_MyJavaNatives_sendFibonaciResult(JNIEnv*/*env*/,10 jobject/*obj*/,11 jintn)12 {13 qDebug()<<"Computedfibonacciis:"<<n;14 }1516 #ifdef__cplusplus17 }18 #endif
Usecase2:callbackaC/C++functionfromJava
p.13
UsingJava_Fully_Qualified_Class_Name_MethodNameJava_Fully_Qualified_Class_Name_MethodName.
Pro:
easiertodeclareeasiertodeclare,youdon'tneedtospecifythefunctionsignature
easiertoregistereasiertoregister,youdon'tneedtoexplicitlyregisterit
Con:
thefunctionnamesarehugehuge:Java_com_kdab_training_MyJavaNatives_sendFibonaciResult
thelibrarywillexportlotsoffunctions
unsaferunsafer,thereisnowayfortheVMtocheckthefunctionsignature
Usecase2:callbackaC/C++functionfromJava
p.14
UseJNIEnv::RegisterNativesJNIEnv::RegisterNativestoregisternativefunctions.
Step4Step4callJNIEnv::RegisterNatives(java_class_ID,methods_vector,n_methods)
Step3Step3findtheIDofjavaclassthatdeclaresthesemethodsusingJniEnv::FindClass
Step2Step2createavectorwithallC/C++methodsthatyouwanttoregister
Step1Step1getJNIEnvpointerbydefiningJNIEXPORTjintJNI_OnLoad(JavaVM*vm,void*/*reserved*/)JNIEXPORTjintJNI_OnLoad(JavaVM*vm,void*/*reserved*/).Youcandefineit(onceper.sofile)(onceper.sofile)inany.cppfileyoulike
Usecase2:callbackaC/C++functionfromJava
p.15
UseJNIEnv::RegisterNatives
1 //C++code2 #include<jni.h>34 //defineournativemethod5 staticvoidsendFibonaciResult(JNIEnv*/*env*/,jobject/*obj*/,jintn)6 {7 qDebug()<<"Computedfibonacciis:"<<n;8 }910 //step211 //createavectorwithallourJNINativeMethod(s)12 staticJNINativeMethodmethods[]={13 {"sendFibonaciResult",//constchar*functionname;14 "(I)V",//constchar*functionsignature15 (void*)sendFibonaciResult//functionpointer}16 };
Usecase2:callbackaC/C++functionfromJava
p.16
UseJNIEnv::RegisterNatives
1 //step12 //thismethodiscalledautomaticallybyJavaafterthe.sofileisloaded3 JNIEXPORTjintJNI_OnLoad(JavaVM*vm,void*/*reserved*/)4 {5 JNIEnv*env;6 //gettheJNIEnvpointer.7 if(vm->GetEnv(reinterpret_cast<void**>(&env),JNI_VERSION_1_6)!=JNI_OK)8 returnJNI_ERR;910 //step311 //searchforJavaclasswhichdeclaresthenativemethods12 jclassjavaClass=env->FindClass("com/kdab/training/MyJavaNatives");13 if(!javaClass)14 returnJNI_ERR;1516 //step417 //registerournativemethods18 if(env->RegisterNatives(javaClass,methods,19 sizeof(methods)/sizeof(methods[0]))<0){20 returnJNI_ERR;21 }22 returnJNI_VERSION_1_6;23 }
Usecase2:callbackaC/C++functionfromJava
p.17
UseJNIEnv::RegisterNatives
Pro:
thenativefunctioncanhaveanynameyouwant
thelibraryneedstoexportonlyonefunction(JNI_OnLoad)(JNI_OnLoad).
safersafer,becausetheVMchecksthedeclaredsignature
Con:
isslightlyhardertouse
Usecase2:callbackaC/C++functionfromJava
p.18
ExtendingyourapplicationusingJNI
SDCardNotificationsExample
SDCardNotificationsExample
p.19
Gameplan
PlumbingPlumbing
understandingAndroidfiles
howtouseanexternalIDEtomanagetheJavapart
importexistingAndroidfiles
debugJava
ArchitectureArchitecture
howtoextendtheJavapartofyourQtApplication
howtodosafecallsfromQtthreadtoAndroidUIthread
howtodosafecallsfromAndroidUIthreadtoQtthread
SDCardNotificationsExample
p.20
SDCardNotificationsExample
UnderstandingAndroidfiles
UnderstandingAndroidfiles
p.21
CopymissingAndroidfiles
copyallfilesfrom<qt_install_path>/src/android/java/to<your_src>/android/folder
makesureyour.profilehasANDROID_PACKAGE_SOURCE_DIRANDROID_PACKAGE_SOURCE_DIRpropertyset
ANDROID_PACKAGE_SOURCE_DIR=$$PWD/android
UnderstandingAndroidfiles
p.22
MeaningoftheAndroidfiles
AndroidManifest.xml-isthesamefilecopiedbyQtCreatorwhenyouclickonCreateCreateAndroidManifest.xmlAndroidManifest.xmlbutton.
version.xml-isusedbyQtCreatortoupdateyourexistingAndroidfiles
res/values/libs.xml-thisfilecontainstheinformationabouttheneededlibs
res/values(-*)/strings.xml-thesefilescontainthe(translated)stringsneededbyJavapart
src/org/kde/necessitas/ministro/*.aidl-thesefilesareusedtoconnecttoMinistroservice
src/org/qtproject/qt5/android/bindings/QtApplication.java-thisclasscontainsallthelogicneededtoforwardalltheactivitycallstotheactivitydelegate
src/org/qtproject/qt5/android/bindings/QtActivity.java-thisistheapplicationactivityclass,itcontainsthelogictoconnecttoMinistroortounpackthebundledQtlibs.Italsoforwardsallcallstotheactivitydelegate.
UnderstandingAndroidfiles
p.23
SDCardNotificationsExample
UsinganexternalIDEforJavapart
UsinganexternalIDEforJavapart
p.24
UsinganexternalIDEtoextendtheJavapart
Sadly,theJavasupportinQtCreatorisclosetozero,soweneedtouseanexternalIDEtoeasilyextendtheJavapart.AndroidprovidestwopowerfulIDEs:
Eclipse
AndroidStudio(thisisverygoodandstableeventhoughitislabeledasbeta).
UsinganexternalIDEforJavapart
p.25
Eclipseusers
IfyouarenotusingADTbundle,thenmakesureyouhaveallADTpluginsinstalledinEclipse.
UsinganexternalIDEforJavapart
p.26
UsinganexternalIDE
TheexternalIDEwillbeusedto:
importexistingAndroidfiles
createandeditJavafiles
debugJavapart
TheexternalIDEwillNOTNOTbeusedtoruntheapplication,westillneedQtCreatorforthatjob!
UsinganexternalIDEforJavapart
p.27
Thispresentationwillteachyouonlytheverybasicusageofbothofthem.Ifyouwanttolearnmorepleasechecktheirowndocumentation.Alsoit'suptoyouwhichoneyouchoosetouse.
UsinganexternalIDEforJavapart
p.28
UsinganexternalIDEforJavapart
ImportexistingAndroidfilesinEclipse
ImportexistingAndroidfilesinEclipse
p.29
WARNING
BeforeyoustarttoimporttheprojectmakesuremakesuretheBuildAutomaticallyBuildAutomaticallyfeatureisturnedOFFisturnedOFF!Otherwisewhenyoustarttobuildyourprojectyou'llgetlotsofmysteriousbuildproblems.UncheckProjectProject->BuildAutomaticallyBuildAutomatically
ImportexistingAndroidfilesinEclipse
p.30
ImportinginEclipse
FileFile->NewNew->Project...Project...
ImportexistingAndroidfilesinEclipse
p.31
ImportinginEclipse
ChooseAndroidProjectfromExistingCodeAndroidProjectfromExistingCodewizard
ImportexistingAndroidfilesinEclipse
p.32
ImportinginEclipse
SelecttherootfolderofyourAndroidfiles(itshouldbeyour_qt_src/androidyour_qt_src/android)andpressFinishFinishbutton.
ImportexistingAndroidfilesinEclipse
p.33
UsinganexternalIDEforJavapart
ImportexistingAndroidfilesinAndroidStudio
ImportexistingAndroidfilesinAndroidStudio
p.34
ImportinginAndroidStudio
FileFile->Importproject...Importproject... doesallthemagic,youjustneedtoselecttherootfolderofyourAndroidfiles(itshouldbeyour_qt_src/androidyour_qt_src/android)andcontinuethewizarduntilitisfinished.
ImportexistingAndroidfilesinAndroidStudio
p.35
UsinganexternalIDEforJavapart
DebuggingJavapart
DebuggingJavapart
p.36
DebugwithEclipse
setthebreakpoints
switchtoDDMSperspective(WindowWindow->OpenperspectiveOpenperspective->DDMSDDMS)
starttheapplication(usingQtCreator)
waitforapplicationtostart
selecttheapplication
clickthedebugicon
DebuggingJavapart
p.37
DebugwithAndroidStudio
setthebreakpoints
attachdebugger(RunRun->AttachdebuggertoAndroidprocessAttachdebuggertoAndroidprocess)
starttheapplication(usingQtCreator)
waitforapplicationtostart
selecttheapplication
clicktheOKOKbutton
DebuggingJavapart
p.38
Debuggingtipsandtricks
Theproblemcomeswhenweneedtostartthedebuggingveryearly.Todothatwearegoingtousetheoldfashionedsleeptrick.ThisishowourcustomcustomonCreateonCreatefunctionlooks:
1 @Override2 publicvoidonCreate(BundlesavedInstanceState)3 {4 try{5 Thread.sleep(10000);6 }catch(InterruptedExceptione){7 e.printStackTrace();8 }9 ....10 }
DebuggingJavapart
p.39
Architecturediagram
SDCardNotificationsExample
p.40
SDCardNotificationsExample
ExtendingJavapart
ExtendingJavapart
p.41
Extending,notchanging
Youshouldnotchangeanyoftheexisting.javafiles,insteadyoushouldextendthem
ThereisnoneedtoextendtheQtApplicationapplicationclass
YoushouldextendonlytheQtActivityclass.TheActivityobjectisveryimportant,you'llneedittoinstantiatemostoftheAndroidclasses
Demoandroid/SD_Card_NotificationsDemoandroid/SD_Card_Notifications
ExtendingJavapart
p.42
ExtendingQtActivity
1 //src/com/kdab/training/MyActivity.java2 packagecom.kdab.training;34 importandroid.os.Bundle;5 importorg.qtproject.qt5.android.bindings.QtActivity;67 publicclassMyActivityextendsQtActivity8 {9 //we'llneeditinBroadcastReceiver10 publicstaticMyActivitys_activity=null;1112 //everytimeyouoverrideamethod,alwaysmakesureyou13 //thencallsupermethodaswell14 @Override15 publicvoidonCreate(BundlesavedInstanceState)16 {17 s_activity=this;18 super.onCreate(savedInstanceState);19 }2021 @Override22 protectedvoidonDestroy()23 {24 super.onDestroy();25 s_activity=null;26 }27 }
ExtendingJavapart
p.43
ExtendingJavapart
ChangedefaultactivitytoAndroidManifest.xml,from:
1 <activity...2 android:name="org.qtproject.qt5.android.bindings.QtActivity"3 ...>;
To:
1 <activity...2 android:name="com.kdab.training.MyActivity"3 ...>;
ExtendingJavapart
p.44
NativeFunctions.javapart1
1 publicclassNativeFunctions{2 //declarethenativefunctions3 //thesefunctionswillbecalledbytheBroadcastReceiverobject4 //whenitreceivesanewnotification5 publicstaticnativevoidonReceiveNativeMounted();6 publicstaticnativevoidonReceiveNativeUnmounted();78 //thisstaticmethodiscalledbyC/C++toregistertheBroadcastReceiver.(step1)9 publicstaticvoidregisterBroadcastReceiver(){10 if(MyActivity.s_activity!=null){11 //QtisrunningonadifferentthreadthanAndroid.(step2)12 //InordertoregisterthereceiverweneedtoexecuteitintheUIthread13 MyActivity.s_activity.runOnUiThread(newRegisterReceiverRunnable());14 }15 }16 }
ExtendingJavapart
p.45
NativeFunctions.javapart2
1 classRegisterReceiverRunnableimplementsRunnable2 {3 //Step34 //thismethodiscalledonAndroidUiThread5 @Override6 publicvoidrun(){7 IntentFilterfilter=newIntentFilter();8 filter.addAction(Intent.ACTION_MEDIA_MOUNTED);9 filter.addAction(Intent.ACTION_MEDIA_UNMOUNTED);10 filter.addDataScheme("file");11 //thismethodmustbecalledonAndroidUiThread12 MyActivity.s_activity.registerReceiver(newSDCardReceiver(),filter);13 }14 }
ExtendingJavapart
p.46
NativeFunctions.javapart3
1 classSDCardReceiverextendsBroadcastReceiver{2 @Override3 publicvoidonReceive(Contextcontext,Intentintent){4 //Step45 //callthenativemethodwhenitreceivesanewnotification6 if(intent.getAction().equals(Intent.ACTION_MEDIA_MOUNTED))7 NativeFunctions.onReceiveNativeMounted();8 elseif(intent.getAction().equals(Intent.ACTION_MEDIA_UNMOUNTED))9 NativeFunctions.onReceiveNativeUnmounted();10 }11 }
ExtendingJavapart
p.47
Architecturediagram,Javapart
ExtendingJavapart
p.48
SDCardNotificationsExample
ExtendingC/C++part
ExtendingC/C++part
p.49
main.cpp
1 #include"mainwindow.h"2 #include<QApplication>3 #include<QAndroidJniObject>45 intmain(intargc,char*argv[])6 {7 QApplicationa(argc,argv);89 //Step1.10 //callregisterBroadcastReceivertoregisterthebroadcastreceiver11 QAndroidJniObject::callStaticMethod<void>("com/kdab/training/NativeFunctions"12 ,"registerBroadcastReceiver"13 ,"()V");1415 MainWindow::instance().show();16 returna.exec();17 }
ExtendingC/C++part
p.50
mainwindow.h
1 classMainWindow:publicQMainWindow2 {3 Q_OBJECT45 public:6 staticMainWindow&instance(QWidget*parent=0);78 publicslots:9 voidonReceiveMounted();10 voidonReceiveUnmounted();1112 private:13 explicitMainWindow(QWidget*parent=0);14 ~MainWindow();1516 private:17 Ui::MainWindow*ui;18 };
ExtendingC/C++part
p.51
mainwindow.cpp
1 MainWindow&MainWindow::instance(QWidget*parent)2 {3 staticMainWindowmainWindow(parent);4 returnmainWindow;5 }67 //Step68 //CallbackinQtthread9 voidMainWindow::onReceiveMounted()10 {11 ui->plainTextEdit->appendPlainText(QLatin1String("MEDIA_MOUNTED"));12 }1314 voidMainWindow::onReceiveUnmounted()15 {16 ui->plainTextEdit->appendPlainText(QLatin1String("MEDIA_UNMOUNTED"));17 }
ExtendingC/C++part
p.52
native.cpppart1
1 //Step4.callbackfromjavainAndroidthread2 //defineournativemethods3 staticvoidonReceiveNativeMounted(JNIEnv*/*env*/,jobject/*obj*/)4 {5 //Step5.DelegatethecalltoQtthread.6 QMetaObject::invokeMethod(&MainWindow::instance(),"onReceiveMounted"7 ,Qt::BlockingQueuedConnection);8 }910 staticvoidonReceiveNativeUnmounted(JNIEnv*/*env*/,jobject/*obj*/)11 {12 //Step5.DelegatethecalltoQtthread.13 QMetaObject::invokeMethod(&MainWindow::instance(),"onReceiveUnmounted"14 ,Qt::BlockingQueuedConnection);15 }
ExtendingC/C++part
p.53
native.cpppart2
1 //createavectorwithallourJNINativeMethod(s)2 staticJNINativeMethodmethods[]={3 {"onReceiveNativeMounted","()V",(void*)onReceiveNativeMounted},4 {"onReceiveNativeUnmounted","()V",(void*)onReceiveNativeUnmounted},5 };67 //thismethodiscalledautomaticallybyJavaafterthe.sofileisloaded8 JNIEXPORTjintJNI_OnLoad(JavaVM*vm,void*/*reserved*/)9 {10 JNIEnv*env;//gettheJNIEnvpointer.11 if(vm->GetEnv(reinterpret_cast<void**>(&env),JNI_VERSION_1_6)!=JNI_OK)12 returnJNI_ERR;1314 //searchforJavaclasswhichdeclaresthenativemethods15 jclassjavaClass=env->FindClass("com/kdab/training/NativeFunctions");16 if(!javaClass)17 returnJNI_ERR;1819 //registerournativemethods20 if(env->RegisterNatives(javaClass,methods,21 sizeof(methods)/sizeof(methods[0]))<0){22 returnJNI_ERR;23 }24 returnJNI_VERSION_1_6;25 }
ExtendingC/C++part
p.54
Architecturediagram,QtJNIpart
ExtendingC/C++part
p.55
Architecturediagram,summary
ExtendingC/C++part
p.56
Questions?
Thankyouforyourtime!
Contactus:
http://www.kdab.comhttp://www.kdab.com
[email protected]@kdab.com
[email protected]@kdab.com
[email protected]@kdab.com
[email protected]@kdab.com