Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Mobile / Android

Build Air Quality Index widgets for both Android and Windows using ThinkAlike (Part II)

5.00/5 (2 votes)
30 Aug 2016LGPL315 min read 29.3K   207  
New samples of using ThinkAlike to accomplish Android/Desktop cross-platform development, including helloworld, Web API access and widget UI.

Image 1 Image 2

Table of Contents

Day 2 -- Service: harmony of cross-platform AsyncTask executors

Have you prepared for the expedition towards the Airpocalypse? Members of the Fellowship who have passed Challenges are gathering at the hall, hurry up!

A Sketch of Deployment

Gandalf the White, leaning on his white magic staff, investigating a manuscript hung on the wall. "Let's start from the deployment diagram." the wizard instructed.

Image 3

"As illustrated on the right, plenty of collectors have been planted in the highly polluted or risky areas, which will obtain Air Quality Index (AQI) data and send them to local databases periodically. An organization called PM2.5.in will integrate those public data and share them through a web service (RESTful API).

"A flexible proxy service, powered by Python runes, will forward client requests to the backend service along with an authorized App Key each time.

"Our duty, however," emphasized Gandalf, "is focused on client side. As you youngsters may have heard about, the client is designed in a MVVM architecture, which can cushion developers against the workload of cross-platform migration."

Legolas, an Elf archer clad in green and brown, raised his hand. "Dearest counsellor, I've heard that another troop has been sent out to handle the View layer incompatibility between Android and JavaFX already."

"Yes, indeed, I hope they can report outstanding achievement tomorrow," admitted Gandalf. "Before that, we have to take care of the data retrieving part, which is invoked by ViewModel layer, and finally accomplished by Data Access layer (DAL)."

Concepts about AsyncTask, Callback

"Around these two layers, as scouts observed, a mechanism of AsyncTask must be set up," the wizard took out another worn scroll and laid it on a table. "In fact it's a conventional all-purpose mechanism as there are general needs to keep devices responsive upon time-consuming operations. See this ancient scroll for details."

Image 4

"A living 3D UML diagram!" said the elf, astonished. "Wait, it seems like a bogie truck is being driven across pit tunnels, whose loads can be investigated by tapping the flickering cyan bead. (marvelous, I can even see Gimli the Dwarf inside of the truck) Danger! they are being attacked by Dragon's Fury on their arriving!"

"'Gimli in the truck', our keen-eyed Legolas!" said Gandalf laughing. "Actually this is a Sequence Diagram depicting the scheme of Callback. I don't know where it came from ... may be originated to the Old Age when function pointers were still employed for addressing callback targets. Nevertheless, concept of Callback is still of the essense in understanding AsyncTask.

"As you have seen, the blocks attached with 'pit tunnels' represent two different roles (or responsibles) and their lifelines. When one role wants to ask for another to prosecute its responsibilities, it will send a call, the orange 'truck', with required data (the 'mine', or parameters) which can be investigated through the cyan blocks, to the other side. Correspondingly, the message receiver will handle the request. In fact the sender shall estimate the required time of handling a task, and makes choice between either awaiting til its finish, or continuing with other business but getting notified on job completion.

"For the former situation, sender's 'truck' will generally come back with results on it, such model is named as a Synchronous call. For the latter however, since the task handler side will need a new 'vehicle' to carry the results back to the invoker, the 'dragon-fire' like Callback scheme come on the stage; from invoker's perspective, the process as a whole is almost transparent (except an extra result handler is required), just like finishing off an AsyncTask.

"Several key factors to survey before a 10-minute hobbit cake break,"

  • How does Callback trace its targets --- Generally by some prior tricks like 'listener'/'observer'/'subscriber' registration.
     
  • So each task handler requires a Callback scheme --- False.

    As mentioned, a task handler concentrating on its core responsibilities smells better; if Asynchronous call is required, it may be invoker's duty to instantiate an all-purpose Callback scheme and appoint it as its specific-purpose delegate. The art of magic will be revealed soon.

  • What's the color of Callback's 'vehicle' --- It's unnecessarily different with the AsyncTask invokers'...

    Good point. In fact, the previously mentioned all-purpose Callback scheme, currently usually being implemented as an AsyncTask executor, will spawn (or have spawned) a blank Worker Thread for its own use. As Executor is an all-purpose broker, it's AsyncTask invoker's duty (as well) to override its execution part. A perfectly designed AsyncTask Executor may be used like this: a specific task (e.g. a time-consuming HTTP request) will override an run() method and be executed in an independent thread; another overridden onSuccess(Results...) method will be invoked on normal completion of the Worker Thread, delivering results back to the origin thread of the AsyncTask invoker.

  • (Optional) Why was Gimli so enthusiastic in enraging Dragon's Fury --- To light up the Great Furnaces, which himself could hardly reach up.

