{"id":1940,"date":"2015-02-03T22:47:51","date_gmt":"2015-02-03T14:47:51","guid":{"rendered":"http:\/\/blog.qdac.cc\/?p=1940"},"modified":"2015-02-04T16:43:48","modified_gmt":"2015-02-04T08:43:48","slug":"%e8%8b%b1-delphi-%e7%bc%96%e5%86%99-android-%e6%9c%8d%e5%8a%a1","status":"publish","type":"post","link":"https:\/\/blog.qdac.cc\/?p=1940","title":{"rendered":"[\u82f1] Delphi \u7f16\u5199 Android \u670d\u52a1"},"content":{"rendered":"<p>\u539f\u6587\u94fe\u63a5\uff1ahttp:\/\/blog.blong.com\/2013\/11\/delphi-and-android-services-part-2.html\uff08\u56fd\u5916\uff09<\/p>\n<p>\u539f\u6587\u6807\u9898\uff1aDelphi \u548c Android \u670d\u52a1<\/p>\n<p>\u6ce8\uff1a\u672c\u6765\u60f3\u7ffb\u8bd1\u7ed9\u5927\u5bb6\uff0c\u4f46\u611f\u89c9\u6211\u8fd9\u82f1\u8bed\u592a\u5dee\uff0c\u5b9e\u5728\u65e0\u6cd5\u7cbe\u51c6\u7684\u5c06\u8001\u5916\u7684\u8fd9\u7bc7\u6587\u7ae0\u7ffb\u8bd1\u51fa\u6765\uff0c\u6240\u4ee5\u539f\u6587\u8f93\u51fa\u5427\u3002\u8c01\u8981\u662f\u80fd\u7ffb\u8bd1\u7684\u4e0d\u9519\uff0c\u8bf7\u8054\u7cfb\u6211\uff0c\u6211\u4f1a\u5c06\u60a8\u7684\u7ffb\u8bd1\u653e\u51fa\u6765\u3002<\/p>\n<p>\u7b2c\u4e00\u90e8\u5206\uff1a<\/p>\n<h3 class=\"post-title entry-title\">Delphi and Android services<\/h3>\n<div class=\"post-header\"><\/div>\n<div id=\"post-body-6097083457374555690\" class=\"post-body entry-content\">\n<p>Since Delphi\u2019s introduction of Android support with the release of XE5, there have been many questions posted hither and thither regarding whether and how you can build service applications with it. The pervading suggestion or suspicion is that maybe Delphi isn\u2019t cut out to build such exotic Android creations.<\/p>\n<p>Well, if your goal is to <em>just<\/em> build some sort of a service app I wouldn\u2019t particularly recommend Delphi for the job right now, simply because it hasn\u2019t got the wherewithal to support you in that endeavour in its current guise, and consequently it\u2019s quite difficult. Really, if you just want to build a service app you\u2019d probably have an easier time using Java or Oxygene for Java in conjunction with the Android SDK.<\/p>\n<p>However if you are invested in building a Delphi Android app, are in the midst of building a FireMonkey application for mobile deployment, and need an Android service as part of that, well it\u2019s quite possible. But as I may have mentioned, it\u2019s neither particularly easy nor especially straightforward.<\/p>\n<p>I am aware that I have (at this time of writing) yet to write up the business of adding Android menus to Delphi apps, employing a splash screen and launching Android activities and getting results back (as demo\u2019d in <a href=\"http:\/\/blog.blong.com\/2013\/10\/my-coderage-session-files.html\" target=\"_blank\">my CodeRage 8 talk<\/a>, albeit with code samples available in that linked post)*.<\/p>\n<p>Now I can add to the list of things I have yet to write up the steps for creating a service application. If you take a look at <a href=\"https:\/\/play.google.com\/store\/apps\/details?id=com.embarcadero.ServiceApp\" target=\"_blank\">this Google Play Store listing<\/a> you\u2019ll see I\u2019ve built an app that, as well as a splash screen, also implements a service.<\/p>\n<p align=\"center\"><a href=\"https:\/\/blog.qdac.cc\/wp-content\/uploads\/2015\/02\/ServiceApp_thumb.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-1945\" src=\"https:\/\/blog.qdac.cc\/wp-content\/uploads\/2015\/02\/ServiceApp_thumb.png\" alt=\"ServiceApp_thumb\" width=\"139\" height=\"244\" \/><\/a><a href=\"https:\/\/blog.qdac.cc\/wp-content\/uploads\/2015\/02\/ServiceApp_thumb.png\">\u00a0<\/a><a href=\"https:\/\/blog.qdac.cc\/wp-content\/uploads\/2015\/02\/ServiceAppProcess_thumb.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-1946\" src=\"https:\/\/blog.qdac.cc\/wp-content\/uploads\/2015\/02\/ServiceAppProcess_thumb.png\" alt=\"ServiceAppProcess_thumb\" width=\"139\" height=\"244\" \/><\/a><\/p>\n<p>So yes, it\u2019s quite feasible to build a service and a broadcast receiver in a Delphi app. That said, I\u2019ve had to be a little creative on exiting the app to avoid a failure being evident on the screen.<\/p>\n<p>As mentioned, these Android aspects that fall outside the standard simple business app paradigm haven\u2019t factored into what is readily supported currently in Delphi\u2019s Android support. Hopefully this will be improved as time goes on to allow all sorts of Android features and facets to be surfaced in Delphi Android code. In the mean time a certain degree of spelunking and skulduggery is needed to achieve certain ends.<\/p>\n<p>I know quite a few people are clamouring for knowledge on how to build service apps. Hopefully in the near future I\u2019ll write it up, but certainly in the <em>nearer<\/em> future I\u2019ll release the code sample, once I\u2019ve exhausted possibilities to clean up the wrinkle around app shutdown. That should satisfy the keener of you out there. In the mean time, be satisfied that it\u2019s been proved to be possible at all.<\/p>\n<p>\u3010\u7b2c\u4e8c\u90e8\u5206\u3011<\/p>\n<\/div>\n<div class=\"post-header\">\n<p>It\u2019s been a while now since <a href=\"http:\/\/blog.blong.com\/2013\/11\/delphi-and-android-services.html\" target=\"_blank\">I posted<\/a> up the link to <a href=\"https:\/\/play.google.com\/store\/apps\/details?id=com.embarcadero.ServiceApp\" target=\"_blank\">my Delphi service example on Google Play<\/a>.<\/p>\n<p>I mentioned at the time that it was not a trivial exercise and was somewhat imperfect in implementation (although I\u2019d done my best to disguise this fact). Indeed you only really see an issue if you are looking at the logcat output from the app (either using the command-line <code><a href=\"http:\/\/developer.android.com\/tools\/help\/adb.html#logcat\" target=\"_blank\">adb logcat<\/a><\/code> command, or using the deprecated <a href=\"http:\/\/developer.android.com\/tools\/debugging\/ddms.html\" target=\"_blank\">DDMS tool<\/a> or the replacement <a href=\"http:\/\/developer.android.com\/tools\/help\/monitor.html\" target=\"_blank\">Monitor tool<\/a>). Alas the app dies while endeavouring to tidy up for exit\u2026<\/p>\n<p>I also mentioned at the time that I was hoping to work out some scheme to remedy the situation before releasing the example source, something to do with pride or perfectionism or some such\u2026 Anyway, with the day job, the evening job, the prep work for last week\u2019s<a href=\"http:\/\/be-delphi.com\/cms\/\" target=\"_blank\">Be-Delphi 3.0 event<\/a> in Belgium my plans haven\u2019t been accomplished and I have still to work out if it\u2019s feasible to clean up the shutdown part of the sample. All my current efforts have come to little in resolving it.<\/p>\n<p>So, given that there have been a number of requests for the source I have now made it available at <a href=\"http:\/\/blong.com\/Downloads\/DelphiServiceDemo.7z\" target=\"_blank\">this download link<\/a>. This is the same as the sample online other than it has no splash screen. This example is free of a splash screen to simplify the example and leave it clearer. There are a couple of examples that incorporate a splash screen and have similar custom build steps available in <a href=\"http:\/\/blog.blong.com\/2013\/10\/my-coderage-session-files.html\" target=\"_blank\">my CodeRage 8 session files<\/a>.<\/p>\n<p>Please note that the archive has a Readme file in it, and you should take the time to read through this file carefully as it explains the non-standard build steps required to get the sample successfully built for deployment. If you don\u2019t follow the Readme file, then you\u2019re unlikely to get any joy with the sample at all.<\/p>\n<p>For your convenience I include the text of the ReadMe file below.<\/p>\n<p>As you will see, since Delphi has no catering for building Delphi services or broadcast receivers, I\u2019ve had to go \u201cround the houses\u201d to do it, using Java stubs, and pertinent thread-switching between the Java and Delphi threads.<\/p>\n<p>As you will also perhaps appreciate, I don\u2019t recommend this as a productive way of building an Android service. However if you need to add a service to a Delphi Android application, then this approach allows you to do so.<\/p>\n<p>But\u2026 as of the time of posting there is a still the small matter of a (slightly brushed under the carpet) death-by-tombstoning bug on shutdown. If any keen reader can work out a clean way of closing I\u2019d be most obliged to hear of it!<\/p>\n<p>[Update]<\/p>\n<p>When the small Java source files get compiled, the Android dx tool expects them to be compiled by the JDK 1.6.x compiler as opposed to the JDK 1.7.x compiler. If you have JDK 1.7.x installed, you hit a problem with dx reporting:<\/p>\n<p><em>bad class file magic (cafebabe) or version (0033.0000)<\/em><\/p>\n<p>However, to avoid forcing a reinstall of JDK 1.6 you might like to modify my build.bat batch files and add in extra <a href=\"http:\/\/docs.oracle.com\/javase\/7\/docs\/technotes\/tools\/windows\/javac.html#options\" target=\"_blank\">command line switches<\/a> to the javac.exe command-lines. You need to insert this after the javac.exe command to force Java 1.6 byte code output, which is digestible by the Android dx command:<\/p>\n<p><code>-source 1.6 -target 1.6<\/code><\/p>\n<hr \/>\n<pre>Service example\r\n===============<\/pre>\n<pre>This project implements an Android service along with a couple\r\nof broadcast receivers to catch and respond to intent messages\r\nflung around the system. The service and receiver classes are\r\nimplemented as extremely shallow Java classes, which need to be\r\ncompiled and converted to an appropriate executable format\r\n(dex), and then merged into the other dex code deployed with an\r\nAndroid app.<\/pre>\n<pre>The Java code calls into native Delphi code to implement\r\nthe \"business end\" of the service and receivers.<\/pre>\n<pre class=\"\">Build the Java support files:\r\n----------------------------<\/pre>\n<pre>In the project's java directory are some source files:<\/pre>\n<pre> - java\\src\\com\\blong\\test\\ActivityReceiver.java\r\n - java\\src\\com\\blong\\test\\SampleService.java\r\n - java\\src\\com\\blong\\test\\ServiceReceiver.java<\/pre>\n<pre>These need to be compiled to .class files, archived into a .jar\r\nfile, converted from Java byte code to DEX (Dalvik Executable)\r\nformat and merged into the normally used (by Delphi's Android\r\ndeployment process) classes.dex.<\/pre>\n<pre>To set this up, follow these steps:<\/pre>\n<pre> - Ensure the useful subdirectory of Android SDK's build-tools\r\ndirectory is on the system PATH (e.g. C:\\Android\\android-sdk-\r\nwindows\\build-tools\\18.0.1 or C:\\Users\\Public\\Documents\\RAD\r\nStudio\\12.0\\PlatformSDKs\\adt-bundle-windows-x86-20130522\r\n\\sdk\\android-4.2.2)<\/pre>\n<pre> - Ensure the Java Development Kit's bin directory is on the\r\nsystem PATH (e.g. C:\\Program Files (x86)\\Java\\jdk1.6.0_23\\bin)<\/pre>\n<pre> - Run a command prompt in the project's java subdirectory and\r\nensure you can successfully launch each of these:<\/pre>\n<pre>    - javac\r\n    - jar\r\n    - dx<\/pre>\n<pre> - Review the build.bat file and ensure the environment\r\nvariables are set correctly:<\/pre>\n<pre>    - ANDROID needs to point to your Android SDK base\r\ndirectory, e.g. C:\\Users\\Public\\Documents\\RAD Studio\\12.0\r\n\\PlatformSDKs\\adt-bundle-windows-x86-20130522\\sdk or\r\nC:\\Android\\android-sdk-windows<\/pre>\n<pre>    - ANDROID_PLATFORM needs to point at an installed SDK\r\nplatform installation, e.g. %ANDROID%\\platforms\\android-15 or %\r\nANDROID%\\platforms\\android-17. Check for one that is installed.<\/pre>\n<pre>    - DX_LIB needs to point to the lib subdirectory under the\r\nAndroid SDK build-tools directory, e.g. %ANDROID%\\build-\r\ntools\\18.0.1\\lib or %ANDROID%\\build-tools\\android-4.2.2\\lib<\/pre>\n<pre>    - EMBO_DEX needs to point to the Delphi-supplied Android\r\nclasses.dex file, wrapped in quotes to take care of any spaces\r\nin the path, e.g. \"C:\\Program Files (x86)\\Embarcadero\\RAD\r\nStudio\\12.0\\lib\\android\\debug\\classes.dex\"<\/pre>\n<pre> - Run build.bat<\/pre>\n<pre> - You should now have a new file in the project directory tree\r\ncalled java\\output\\dex\\classes.dex<\/pre>\n<pre>This file replaces the supplied classes.dex and has the Java\r\nservice and broadcast receiver compiled classes included in it.<\/pre>\n<pre>Set the new classes.dex for deployment:\r\n--------------------------------------<\/pre>\n<pre>Open the project in the IDE<\/pre>\n<pre>Choose Project | Deployment<\/pre>\n<pre>Note that the classes.dex file from the project's\r\njava\\output\\dex directory is set to be deployed.<\/pre>\n<pre>You must ensure that the default classes.dex file is de-\r\nselected. As you switch configurations, this de-selection will\r\nbe lost (the file will be re-set to be deployed) and will need\r\nto again be de-selected. This is an IDE bug with Delphi XE5 RTM\r\nand XE5 UP1 and is logged in Quality Central as bug 118472.<\/pre>\n<pre>Register the service:\r\n--------------------<\/pre>\n<pre>This has already been done for this demo. It involves the\r\nfollowing:<\/pre>\n<pre> - Open the Android manifest template,\r\nAndroidManifest.template.xml, which is generated on first\r\ncompile, from the project directory<\/pre>\n<pre> - Add in a description of the service in the application\r\nelement:\r\n \r\n&lt;service android:name=\"com.blong.test.SampleService\" \/&gt;<\/pre>\n<pre>Build the Delphi Android application library:\r\n--------------------------------------------<\/pre>\n<pre>In the IDE choose Project | Compile ServiceApp (or press\r\nCtrl+F9)<\/pre>\n<pre>Deploy the library to an Android application package (.apk):\r\n-----------------------------------------------------------<\/pre>\n<pre>In the IDE choose Project | Deploy libServiceApp.so<\/pre>\n<pre>Install the app on the device:\r\n-----------------------------<\/pre>\n<pre>In the IDE choose Run | Run Without Debugging (or press\r\nCtrl+Shift+F9).<\/pre>\n<pre>This will implicitly do the compile and deploy steps above, so\r\nthey are actually optional.<\/pre>\n<pre class=\"\">This step will uninstall the app if already installed and then\r\ninstall the newly built version.\r\n<\/pre>\n<\/div>\n<p>\u7b2c\u4e09\u90e8\u5206\uff1a<\/p>\n<p>Once upon a time I uploaded a Delphi sample app to the Google Play Store, written in Delphi XE5 and implementing an Android service. I wrote about the published app <a href=\"http:\/\/blog.blong.com\/2013\/11\/delphi-and-android-services.html\" target=\"_blank\">in this post<\/a> and supplied the source <a href=\"http:\/\/blog.blong.com\/2013\/11\/delphi-and-android-services-part-2.html\" target=\"_blank\">in this follow-up post<\/a>.<\/p>\n<p>There was an issue that\u2019s been hanging around with that sample project. When pulled into Delphi XE7 and updated so it all compiled nicely, it didn\u2019t play ball when deployed. Instead it hung.<\/p>\n<p>I\u2019ve eventually worked through the to-do list enough to take a look at the problem. For one reason or another starting the service in the <span style=\"font-family: 'Courier New';\">OnCreate<\/span> event handler caused an issue thanks to some required thread synchronisation, which no longer functions as I wanted it to.<\/p>\n<p>I\u2019ve worked around the problem by very slightly delaying the service starting by use of a one-shot timer. Then the form setup is done and dusted by the time the service is invoked and all seems to be well.<\/p>\n<p><a href=\"http:\/\/blong.com\/Downloads\/DelphiServiceDemo.7z\" target=\"_blank\">An updated download archive is available here<\/a>, which contains the original Delphi XE5 project and a Delphi XE7 equivalent, which should now be functionally equivalent.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>\u539f\u6587\u94fe\u63a5\uff1ahttp:\/\/blog.blo [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"site-sidebar-layout":"default","site-content-layout":"","ast-site-content-layout":"default","site-content-style":"default","site-sidebar-style":"default","ast-global-header-display":"","ast-banner-title-visibility":"","ast-main-header-display":"","ast-hfb-above-header-display":"","ast-hfb-below-header-display":"","ast-hfb-mobile-header-display":"","site-post-title":"","ast-breadcrumbs-content":"","ast-featured-img":"","footer-sml-layout":"","ast-disable-related-posts":"","theme-transparent-header-meta":"","adv-header-id-meta":"","stick-header-meta":"","header-above-stick-meta":"","header-main-stick-meta":"","header-below-stick-meta":"","astra-migrate-meta-layouts":"default","ast-page-background-enabled":"default","ast-page-background-meta":{"desktop":{"background-color":"var(--ast-global-color-4)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"tablet":{"background-color":"","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"mobile":{"background-color":"","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""}},"ast-content-background-meta":{"desktop":{"background-color":"var(--ast-global-color-5)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"tablet":{"background-color":"var(--ast-global-color-5)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"mobile":{"background-color":"var(--ast-global-color-5)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""}},"footnotes":""},"categories":[145,8,21],"tags":[319],"class_list":["post-1940","post","type-post","status-publish","format-standard","hentry","category-android","category-delphi","category-misc","tag-319"],"views":8441,"_links":{"self":[{"href":"https:\/\/blog.qdac.cc\/index.php?rest_route=\/wp\/v2\/posts\/1940","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/blog.qdac.cc\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blog.qdac.cc\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blog.qdac.cc\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/blog.qdac.cc\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=1940"}],"version-history":[{"count":5,"href":"https:\/\/blog.qdac.cc\/index.php?rest_route=\/wp\/v2\/posts\/1940\/revisions"}],"predecessor-version":[{"id":1981,"href":"https:\/\/blog.qdac.cc\/index.php?rest_route=\/wp\/v2\/posts\/1940\/revisions\/1981"}],"wp:attachment":[{"href":"https:\/\/blog.qdac.cc\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=1940"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.qdac.cc\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=1940"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.qdac.cc\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=1940"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}