Deprecated: Creation of dynamic property wpdb::$categories is deprecated in /var/www/twodee/blog/wp-includes/wp-db.php on line 668

Deprecated: Creation of dynamic property wpdb::$post2cat is deprecated in /var/www/twodee/blog/wp-includes/wp-db.php on line 668

Deprecated: Creation of dynamic property wpdb::$link2cat is deprecated in /var/www/twodee/blog/wp-includes/wp-db.php on line 668

Deprecated: Return type of WP_Theme::offsetExists($offset) should either be compatible with ArrayAccess::offsetExists(mixed $offset): bool, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in /var/www/twodee/blog/wp-includes/class-wp-theme.php on line 554

Deprecated: Return type of WP_Theme::offsetGet($offset) should either be compatible with ArrayAccess::offsetGet(mixed $offset): mixed, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in /var/www/twodee/blog/wp-includes/class-wp-theme.php on line 595

Deprecated: Return type of WP_Theme::offsetSet($offset, $value) should either be compatible with ArrayAccess::offsetSet(mixed $offset, mixed $value): void, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in /var/www/twodee/blog/wp-includes/class-wp-theme.php on line 535

Deprecated: Return type of WP_Theme::offsetUnset($offset) should either be compatible with ArrayAccess::offsetUnset(mixed $offset): void, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in /var/www/twodee/blog/wp-includes/class-wp-theme.php on line 544

Deprecated: Return type of WP_REST_Request::offsetExists($offset) should either be compatible with ArrayAccess::offsetExists(mixed $offset): bool, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in /var/www/twodee/blog/wp-includes/rest-api/class-wp-rest-request.php on line 960

Deprecated: Return type of WP_REST_Request::offsetGet($offset) should either be compatible with ArrayAccess::offsetGet(mixed $offset): mixed, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in /var/www/twodee/blog/wp-includes/rest-api/class-wp-rest-request.php on line 980

Deprecated: Return type of WP_REST_Request::offsetSet($offset, $value) should either be compatible with ArrayAccess::offsetSet(mixed $offset, mixed $value): void, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in /var/www/twodee/blog/wp-includes/rest-api/class-wp-rest-request.php on line 992

Deprecated: Return type of WP_REST_Request::offsetUnset($offset) should either be compatible with ArrayAccess::offsetUnset(mixed $offset): void, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in /var/www/twodee/blog/wp-includes/rest-api/class-wp-rest-request.php on line 1003

Deprecated: Return type of WP_Block_List::current() should either be compatible with Iterator::current(): mixed, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in /var/www/twodee/blog/wp-includes/class-wp-block-list.php on line 151

Deprecated: Return type of WP_Block_List::next() should either be compatible with Iterator::next(): void, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in /var/www/twodee/blog/wp-includes/class-wp-block-list.php on line 175

Deprecated: Return type of WP_Block_List::key() should either be compatible with Iterator::key(): mixed, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in /var/www/twodee/blog/wp-includes/class-wp-block-list.php on line 164

Deprecated: Return type of WP_Block_List::valid() should either be compatible with Iterator::valid(): bool, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in /var/www/twodee/blog/wp-includes/class-wp-block-list.php on line 186

Deprecated: Return type of WP_Block_List::rewind() should either be compatible with Iterator::rewind(): void, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in /var/www/twodee/blog/wp-includes/class-wp-block-list.php on line 138

Deprecated: Return type of WP_Block_List::offsetExists($index) should either be compatible with ArrayAccess::offsetExists(mixed $offset): bool, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in /var/www/twodee/blog/wp-includes/class-wp-block-list.php on line 75

Deprecated: Return type of WP_Block_List::offsetGet($index) should either be compatible with ArrayAccess::offsetGet(mixed $offset): mixed, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in /var/www/twodee/blog/wp-includes/class-wp-block-list.php on line 89

Deprecated: Return type of WP_Block_List::offsetSet($index, $value) should either be compatible with ArrayAccess::offsetSet(mixed $offset, mixed $value): void, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in /var/www/twodee/blog/wp-includes/class-wp-block-list.php on line 110

