Power-up your Android-Fu with Kotlin
@nicolas_frankel
Nicolas Fränkel, 12 May 2016
Me, Myself and IBlah, blah, blah…
@nicolas_frankel #kotlin #android
My experience in AndroidBack-end Java developer
Developing a To Do list application with some improvements
Images
Alarm
Multiple lists@nicolas_frankel #kotlin #android
My personal viewDeveloping Android app is a pain
Backend Java is very mature compared to Android
High-level libraries required to cope up with low-level APIs
@nicolas_frankel #kotlin #android
Starting stackDagger 2
Butterknife
Retro-lambda
Streams
Green Robot’s EventBus
Picasso@nicolas_frankel #kotlin #android
OutlineKotlin - the language
Libraries
Stdlib
Kotlin extensions for Android
Anko
@nicolas_frankel #kotlin #android
KotlinLanguage developed by JetBrains
Open Source
Compiles to
JVM bytecode
JavaScript (experimental)
A "simpler Scala"@nicolas_frankel #kotlin #android
KotlinFunctional and object-orientedStatically typedNull safetyNo checked exceptionsNamed & optional argumentsLambdasExtension functionsJava compatibility(And more...)
@nicolas_frankel #kotlin #android
Hello world!package hello // optional semicolons
// namespace-level functions// types on the right// no special syntax for arrays// optional return typefun main(args: Array<String>) { println("Hello, world!")}
@nicolas_frankel #kotlin #android
Setup - build.gradlebuildscript { ext.kotlin_version = '1.0.0-beta-1038' repositories { jcenter() } dependencies { classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" }}
@nicolas_frankel #kotlin #android
Setup – app/build.gradleapply plugin: 'kotlin-android'
android { sourceSets { main.java.srcDirs += 'src/main/kotlin' test.java.srcDirs += 'src/test/kotlin' }}
@nicolas_frankel #kotlin #android
Sample codepublic class AddTaskEvent extends AbstractTaskEvent {
private final List list;
public AddTaskEvent(Task task, List list) { super(task); this.list = list; }
public List getList() { return list; }} @nicolas_frankel #kotlin #android
Pain pointsVerbose!
Not specific to Android
Unfortunately Java
@nicolas_frankel #kotlin #android
Kotlin solutionclass AddTaskEvent(task: Task, val list: List)
: AbstractTaskEvent(task)
@nicolas_frankel #kotlin #android
Sample codepublic class KeyboardDisplay {
public void show(Activity a) { InputMethodManager imm = (InputMethodManager) a.getSystemService(INPUT_METHOD_SERVICE); imm.toggleSoftInput(SHOW_FORCED, 0); }
public void hide(Activity a) { InputMethodManager imm = (InputMethodManager) a.getSystemService(INPUT_METHOD_SERVICE); imm.toggleSoftInput(HIDE_IMPLICIT_ONLY, 0); }}
@nicolas_frankel #kotlin #android
Pain pointsArtificial grouping
Utility class
No state so not a real Object
@nicolas_frankel #kotlin #android
Kotlin solutionfun show(a: Activity) { val imm = a.getSystemService(INPUT_METHOD_SERVICE) as InputMethodManager imm.toggleSoftInput(SHOW_FORCED, 0)}
fun hide(a: Activity) { val imm = a.getSystemService(INPUT_METHOD_SERVICE) as InputMethodManager imm.toggleSoftInput(HIDE_IMPLICIT_ONLY, 0)}
Sample code• SQLiteDatabase db = getReadableDatabase();
db.query(TASK_TABLE, new String[] { T_ID_COL, T_NAME_COL }, null, null, null, null, T_PRIO_COL);
@nicolas_frankel #kotlin #android
Pain pointsReally, pass all those null values?
Create local variable for ease of use
@nicolas_frankel #kotlin #android
Kotlin solution – part 1fun SQLiteDatabase.query(table:String, columns:Array<String>, selection:String? = null, selectionArgs:Array<String>? = null, groupBy:String? = null, having:String? = null, orderBy:String? = null): Cursor { return query(table, columns, selection, selectionArgs, groupBy, having, orderBy)}
@nicolas_frankel #kotlin #android
Kotlin solution – part 2readableDatabase.query(TASK_TABLE,
arrayOf(T_ID_COL, T_NAME_COL), orderBy = T_PRIO_COL)
@nicolas_frankel #kotlin #android
One-use private method
@nicolas_frankel #kotlin #android
Kotlin solutionNested methods
@nicolas_frankel #kotlin #android
Kotlin LibraryStandard API for the language
@nicolas_frankel #kotlin #android
Setup – app/build.gradledependencies {
compile "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
}
@nicolas_frankel #kotlin #android
Sample codepublic class DisplayMetricsGetter {
public DisplayMetrics getFrom(Context c) { WindowManager wm = (WindowManager) c.getSystemService(WINDOW_SERVICE); DisplayMetrics metrics = new DisplayMetrics(); wm.getDefaultDisplay().getMetrics(metrics); return metrics; }}
@nicolas_frankel #kotlin #android
Pain pointsUtility class
Cannot return the object directly
Have to create a local variable
@nicolas_frankel #kotlin #android
Kotlin solutionfun getFrom(c: Context): DisplayMetrics { val wm = c.getSystemService(Context.WINDOW_SERVICE) as WindowManager val metrics = DisplayMetrics() wm.defaultDisplay.getMetrics(metrics) return metrics}
@nicolas_frankel #kotlin #android
Kotlin solutionfun getFrom(c: Context): DisplayMetrics { val wm = c.getSystemService(Context.WINDOW_SERVICE) as WindowManager return DisplayMetrics().apply { wm.defaultDisplay.getMetrics(this) }}
@nicolas_frankel #kotlin #android
Kotlin extensions for Android
Plugin for the Kotlin compiler
Provide a "synthetic property" for each widget in a layout
@nicolas_frankel #kotlin #android
Setup – app/build.gradlebuildscript { repositories { jcenter() } dependencies { classpath "org.jetbrains.kotlin:kotlin-android-extensions:$kotlin_version" }}
@nicolas_frankel #kotlin #android
Sample Codepublic class AActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstState) { super.onCreate(savedInstaState); setContentView(R.layout.task_add_item); View imageView = findViewById(R.id.new_task_img); imageView.setOnClickListener(...); }}
@nicolas_frankel #kotlin #android
Sample Code – With Butterknifepublic class AddActivity extends AppCompatActivity {
@Bind(R.id.new_task_img) View imageView;
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.task_add_item); ButterKnife.bind(this); imageView.setOnClickListener(...); }} @nicolas_frankel #kotlin #android
Pain pointsDo I have to detail???
@nicolas_frankel #kotlin #android
Kotlinx solutionimport kotlinx.android.synthetic.task_add_item.new_task_img
class AddTaskActivity : AppCompatActivity() {
override fun onCreate(savedInstState: Bundle?) { super.onCreate(savedInstState) setContentView(R.layout.task_add_item) new_task_img.setOnClickListener(...) }} @nicolas_frankel #kotlin #android
AnkoKotlin library for Android
Meant to replace XML with fluent DSL for GUI
But a lot of other niceties
@nicolas_frankel #kotlin #android
Code samplepublic class AddAlarmClickListener implements View.OnClickListener {
@Override public void onClick(View view) { View rootView = view.getRootView(); ViewFlipper flip = (ViewFlipper) rootView.findViewById(R.id.new_task_flip); flip.setDisplayedChild(1); }}
@nicolas_frankel #kotlin #android
Pain pointsCast
Setter (?)
@nicolas_frankel #kotlin #android
Anko solutionimport org.jetbrains.anko.*
class AddAlarmClickListener: View.OnClickListener {
override fun onClick(view: View) { val parent:View = view.rootView val flip = parent.find<ViewFlipper>(R.id.new_task_flip) flip.displayedChild = 1 }} @nicolas_frankel #kotlin #android
Code samplefun show(a: Activity) { val imm = a.getSystemService(INPUT_METHOD_SERVICE) as InputMethodManager imm.toggleSoftInput(SHOW_FORCED, 0)}
fun hide(a: Activity) { val imm = a.getSystemService(INPUT_METHOD_SERVICE) as InputMethodManager imm.toggleSoftInput(HIDE_IMPLICIT_ONLY, 0)}
Pain pointsMap-based API
Cast
@nicolas_frankel #kotlin #android
Anko solutiona.inputMethodManager.toggleSoftInput( SHOW_FORCED, 0)a.inputMethodManager.toggleSoftInput( HIDE_IMPLICIT_ONLY, 0)
@nicolas_frankel #kotlin #android
Code sampleAlertDialog.Builder builder = new AlertDialog.Builder(activity);builder.setMessage("New list name");EditText editText = new EditText(activity);editText.setId( android.support.v7.appcompat.R.id.select_dialog_listview);String listName = global.getList().getName();editText.setText(listName, NORMAL);editText.selectAll();builder.setView(editText);builder.setPositiveButton("OK", editListNameListener);builder.setNegativeButton("Cancel", (dialog, which) -> { dialog.cancel();});AlertDialog dialog = builder.create();dialog.show();
@nicolas_frankel #kotlin #android
Pain pointsVerbose +++
Not structured
@nicolas_frankel #kotlin #android
Anko solutionview.context.alert('New list name') { builder.setPositiveButton('OK', editListNameListener) negativeButton('Cancel') { cancel() } customView { editText(global.list.name) { id = an.sup.v7.appcompat.R.id.select_dialog_listview }.selectAll() }}.show()
@nicolas_frankel #kotlin #android
Final stackDagger 2
Butterknife Kotlin ext.
Retro-lambda Kotlin
Streams Kotlin
Green Robot’s EventBus
Picasso@nicolas_frankel #kotlin #android
Migration tacticsStart with standard classesThen with Android-dependent classes
Use Android Studio provided migration toolTake a classMigrate to Kotlin extensionMigrate to AnkoAfter each single change, test!
Repeat
@nicolas_frankel #kotlin #android
PitfallNull-able types
@nicolas_frankel #kotlin #android
Null-able vs. non null-able typesDifferent type whether value can be
null or not
T: cannot be null
T?: can be null
@nicolas_frankel #kotlin #android
Null-able vs. non null-able typesParameter types should use the right
kind
Know your API
Or read the docs
Or be conservative
@nicolas_frankel #kotlin #android
My experienceMore expressive
Higher-level abstractions
One single class I couldn’t migrate
Dagger 2 module
@nicolas_frankel #kotlin #android
Q&Ahttp://blog.frankel.ch/
@nicolas_frankel
http://frankel.in/
@nicolas_frankel #kotlin #android
Top Related