AsyncTask, Android's implementation, and Burglar

Even while enjoying Baggins' seed cakes, members of the Fellowship began to argue about the complicacy of their upcoming task.

"It's hard to deal with Java, no matter plus what FX (effects); I prefer aXe-Sharp," grumbled a dwarf, took a deep swig of coffee.

"All object oriented techiniques are alike, my forte," replied Legolas the Archer, grinning. "Being a quick learner will be a plus, if not a multiplication, to a robust warrior like you, in my humble opinion."

A big package was dragged into the hall and interrupted their talk. "Reinforcements arrived at last," said Gandolf, excited. "All key factors I mentioned before, are implemented in Android devices already. Our task is to analyse the secret inside and hopefully separate off platform-independent Adapters as we have done on UI components.

"Goods in this package is just the codes poached from Android's core codebase, by Bilbo!" decalred the wizard. Dwarves, hobbits and elves jumped, cheered, even twirled around. "Manufacturer of Android is our alliance however," after a pause Gandalf reminded. Another pause, a tinkle of a dropped spoon was heard. "The difficulty is not so great to get all codes, as to mine the necessary segament and even dig out the common essense which is compatible to other Java platforms."

"Lets take a look at Bilbo's AsyncTask collection, along with those referenced from classic Java toolkit."

  • android.os.AsyncTask
  • android.os.Handler
  • android.os.Looper
  • android.os.Message
  • java.util.concurrent.Executors
  • java.util.concurrent.ExecutorService
  • java.util.concurrent.FutureTask
  • java.util.concurrent.ThreadPoolExecutor

Gandalf took out a short clay-pipe, his eyes glistened with wisdom. "There are three problems lie ahead: parameter delivery, worker thread spawning, and results feedback."

Task 1. Parameter Transferring

Smoke-rings sailed up into the air in varied and mysterious shapes. Some faded away, while some strengthened in more prominent color. One hour passed, yet another with no tea break ... A dwarf coughed and fell back to sleep, no sooner than a voice raised, "We got it!"

Overview

Image 5
(Click to enlarge)

"Complicated though it looks, this Collaboration Diagram may clarify all call flows and responsiblities of the new AsyncTask scheme. Core of the design is the cross-platform AsyncExecutor, in yellow background, which has an Android implementation (upper middle) and a JavaFX equivalent (lower middle). In brief, a platform-independent FutureTask object can be initialized by invoker and passed to either of the executors to complete an Asynchronous call."

"Diagram of more than a dozen nodes overwhelms me," said Legolas, took a deep breath.

"Just a dozen indeed," said Gandalf. "Three nodes in yellow are described already, five in blue are referenced from Android or Java SDK, while remaining four are the invoker class and auxiliaries. Numbers above the linkages marks the sequence of messages exchanged among the objects.

Task Discussion

"Our first task is to carry the data required by the task handler within a 'truck', as painted in the Callback scroll. The problem is that, there is an AsyncTask executor which will be a delegate of the task invoker to invoke the task, asynchronously; who do you think will produce the data, and whom should the parameters be delivered to firstly?"

"I think that the AsyncTask invoker, in our context the ViewModel layer, may know the data most; whereas AsyncTask executor as the direct invoker of the task should be notified the parameters first," analysed Legolas. "In addition, Android SDK uses android.os.AsyncTask<Params, Progress, Result> as its all-purpose AsyncTask Executor. Since we tend to wrap the platform-specific implementations rather than re-inventing the wheel, we will have to pass the Params through it."

"You noticed the idiom in Android, that's laudable," said Gandalf. "but not enough. It's true that they call AsyncTask.execute() to execute an asynchronous task, but you should have found, if you checked source of the android.os.AsyncTask, internally they employ a static java.util.concurrent.ThreadPoolExecutor for all android.os.AsyncTask instances."

"You mean.. core of the android.os.AsyncTask is a java.util.concurrent.ThreadPoolExecutor, which should be the true target of our wrapping?" asked Legolas, seized the truth after thinking twice.

Solution

