We are looking for an energetic and resourceful person to promote our new app on college campuses and in other local communities. The app is a private messaging tool for individuals — like Snapchat for text, but a whole lot better than anything you’ve seen. We have the resources and drive to redefine intimate conversation for the mobile world and your efforts will be a critical part of our app’s success.
Duties
You will be responsible for getting college students to download and use our app — initially on Chicagoland campuses, and later throughout the Midwest and nationally.
You will write emails, Tweets, Facebook and blog posts and design posters to promote the app.
You will gather feedback from customers by whatever means are most appropriate — holding focus groups, tracking down individuals, sending emails, managing a Facebook page, etc.
Based on this feedback you will help to shape the app’s technical features and user interface.
Requirements
You must enjoy meeting new people!
Strong written and verbal communication skills
Creativity, attention to detail, and a strong work ethic
Familiarity with various popular mobile platforms, social networks, and messaging apps
Ability to commute to our Chicago Loop office and to nearby Chicago college campuses
Ability to occasionally travel outside of Chicago
Preferred experience
Experience promoting events or activities — for example, as a rush chair, club officer, political campaign volunteer, fundraiser, etc.
Design tools such as Adobe Creative Suite
Photography or videography experience
HTML or other web design methods
About Vaporstream
Vaporstream is a leader in Streamed E-Communications for the business world. Our office is on the 97th floor of the Willis Tower in Chicago.
This is a paid, full-time position beginning January 2014 and reporting to Steve Tarzia. To apply, please email.
The following post covers the theoretical formulation and implementation of
an Android IntentService that
provides for the centralization and delegation of network calls that an application might make while
communicating with a web service.
There are many forums and blogs that discuss the relative merits and uses of different
tools such as the AsyncTask, Java Thread,
Looper, Handler,
Loader, and the list goes on. I propose
that the following method produces one of the most scalable, modular, and elegant solutions possible given
this particular problem space.
Stepping back for a moment, I’d like a chance to explain why this solution is worth blogging about at all.
If one’s world view were completely based on the field of Computer Science, they would likely be convinced that everything
happens in a controlled and sequential manner. It’s a model that was created by Turing and others in an
attempt to simplify the inherently complex and concurrent world we live in.
Concurrent programming has long been seen as an advanced topic or even as
niche as in the field of supercomputing where machines try to make the most out
of every cycle of an expensive CPU. Mobile devices change this landscape drastically
as they require an increasing number of simultaneous tasks to be executed
often taking advantage of multiple physical CPU cores.
Even in the context of a single process, care must be taken to delegate blocking
or long running code to separate threads of execution. In the world of Android,
not adhering to this rule will quickly land you in the painful world of the Application
Not Responding (ANR) errors. The problem for Android neophytes is two fold. First, as
mentioned, it is entirely likely that newer developers aren’t particularly comfortable with
multi-threading to begin with. This is compounded by the second problem which is that
the Android framework provides many useful tools to choose from. So many, in fact, that
the difference and use cases for each is often a hot topic of debate and confusion.
What is the IntentService and why do I care?
As we know the Android Framework consists of four main components:
the Activity,
Service,
ContentProvider, and
Broadcast Receiver. While an Activity has a direct relationship to a visible screen in an
application and is meant to be relatively short-lived, the Service has
no direct affiliation with an Activity or screen. When there is a need for longer running background processes
in an application, the Service is generally the weapon of choice because it can persist
across Activity boundaries.
While the Service class can be subclassed directly, it is much more common to use the IntentService flavor due to several inherent benefits:
It launches its own work thread
An important fact that is often overlooked when using a Service is
that they do not, by default, create a new thread. Therefore, if a
regular Service is subclassed, all of its work will happen on the UI
thread unless the implementor explicitly creates another. While this
is perhaps a semantic difference, the IntentService handles this concern by automagically running all of its main loop work on a separate thread - correctly freeing up the UI thread and saving us
from writing a fair amount of boiler plate code.
It terminates when the work in its main loop is finished
Services will persist from their creation until they are explicitly
stopped. This can be error prone and can produce behaviors that are
difficult to catch and debug. The IntentService relieves the caller
from the responsibility by simply running the work it is given, and
stopping itself.
Ease of implementation when using the Command pattern
A Service is generally started from an Activity. The communication
channel between the caller Activity and Service is defined at creation
and can use one of two patterns: Command or Bind.
The Command pattern is very simple to implement and understand. When a Service is
started, an identifier is passed in along with an
Intent. These
respectively tell the Service what it should do, and pass the Service the data it needs
to do that task. The Identifier is also later used by the calling Activity to determine
what the Service was doing when it sends back the result.
To Bind with a Service is to set up a two-way API that can be used by the Activity and
Service to communicate for the entire lifecycle. While this gives more control over how the
two send messages, it is significantly more difficult to implement well especially when the bound
Activity undergoes configuration changes. Since the IntentService simply runs, does its work, and
returns a result, it makes it the perfect candidate for the Command pattern.
The code
For the purposes of this post, we will assume we only have three source files: The LoginActivity,
the ActionService, and the SuccessActivity. We will simply display two Text fields and a button to a user
so that they can log in. If the login is successful, the SuccessActivity comes up and signals that we have
successfully logged in. If unsuccessful, we tell the user to get their act together and try again.
To let our Service have more than one possible action to take, we also provide a button called “Info” on
the login screen that will ask the server for the newest info text. On Receipt, our Activity will display
the info text in a label below all the other controls.
Architect your Service
All web calls and communications are now encapsulated in our subclass of IntentService. We simply check
the incoming message from the calling Activity, act on the request, and return the result.
publicclassActionServiceextendsIntentService{/* Arbitrary int identifier. Used to Identify the Service as the origin of returning results to the calling Activity. */publicfinalstaticintACTION_SERVICE_ID=111;/* String and int constants used either by both the Action Service and caller Activity. This defines the protocol by which we pass information back and forth between the two via Intent extras. */publicfinalstaticStringPENDING_RESULT="PENDING_RESULT";publicfinalstaticStringACTION_TYPE="ACTION_TYPE";publicfinalstaticStringEMAIL_ADDR="email";publicfinalstaticStringPASSWORD="password";publicfinalstaticStringINFO_TEXT="info";publicfinalstaticStringRESULT_CODE="RESULT_CODE";publicfinalstaticStringWORK_DONE="WORK_DONE";publicfinalstaticintCODE_OK=0;publicfinalstaticintCODE_ERROR=1;/* All the types of actions this Service can perform */publicenumActionType{ACTION_LOGIN,ACTION_INFO,ACTION_NONE);publicActionService(){super("ActionService");}@OverrideprotectedvoidonHandleIntent(IntentincomingIntent){/* From the Activity, we parse out what the caller wants the Service to do, and the callback (PendingIntent) that we are to call when the work is done */ActionTypeworkToDo=(ActionType)incomingIntent.getSerializableExtra(ACTION_TYPE,ACTION_NONE);PendingIntentpendingResult=incomingIntent.getParcelableExtra(PENDING_RESULT);/* The Intent that we will add the results to and send back to the caller */IntentreturnIntent=newIntent();/* The result of this action - assume an error by default, change value on success */intworkResultCode=CODE_ERROR;switch(workToDo){caseACTION_LOGIN:// we are logging in, parse the credentials for use in the web callStringemail=incomingIntent.getStringExtra(EMAIL_ADDR);Stringpassword=incomingIntent.getStringExtra(PASSWORD);workResultCode=sendLoginRequest(email,password);break;caseACTION_INFO:// this is an info call, simply tell the server we want the infoworkResultCode=sendInfoRequest();/* In the case of this GET request, our info is returned from the server in the form of a JSON String. The details and error checking are left out here but after parsing the string and confirming a successful request, we have access to the raw info via getInfoString() */returnIntent.putExtra(INFO_TEXT,myHttpClass.getJsonResponse.getInfoString());break;caseACTION_NONE:// NOOPbreak;}try{/* inform the Activity which of the web service calls these results are for */returnIntent.putExtra(WORK_DONE,workToDo);/* Call back to the Activity with the result code and the data to parse if successful */pendingResult.send(this,workResultCode,returnIntent);}catch(PendingIntent.CanceledExceptione){Log.e(getClass().getSimpleName(),"Pending intent was canceled",e);}}}/* The following two methods would call into your http classes to do the action POST or GET. The actual implementation of these calls are out of scope for this particular post but may be covered in the future. All we care about for now is that they execute some call to our server and return either 0 if successful for 1 if not. Note that this implementation calls for these methods to block this thread but it would not have to be done this way. */privateintsendLoginRequest(Stringemail,Stringpassword){returnmyHttpClass.doSendLoginRequest(email,password);}privateintsendInfoRequest(){returnmyHttpClass.doSendInfoRequest();}}
Call your Service and handle the return
Our main Activity is the LoginActivity. This is simply a screen presented to the user that collects
both a username and password input. This information is packaged up and sent to our IntentService
so that it may contact our web service for us. We make no direct networking calls in this class.
Call your Service when the login button or info button is pressed (in LoginActivity)
publicclassLoginActivityextendsActivity{@OverridepublicvoidonCreate(BundlesavedInstanceState){super.onCreate(savedInstanceState);// Here we bind our XML Text fields and buttons. // emailEditText - the edit text where users enter the email for loginemailEditText=(EditText)findViewById(R.id.email);// passwordEditText - the edit text where users enter the password for loginpasswordEditText=(EditText)findViewById(R.id.password);// loginButton - the button users click to log inloginButton=(Button)findViewById(R.id.login);// infoButton - the button the user hits to get updated info from the serverinfoButton=(Button)findViewById(R.id.infoButton);// infoTextView - the text view where the info from the server will displayinfoTextView=(TextView)findViewById(R.id.info);loginButton.setOnClickListener(newOnClickListener(){@OverridepublicvoidonClick(Viewv){Intentextras=newIntent(this,ActionService.class);extras.putExtra(ActionService.EMAIL_ADDR,emailEditText.getText().toString());extras.putExtra(ActionService.PASSWORD,passwordEditText.getText().toString());startActionService(ActionType.ACTION_LOGIN,extras);}}infoButton.setOnClickListener(newOnClickListener(){@OverridepublicvoidonClick(Viewv){Intentextras=newIntent(this,ActionService.class);startActionService(ActionType.ACTION_INFO,extras);}}}privatevoidstartActionService(ActionTypesendType,Intentextras){/* create a pending intent callback for when we send back result info from our Service to our Activity. The Receipt of the PendingIntent will go through onActivityResult just as if we had called startActivityForResult */PendingIntentpendingResult=createPendingResult(ActionService.ACTION_SERVICE_ID,newIntent(this,LoginActivity.class),0);extras.putExtra(ActionService.ACTION_TYPE,sendType);extras.putExtra(ActionService.PENDING_RESULT,pendingResult);startService(extras);}/* Here is the entry point for the returning PendingIntent. -- requestCode = The identifier letting us know where these results are coming from. -- resultCode = The code telling us whether our webservce calls succeeded or failed. -- data = The bundled extras that we can parse our results from once we know the call succeeded.*/@OverrideprotectedvoidonActivityResult(intrequestCode,intresultCode,Intentdata){if(requestCode==ActionService.ACTION_SERVICE_ID){if(resultCode==CODE_OK){ActionTypeactionPerformed=(ActionType)data.getSerializableExtra(ActionService.WORK_DONE);processServiceReturn(actionPerformed,data);}else{// here we could either fail silently or pop a dialog or Toast up informing the user there was a problem.}}}/* Finally, once we are here we know the service call succeeded and we can act accordingly. */privatevoidprocessServiceReturn(ActionTypeaction,Intentextras){switch(action){caseACTION_LOGIN:// Login success! Simply show the success ActivityIntentsuccessIntent=newIntent(this,SuccessActivity.class);startActivity(successIntent);finish();break;caseACTION_INFO:// Got the info String, display itStringinfo=data.getStringExtra(ActionService.INFO_TEXT);infoTextView.setText(info);break;caseACTION_NONE:// NOOPbreak;}}}
Summary and future work
While this setup may look complicated at first, I found it to be by far the cleanest of my reusable patterns. This example
includes only two web calls but one can easily see that it scales very well - simply add additional cases and Action types for
the Service to handle. I have used the PendingIntent callback in both these calls which serves us well when we are sitting at
a particular Activity and wanting the result. If, however, there will be multiple Activites and you want the result no matter
which one the user is on, you might consider Broadcast Receivers. This along with implementation of JSON marshalling/unmarshalling
are among the candidates for future blog posts. Meanwhile, I hope to never catch any of you using messy, inline, anonymous AsyncTasks
for web calls again!
Perhaps the biggest challenge that today’s software companies face is
in supporting the multitude of mobile platforms on the market.
Everyone wants their software to run on dozens of popular Android
phones, iPhone, iPad, Blackberry 7 and 10, Windows phone, etc. But
these platforms are all drastically different.
Software companies and indie developers have two basic choices:
Writing “native” apps, meaning a different codebase for each platform.
That’s a lot of work.
Writing a “cross-platform” app using one of several tools to
write code that runs on several platforms.
In this post I’ll explain why we chose the first. This
“cross-platform” question comes up again and again because the
potential cost savings are huge. We’ve done a lot of research, so
here goes:
The summary: You can’t do better than native
None of the platform vendors (neither Apple, nor Google, Blackberry,
Microsoft, etc.) make your job easy. As far as development goes, they
all live in their own bubbles. The platform vendors expect you to
be writing native code using native libraries. If you’re obsessive
about your app’s design and user experience (and you should be!)
then you have no choice but native.
A footnote on Blackberry 10
Blackberry 10 is an exception to the bubble rule – they acknowledge
that you might have already written an Android app, and they must have
realized it would be great if you could just run it on Blackberry 10
as-is. Blackberry 10 was so late to the game that they had little
choice here. However, there seems to be a lot missing from their
Android support, and, after all, your Android app will look out of
place on a Blackberry. We tried it – no dice.
The deal-killers
I’ll focus on the disadvantages of each cross-platform approach
because the advantage is obvious – reduced development effort. This
could mean either hiring fewer developers or putting more resources
into new features and improvements. We experimented with various
cross-platform development tools, but the bulk of my conclusions come
from downloading and playing around with apps featured on each of
the tool vendor’s web sites. The logic is that these featured apps
must represent close to the very best you can achieve. We tried these
featured apps on both Android and iPhone.
PhoneGap, Sencha Touch and other HTML5 approaches
HTML5 is a powerful tool for creating rich web content.
Unfortunately, it renders incrementally like a web page. That is,
the buttons, text, and graphics pop on the screen individually, from
top to bottom and with some re-layouts occuring while it was
rendering. This rendering process only takes a split second but it is
still noticeable and unacceptable. By contrast, screens for native
apps are rendered before they are shown. Untappd (representing
PhoneGap) and Getographer (representing Sencha Touch) seemed to be
good examples of their respective technologies.
Marmalade – converting to native controls
Marmalade allows you to write apps that magically look native on each
platform. Your switches, buttons, and tables are converted into the
native equivalent on each platform. The problem here is that the size
of these controls is different on each platform, so the layout is
never going to look quite right on all platforms. It’s difficult
enough to get the layout precisely correct in a native Android app
(because of the variety of screen resolutions, pixel densities, and
keyboard sizes); you really don’t want to exacerbate this problem.
I’m talking about difficult layouts involving dynamic content such
detail popovers or auto-completion tables. Tweaking the layout to
work on every Andoid device plus iOS, Blackberry, and others sounds
like a nightmare.
OpenGL
Game developers actually have an easier time supporting multiple
platforms because they generally don’t use any native controls. Most
games use a full-screen OpenGL view and use custom-drawn buttons,
scrollers, and popup windows. Of course, game design shops have the
graphic design resources to do this, and there is no expectation that
the app will look native. In fact, games are meant to be immersive
experiences – the developers want you to forget you’re using an
iPhone at all and custom controls help to achieve this immersion and
distraction. So, you could write a cross-platform calendar or
file-sharing app in OpenGL using made-from-scratch controls but it
would be difficult and would create the wrong experience.
Conclusion
The various cross-platforms tools are each very sophisticated and are
brilliant technical achievements in their own ways. If your app is a
game, then go for it. If your app has simple, static screens and
rendering performance is unimportant, then, by all means, use one of
the cross-platform tools. But if your app is the primary way your
customers interact your brand then you need more than a simply
functional app – you need a carefully-designed, native app.
We held our first ShipIt Day on April 23rd and I am happy to report
that it was a great success. “ShipIt Day” is a term invented by
Atlassian for
full-day activity that it runs at its engineering offices every quarter. It’s
part hackathon and part 20% time.
The goal is to design, build, and ship a new product or feature in a
single day. The product should be novel and useful to the company.
It’s a time for pie-in-the-sky ideas, not incremental thinking. Above
all, it’s a a chance for everyone on the team to focus on shipping – cutting through technical and political barriers to just get ship done.
To reflect our department and culture, Vaporstream’s ShipIt day was
slightly different than other companies’:
It occurred during regular work hours, not as a 24-hour activity.
We all have families at home. I haven’t pulled an all-nighter since
my college days (and I don’t plan to ever again). Sleep deprivation
is not fun and certainly not productive.
It took place in our conference room, allowing us to interact more
directly. Our daily work tends to be on several independent
projects so we don’t have an open-plan office. For ShipIt day, we
worked in close quarters, which was made for a change.
There was no competition between developers, only against the clock.
Instead of awarding a trophy to the “best” project (as Atlassian
does), we awarded participation ribbons and high-fives all around
(just kidding, there were no ribbons nor physical contact of any
kind.)
Daily self-assessment
We decided to build an app based
on a personal and professional development exercise described by
Marshall Goldsmith at one of his recent
executive workshops. Jason, Kenny and Patrick passed it on to the rest of us in the company. Here’s how Marshall describes it:
You write a list of perhaps a dozen questions intended to promote
personal and professional development. For example, you might ask
“was I physically active today?” and “did I hone my plans for the
future?”.
You swaps lists with a friend. The friend’s task is to telephone
you every day and ask you your questions.
The point is to reinforce good habits with peer pressure. The entire
process should take just two minutes per day.
“Ten Questions”
We liked Goldsmith’s concept, but our goal was to make this exercise
more convenient through a smartphone app implementation. We tentatively called the app Ten Questions.
The app alerts you every day to prompt you to answer your questions
in the form of a electronic questionnaire.
The app shows a participation “scoreboard” showing on which of the
past 7 days each of your friends answered his or her questions.
We thought that this design would add convenience without sacrificing
the necessary social pressure.
The app starts with a set of ten “default” questions, but this can
be cutomized by the user. Answers are entered with set of five radio buttons
(a Likert scale).
Building the app involved wireframes,
APIs, database schema, Java servlets, an iOS app, Android app, and a
Push Notification service. Most of this was done on ShipIt day. We
wrapped things up as “homework” over the subsequent week or so.
Lessons learned
Designing and shipping a non-trivial app in 9 hours is difficult,
but not impossible. We came very close.
It is is very easy to justify allocating a single workday each
quarter for ShipIt day; it combines team-building with technical
skills development and its products will have real value to company.
Laptop computers are really bad for your work posture (see the video
above).
Challenges
In the future I would like to expand ShipIt day beyond just the
software engineering team. Quality Assurance and Network Operations
can certainly participate – maybe even Sales, Marketing, and Customer
Support. As a team-building exercise, ShipIt works well because
people buy in. ShipIt is about empowerment and execution; about
practicing the things that everyone on the team should value. These
are universal values, nothing specific to engineering.