Deprecated: Return type of WP_Block_List::offsetUnset($index) should either be compatible with ArrayAccess::offsetUnset(mixed $offset): void, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in /var/www/twodee/blog/wp-includes/class-wp-block-list.php on line 127

Deprecated: Return type of WP_Block_List::count() should either be compatible with Countable::count(): int, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in /var/www/twodee/blog/wp-includes/class-wp-block-list.php on line 199

Deprecated: Creation of dynamic property Automattic\Jetpack\Connection\Manager::$error_handler is deprecated in /var/www/twodee/blog/wp-content/plugins/jetpack/vendor/automattic/jetpack-connection/src/class-manager.php on line 91

Deprecated: Creation of dynamic property WPCOM_REST_API_V2_Endpoint_Instagram_Gallery::$is_wpcom is deprecated in /var/www/twodee/blog/wp-content/plugins/jetpack/_inc/lib/core-api/wpcom-endpoints/class-wpcom-rest-api-v2-endpoint-instagram-gallery.php on line 23

Deprecated: Creation of dynamic property WPCOM_REST_API_V2_Endpoint_Mailchimp::$wpcom_is_wpcom_only_endpoint is deprecated in /var/www/twodee/blog/wp-content/plugins/jetpack/_inc/lib/core-api/wpcom-endpoints/class-wpcom-rest-api-v2-endpoint-mailchimp.php on line 17

Deprecated: Creation of dynamic property WPCOM_REST_API_V2_Endpoint_Tweetstorm_Gather::$wpcom_is_wpcom_only_endpoint is deprecated in /var/www/twodee/blog/wp-content/plugins/jetpack/_inc/lib/core-api/wpcom-endpoints/class-wpcom-rest-api-v2-endpoint-tweetstorm-gather.php on line 21

Deprecated: Creation of dynamic property WPCOM_REST_API_V2_Endpoint_Tweetstorm_Gather::$is_wpcom is deprecated in /var/www/twodee/blog/wp-content/plugins/jetpack/_inc/lib/core-api/wpcom-endpoints/class-wpcom-rest-api-v2-endpoint-tweetstorm-gather.php on line 22

Deprecated: Creation of dynamic property WPCOM_REST_API_V2_Endpoint_Gutenberg_Available_Extensions::$wpcom_is_site_specific_endpoint is deprecated in /var/www/twodee/blog/wp-content/plugins/jetpack/_inc/lib/core-api/wpcom-endpoints/gutenberg-available-extensions.php on line 20

Deprecated: Creation of dynamic property WPCOM_REST_API_V2_Endpoint_Memberships::$wpcom_is_wpcom_only_endpoint is deprecated in /var/www/twodee/blog/wp-content/plugins/jetpack/_inc/lib/core-api/wpcom-endpoints/memberships.php on line 23

Deprecated: Creation of dynamic property WPCOM_REST_API_V2_Endpoint_Memberships::$wpcom_is_site_specific_endpoint is deprecated in /var/www/twodee/blog/wp-content/plugins/jetpack/_inc/lib/core-api/wpcom-endpoints/memberships.php on line 24

Deprecated: Creation of dynamic property WPCOM_REST_API_V2_Endpoint_Subscribers::$wpcom_is_wpcom_only_endpoint is deprecated in /var/www/twodee/blog/wp-content/plugins/jetpack/_inc/lib/core-api/wpcom-endpoints/subscribers.php on line 15

Deprecated: Creation of dynamic property Automattic\Jetpack\Sync\Queue::$random_int is deprecated in /var/www/twodee/blog/wp-content/plugins/jetpack/vendor/automattic/jetpack-sync/src/class-queue.php on line 38

Deprecated: Creation of dynamic property Automattic\Jetpack\Sync\Queue::$random_int is deprecated in /var/www/twodee/blog/wp-content/plugins/jetpack/vendor/automattic/jetpack-sync/src/class-queue.php on line 38

Deprecated: Creation of dynamic property WPCOM_Widget_Goodreads::$shelves is deprecated in /var/www/twodee/blog/wp-content/plugins/jetpack/modules/widgets/goodreads.php on line 33