"Executor is executor," concluded the wizard. "no matter whatever kind of platform-specific class was employed. We must have our own platform-independent AsyncTask class, naming it as thinkalike.generic.concurrent.FutureTask<Params, Result>, to point to the expected task handler (e.g. defeating Sauron), and convey the required data (e.g. the Ring).

"An Executor merely knows to execute a specified Callable, calling its call() in a new thread, and no further details. While a FutureTask, when its call() method is called, will contact the corresponding task handler and deliver parameters to it.

Java
Executor<AqiInfo> asynExec = Loader.getInstance().getPlatform().getFactory().createAsyncExecutor(...);
FutureTask<String, AqiInfo> task = new FutureTask<String, AqiInfo>(_aqiAreaCode){
	//Note that this is a protected method. A public call() method will be called by Executor
	//and eventually call this method with parameters which are initialized in constructor method.
	@Override
	protected AqiInfo call(String... params) {
		if(params==null || params.length<1)
			throw new NullPointerException();
		String aqiAreaCode = params[0];
		return AqiInfoLoader.loadAqiInfo(aqiAreaCode);
	}
	......
};
asynExec.execute(task);

"My dear fellow," said Gandalf, looked toward Legolas. "Can you recognize which kind of Design Pattern is employed here, and draw a diagram for me?"

The Elf archer gazed at the Collaboration Diagram awhile and answered, "Adapter, one of the structural patterns; and Two-layer nested, like the blackberry and apple pie I've just enjoyed."

Image 6

Task Invoker demanded Task Handler,
Impatient to wait, AsyncExecutor dispatched.
Task Handler personated Callable Adaptor,
Interface of calling, apparently matched.
Callable Adaptor carried Task Parameter,
Innocent about all, Executor launched.

Task 2. Spawning a Worker Thread

"Spawn a 'Thread'? You mean to spawn a Three-headed dragon here, don't you?" Chief Craftsman, a dwarf, asked in confusion.

"They mean the same if being used as a 'vehicle' on which a task may be executed," answered Legolas the Elf, a sheet of Collaboration Diagram in front of him. "In fact Gandalf the White has set off for other affairs after explaining the design to me; and we'd better forge the mechanism quickly if we can..."

"Of course! Make the remaining points clear and you will have my lead time," promised the craftsman, firmly.

Task Discussion

"The second task, as Gandalf instructed, is to determine where should the Worker Thread be spawned," explained Legolas. "A Worker Thread, is a space and time allocated to task handler to accomplish its task."

"I'm sorry I have to interrupt you before explaining the Theory of Relativity to me...; I mean I've understood the concept." said the Chief Craftsman.

"Well, rapid shooting mode then," said Legolas. "Clearly in Android it's android.os.AsyncTask's duty to spawn the Worker Thread; when switching to JavaFX however, where the class available no longer, we have to maintain a java.util.concurrent.ThreadPoolExecutor by ourselves, experiencelessly though. Investigating through source of both classes, 2.7K lines or more, we spotted an addWorker() method inside the ThreadPoolExecutor class which launchs worker thread (t.start();) for each Worker (which is an inner class, wrapping Runnable task for lock/unlock control).

"Then we turned to javafx.concurrent.Task, which is said most suitable for programming asynchronous tasks in JavaFX. Awaiting for us, fortunately, is that the class inherits from FutureTask and has an interface to Runnable. Eventually, we managed to mimic source of the android.os.AsyncTask to maintain a static ThreadPoolExecutor, so as to successively spawn worker thread for each javafx.concurrent.Task, correspondingly calling thinkalike.generic.concurrent.FutureTask, and ending up with the substantial task and its Parameter specified by the task invoker.

"... Yeah, well done," commented the dwarf, after a short silence, squeezing a smile.

Solution

"Let me confirm," said the dwarf. "Fundamentally, java.util.concurrent.ThreadPoolExecutor as a common component will be responsible for spawning Worker Threads in Android and JavaFX platforms. thinkalike.generic.concurrent.FutureTask defined outside by the Task Invoker, can be submitted through the shell of AsyncExecutor and gracefully ride on the spawned Worker Thread at last."

Task 3. Return of the Result (to the origin thread)

"I have to admit," said Legolas, "there are more complicated stuffs hidden in this Collaboration Diagram. For instance, android.os.AsyncTask of the Android SDK internally employs other platform-specific classes, such like Message, Loop and Handler, so as to send back the Result through a message mechanism; whereas javafx.concurrent.Task takes use of FX application thread to do such delivery."

