====================[ Readings ]==================== This lecture was heavy on code samples. As usual, you should allow some time to tweak a code sample to see what it does; remember the unofficial motto of this course is http://www.cs.dartmouth.edu/~sergey/cs65/changing-stuff-and-seeing-what-happens.png --------------------[ Retrofit ]-------------------- Last time we discussed how GSON library used Java Reflection API to convert a set of Java class definitions to a parser that took JSON strings and produced Java objects from them, removing the need for using getJSONObject and getJSONArray on a generic JSONObject returned by Volley's Response.Listener (see "Getting parsed JSON objects back instead of String" in previous lecture notes). The Retrofit2 library from Square Inc. takes this further. If you haven't read about Java reflection, stop here and read the tutorial linked from the previous lecture! The idea of Retrofit is to both auto-generate JSON response parsing code from the Java class definitions of the data you expect to get back _and_ to make HTTP API for getting (GET-ing) such data very easy to describe. So Retrofit uses both Reflection to auto-generate the parsers, and Java annotations (code clauses starting with @) to specify HTTP-specific detail such as URLs and headers. Read http://square.github.io/retrofit/ (start at "API Declaration"; you may skip the Github example at the top unless you are a fan of Github APIs). Java annotations are an extremely powerful language extension feature of Java. With it, you can extend the Java compiler with custom additions to the language, which automatically add code to functions based on the contents of the annotation tags. The power this gives you is comparable with C/C++ macros, but has better tractability, because macros operate only on strings, whereas Java annotation processors can tap into Java's Reflection API. Java annotations turned out to be so useful for keeping track of complexity that Android Studio demands them (just try doing without @Override!) However, they now go much further; entire frameworks like Dagger, RoboGuice, and ButterKnife depend on them, and this trend will likely continue. See https://www.objc.io/issues/11-android/dependency-injection-in-java/ for examples. Java annotations such as used by Retrofit's @GET and @Headers are an example of what is called "Domain-specific Languages" or DSLs in the programming languages terminology. The idea is that, rather that copy-pasting some boilerplate code, the system defines succinct and idiomatic ways of specifying what the code should do, and packages tools to automatically generate such boilerplate code from these succinct descriptions where these code injections belong. This approach recognizes that the primary programming language will always be somewhat behind in expressiveness (such as Java for Android, chosen for historical reasons). So a good programmings language should include basic means for auto-generating code that must be reasonably well integrated with the language's type system, to make compile-time type checking of auto-generated code still powerful useful enough to save you from runtime crashes. More info: https://medium.com/fueled-android/10-tricks-to-write-annotations-in-java-89c8f11dfc4b https://medium.com/@aitorvs/annotation-processing-in-android-studio-7042ccb83024 --------------------[ Retrofit example ]-------------------- I showed an example from the Commonsware "Busy Coder's Guide" textbook that uses Retrofit to extract the National Weather Service forecasts for the phone's location. The original example is at https://github.com/commonsguy/cw-omnibus/tree/master/Location/Classic This example has a weakness, which we saw today in class. It depends on the ready availability of the latest GPS location on the phone. When both GPS and Wi-Fi are not available, this code gets stuck in a "Loading..." loop (and this is why I hate "spinner" animations: they tell you absolutely nothing about whether any work is being done or what impedes it; lying to the user is not nice, even though it's not hard to get away with it in most cases---but I say if you claim to show progress, you'd better show actual progress!). So I patched and instrumented this example a bit. Find it in examples/Weather2.zip Warning: That's plenty of code, meant for the output to look nice. Leave yourself some time to pick through it! Note that the MainActivity contains almost no application logic. It only dispatches initial permissions check for the GPS location, and hands off all the actual logic of retrieving and displaying the NWS forecasts to a Fragment. That Fragment is set to persist through flips and other destruction/recreation of Activities (by calling setRetainInstance(true) when first created). Observe the Log.d(..) messages with the "FRAG" tag to see how it works; recall the Fragment lifecycle diagrams in https://stackoverflow.com/questions/36339811/when-is-onattach-called-during-the-fragment-lifecycle ====================[ Location ]==================== Android's LocationManager exposes the GPS receiver's "last known location" (since no GPS may be available at the time of the request, e.g., indoors, and there's no telling when it will next become available) and allows you to register a Listener for updates. Modified Weather2 code shows the use of both mechanisms. Read: https://developer.android.com/training/location/retrieve-current.html ====================[ Services ]==================== Services are fairly straightforward. These are tasks that do not control any UI elements and run in the "background" (see below for the precise meaning of this; there's such a thing as a Foreground service). Services get started with Intents same as Activities, but with startService(intent) rather than startActivity(..) or startActivityForResult(..). They also get stopped by Intents. examples/PurringService/ shows a simple example of playing a sounds in the background even after the app that started the service is completely destroyed; also, the service only stops when the app sends it a "stop" message, with another Intent. Highlights of that code: 1. A service runs in the UI thread! Quoting https://developer.android.com/guide/components/services.html: Caution: A service runs in the main thread of its hosting process; the service does not create its own thread and does not run in a separate process unless you specify otherwise. If your service is going to perform any CPU-intensive work or blocking operations, such as MP3 playback or networking, you should create a new thread within the service to complete that work. By using a separate thread, you can reduce the risk of Application Not Responding (ANR) errors, and the application's main thread can remain dedicated to user interaction with your activities. So to do any work, a Service must spin off a worker thread, e.g., via AsyncTask. Being a Service is about interacting with the OS rather than about threading; these are orthogonal concerns. If you want to add threading for free, though, use IntentService! (see https://developer.android.com/guide/components/services.html#ExtendingIntentService) 2. To persist after the app exits, the service's onStartCommand(..) entry point must return START_STICKY . Read the Android Guide and study the lifecycle diagrams at https://developer.android.com/guide/components/services.html#Lifecycle IMPORTANT: As of Android 8.0 (API 26), background services are denied real-time updates of Location! If you allow your phone to be upgraded to 8.0, you will need to follow methods in https://developer.android.com/about/versions/oreo/background-location-limits.html ====================[ Google Maps ]==================== Google Maps can be integrated into your apps as either a separate activity (extending GoogleMap) or a fragment (extending MapFragment). You will find a simple demo in examples/MereMapJava (in Java) and examples/MereMapKt (in Kotlin). These apps do the same thing: show a map centered in Hanover and zoomed to the level of single buildings (play with the zoom factor to see what different zoom levels do). Clicks on the map place a marker with the GPS coordinate of that point (see how GPS coordinates change as you move around on the map). The map can be zoomed and moved both manually and automatically; tags can be interacted with via callbacks, etc. https://developers.google.com/maps/documentation/android-api/map gives an overview of the API.