Deprecated: DateTime::__construct(): Passing null to parameter #1 ($datetime) of type string is deprecated in /var/www/twodee/blog/wp-includes/script-loader.php on line 348

Deprecated: Creation of dynamic property Advanced_Editor_Tools::$toolbar_classic_block is deprecated in /var/www/twodee/blog/wp-content/plugins/tinymce-advanced/tinymce-advanced.php on line 347

Deprecated: Creation of dynamic property Advanced_Editor_Tools::$toolbar_block is deprecated in /var/www/twodee/blog/wp-content/plugins/tinymce-advanced/tinymce-advanced.php on line 349

Deprecated: Creation of dynamic property Advanced_Editor_Tools::$toolbar_block_side is deprecated in /var/www/twodee/blog/wp-content/plugins/tinymce-advanced/tinymce-advanced.php on line 350

Deprecated: Creation of dynamic property Advanced_Editor_Tools::$panels_block is deprecated in /var/www/twodee/blog/wp-content/plugins/tinymce-advanced/tinymce-advanced.php on line 351

Deprecated: Creation of dynamic property Advanced_Editor_Tools::$used_block_buttons is deprecated in /var/www/twodee/blog/wp-content/plugins/tinymce-advanced/tinymce-advanced.php on line 354

Deprecated: Creation of dynamic property WP_Block_Type::$plan_check is deprecated in /var/www/twodee/blog/wp-includes/class-wp-block-type.php on line 333

Deprecated: Creation of dynamic property WP_Block_Type::$plan_check is deprecated in /var/www/twodee/blog/wp-includes/class-wp-block-type.php on line 333

Deprecated: Creation of dynamic property WP_Block_Type::$plan_check is deprecated in /var/www/twodee/blog/wp-includes/class-wp-block-type.php on line 333

Deprecated: Creation of dynamic property WP_Block_Type::$plan_check is deprecated in /var/www/twodee/blog/wp-includes/class-wp-block-type.php on line 333

Deprecated: Creation of dynamic property WP_Block_Type::$plan_check is deprecated in /var/www/twodee/blog/wp-includes/class-wp-block-type.php on line 333

Deprecated: Creation of dynamic property WP_Block_Type::$plan_check is deprecated in /var/www/twodee/blog/wp-includes/class-wp-block-type.php on line 333

Deprecated: Creation of dynamic property WP_Term::$object_id is deprecated in /var/www/twodee/blog/wp-includes/class-wp-term.php on line 198

Deprecated: Creation of dynamic property WP_Term::$object_id is deprecated in /var/www/twodee/blog/wp-includes/class-wp-term.php on line 198

Deprecated: Creation of dynamic property WP_Term::$object_id is deprecated in /var/www/twodee/blog/wp-includes/class-wp-term.php on line 198
» SENG 440: Lecture 8 – AsyncTask

teaching machines

SENG 440: Lecture 8 – AsyncTask

March 14, 2019 by . Filed under
Deprecated: Creation of dynamic property WP_Term::$cat_ID is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 378

Deprecated: Creation of dynamic property WP_Term::$category_count is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 379

Deprecated: Creation of dynamic property WP_Term::$category_description is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 380

Deprecated: Creation of dynamic property WP_Term::$cat_name is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 381

Deprecated: Creation of dynamic property WP_Term::$category_nicename is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 382

Deprecated: Creation of dynamic property WP_Term::$category_parent is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 383

Deprecated: Creation of dynamic property WP_Term::$cat_ID is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 378

Deprecated: Creation of dynamic property WP_Term::$category_count is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 379

Deprecated: Creation of dynamic property WP_Term::$category_description is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 380

Deprecated: Creation of dynamic property WP_Term::$cat_name is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 381

Deprecated: Creation of dynamic property WP_Term::$category_nicename is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 382

Deprecated: Creation of dynamic property WP_Term::$category_parent is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 383

Deprecated: Creation of dynamic property WP_Term::$cat_ID is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 378

Deprecated: Creation of dynamic property WP_Term::$category_count is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 379