"Fortunately all these details needn't to be worried about, don't they?" said the craftsman. "Besides that, I noticed that thinkalike.generic.concurrent.FutureTask is a Callable rather than Runnable. But why?"

"The Callable interface is similar to Runnable," answered Legolas. "both are designed for classes whose instances are potentially executed by another thread. A Runnable, however, could neither return a Result, which is the last link in AsyncTask handling, nor could it throw helpful exceptions."

Task Discussion

"The origin thread, in which the task invoker resides, will be waiting for the return of the asynchronous handling Result," analysed Legolas the Elf. "Such an origin thread generally refers to (in most cases it just is) a 'UI thread'(in SWT, .NET, Android), an 'Event Dispath Thread'(EDT, in AWT, Swing) or a 'FX Application Thread'(in JavaFX). Because UI manipulations must neither be blocked by a time intensive task, nor be flooded with the rendering requests on receiving AsyncTask Results, there must be a queue bound to the origin thread, so as to be sound and thread-safe."

"Nice. And forgive me if I make a mistake," said the dwarf, taking up where Legolas left off. "In short, different platform has different implementation of such AsyncTask Result delivering and queueing. Nice encapsulation though, in order to reuse it, we need to declare a platform-independent FutureTask class compatible to the Callable interface so as to delegate the Result to the android.os.AsyncTask or the javafx.concurrent.Task."

"You made it," said Legolas. "Callable interface does not care about Task Parameters, so we'll overload the constructor to initialize them, as you remember. The parameters needn't to be delivered to the platform-specific AsyncTask Executor, who fulfil its role by spawning Worker Thread and sending back the Task Result."

"Three cheers for Master Gandalf!" the dwarf whooped for the victory.

"Now since you have grasped all the essentials, may me unseal another tricky problem? And hope not your last straw." said Legolas, grinning again. "Cancellation is yet another kind of Result. For a time consuming task, users must have the right to interrupt it. Moreover, programmers may also want to stop unneccessary jobs -- rendering ListView cells that have already slided out of the boundary, for example. For resource-constrained platforms, a cancellable asynchronous computation is a must..."

Solution

"So java.util.concurrent.FutureTask is the key," the dwarf Chief Craftsman standed on a chair, explained to his crew. "not the FutureTask we discussed so far, but its namesake in JDK.

"java.util.concurrent.FutureTask implements RunnableFuture<V> interface, which extends Runnable and Future<V>, and thus has concrete implementation on Cancellation related methods: cancel(), isCancelled(), isDone() and get() which will return Result or exceptions. In fact, both the android.os.AsyncTask and the javafx.concurrent.Task employed this class internally! The only differenece is that the Android AsyncTask embedded ThreadPoolExecutor and can be used alone, while the JavaFX Task did not.

"Since cancellability has been demanded, our FutureTask can delegate relevant requests to an embedded JDK FutureTask. That will be a quick fix. A better solution, however, as I've discussed with Legolas, is not to duplicate FutureTask in Task and Task Executor, but to make our FutureTask directly inherit platform-specific AsyncTask class, through Factory of course. In other words, right half of the Collaboration Diagram will be simplified to platform-independent ThreadPoolExecutor, and left half will be heavier yet more precise!"

"Refactoring! Refactoring!" shouted the crafsmen, waving their hammers, axes and coffee mugs.

Break Time

Gandalf the White was sitting inside a green camp, reading a script delivered by an eagle. "Marvelous," murmured the wizard, chuckling, half to himself. "They have revised my design, the Collaboration Diagram. However, the essential components are unchanged: Parameter, Worker Thread and Result; and so as the interfaces.

"From source of the android.os.AsyncTask, to how to hook back to a specified Task through an AyncExecutor, then confirming role of the ThreadPoolExecutor, next the Task class in JavaFX, after that delivery of the Result across threads. Addtionally the Cancellation... Well, harmony of cross-platform AsyncTask handling finally comes."

Pippin and Merry, two young hobbits carrying devices in the camp, heard the words and looked at each other, chuckled.

True harmony buds not before ash fire of the conflicts.

TO BE CONTINUED...

History

07-25-2015: 1st edition

License

This article, along with any associated source code and files, is licensed under The GNU Lesser General Public License (LGPLv3).

License

This article, along with any associated source code and files, is licensed under The GNU Lesser General Public License (LGPLv3)