android development with scala and sbt
TRANSCRIPT
Android Development with Scala and SBT
--Anton YalyshevJetBrains
AnDevCon | Contents
Contents:
1. Build tool: SBT
2. Scala for Android development
3. Simple application
4. 3rd party libraries
AnDevCon | Scala Build Tool
// Project’s namename := "my-project"
version := "1.0.0"
// Add dependencieslibraryDependencies ++= Seq( "net.databinder" %% "dispatch-google" % "0.7.8", "net.databinder" %% "dispatch-meetup" % "0.7.8")
// Add dependency for testslibraryDependencies += "junit" % "junit" % "4.8" % "test"
// Define a repository by project’s version publishTo := Some(if (version.value endsWith "-SNAPSHOT") "http://example.com/maven/snapshots" else "http://example.com/maven/releases")
AnDevCon | Scala Build Tool > Features
Distinctive features
● Runs in Command line
● More support in Scala ecosystem
● Convenient cross-compilation and cross-publishing
● Fast tasks execution
● Everything is a task with return value
AnDevCon | Scala Build Tool Common tasks
Command Description
android:run Compile, package a debug apk and deploy it to an active device
android:packageRelease Create a signed apk
android:packageDebug Create a debug apk
test Run unit tests and report results
reload Apply changes that have been made to your sbt configuration
compile Compile the source code
clean Remove all compiled and generated code, delete ProGuard cache.
AnDevCon | Scala for Android development
Benefits:● Null is replaced by Option type● Lazy values for declaration and processing● 3rd party libraries● Resolve concurrency problems
Prior information:● 65K global method limit● Tools versions compatibility
AnDevCon | Scala for Android development > Option type
Option [ T ]
Some [ T ] None
case class User( firstName: String, lastName: String, phone: Option[String])
val user = User("John", "Doe", None)println("Phone: " + user.phone.getOrElse("not specified"))
AnDevCon | Scala for Android development > Lazy values
Keyword Data type № of evaluations When?
val Immutable data Only once At the time of definition
Lazy val Immutable data Only once When we access it for first time
var Mutable data Only once At the time of definition
def Methods and Functions Every-time we access it When we access it
AnDevCon | Scala for Android development > Lazy values
class AndroidWay extends Activity { TextView name; ImageView thumbnail;
public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); name = (TextView) findViewById(R.id.name); thumbnail = (ImageView) findViewById(R.id.thumbnail); }}
class ButterKnifeWay extends Activity { @BindView(R.id.title) TextView title; @BindView(R.id.thumbnail) ImageView thumbnail;
@Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.simple_activity); ButterKnife.bind(this); // ... }}
class ScaloidWay extends SActivity {
lazy val name = findView(TR.id.name) lazy val thumbnail = new ImageView()
onCreate { contentView = new SVerticalLayout { name.here thumbnail.here } }}
AnDevCon | Scala for Android development > TR
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android">
<TextView android:id="@+id/my_title" android:layout_width="wrap_content" android:layout_height="wrap_content" />
</LinearLayout>
public class MyActivity extends Activity { @Override protected void onCreate( Bundle savedInstanceState ) { super.onCreate( savedInstanceState );
setContentView( R.layout.view );
TextView title = (TextView) findViewById( R.id.my_title ); title.setText( "Hello Java!" ); }}
class MyActivity extends Activity { override def onCreate( savedInstanceState: Bundle ) = { super.onCreate( savedInstanceState )
setContentView( R.layout.main )
val title = findViewById( R.id.my_title ).asInstanceOf[TextView] title.setText( "Hello Scala!" ) }}
class MyActivity extends Activity with TypedActivity { override def onCreate( savedInstanceState: Bundle ) = { super.onCreate( savedInstanceState )
setContentView( R.layout.main )
val title = findView( TR.my_title ) title.setText( "Hello Scala!" ) }}
AnDevCon | Scala for Android development > Concurrency management
Concurrency problems are resolved by
○ Futures
○ scala-async
○ Akka. UI Fragments -> actors
○ resolvable: to fill data structure from different endpoints
AnDevCon | Scala for Android > Concurrency > Futures
new AsyncTask[String, Void, String] { def doInBackground(params: Array[String]) = { doAJobTakeSomeTime(params) }
override def onPostExecute(result: String) { alert("Done!", result) }}.execute("param")
Future { val result = doAJobTakeSomeTime(params) runOnUiThread(alert("Done!", result))}
val future = async { val f1 = async { … ; true } val f2 = async { … ; 42 } if (await(f1)) await(f2) else 0}
AnDevCon | Scala for Android > Concurrency > Actor model
Actor
UI Fragment 2
Actor
UI Fragment 1
Actor
UI Fragment 3
Actor
UI Fragment 4
AnDevCon | Scala for Android development >
Frequent questions:
● 65K global method limit ⇒ ProGuard
● Tools versions compatibility
○ Target bytecode version: 1.7
○ Android build tools <= 23.*
AnDevCon | Steps to create simple application
IDE →
AnDevCon | 3rd party libraries > Scaloid > Overview
Benefits:● Easy to use● Compatible with legacy Java code● Production quality
Principles:● Simplicity; ● Programmability;● Type-safety.
AnDevCon | 3rd party libraries > Scaloid > Short and easy code
val button = new Button(context)button.setText("Greet")button.setOnClickListener(new OnClickListener() { def onClick(v: View) { Toast.makeText(context, "Hello!", Toast.LENGTH_SHORT).show() }})layout.addView(button)
SButton("Greet", toast("Hello!"))
AnDevCon | 3rd party libraries > Scaloid > Short and easy code
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:orientation="vertical" android:layout_width="match_parent"android:layout_height="wrap_content" android:padding="200dip"><TextView android:layout_width="match_parent" android:text="ID" android:id="@+id/userid"
android:layout_height="wrap_content" /><EditText android:layout_width="match_parent" android:layout_height="wrap_content"
android:id="@+id/userid" /><Button android:layout_width="match_parent" android:text="Sign in" android:id="@+id/signin"
android:layout_height="wrap_content"/></LinearLayout>
EditText userId = (EditText) findByViewId(R.id.userid);Button signin = (Button) findByViewId(R.id.signin);signin.setOnClickListener(new View.OnClickListener() { public void onClick (View v) { signin(userId.getText()) }}); new SVerticalLayout {
STextView("ID") val userId = SEditText() SButton("Sign in", signin(userId.text))}.padding(20dip)
AnDevCon | 3rd party libraries > Scaloid > Short and easy code
broadcastReceiver(ConnectivityManager.CONNECTIVITY_ACTION) { doSomething()}(this, onStartStop)
Future { alert("Done!", doAJobTakeSomeTime(params))}
AnDevCon | 3rd party libraries > Macroid
Macroid: motivation● XML approach limitations
● Inconvenient namespace system
● Potential risk of NPE
● Adaptation of Scala language features
AnDevCon | 3rd party libraries > Macroid > Components
Button:
new Button(ctx) w[Button]
Layout:
l[LinearLayout]( w[Button] w[TextView])
Properties: w[Button] <~ text("Hello!") <~ TextTweaks.large
AnDevCon | 3rd party libraries > Macroid > Tweaking
def largeText(str: String) = text (str ) + TextTweaks.large + padding (left = 8 dp)
w[Button] <~ largeText("...")
Tweaks composition ? <~ ?
ButtonList [ Button ]
Tweak [ Button ]List [ Tweak [ Button ]
Option [ Button ],...
Option [ Tweak [ Button ]Future [ Tweak [ Button ]
val caption: Future[String] = Future { ...}
myTextView <~ caption.map(text)
AnDevCon | 3rd party libraries > Macroid > Snails
Fade-in Text: ”Click me!”
Snails
mybutton <~~ fadeIn(400) <~ text("Click me!") <~~ fadeOut(400)
await
val blink = fadeIn(400) ++ delay(2000) ++ fadeOut(400)
myTextView <~~ blink
await
AnDevCon | 3rd party libraries > Macroid > UI Actions
val action = myTextView <~ text(“Hi!”) <~ show...action.run// orrunUi(action)
val action1 = myTextView <~ text(“Hi!”) <~ show
val action2 = myProgressBar <~ hide ...runUi(action1 ~ action2)
runUi { (myProgressBar <~~ fadeOut(400)) ~~ (myTextView <~~ blink) ~~ (myOtherVextView <~ text("Hi!"))}
AnDevCon | 3rd party libraries > Macroid > Operators
Right now Await
Apply <~ <~~
Combine + ++
UI Actions seq. ~ ~~
AnDevCon | 3rd party libraries > Macroid > Other features
● Scala’s implicit parameters system
● Pattern matching mechanism ● Macroid lib. Media query system
● Macroid Bricks system
● Scala types system● Contexts management● Adaptive Layout● Simple work work Fragments● Data sources adapter
AnDevCon | 3rd party libraries > Macroid > Akka
Actor
UI Fragment 1
Actor
UI Fragment 1
Actor
UI Fragment 1
Actor
UI Fragment 1
libraryDependencies ++= Seq(
// this library
aar("org.macroid" %% "macroid-akka" % "2.0.0-M4"),
// akka, if not included before
"com.typesafe.akka" %% "akka-actor" % "2.3.9"
)
AnDevCon | Summary
1. SBT as de-facto build tool for Scala projects
2. Scala language features help us in
- risks of NPE
- values calculation control
- concurrency problems
3. 3rd party libraries
- type-safety
- simplification in work with platform
AnDevCon | Outro
Real-world Android Scala SBT projects:● Tuner & Metronome● Translate Bubble● MyCarFinder● ScalaDays official app● Bump and Flock● Android MusicPlayer● 9 Cards Home Launcher● Scala API Demos● SSH Beam● Douban Books● L Camera● ...
ProGuard: proguard.sourceforge.net Scaloid: github.com/pocorall/scaloid Macroid: macroid.github.io
Scala Plugin: https://github.com/JetBrains/intellij-scala
--Anton [email protected]