Deprecated: Creation of dynamic property WP_Term::$category_description is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 380

Deprecated: Creation of dynamic property WP_Term::$cat_name is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 381

Deprecated: Creation of dynamic property WP_Term::$category_nicename is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 382

Deprecated: Creation of dynamic property WP_Term::$category_parent is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 383
lectures, semester1-2019, seng440.

Dear students,

Today we explore how to fetch JSON data from a web service, which we will eventually show in a list. Our app, which we’ll call Lately, will grab headlines from News API. In this lecture, we’ll be grabbing headlines from the BBC. Helping us today is Android’s AsyncTask. We’ll continue this example in the next two lectures, where we’ll add a RecyclerView to show a custom listen and use fragments to show multiple “screens” at a time.

Before we forget, here’s your TODO list for next time:

Let’s start making our app.

NewsAPI

We’ll start by examining the News API web service. We fetch the top headlines from a news source by issuing an HTTP GET request of this URL:

https://newsapi.org/v2/top-headlines?sources=bbc-news&apiKey=KEY

KEY must be replaced by the string that News API gives you when you register. The JSON we get from this URL looks something like this:

{
  "status": "ok",
  "totalResults": 10,
  "articles": [
    {
      "source": {
        "id": "bbc-news",
        "name": "BBC News"
      },
      "author": "BBC News",
      "title": "Facebook services suffer widespread outages",
      "description": "Social network acknowledges problems with \"Facebook family of apps\", though the cause is unknown.",
      "url": "http:\/\/www.bbc.co.uk\/news\/technology-47562281",
      "urlToImage": "https:\/\/ichef.bbci.co.uk\/news\/1024\/branded_news\/3CAB\/production\/_106013551_whbranded_newsubject.jpg",
      "publishedAt": "2019-03-13T20:50:53Z",
      "content": "Image copyrightGetty ImagesImage caption\r\n The Facebook \"family\" of apps was suffering issues, the company confirmed\r\nSeveral of Facebooks products are suffering partial outages, affecting users globally.\r\nFacebooks main product, its Messenger service, image-\u2026 [+3448 chars]"
    },
    {
      "source": {
        "id": "bbc-news",
        "name": "BBC News"
      },
      "author": "BBC Sport",
      "title": "Sky Brown: Meet the 10-year-old GB skateboarding sensation - BBC Sport",
      "description": "Skateboarder Sky Brown, 10, tells BBC Sport about her hopes to become Britain's youngest ever summer Olympian.",
      "url": "http:\/\/www.bbc.co.uk\/sport\/av\/get-inspired\/47558160",
      "urlToImage": "https:\/\/m.files.bbci.co.uk\/modules\/bbc-morph-sport-page\/2.15.12\/images\/bbc-sport-logo.png",
      "publishedAt": "2019-03-13T20:37:48.5503711Z",
      "content": null
    },
    {
      "source": {
        "id": "bbc-news",
        "name": "BBC News"
      },
      "author": "BBC News",
      "title": "California governor halts executions",
      "description": "A moratorium and a temporary reprieve has been announced for all 737 death row inmates in the state.",
      "url": "http:\/\/www.bbc.co.uk\/news\/world-us-canada-47549422",
      "urlToImage": "https:\/\/ichef.bbci.co.uk\/news\/1024\/branded_news\/1C11\/production\/_86658170_gettyimages-1304767.jpg",
      "publishedAt": "2019-03-13T20:30:47Z",
      "content": "Image copyrightGetty ImagesImage caption\r\n The last execution in California was carried out in 2006\r\nCalifornia Governor Gavin Newsom has issued a moratorium on executions and a temporary reprieve for all 737 inmates on death row in the state.\r\nThe order issu\u2026 [+2385 chars]"
    },
    {
      "source": {
        "id": "bbc-news",
        "name": "BBC News"
      },
      "author": "BBC News",
      "title": "MPs vote to reject no-deal Brexit",
      "description": "MPs reject a no-deal Brexit by 312 to 308, in a non-binding vote",
      "url": "http:\/\/www.bbc.co.uk\/news\/uk-politics-47562995",
      "urlToImage": "https:\/\/ichef.bbci.co.uk\/news\/1024\/branded_news\/7A23\/production\/_97176213_breaking_news_bigger.png",
      "publishedAt": "2019-03-13T19:17:28Z",
      "content": "MPs reject a no-deal Brexit by 312 to 308, in a non-binding vote\r\nThis breaking news story is being updated and more details will be published shortly. Please refresh the page for the fullest version.\r\nYou can receive Breaking News on a smartphone or tablet v\u2026 [+89 chars]"
    },
    {
      "source": {
        "id": "bbc-news",
        "name": "BBC News"
      },
      "author": "BBC News",
      "title": "US to ground all Boeing crash aircraft",
      "description": "It follows the lead of many other countries after two Boeing 737 Max 8 crashes in five months.",
      "url": "http:\/\/www.bbc.co.uk\/news\/business-47562727",
      "urlToImage": "https:\/\/ichef.bbci.co.uk\/news\/1024\/branded_news\/7A23\/production\/_97176213_breaking_news_bigger.png",
      "publishedAt": "2019-03-13T18:48:56Z",
      "content": "President Donald Trump has issued an emergency order to ground all Boeing 737 Max aircraft following the crash of an Ethiopian Airlines jet on Sunday.\r\nThe Federal Aviation Administration had previously held out while many countries banned the aircraft from f\u2026 [+959 chars]"
    },
    {
      "source": {
        "id": "bbc-news",
        "name": "BBC News"
      },
      "author": "BBC News",
      "title": "Exams whizz at centre of US cheat scandal",
      "description": "Prosecutors say Mark Riddell, 36, aced tests for students because he is a \"really smart guy\".",
      "url": "http:\/\/www.bbc.co.uk\/news\/world-us-canada-47544392",
      "urlToImage": "https:\/\/ichef.bbci.co.uk\/news\/1024\/branded_news\/49C9\/production\/_105998881_1504b6ba-fcd8-42b2-bd70-bfa5a58bb2e7.jpg",
      "publishedAt": "2019-03-13T17:21:12Z",
      "content": "Image copyrightIMG AcademyImage caption\r\n Riddell studied at Harvard, according to an online biography\r\nA 36-year-old director of university entrance exam preparations at a Florida school catering towards elite athletes is at the centre of a US college cheati\u2026 [+3512 chars]"
    },
    {
      "source": {
        "id": "bbc-news",
        "name": "BBC News"
      },
      "author": "BBC News",
      "title": "Lori Loughlin to face college scam charges",
      "description": "The sitcom actress and Desperate Housewives' Felicity Huffman are among 50 charged in a college scam.",
      "url": "http:\/\/www.bbc.co.uk\/news\/world-us-canada-47557056",
      "urlToImage": "https:\/\/ichef.bbci.co.uk\/news\/1024\/branded_news\/14545\/production\/_105996238_mediaitem105996237.jpg",
      "publishedAt": "2019-03-13T16:04:32Z",
      "content": "Image copyrightGetty ImagesImage caption\r\n Felicity Huffman (left) and Lori Loughlin (right) were among those charged\r\nUS actress Lori Loughlin, of the sitcom Full House, is expected to surrender to authorities in Los Angeles after being charged in a college \u2026 [+7969 chars]"
    },
    {
      "source": {
        "id": "bbc-news",
        "name": "BBC News"
      },
      "author": "BBC News",
      "title": "Deadly shooting at Brazilian school",
      "description": "At least five students and an adult are killed at the school in Suzano, S\u00e3o Paulo state, police say.",
      "url": "http:\/\/www.bbc.co.uk\/news\/world-latin-america-47558141",
      "urlToImage": "https:\/\/ichef.bbci.co.uk\/news\/1024\/branded_news\/1660C\/production\/_106006619_brazilsaopaulo9760319.png",
      "publishedAt": "2019-03-13T14:01:41Z",
      "content": "At least five students have been killed in a shooting at a school in S\u00e3o Paulo state in Brazil, police say.\r\nOne adult was also killed in the attack at the Professor Raul Brasil state school in Suzano.\r\nTwo \"armed and hooded adolescents\" had carried out the a\u2026 [+163 chars]"
    },
    {
      "source": {
        "id": "bbc-news",
        "name": "BBC News"
      },
      "author": "BBC News",
      "title": "Fears for pupils in Nigeria school collapse",
      "description": "The primary school was on the top floor of a building in the city of Lagos, reports say.",
      "url": "http:\/\/www.bbc.co.uk\/news\/world-africa-47555373",
      "urlToImage": "https:\/\/ichef.bbci.co.uk\/news\/1024\/branded_news\/178CC\/production\/_106006469_127.jpg",
      "publishedAt": "2019-03-13T12:08:34Z",
      "content": "Image caption\r\n There is no official word yet on casualties\r\nA building containing a primary school has collapsed in the Nigerian city of Lagos, with reports saying pupils are trapped in the rubble.\r\nThe school is said to have been on the top floor of the thr\u2026 [+514 chars]"
    },
    {
      "source": {
        "id": "bbc-news",
        "name": "BBC News"
      },
      "author": "BBC News",
      "title": "Reaction as MPs prepare for no-deal vote",
      "description": "Latest updates as MPs prepare to vote on whether to leave the EU without a deal on 29 March.",
      "url": "http:\/\/www.bbc.co.uk\/news\/live\/uk-politics-parliaments-47529293",
      "urlToImage": "https:\/\/c.files.bbci.co.uk\/0C4D\/production\/_105994130_may2.jpg",
      "publishedAt": "2019-03-13T07:38:32.4004145Z",
      "content": "Mrs May's deal was defeated in the Commons on Tuesday evening by 149 votes.Image caption: Mrs May's deal was defeated in the Commons on Tuesday evening by 149 votes.\r\nGood morning.\r\nSo where are we now over Brexit? \r\nLast night, MPs\r\noverwhelmingly threw out \u2026 [+791 chars]"
    }
  ]
}

