Skip to content

Using Google Play Services #87

Closed
BartAdv opened this Issue Mar 5, 2014 · 34 comments

5 participants

@BartAdv
BartAdv commented Mar 5, 2014

Documentation mentions :use-google-api 1, as the way to use additional APIs such as the Google Play Services. However, it just looks in sdk/add-ons directory, whereas Google Play Services are installed in `sdk/extras'.

It's possible to refer them via :external-classes-paths, but shouldn't there some more easily-configurable way? Should it be hardcoded in the same way the add-ons are searched? (Not sure about this, as (don't know really why), SDK documentation mentions that one should reference local copy instead of the libs installed under sdk directory 2)

@alexander-yakushev
Clojure/Android member

Thing is, Google APIs (such as Maps) indeed reside in add-ons directory. GgPS for some reason are put into extra. They need some special treatment, as they are not version-dependent. Also it seems that guidelines suggest to use it as a project dependency rather than a dependency JAR. Figuring out the correct way to handle this would be preferable before adding this functionality to lein-droid.

@arundilipan

I'm also having some trouble integrating Google Play Services into my app, the main issue is that if i go the suggested route of the project dependency, then it requires a clojure project rather than a java project. Doing it the way @BartAdv describes using :external-classes-paths leads to Dex running out of heap space. Any advice?

@rhg
rhg commented Jun 6, 2014
@arundilipan

That's only part of the issue. I've been trying to get lein-droid to work with the google play services api, and it just will not work whatsoever. I've been hitting a brick wall for a while. I really need some help.

@alexander-yakushev
Clojure/Android member

@arundilipan You can turn a Java project into a Clojure project by running lein droid init inside Java project directory. I haven't done this for a while, so I'm not sure it still works, but you can try.

Also, have you uncommented the lines in project.clj that increase the heap size for dexer? That usually solves the problem with OutOfMemory exception.

@arundilipan

I'll tried changing the heap size and I ended up with too my references. I dunno what's going on.

@alexander-yakushev
Clojure/Android member

I ended up with too my references

What do you mean?

@arundilipan

It says that I have over 70000 references to the packages instead of the allowed 65535. I meant to say too many references.

@alexander-yakushev
Clojure/Android member

Well yes, there is such problem. It can be possibly solved with incremental dexing, but there is no such functionality in lein-droid yet, and unfortunately I don't have time this summer to work on lein-droid.

@arundilipan

Then how would I integrate the Google play services for ads?

@alexander-yakushev
Clojure/Android member

You could try investigating the incremental dexing on your own, and try to perform it manually on the compiled classes. If you end up with something PR-looking, I will gladly merge it.

@arundilipan

Is there any way I can use the library project in lein? If so how would I go about dealing with it?

@arundilipan

I tried that, but the problem is that the library project isn't a leiningen project, that is, there is no project.clj. Therefore adding the google play services to my :project-dependencies fails. I tried the method of including the jar in my :external-classes-paths, but that led to the error that I previously mentioned.

@alexander-yakushev
Clojure/Android member

In the tutorial it is said to execute lein init in the dependency project's directory first.

@arundilipan

Running lein init doesn't work. Lein doesn't have init command for some reason.

@arundilipan

I managed to get around using admob, but I still need to use project dependencies. How long would dex take to compile my project? There is a noticeable difference in compile time with and without the project dependency. My project never gets to build with external dependncies. I would appreciate any help on the issue.

@alexander-yakushev
Clojure/Android member

OK, I only tested project dependencies with ActionBarSherlock library and it worked. I don't know why dexing takes so much time, but I remember Zach once had a similar issue on Windows.

Can you check if there are many classes in dependency's target/ directory once you compile it?

@sakuraiyuta

I have nearly same problem...maybe.

I use GooglePlayService API for Admob and GoogleAnalytics.
After variously tried, finally succeeded to use it...only debug build.

First, I tested building project with :use-google-api true in my project.clj, but that wasn't worked.
After some configuration, I found out that need to write dependency and some tricks.

ex) add keyword :project-dependencies and :external-classes-paths into :android section on project.clj like that:

    :project-dependencies ["/opt/android-sdk/extras/google/google_play_services/libproject/google-play-services_lib"]
    :external-classes-paths ["/opt/android-sdk/extras/google/google_play_services/libproject/google-play-services_lib/libs/google-play-services.jar"]

and need to put new project.clj on directory that contains GooglePlayService API project in your AndroidSDK's extras/google/google_play_services/libproject/google-play-services_lib/ path, easy to make this using lein droid init.

(defproject google-play-services_lib/google-play-services_lib "6.1.71-000"
  :description "FIXME: Android library description"
  :url "http://example.com/FIXME"
  :license {:name "Eclipse Public License"
            :url "http://www.eclipse.org/legal/epl-v10.html"}
  :min-lein-version "2.0.0"

  :source-paths ["src/clojure" "src"]
  :java-source-paths ["src/java" "gen"]
  :javac-options ["-target" "1.6" "-source" "1.6" "-Xlint:-options"]

  :dependencies [[org.clojure-android/clojure "1.6.0-RC1"]
                 [neko/neko "3.0.1"]]

  :android {:target-version "9"
            :library true})

It works well on lain-droid 0.2.4 and debug build, but fails release build.

On some newer version of lain-droid, need to use command lein with-profile release droid for building release version.
It means that project need to have :build-type :release element in profile's :android section.

It seems there are no problems...really? Unfortunately answer is no. There were some problems that confuses me.

build-project-dependencies function on lein-droid ignores root project's profile.
If you use :project-dependencies, it precedes dependencies profile than root project's profile.
Result, in spite of executing release build, it makes debug build.
And lein-droid loses some settings like :sdk-path, so build fails.

Of course, I might have a lot of mistakes.
(for example...my ${HOME}/.lein/profiles.clj)
Please teach me if you have solutions.

Related to this, and I discovered other problems for building project.
However, I didn't check on details yet, so I will make it clearly and create new issue.

@alexander-yakushev
Clojure/Android member

All right... I think, this can be fixed by adding this to dependency project's project.clj:

:profiles {:release {:build-type :release}}

The problem likely stems from the fact, that older lein-droid used just the current profile to determine whether the build is debug or release. Now it needs this field.

If this fix works, please create a new issue, and then I will fix lein droid init template. You can see I haven't touched it in a while (the dependencies are outdated etc.)

@sakuraiyuta

I'm Sorry to kept you waiting.

added to dependency project's project.clj:

:profiles {:release {:build-type :release}}

and build release.

$ DEBUG=1 lein with-profile release do clean, droid doall

output(some omitted, line "---"):

Leiningen's classpath: /home/yuta/.lein/self-installs/leiningen-2.5.0-standalone.jar
Applying task with-profile to [release do clean, droid doall]
Applying task do to (clean, droid doall)
Applying task clean to []
Applying task droid to [doall]
Building project dependency /opt/android-sdk/extras/google/google_play_services/libproject/google-play-services_lib ...
Generating manifest...
Generating R.java...
/opt/android-sdk/build-tools/21.1.1/aapt package --non-constant-id -f -m -M /opt/android-sdk/extras/google/google_play_services/libproject/google-play-services_lib/AndroidManifest.xml -S /opt/android-sdk/extras/google/google_play_services/libproject/google-play-services_lib/target/res -S /opt/android-sdk/extras/google/google_play_services/libproject/google-play-services_lib/res -I /opt/android-sdk/platforms/android-9/android.jar -J /opt/android-sdk/extras/google/google_play_services/libproject/google-play-services_lib/target/gen --generate-dependencies
Applying task javac to nil
Running javac with [-target 1.6 -source 1.6 -Xlint:-options @/tmp/.leiningen-cmdline5542040106851098650.tmp]
Compiling 1 source files to /opt/android-sdk/extras/google/google_play_services/libproject/google-play-services_lib/target/classes
Applying task compile to nil
All namespaces already AOT compiled.
Running javac with [-target 1.6 -source 1.6 -Xlint:-options @/tmp/.leiningen-cmdline4268190429488884665.tmp]
Compiling 1 source files to /opt/android-sdk/extras/google/google_play_services/libproject/google-play-services_lib/target/classes
Compiling Clojure files...
Project classpath: (/opt/android-sdk/tools/support/annotations.jar /opt/android-sdk/platforms/android-9/android.jar /opt/android-sdk/extras/google/google_play_services/libproject/google-play-services_lib/test /opt/android-sdk/extras/google/google_play_services/libproject/google-play-services_lib/src/clojure /opt/android-sdk/extras/google/google_play_services/libproject/google-play-services_lib/src /opt/android-sdk/extras/google/google_play_services/libproject/google-play-services_lib/dev-resources /opt/android-sdk/extras/google/google_play_services/libproject/google-play-services_lib/resources /opt/android-sdk/extras/google/google_play_services/libproject/google-play-services_lib/target/classes /home/yuta/.m2/repository/org/clojure-android/clojure/1.6.0-RC1/clojure-1.6.0-RC1.jar /home/yuta/.m2/repository/neko/neko/3.0.1/neko-3.0.1.jar /home/yuta/.m2/repository/org/clojure/tools.nrepl/0.2.6/tools.nrepl-0.2.6.jar /home/yuta/.m2/repository/clojure-complete/clojure-complete/0.2.3/clojure-complete-0.2.3.jar)
Build type: debug, dynamic compilation: enabled, remote REPL: enabled.
------------------------
Building dependency complete.
Generating manifest...
Generating R.java...
------------------------
Compiling 5 source files to /home/yuta/git/myproject/target/release/classes
Applying task compile to nil
All namespaces already AOT compiled.
Running javac with [-target 1.6 -source 1.6 -Xlint:-options @/tmp/.leiningen-cmdline3803311697534617080.tmp]
Compiling Clojure files...
Project classpath: (/opt/android-sdk/tools/support/annotations.jar /opt/android-sdk/platforms/android-15/android.jar /home/yuta/git/myproject/test /home/yuta/git/myproject/src/clojure /home/yuta/git/myproject/resources /home/yuta/git/myproject/target/release/classes /home/yuta/.m2/repository/neko/neko/3.0.2/neko-3.0.2.jar /home/yuta/.m2/repository/clj-time/clj-time/0.6.0/clj-time-0.6.0.jar /home/yuta/.m2/repository/joda-time/joda-time/2.2/joda-time-2.2.jar /home/yuta/.m2/repository/org/clojure-android/clojure/1.6.0-RC1/clojure-1.6.0-RC1.jar /home/yuta/.m2/repository/org/clojure/tools.nrepl/0.2.6/tools.nrepl-0.2.6.jar /home/yuta/.m2/repository/clojure-complete/clojure-complete/0.2.3/clojure-complete-0.2.3.jar /opt/android-sdk/extras/google/google_play_services/libproject/google-play-services_lib/libs/google-play-services.jar /opt/android-sdk/extras/google/google_play_services/libproject/google-play-services_lib/target/classes)
Build type: release, dynamic compilation: disabled, remote REPL: disabled.
Applying task javac to nil
Running javac with [-target 1.6 -source 1.6 -Xlint:-options @/tmp/.leiningen-cmdline2586393479411461558.tmp]
------------------------
Compilation succeeded.
Creating DEX....
------------------------
Creating APK...
------------------------
Installing APK...
------------------------
Success

Launching APK...
/opt/android-sdk/platform-tools/adb -s CB5125TPMG shell am start -n com.example.myproject/com.example.myproject.SplashActivity
Starting: Intent { cmp=com.example.myproject/.SplashActivity }
Binding device port 9999 to local port 9999 ...
/opt/android-sdk/platform-tools/adb -s CB5125TPMG forward tcp:9999 tcp:9999

Release build and install succeeded, but throws Exception when running.

12-01 00:57:04.267: E/AndroidRuntime(472): java.io.FileNotFoundException: Could not locate clojure/tools/nrepl/server__init.class or clojure/tools/nrepl/server.clj on classpath: 
12-01 00:57:04.267: E/AndroidRuntime(472):  at clojure.lang.RT.load(RT.java:468)
12-01 00:57:04.267: E/AndroidRuntime(472):  at clojure.lang.RT.load(RT.java:436)
12-01 00:57:04.267: E/AndroidRuntime(472):  at clojure.core$load$fn__5066.invoke(core.clj:5640)
12-01 00:57:04.267: E/AndroidRuntime(472):  at clojure.core$load.doInvoke(core.clj:5640)
12-01 00:57:04.267: E/AndroidRuntime(472):  at clojure.lang.RestFn.invoke(RestFn.java:408)
12-01 00:57:04.267: E/AndroidRuntime(472):  at clojure.core$load_one.invoke(core.clj:5446)
12-01 00:57:04.267: E/AndroidRuntime(472):  at clojure.core$load_lib$fn__5015.invoke(core.clj:5485)
12-01 00:57:04.267: E/AndroidRuntime(472):  at clojure.core$load_lib.doInvoke(core.clj:5485)
12-01 00:57:04.267: E/AndroidRuntime(472):  at clojure.lang.RestFn.applyTo(RestFn.java:142)
12-01 00:57:04.267: E/AndroidRuntime(472):  at clojure.core$apply.invoke(core.clj:626)
12-01 00:57:04.267: E/AndroidRuntime(472):  at clojure.core$load_libs.doInvoke(core.clj:5524)
12-01 00:57:04.267: E/AndroidRuntime(472):  at clojure.lang.RestFn.applyTo(RestFn.java:137)
12-01 00:57:04.267: E/AndroidRuntime(472):  at clojure.core$apply.invoke(core.clj:628)
12-01 00:57:04.267: E/AndroidRuntime(472):  at clojure.core$use.doInvoke(core.clj:5609)
12-01 00:57:04.267: E/AndroidRuntime(472):  at clojure.lang.RestFn.invoke(RestFn.java:408)
12-01 00:57:04.267: E/AndroidRuntime(472):  at neko.init$start_repl.doInvoke(init.clj:52)
12-01 00:57:04.267: E/AndroidRuntime(472):  at clojure.lang.RestFn.applyTo(RestFn.java:137)
12-01 00:57:04.267: E/AndroidRuntime(472):  at clojure.core$apply.invoke(core.clj:628)
12-01 00:57:04.267: E/AndroidRuntime(472):  at neko.init$init.doInvoke(init.clj:99)
12-01 00:57:04.267: E/AndroidRuntime(472):  at clojure.lang.RestFn.invoke(RestFn.java:410)
12-01 00:57:04.267: E/AndroidRuntime(472):  at com.example.myproject.application$app_on_create.invoke(application.clj:96)
12-01 00:57:04.267: E/AndroidRuntime(472):  at clojure.lang.Var.invoke(Var.java:379)
12-01 00:57:04.267: E/AndroidRuntime(472):  at com.example.myproject.MainApplication$1.run(MainApplication.java:89)
12-01 00:57:04.267: E/AndroidRuntime(472):  at java.lang.Thread.run(Thread.java:841)

Neko tries to start nrepl-server and failed.
During the build, seems debug configuration would be mixed.

Building project dependency /opt/android-sdk/extras/google/google_play_services/libproject/google-play-services_lib ...
------------------------
Build type: debug, dynamic compilation: enabled, remote REPL: enabled.
------------------------
Building dependency complete.
------------------------

However, next root project seems builded as release.

Compiling 5 source files to /home/yuta/git/myproject/target/release/classes
Applying task compile to nil
All namespaces already AOT compiled.
Running javac with [-target 1.6 -source 1.6 -Xlint:-options @/tmp/.leiningen-cmdline3803311697534617080.tmp]
Compiling Clojure files...
Project classpath: (/opt/android-sdk/tools/support/annotations.jar /opt/android-sdk/platforms/android-15/android.jar /home/yuta/git/myproject/test /home/yuta/git/myproject/src/clojure /home/yuta/git/myproject/resources /home/yuta/git/myproject/target/release/classes /home/yuta/.m2/repository/neko/neko/3.0.2/neko-3.0.2.jar /home/yuta/.m2/repository/clj-time/clj-time/0.6.0/clj-time-0.6.0.jar /home/yuta/.m2/repository/joda-time/joda-time/2.2/joda-time-2.2.jar /home/yuta/.m2/repository/org/clojure-android/clojure/1.6.0-RC1/clojure-1.6.0-RC1.jar /home/yuta/.m2/repository/org/clojure/tools.nrepl/0.2.6/tools.nrepl-0.2.6.jar /home/yuta/.m2/repository/clojure-complete/clojure-complete/0.2.3/clojure-complete-0.2.3.jar /opt/android-sdk/extras/google/google_play_services/libproject/google-play-services_lib/libs/google-play-services.jar /opt/android-sdk/extras/google/google_play_services/libproject/google-play-services_lib/target/classes)
Build type: release, dynamic compilation: disabled, remote REPL: disabled.

It's like a confusing problem.

You need completely build-log-file? Or any I need to do?

@sakuraiyuta

If you want to check building project yourself, please check my test repo.

@sakuraiyuta

Finally, I succeeded executing release-builded app with Admob.
Now, I'm creating summarised description.
At the latest , and will be able to post on the day after tomorrow .

@alexander-yakushev
Clojure/Android member

Great news. I'm sorry I didn't respond. The problem is probably caused by the fact that current profile is not propagated to dependency projects, and it is hard to do that — we can't be sure that the dependency project has :release profile too. Anyway, I'll be happy to hear your description!

@sakuraiyuta

How to use GgPS lib on newer lein-droid

This post explains how to use Google Play Service library.
Following steps, you can use Admob, GoogleAnalytics, and others.
Of course, also can write layout xml file that contains AdView(if you want).

Migrating old project and profile

At first, when you already have projects created by older version of lein-droid, you have to check and fix some configulation.

project.clj:

  • :profiles {...} elements contains {:default [:dev]}?
  :profiles {:default [:dev] ;; <- need, on newer version
             ...}
  • :dev|:release's value is vector? and contains keyword :android-common?
     :dev
     [:android-common :android-user ;; <- notice, the value is vector, and contains :android-common
      {:dependencies [[org.clojure/tools.nrepl "0.2.3"]]
       :target-path "target/debug"
       :android {:aot :all-with-unused
                 :rename-manifest-package "sample.lein.droid.admob.debug"
                 :manifest-options {:app-name "sample-lein-droid-admob - debug"}}}]
  • :release [{:android ...}]'s value contains :build-type :release?
    :release
    [:android-common
     {:target-path "target/release"
      :android
      {:ignore-log-priority [:debug :verbose]
       :aot :all
       :build-type :release}}]} ;; <- need, on newer version

${HOME}/.lein/profiles.clj:

  • :android-common already exists and contains :android {:sdk-path "..."}?
;; My sample
{:user {:plugins [[lein-pprint "1.1.1"]
                  [lein-kibit "0.0.8"]
                  [lein-droid "0.3.0-beta4"]]}
 :android-user {:plugins [[lein-pprint "1.1.1"]
                          [lein-kibit "0.0.8"]]}
 :android-common {:android {:sdk-path "/opt/android-sdk" ;; <- need, on newer version
                            :keypass "android"
                            :storepass "android"}}}

One way to recreate project.clj: copy old file, generate using lein droid init, and merge present one manually.
See also: issue #111, #114 .

Fix your project's project.clj

  • add dependencies GgPS.
  :android {...
            :project-dependencies ["/opt/android-sdk/extras/google/google_play_services/libproject/google-play-services_lib"]
            :external-classes-paths ["/opt/android-sdk/extras/google/google_play_services/libproject/google-play-services_lib/libs/google-play-services.jar"]
            ...
  • if you already use GgPS, and present project.clj has these elements, no need to fix.

GgPS's project.clj

Put GgPS's project.clj into your GgPS directory.
ex) /opt/android-sdk/extras/google/google_play_services/libproject/google-play-services_lib/

(defproject google-play-services_lib/google-play-services_lib "6.1.71-000"
  :description "FIXME: Android library description"
  :url "http://example.com/FIXME"
  :license {:name "Eclipse Public License"
            :url "http://www.eclipse.org/legal/epl-v10.html"}
  :min-lein-version "2.0.0"

  :source-paths ["src/clojure" "src"]
  :java-source-paths ["src/java" "gen"]
  :javac-options ["-target" "1.6" "-source" "1.6" "-Xlint:-options"]
  :java-only true

  :profiles {:default [:release]
             :release [:android-common]}
  :android {:target-version "9"
            :library true})

How to create it manually:

  • At first, generate using lein droid init.
  • And some fixes:
    • remove :dependencies,
    • add :java-only true,
    • add :profiles {...} section.

No need clojure and neko for building GgPS, so remove :dependencies and add :java-only true.

:profiles section is a bit complex.

:default [:release] element needs for lein-droid, to force using :release configulation.
If it's not there, lein-droid can't find :sdk-path in spite of writing it on ${HOME}/.lein/profiles.clj: :android-common.

Next key, :release, need to write value as vector, and add keyword :android-common for same reason.
There are no need additional elements like :android {:build-type :release}.

If you don't keep these points, build process makes GGpS that contains clojure and neko with debug.
When debug configuration mixed, it makes you irritated reliably.
In such cases, you must execute lein clean in GgPS lib's directory.
Unfortunatelly, executing lein clean in your root project, doesn't target to dependencies.
It's good idea to add cleaning-deps task on your build process, like as a script.

Build

Execute:

  • lein droid doall for debug
  • lein with-profile droid doall for release

Once again: Cleaning up dependencies project is strongly recommended.

Digression

I found some (maybe) unexpected effects occuerred in lein-droid/src/leiningen/droid/build.clj:161.

(defn build
  "Metatask. Builds dependencies, compiles and creates DEX (if not a library)."
  [{{:keys [library]} :android :as project}]
  (if library
    (doto project
      build-project-dependencies code-gen compile crunch-resources)
    (doto project
      build-project-dependencies code-gen compile create-dex)))

If symbol project has :project-dependencies and dependencies contains clojure project, sometimes value in project changes between processing fn build-project-dependencies and fn code-gen.
Hmm...symbol project is mutable? I don't have much experience at clojure, so I can't understand what was happened.

Thanks!

@alexander-yakushev
Clojure/Android member

This is very nice and complete description, Yuta! I can see you solved the profile problem in a dependency project by making a single release profile and removing Clojure and Neko dependencies (which are totally unnecessary there, I agree). That is a good solution. Anyway I still have to fix the profile propagation problem, I hope to get to it some time soon.

Thanks for persistence!

@sakuraiyuta

Hi Alex! Many thanks for your comment!
I'm glad to discuss with you, I was worried that my poor english makes you frustrated.

I use lein-droid from 0.1.0(slideshare, sorry japanese) and paused developping by some reason. Long time passed, now, this project has many update, so, until I get used to, took a lot of time.
However, it's all right now.

I feel the project is going to more good one.
I'm looking forward to meeting next version!

@sakuraiyuta

P.S. Create gisted description.
Please copy it if you want.

@alexander-yakushev
Clojure/Android member

You shouldn't worry about the language, Yuta, I understand you very well. In fact it is very exciting to know that there is interest for Clojure-Android in different countries, even more so in Japan.

As for your detailed guide, I will try to fix as much as I can on my side, and then intergrate your guide into the tutorial (with attribution to you, of course).

@alexander-yakushev
Clojure/Android member

sometimes value in project changes between processing fn build-project-dependencies and fn code-gen.

This could mean that build is called recursively by the dependency project and thus project holds a different map. Is it the case?

@sakuraiyuta

sometimes value in project changes between processing fn build-project-dependencies and fn code-gen.

This could mean that build is called recursively by the dependency project and thus project holds a different map. Is it the case?

I thought so. But looks different...I don't make it clearly yet.
I'll fix sample admob project and check for details what effects occurred there (with debug-print-implemented lein-droid, yes, it's traditional way).
It maybe takes several days because my tedious dayjob. 😞

@sakuraiyuta

Sorry my mistake. I finally found no problems.
I wonder if lein-droid shows Build type: debug when building dependencies.
(in spite of writing :default [:release] to dependencies project.clj, and execute lein droid build)
However, it's no big deal. Build-process works well.

In Japan, amount of clojure-android users looks still less...but hopeful project is growing up(e.g. CLAN, by @ayamada, you know as contributor of lein-droid).
I hope that lein-droid is more used in Japan.

@alexander-yakushev
Clojure/Android member

It might not make sense to have a "debug" profile for dependencies, if they are just Java projects. I'll probably just remove multiple profiles from library template.

@alexander-yakushev
Clojure/Android member

After a long time this issue has been open, I'm finally glad to close it. I don't expect @BartAdv to use Clojure-Android anymore, but still mentioning you. It is very easy to include Google Play Services now.

@sakuraiyuta Thank you for the work you've done on this. Sorry I've never published your guide separately. Still I very much value your contribution, and it helped me to setup the solution we have now.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Something went wrong with that request. Please try again.