How-to: create a standalone app that doesn't need ministro
warning: this isn't a copy-paste howto. You will need to understand the logic and apply it to your own app.
I assume that your app already works OK with ministro and with the 'use local qt libs' option. If not, make it work first and only then come back here.
The main idea is to mimic what necessitas does when you use the 'deploy qt libs' and 'use local qt libs' options in the project run configuration.
I found out that these are the things it does:
deploy a large amount of files and puts them in the path /data/local/qt/ on your emulator.
You will need to get a copy of these files. (see step 1).
Pass some parameters to QtActivity.java. And lator on pass more parameters to the method loadApplication(Bundle loaderParams).
You will need to find out what these parameters are. (step 2).
Some of the parameters are files which your application needs in order to run properly. These files will need to be either added to your /android/libs/ path, or you will have to copy them manually to the device and then tell QtActivity.java where to look for them.
Some other parameters are Strings that you will need simply to pass forward in the same way.
i attached all of my code, you can use it under the included license.
As a first step you will need to get a copy of the libraries Necessitas uses. To achieve this, deploy your app to an emulator using the 'deploy qt libs' option. Then use ddms (from android sdk) to pull these files back to your pc.
Note 1: the deployed file are different depending on the type of your emulator, so you will need to hold a copy of all options. Right now I only know of a difference between arm v5 and arm v7 architectures, so you will need to get one copy of the files from an API 14 emulator for arm v7 and another copy from a different emulator for arm v5. (AFAIK arm v5 and v6 act the same so no need to worry about v6 at all).
Note 2: not all of these files are actually used. It is recommanded to find out which files you need and which you don't. (more will be explained in the next steps).
From this step and here after we will be editing the file QtActivity.java which is automatically created by necessitas and put in your <app-dir>/android/src/org/kde/necessitas/origo/ path. It is recommended (but not necessary) to use eclipse for these modifications.
In the method startApp you will find the following piece of code:
// use local libs
if (getIntent().getExtras()!= null && getIntent().getExtras().containsKey("use_local_qt_libs")
&& getIntent().getExtras().getString("use_local_qt_libs").equals("true"))
{
ArrayList<String> libraryList= new ArrayList<String>();
String localPrefix="/data/local/qt/";
if (getIntent().getExtras().containsKey("libs_prefix")) {
localPrefix=getIntent().getExtras().getString("libs_prefix");
// Log.d("izar", "local prefix = " +localPrefix);
}
if (m_qtLibs != null)
for(int i=0;i<m_qtLibs.length;i++)
{
libraryList.add(localPrefix+"lib/lib"+m_qtLibs[i]+".so");
}
if (getIntent().getExtras().containsKey("load_local_libs"))
{
String []extraLibs=getIntent().getExtras().getString("load_local_libs").split(":");
for (String lib:extraLibs) {
// Log.d("izar", "lib: " + lib);
if (lib.length()>0)
libraryList.add(localPrefix+lib);
}
}
String dexPaths = new String();
String pathSeparator = System.getProperty("path.separator", ":");
if (getIntent().getExtras().containsKey("load_local_jars"))
{
String []jarFiles=getIntent().getExtras().getString("load_local_jars").split(":");
for (String jar:jarFiles)
if (jar.length()>0)
{
// Log.d("izar", "jar: " + jar);
if (dexPaths.length()>0)
dexPaths+=pathSeparator;
dexPaths+=localPrefix+jar;
}
}
// Log.d("izar", "dex paths: " +dexPaths);
Bundle loaderParams = new Bundle();
loaderParams.putInt(ERROR_CODE_KEY, 0);
loaderParams.putString(DEX_PATH_KEY, dexPaths);
loaderParams.putString(LOADER_CLASS_NAME_KEY, getIntent().getExtras().containsKey("loader_class_name")
?getIntent().getExtras().getString("loader_class_name")
:"org.kde.necessitas.industrius.QtActivityDelegate");
// Log.d("izar", "LOADER_CLASS_NAME_KEY: " + (getIntent().getExtras().containsKey("loader_class_name")? getIntent().getExtras().getString("loader_class_name") :"org.kde.necessitas.industrius.QtActivityDelegate"));
loaderParams.putStringArrayList(NATIVE_LIBRARIES_KEY, libraryList);
loaderParams.putString(ENVIRONMENT_VARIABLES_KEY,"QML_IMPORT_PATH=/data/local/qt/imports\tQT_PLUGIN_PATH=/data/local/qt/plugins");
loaderParams.putString(APPLICATION_PARAMETERS_KEY,"-platform\tandroid");
Log.d("izar", "loader params:\n" + loaderParams +"\n<<end>>\n");
loadApplication(loaderParams);
return;
}
you basically need to use the method Log.d() on any string you don't know the value of. Then run your app once again, using the 'use local libs' option, and get the results in the output.
In this step we will be adding the files to the target device, and telling your app where to find them.
This is what I found out about my app:
First i need to add the libraries displayed in <app-dir>/android/res/values/libs.xmlto the libraryList. Then I need to add another file named "lidandroid-#" where '#' is a number that depends on the target device's API level.
All of these library files need to go into your <app-dir>/android/libs/armeabi/ or /armeabi-v7a/ path, so that thay will be automatically installed on the device. Tell your QtActivity to look for them in the /data/data/<app package name>/libs (instead of localPrefix+"lib").
The last file I found that is used by my app is QtIndustrius-#.jar where '#' is a number that depends on the target device's API level. This file can not be deployed automatically so you will need to create your own method to do this, and then tell QtActivity where the file is.
I used a class called CopyResources (which I wrote) to perform this task. It copys files from the assests folder to a location on the target device. I used the built in getFilesDir() method to give me the best path.
The rest of the parameters that go into loaderParams can probably copied from what QtActivity does when it receives the 'use local libs' command. You will have to try and see what happens.
First read everything again and make sure that you really understood.
Try to figure out if your app does something differently than mine.
I don't promise to help anyone, but you may ask as a last resort.
Pleas don't do this.
Minidtro exists for a good reason.
On 12/02/2012 12:00 PM, Ray Donnelly wrote:
> Pleas don't do this.
>
> Minidtro exists for a good reason.
Hi,
Sorry, dropping in after a long time away...
Is there a more offical option other than doing what avraham suggests if
you need to ship a customised Qt build with your app? Does Ministro now
support multiple side-by-side Qt builds? Including custom app-specific
builds?
I've used Qt for over 3 years and I have *never* been able to ship Qt
libs built from stock sources -- local patches have always been required
to fix bugs. Of course this is not a specific problem with qt-android
but until such time as Qt project sorts out their QA I think this
use-case needs to be accomodated somehow.
Thanks
Ross
Hello Ross, long time no hear from!
Well, in theory if the patches are definitely good, we would consider
applying them to the Android version (though BogDan will have final
say on this of course).
For developers working on Necessitas or testing local changes, you can
use local Qt libs (copying your own Qt libs over the ones the
Necessitas installer creates), but that doesn't address final
deployment...
There isn't a way to hook your own libs into Ministro at present
(though there is a capability for different branches which is subtly
different), the hope was obviously to reduce the size-impact of Qt
libs on the user's device(s) and having the ability for apps to
declare their own versions would work against that (and create a
QA/reputation problem if there's bugs in your own libs)...
So this needs a lot of discussion...
Cheers,
Ray.
For developers working on Necessitas or testing local changes, you can
use local Qt libs (copying your own Qt libs over the ones the
Necessitas installer creates), but that doesn't address final
deployment...
No implementation found for native Lorg/kde/necessitas/industrius/QtNative;.startQtAndroidPlugin ()Z
Please, take a look on files attached.
Thanks, Rustem.
Just give me some time, i`m working now but when a came home i will write document with comments.
I've been following this thread closely, because I've been worried about the common user reaction, when they discover they have additonal libraries to install. Now that I've got the first reactions, I can tell it is a problem. Some users doesn't complete the installation, because they think we are installing a malware in their back. Some others get stuck. Those who go till the end find it annoying.
I think static should definitively be an option, if not the default way.
Best regards.
Before I'll answer to some of your questions, I want to clarify a few things.
We, the people behind Necessitas
(http://necessitas.kde.org/people.php), are doing this job for *FREE*
only on our spare time, just because we love to do it.
We are *COMMITTED* to do what *we believe* it is the best for Qt, for
developer and the most important for *USERS*.
Hi Nal Sav
Hi Nal Sav
what is the android folder .. / assets?
How-to: create a standalone app that doesn't need ministro
warning: this isn't a copy-paste howto. You will need to understand the logic and apply it to your own app.
I assume that your app already works OK with ministro and with the 'use local qt libs' option. If not, make it work first and only then come back here.
Main idea
The main idea is to mimic what necessitas does when you use the 'deploy qt libs' and 'use local qt libs' options in the project run configuration.
I found out that these are the things it does:
deploy a large amount of files and puts them in the path /data/local/qt/ on your emulator.
You will need to get a copy of these files. (see step 1).
Pass some parameters to QtActivity.java. And lator on pass more parameters to the method loadApplication(Bundle loaderParams).
You will need to find out what these parameters are. (step 2).
Some of the parameters are files which your application needs in order to run properly. These files will need to be either added to your /android/libs/ path, or you will have to copy them manually to the device and then tell QtActivity.java where to look for them.
Some other parameters are Strings that you will need simply to pass forward in the same way.
i attached all of my code, you can use it under the included license.
Step 1
As a first step you will need to get a copy of the libraries Necessitas uses. To achieve this, deploy your app to an emulator using the 'deploy qt libs' option. Then use ddms (from android sdk) to pull these files back to your pc.
Note 1: the deployed file are different depending on the type of your emulator, so you will need to hold a copy of all options. Right now I only know of a difference between arm v5 and arm v7 architectures, so you will need to get one copy of the files from an API 14 emulator for arm v7 and another copy from a different emulator for arm v5. (AFAIK arm v5 and v6 act the same so no need to worry about v6 at all).
Note 2: not all of these files are actually used. It is recommanded to find out which files you need and which you don't. (more will be explained in the next steps).
Step 2
From this step and here after we will be editing the file QtActivity.java which is automatically created by necessitas and put in your <app-dir>/android/src/org/kde/necessitas/origo/ path. It is recommended (but not necessary) to use eclipse for these modifications.
In the method startApp you will find the following piece of code:
// use local libs
if (getIntent().getExtras()!= null && getIntent().getExtras().containsKey("use_local_qt_libs")
&& getIntent().getExtras().getString("use_local_qt_libs").equals("true"))
{
ArrayList<String> libraryList= new ArrayList<String>();
String localPrefix="/data/local/qt/";
if (getIntent().getExtras().containsKey("libs_prefix")) {
localPrefix=getIntent().getExtras().getString("libs_prefix");
// Log.d("izar", "local prefix = " +localPrefix);
}
if (m_qtLibs != null)
for(int i=0;i<m_qtLibs.length;i++)
{
libraryList.add(localPrefix+"lib/lib"+m_qtLibs[i]+".so");
}
if (getIntent().getExtras().containsKey("load_local_libs"))
{
String []extraLibs=getIntent().getExtras().getString("load_local_libs").split(":");
for (String lib:extraLibs) {
// Log.d("izar", "lib: " + lib);
if (lib.length()>0)
libraryList.add(localPrefix+lib);
}
}
String dexPaths = new String();
String pathSeparator = System.getProperty("path.separator", ":");
if (getIntent().getExtras().containsKey("load_local_jars"))
{
String []jarFiles=getIntent().getExtras().getString("load_local_jars").split(":");
for (String jar:jarFiles)
if (jar.length()>0)
{
// Log.d("izar", "jar: " + jar);
if (dexPaths.length()>0)
dexPaths+=pathSeparator;
dexPaths+=localPrefix+jar;
}
}
// Log.d("izar", "dex paths: " +dexPaths);
Bundle loaderParams = new Bundle();
loaderParams.putInt(ERROR_CODE_KEY, 0);
loaderParams.putString(DEX_PATH_KEY, dexPaths);
loaderParams.putString(LOADER_CLASS_NAME_KEY, getIntent().getExtras().containsKey("loader_class_name")
?getIntent().getExtras().getString("loader_class_name")
:"org.kde.necessitas.industrius.QtActivityDelegate");
// Log.d("izar", "LOADER_CLASS_NAME_KEY: " + (getIntent().getExtras().containsKey("loader_class_name")? getIntent().getExtras().getString("loader_class_name") :"org.kde.necessitas.industrius.QtActivityDelegate"));
loaderParams.putStringArrayList(NATIVE_LIBRARIES_KEY, libraryList);
loaderParams.putString(ENVIRONMENT_VARIABLES_KEY,"QML_IMPORT_PATH=/data/local/qt/imports\tQT_PLUGIN_PATH=/data/local/qt/plugins");
loaderParams.putString(APPLICATION_PARAMETERS_KEY,"-platform\tandroid");
Log.d("izar", "loader params:\n" + loaderParams +"\n<<end>>\n");
loadApplication(loaderParams);
return;
}
you basically need to use the method Log.d() on any string you don't know the value of. Then run your app once again, using the 'use local libs' option, and get the results in the output.
Step 3
In this step we will be adding the files to the target device, and telling your app where to find them.
This is what I found out about my app:
First i need to add the libraries displayed in <app-dir>/android/res/values/libs.xmlto the libraryList. Then I need to add another file named "lidandroid-#" where '#' is a number that depends on the target device's API level.
All of these library files need to go into your <app-dir>/android/libs/armeabi/ or /armeabi-v7a/ path, so that thay will be automatically installed on the device. Tell your QtActivity to look for them in the /data/data/<app package name>/libs (instead of localPrefix+"lib").
The last file I found that is used by my app is QtIndustrius-#.jar where '#' is a number that depends on the target device's API level. This file can not be deployed automatically so you will need to create your own method to do this, and then tell QtActivity where the file is.
I used a class called CopyResources (which I wrote) to perform this task. It copys files from the assests folder to a location on the target device. I used the built in getFilesDir() method to give me the best path.
Step 4
The rest of the parameters that go into loaderParams can probably copied from what QtActivity does when it receives the 'use local libs' command. You will have to try and see what happens.
Didn't work what to do
First read everything again and make sure that you really understood.
Try to figure out if your app does something differently than mine.
I don't promise to help anyone, but you may ask as a last resort.