For our app, we just want to show the titles and jump to their URLs, so we’ll distill this down to a simple model for Headline:

class Headline(val text: String, val url: String)

After you hang out with web services a while, you realize that your code to fabricate the URL tends toward ugly. The parameters need to be encoded. The string gets really long. Abstractions can make ugly beautiful, so let’s write a quick helper method that accepts the path and a map of the query parameters and yields a URL:

fun parameterizeUrl(url: String, parameters: Map<String, String>): URL {
  val builder = Uri.parse(url).buildUpon()
  parameters.forEach { key, value -> builder.appendQueryParameter(key, value) }
  val uri = builder.build()
  return URL(uri.toString())
}

Much of the work is be done by the Uri class. However, Uri is immutable; we need to create a Uri.Builder in order to append the parameters. Once we have a complete Uri, we can convert it to a URL. What a gauntlet.

We create our URL above with this call:

val parameters = mapOf("sources" to "bbc-news", "apiKey" to KEY)
val url = parameterizeUrl("https://newsapi.org/v2/top-headlines", parameters)

Isn’t that beautiful?

Downloading on Android

Let’s pull down this JSON in our app. There are lots of libraries that are happy to help us, but external dependencies have benefits and costs. Let’s stick with stock Android, which provides HttpsUrlConnection, a utility for issuing HTTP requests. We open a URL and then read from an InputStream that feeds the JSON string back to us. We expand this string into a JSONObject structure:

fun getJson(url: URL): JSONObject {
  val connection = url.openConnection() as HttpsURLConnection
  try {
    val json = BufferedInputStream(connection.inputStream).readBytes().toString(Charset.defaultCharset())
    return JSONObject(json)
  } finally {
    connection.disconnect()
  }
}

Let’s call this in our onCreate method to get our headlines:

val parameters = mapOf("sources" to "bbc-news", "apiKey" to KEY)
val url = parameterizeUrl("https://newsapi.org/v2/top-headlines", parameters)
val headlines = getJson(url)

It fails with a NetworkOnMainThreadException exception. What gives?

We are trying to send a request out from our phone, up to the nearest cell tower, bouncing tower to tower, into our provider’s wired network, through cables that span the ocean floor, to some other continent, through the United States intelligence offices, and finally on to its destination, only for the result to come back in a similar manner. And we’re trying to do this on the same thread that listens for button clicks.

Our mobile operating systems do not want us to run blocking code on the UI thread. In the case of network activity, they dynamically enforce this. So, we need to run our download on some other thread. Usually we want to take the results of our network activity and update the UI in some way, so we will need to have some sort of synchronization between our background thread and the UI thread.

Android provides a solution for this background-then-UI pattern called AsyncTask, which we subclass. It automatically starts up a thread for us, which executes our doInBackground method. Then it passes its returned value to our onPostExecute method, which is run on the UI thread. Our subclass for fetching a list of headlines takes on this overall shape:

class HeadlinesDownloader(val activity: MainActivity) : AsyncTask<URL, Void, List<Headline>>() {
  override fun doInBackground(vararg urls: URL): List<Headline> {
    headlines = ... some background computation ...
    return headlines
  }

  override fun onPostExecute(headlines: List<Headline>) {
    super.onPostExecute(headlines)
    ... update the UI ...
  }
}

We move our fetch of the JSON into doInBackground, where it is free to issue network requests. Then we rip up the JSON structure and turn each article into a Headline:

override fun doInBackground(vararg urls: URL): List<Headline> {
  val result = getJson(url)

  val headlinesJson = result.getJSONArray("articles")
  val headlines = (0 until headlinesJson.length()).map { i ->
    val headline = headlinesJson.getJSONObject(i)
    Headline(headline.getString("title"), headline.getString("url"))
  }

  return headlines
}

JSONArray doesn’t really support iteration, so this is a bit of a hack. We create a range of the array’s indices, and then map that into a list of headlines. The return value is fed into onPostExecute, where we update the UI. We haven’t figured out the UI yet, so let’s just pop up a Toast:

override fun onPostExecute(headlines: List<Headline>) {
  super.onPostExecute(headlines)
  Toast.makeText(activity, headlines.joinToString(","), Toast.LENGTH_SHORT).show()
}

Note that if we try to be sneaky and update the UI on our background thread, we will generally get a CalledFromWrongThreadException. (Toast behaves a little differently, raising a different exception.)

We fire off our AsyncTask from onCreate:

HeadlinesDownloader(this).execute(url)

And it fails! The logs tell us that we need to request permission to access the internet. We declare this in the manifest:

<uses-permission android:name="android.permission.INTERNET" />

The INTERNET permission is not considered dangerous and doesn’t need to be approved with a dialog. When we run this again, a very bloated Toast appears.

Context Leaks

The AsyncTask hides a lot of threading and synchronization yuck from us. Like any human invention, it does have a weakness. What if the activity that start the AsyncTask expires, perhaps because of a very innocent configuration change? What then is onPostExecute supposed to do? The UI that it’s responsible for updating may no longer be visible.

One solution is to use a WeakReference to wrap around the activity. A normal strong reference prevents an object from being freed by the garbage collector. A weak reference also hangs on to an object, but if all strong references to it go away, the object will be freed the next time the garbage collector runs. We switch our AsyncTask constructor to receive a strong reference but retain a WeakReference:

class HeadlinesDownloader(activity: MainActivity) : AsyncTask<URL, Void, List<Headline>>() {
  private val context = WeakReference(activity)

  ...
}

In onPostExecute, we only update the UI if the referenced object is not null. We could use a conditional statement for this, but we’re in Kotlin. We use its ? operator and a let block:

override fun onPostExecute(headlines: List<Headline>) {
  super.onPostExecute(headlines)
  context.get()?.let { Toast.makeText(it, headlines.joinToString(","), Toast.LENGTH_SHORT).show() }
}

That’s enough for today. See you next time!

Sincerely,

P.S. It’s time for a haiku, which was more relevant when I thought we were going to talk about lists!

To break free of lists
Work on one thing at a time
Save the rest for now