With Micronaut on iOS & Android...WITH MICRONAUT ON IOS & ANDROID NEVERNULL • Overview & Deep...
Transcript of With Micronaut on iOS & Android...WITH MICRONAUT ON IOS & ANDROID NEVERNULL • Overview & Deep...
www.nevernull.io © 2020 NEVERNULL® GmbH. All Rights Reserved.
With Micronaut on iOS & Android A modern Java-based approach for mobile app development
Dr. Daniel Thommes
NEVERNULL® GmbH
www.nevernull.io © 2020 NEVERNULL® GmbH. All Rights Reserved.
2
WITH MICRONAUT ON IOS & ANDROIDNEVERNULL
• Overview & Deep Dive:
Dependency Injection
systems for mobile Java
• How Java Forum app
applies DI
• The app’s internals
• ORM
• Sync
• Rx
• Cross-Platform GUI
www.nevernull.io © 2020 NEVERNULL® GmbH. All Rights Reserved.
3
DEPENDENCY INJECTION REQUIREMENTSNEVERNULL
• Why Dependency Injection?
• Component-based development
• Polymorphic use of interchangeable implementations
• Maximizing code re-use
• Increase productivity
Requirements
• Good Performance (on mobile)
• Good Developer Experience
www.nevernull.io © 2020 NEVERNULL® GmbH. All Rights Reserved.
4
DEPENDENCY INJECTION ON ANDROIDNEVERNULL
History of DI systems for mobile
• RoboGuice (2011)
• RoboSpring (2012)
• Dagger 1.0 (2012)
• Dagger 2.0 (2012)
• Micronaut (2018)
www.nevernull.io © 2020 NEVERNULL® GmbH. All Rights Reserved.
5
PERFORMANCE OF DINEVERNULL
• Problem: Frequent restarts due to limited memory and
missing swapping
• Reflection is slow, especially on mobile.
• Reflection-based DI metadata allocate a lot of memory.
• Result: Slow start times for reflection-based DI
www.nevernull.io © 2020 NEVERNULL® GmbH. All Rights Reserved.
6
PERFORMANCE OF DINEVERNULL
public class MainActivity extends AppCompatActivity {
@Inject private Vibrator vibrator;
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); DISystem.inject(this); }}
MainActivity
AppCompatActivity
FragmentActivity
androidx.activity
ComponentActivity
androidx.core.app
ComponentActivity
Activity
ContextThemeWrapper
ContextWrapper
Context
www.nevernull.io © 2020 NEVERNULL® GmbH. All Rights Reserved.
7
PERFORMANCE OF DINEVERNULL
ExtraData addContentView applyOverrideConfiguration attachBaseContext bindIsolatedService bindService bindService checkCallingOrSelfPermission checkCallingOrSelfUriPermission checkCallingPermission checkCallingUriPermission checkPermission checkSelfPermission checkUriPermission checkUriPermission clearWallpaper clone closeContextMenu closeOptionsMenu createConfigurationContext createContextForSplit createDeviceProtectedStorageContext createDisplayContext createPackageContext createPendingResult databaseList deleteDatabase deleteFile deleteSharedPreferences dismissDialog dismissKeyboardShortcutsHelper dispatchGenericMotionEvent dispatchKeyEvent dispatchKeyShortcutEvent dispatchPopulateAccessibilityEvent dispatchTouchEvent dispatchTrackballEvent dump enforceCallingOrSelfPermission enforceCallingOrSelfUriPermission enforceCallingPermission enforceCallingUriPermission enforcePermission enforceUriPermission enforceUriPermission enterPictureInPictureMode enterPictureInPictureMode equals fileList
finalize findViewById finish finishActivity finishActivityFromChild finishAffinity finishAfterTransition finishAndRemoveTask finishFromChild getColor getColorStateList getDatabasePath getDir getDrawable getExternalFilesDir getExternalFilesDirs getExtraData getFileStreamPath getPreferences getSharedPreferences getString getString getSystemService getSystemService getSystemServiceName getText grantUriPermission hashCode hasWindowFocus invalidateOptionsMenu managedQuery moveDatabaseFrom moveSharedPreferencesFrom moveTaskToBack navigateUpTo navigateUpToFromChild notify notifyAll obtainStyledAttributes obtainStyledAttributes obtainStyledAttributes obtainStyledAttributes onActionModeFinished onActionModeStarted onActivityReenter onActivityResult onApplyThemeResource onAttachedToWindow onAttachFragment onAttachFragment
onBackPressed onChildTitleChanged onConfigurationChanged onContentChanged onContextItemSelected onContextMenuClosed onCreate onCreate onCreateContextMenu onCreateDescription onCreateDialog onCreateDialog onCreateNavigateUpTaskStack onCreateOptionsMenu onCreatePanelMenu onCreatePanelView onCreateSupportNavigateUpTaskStack onCreateThumbnail onCreateView onCreateView onDestroy onDetachedFromWindow onEnterAnimationComplete onGenericMotionEvent onGetDirectActions onKeyDown onKeyLongPress onKeyMultiple onKeyShortcut onKeyUp onLocalVoiceInteractionStarted onLocalVoiceInteractionStopped onLowMemory onMenuItemSelected onMenuOpened onMultiWindowModeChanged onMultiWindowModeChanged onNavigateUp onNavigateUpFromChild onNewIntent onNightModeChanged onOptionsItemSelected onOptionsMenuClosed onPanelClosed onPause onPerformDirectAction onPictureInPictureModeChanged onPictureInPictureModeChanged onPointerCaptureChanged onPostCreate
onPostCreate onPostResume onPrepareDialog onPrepareDialog onPrepareNavigateUpTaskStack onPrepareOptionsMenu onPrepareOptionsPanel onPreparePanel onPrepareSupportNavigateUpTaskStack onProvideAssistContent onProvideAssistData onProvideKeyboardShortcuts onProvideReferrer onRequestPermissionsResult onRestart onRestoreInstanceState onRestoreInstanceState onResume onResumeFragments onRetainCustomNonConfigurationInstance onRetainNonConfigurationInstance onSaveInstanceState onSaveInstanceState onSearchRequested onSearchRequested onStart onStateNotSaved onStop onSupportActionModeFinished onSupportActionModeStarted onSupportContentChanged onSupportNavigateUp onTitleChanged onTopResumedActivityChanged onTouchEvent onTrackballEvent onTrimMemory onUserInteraction onUserLeaveHint onVisibleBehindCanceled onWindowAttributesChanged onWindowFocusChanged onWindowStartingActionMode onWindowStartingActionMode onWindowStartingSupportActionMode openContextMenu openFileInput openFileOutput openOptionsMenu openOrCreateDatabase
openOrCreateDatabase overridePendingTransition peekWallpaper postponeEnterTransition putExtraData recreate registerActivityLifecycleCallbacks registerComponentCallbacks registerForContextMenu registerReceiver registerReceiver registerReceiver registerReceiver releaseInstance removeDialog removeStickyBroadcast removeStickyBroadcastAsUser reportFullyDrawn requestDragAndDropPermissions requestPermissions requestShowKeyboardShortcuts requestVisibleBehind requestWindowFeature requireViewById revokeUriPermission revokeUriPermission runOnUiThread sendBroadcast sendBroadcast sendBroadcastAsUser sendBroadcastAsUser sendOrderedBroadcast sendOrderedBroadcast sendOrderedBroadcastAsUser sendStickyBroadcast sendStickyBroadcastAsUser sendStickyOrderedBroadcast sendStickyOrderedBroadcastAsUser setContentView setFeatureDrawable setFeatureDrawableAlpha setFeatureDrawableResource setFeatureDrawableUri setResult setVrModeEnabled shouldShowRequestPermissionRationale shouldUpRecreateTask showAssist showDialog showDialog
showLockTaskEscapeMessage startActionMode startActionMode startActivities startActivities startActivity startActivity startActivityForResult startActivityForResult startActivityFromChild startActivityFromChild startActivityFromFragment startActivityFromFragment startActivityFromFragment startActivityFromFragment startActivityIfNeeded startActivityIfNeeded startForegroundService startInstrumentation startIntentSender startIntentSender startIntentSenderForResult startIntentSenderForResult startIntentSenderFromChild startIntentSenderFromChild startIntentSenderFromFragment startLocalVoiceInteraction startLockTask startManagingCursor startNextMatchingActivity startNextMatchingActivity startPostponedEnterTransition startSearch startService startSupportActionMode stopLocalVoiceInteraction stopLockTask stopManagingCursor stopService superDispatchKeyEvent supportFinishAfterTransition supportInvalidateOptionsMenu supportNavigateUpTo supportPostponeEnterTransition supportRequestWindowFeature supportShouldUpRecreateTask supportStartPostponedEnterTransition takeKeyEvents toString triggerSearch
unbindService unregisterActivityLifecycleCallbacks unregisterComponentCallbacks unregisterForContextMenu unregisterReceiver updateServiceGroup validateRequestPermissionsRequestCode wait wait wait ACCESSIBILITY_SERVICE ACCOUNT_SERVICE ACTIVITY_SERVICE ALARM_SERVICE APP_OPS_SERVICE APPWIDGET_SERVICE AUDIO_SERVICE BATTERY_SERVICE BIND_ABOVE_CLIENT BIND_ADJUST_WITH_ACTIVITY BIND_ALLOW_OOM_MANAGEMENT BIND_AUTO_CREATE BIND_DEBUG_UNBIND BIND_EXTERNAL_SERVICE BIND_IMPORTANT BIND_INCLUDE_CAPABILITIES BIND_NOT_FOREGROUND BIND_NOT_PERCEPTIBLE BIND_WAIVE_PRIORITY BIOMETRIC_SERVICE BLUETOOTH_SERVICE CAMERA_SERVICE CAPTIONING_SERVICE CARRIER_CONFIG_SERVICE CLIPBOARD_SERVICE COMPANION_DEVICE_SERVICE CONNECTIVITY_SERVICE CONSUMER_IR_SERVICE CONTEXT_IGNORE_SECURITY CONTEXT_INCLUDE_CODE CONTEXT_RESTRICTED CROSS_PROFILE_APPS_SERVICE DEFAULT_KEYS_DIALER DEFAULT_KEYS_DISABLE DEFAULT_KEYS_SEARCH_GLOBAL DEFAULT_KEYS_SEARCH_LOCAL DEFAULT_KEYS_SHORTCUT DEVICE_POLICY_SERVICE DISPLAY_SERVICE DOWNLOAD_SERVICE
DROPBOX_SERVICE EUICC_SERVICE FINGERPRINT_SERVICE FOCUSED_STATE_SET HARDWARE_PROPERTIES_SERVICE INPUT_METHOD_SERVICE INPUT_SERVICE IPSEC_SERVICE JOB_SCHEDULER_SERVICE KEYGUARD_SERVICE LAUNCHER_APPS_SERVICE LAYOUT_INFLATER_SERVICE LOCATION_SERVICE MEDIA_PROJECTION_SERVICE MEDIA_ROUTER_SERVICE MEDIA_SESSION_SERVICE MIDI_SERVICE MODE_APPEND MODE_ENABLE_WRITE_AHEAD_LOGGING MODE_MULTI_PROCESS MODE_NO_LOCALIZED_COLLATORS MODE_PRIVATE MODE_WORLD_READABLE MODE_WORLD_WRITEABLE NETWORK_STATS_SERVICE NFC_SERVICE NOTIFICATION_SERVICE NSD_SERVICE POWER_SERVICE PRINT_SERVICE RECEIVER_VISIBLE_TO_INSTANT_APPS RESTRICTIONS_SERVICE RESULT_CANCELED RESULT_FIRST_USER RESULT_OK ROLE_SERVICE SEARCH_SERVICE SENSOR_SERVICE SHORTCUT_SERVICE STORAGE_SERVICE STORAGE_STATS_SERVICE SYSTEM_HEALTH_SERVICE TELECOM_SERVICE TELEPHONY_SERVICE TELEPHONY_SUBSCRIPTION_SERVICE TEXT_CLASSIFICATION_SERVICE TEXT_SERVICES_MANAGER_SERVICE TRIM_MEMORY_BACKGROUND TRIM_MEMORY_COMPLETE TRIM_MEMORY_MODERATE
TRIM_MEMORY_RUNNING_CRITICAL TRIM_MEMORY_RUNNING_LOW TRIM_MEMORY_RUNNING_MODERATE TRIM_MEMORY_UI_HIDDEN TV_INPUT_SERVICE UI_MODE_SERVICE USAGE_STATS_SERVICE USB_SERVICE USER_SERVICE VIBRATOR_SERVICE WALLPAPER_SERVICE WIFI_AWARE_SERVICE WIFI_P2P_SERVICE WIFI_RTT_RANGING_SERVICE WIFI_SERVICE WINDOW_SERVICE
www.nevernull.io © 2020 NEVERNULL® GmbH. All Rights Reserved.
8
PERFORMANCE OF DINEVERNULL
ExtraData addContentView applyOverrideConfiguration attachBaseContext bindIsolatedService bindService bindService checkCallingOrSelfPermission checkCallingOrSelfUriPermission checkCallingPermission checkCallingUriPermission checkPermission checkSelfPermission checkUriPermission checkUriPermission clearWallpaper clone closeContextMenu closeOptionsMenu createConfigurationContext createContextForSplit createDeviceProtectedStorageContext createDisplayContext createPackageContext createPendingResult databaseList deleteDatabase deleteFile deleteSharedPreferences dismissDialog dismissKeyboardShortcutsHelper dispatchGenericMotionEvent dispatchKeyEvent dispatchKeyShortcutEvent dispatchPopulateAccessibilityEvent dispatchTouchEvent dispatchTrackballEvent dump enforceCallingOrSelfPermission enforceCallingOrSelfUriPermission enforceCallingPermission enforceCallingUriPermission enforcePermission enforceUriPermission enforceUriPermission enterPictureInPictureMode enterPictureInPictureMode equals fileList
finalize findViewById finish finishActivity finishActivityFromChild finishAffinity finishAfterTransition finishAndRemoveTask finishFromChild getColor getColorStateList getDatabasePath getDir getDrawable getExternalFilesDir getExternalFilesDirs getExtraData getFileStreamPath getPreferences getSharedPreferences getString getString getSystemService getSystemService getSystemServiceName getText grantUriPermission hashCode hasWindowFocus invalidateOptionsMenu managedQuery moveDatabaseFrom moveSharedPreferencesFrom moveTaskToBack navigateUpTo navigateUpToFromChild notify notifyAll obtainStyledAttributes obtainStyledAttributes obtainStyledAttributes obtainStyledAttributes onActionModeFinished onActionModeStarted onActivityReenter onActivityResult onApplyThemeResource onAttachedToWindow onAttachFragment onAttachFragment
onBackPressed onChildTitleChanged onConfigurationChanged onContentChanged onContextItemSelected onContextMenuClosed onCreate onCreate onCreateContextMenu onCreateDescription onCreateDialog onCreateDialog onCreateNavigateUpTaskStack onCreateOptionsMenu onCreatePanelMenu onCreatePanelView onCreateSupportNavigateUpTaskStack onCreateThumbnail onCreateView onCreateView onDestroy onDetachedFromWindow onEnterAnimationComplete onGenericMotionEvent onGetDirectActions onKeyDown onKeyLongPress onKeyMultiple onKeyShortcut onKeyUp onLocalVoiceInteractionStarted onLocalVoiceInteractionStopped onLowMemory onMenuItemSelected onMenuOpened onMultiWindowModeChanged onMultiWindowModeChanged onNavigateUp onNavigateUpFromChild onNewIntent onNightModeChanged onOptionsItemSelected onOptionsMenuClosed onPanelClosed onPause onPerformDirectAction onPictureInPictureModeChanged onPictureInPictureModeChanged onPointerCaptureChanged onPostCreate
onPostCreate onPostResume onPrepareDialog onPrepareDialog onPrepareNavigateUpTaskStack onPrepareOptionsMenu onPrepareOptionsPanel onPreparePanel onPrepareSupportNavigateUpTaskStack onProvideAssistContent onProvideAssistData onProvideKeyboardShortcuts onProvideReferrer onRequestPermissionsResult onRestart onRestoreInstanceState onRestoreInstanceState onResume onResumeFragments onRetainCustomNonConfigurationInstance onRetainNonConfigurationInstance onSaveInstanceState onSaveInstanceState onSearchRequested onSearchRequested onStart onStateNotSaved onStop onSupportActionModeFinished onSupportActionModeStarted onSupportContentChanged onSupportNavigateUp onTitleChanged onTopResumedActivityChanged onTouchEvent onTrackballEvent onTrimMemory onUserInteraction onUserLeaveHint onVisibleBehindCanceled onWindowAttributesChanged onWindowFocusChanged onWindowStartingActionMode onWindowStartingActionMode onWindowStartingSupportActionMode openContextMenu openFileInput openFileOutput openOptionsMenu openOrCreateDatabase
openOrCreateDatabase overridePendingTransition peekWallpaper postponeEnterTransition putExtraData recreate registerActivityLifecycleCallbacks registerComponentCallbacks registerForContextMenu registerReceiver registerReceiver registerReceiver registerReceiver releaseInstance removeDialog removeStickyBroadcast removeStickyBroadcastAsUser reportFullyDrawn requestDragAndDropPermissions requestPermissions requestShowKeyboardShortcuts requestVisibleBehind requestWindowFeature requireViewById revokeUriPermission revokeUriPermission runOnUiThread sendBroadcast sendBroadcast sendBroadcastAsUser sendBroadcastAsUser sendOrderedBroadcast sendOrderedBroadcast sendOrderedBroadcastAsUser sendStickyBroadcast sendStickyBroadcastAsUser sendStickyOrderedBroadcast sendStickyOrderedBroadcastAsUser setContentView setFeatureDrawable setFeatureDrawableAlpha setFeatureDrawableResource setFeatureDrawableUri setResult setVrModeEnabled shouldShowRequestPermissionRationale shouldUpRecreateTask showAssist showDialog showDialog
showLockTaskEscapeMessage startActionMode startActionMode startActivities startActivities startActivity startActivity startActivityForResult startActivityForResult startActivityFromChild startActivityFromChild startActivityFromFragment startActivityFromFragment startActivityFromFragment startActivityFromFragment startActivityIfNeeded startActivityIfNeeded startForegroundService startInstrumentation startIntentSender startIntentSender startIntentSenderForResult startIntentSenderForResult startIntentSenderFromChild startIntentSenderFromChild startIntentSenderFromFragment startLocalVoiceInteraction startLockTask startManagingCursor startNextMatchingActivity startNextMatchingActivity startPostponedEnterTransition startSearch startService startSupportActionMode stopLocalVoiceInteraction stopLockTask stopManagingCursor stopService superDispatchKeyEvent supportFinishAfterTransition supportInvalidateOptionsMenu supportNavigateUpTo supportPostponeEnterTransition supportRequestWindowFeature supportShouldUpRecreateTask supportStartPostponedEnterTransition takeKeyEvents toString triggerSearch
unbindService unregisterActivityLifecycleCallbacks unregisterComponentCallbacks unregisterForContextMenu unregisterReceiver updateServiceGroup validateRequestPermissionsRequestCode wait wait wait ACCESSIBILITY_SERVICE ACCOUNT_SERVICE ACTIVITY_SERVICE ALARM_SERVICE APP_OPS_SERVICE APPWIDGET_SERVICE AUDIO_SERVICE BATTERY_SERVICE BIND_ABOVE_CLIENT BIND_ADJUST_WITH_ACTIVITY BIND_ALLOW_OOM_MANAGEMENT BIND_AUTO_CREATE BIND_DEBUG_UNBIND BIND_EXTERNAL_SERVICE BIND_IMPORTANT BIND_INCLUDE_CAPABILITIES BIND_NOT_FOREGROUND BIND_NOT_PERCEPTIBLE BIND_WAIVE_PRIORITY BIOMETRIC_SERVICE BLUETOOTH_SERVICE CAMERA_SERVICE CAPTIONING_SERVICE CARRIER_CONFIG_SERVICE CLIPBOARD_SERVICE COMPANION_DEVICE_SERVICE CONNECTIVITY_SERVICE CONSUMER_IR_SERVICE CONTEXT_IGNORE_SECURITY CONTEXT_INCLUDE_CODE CONTEXT_RESTRICTED CROSS_PROFILE_APPS_SERVICE DEFAULT_KEYS_DIALER DEFAULT_KEYS_DISABLE DEFAULT_KEYS_SEARCH_GLOBAL DEFAULT_KEYS_SEARCH_LOCAL DEFAULT_KEYS_SHORTCUT DEVICE_POLICY_SERVICE DISPLAY_SERVICE DOWNLOAD_SERVICE
DROPBOX_SERVICE EUICC_SERVICE FINGERPRINT_SERVICE FOCUSED_STATE_SET HARDWARE_PROPERTIES_SERVICE INPUT_METHOD_SERVICE INPUT_SERVICE IPSEC_SERVICE JOB_SCHEDULER_SERVICE KEYGUARD_SERVICE LAUNCHER_APPS_SERVICE LAYOUT_INFLATER_SERVICE LOCATION_SERVICE MEDIA_PROJECTION_SERVICE MEDIA_ROUTER_SERVICE MEDIA_SESSION_SERVICE MIDI_SERVICE MODE_APPEND MODE_ENABLE_WRITE_AHEAD_LOGGING MODE_MULTI_PROCESS MODE_NO_LOCALIZED_COLLATORS MODE_PRIVATE MODE_WORLD_READABLE MODE_WORLD_WRITEABLE NETWORK_STATS_SERVICE NFC_SERVICE NOTIFICATION_SERVICE NSD_SERVICE POWER_SERVICE PRINT_SERVICE RECEIVER_VISIBLE_TO_INSTANT_APPS RESTRICTIONS_SERVICE RESULT_CANCELED RESULT_FIRST_USER RESULT_OK ROLE_SERVICE SEARCH_SERVICE SENSOR_SERVICE SHORTCUT_SERVICE STORAGE_SERVICE STORAGE_STATS_SERVICE SYSTEM_HEALTH_SERVICE TELECOM_SERVICE TELEPHONY_SERVICE TELEPHONY_SUBSCRIPTION_SERVICE TEXT_CLASSIFICATION_SERVICE TEXT_SERVICES_MANAGER_SERVICE TRIM_MEMORY_BACKGROUND TRIM_MEMORY_COMPLETE TRIM_MEMORY_MODERATE
TRIM_MEMORY_RUNNING_CRITICAL TRIM_MEMORY_RUNNING_LOW TRIM_MEMORY_RUNNING_MODERATE TRIM_MEMORY_UI_HIDDEN TV_INPUT_SERVICE UI_MODE_SERVICE USAGE_STATS_SERVICE USB_SERVICE USER_SERVICE VIBRATOR_SERVICE WALLPAPER_SERVICE WIFI_AWARE_SERVICE WIFI_P2P_SERVICE WIFI_RTT_RANGING_SERVICE WIFI_SERVICE WINDOW_SERVICE
Avoid reflection-based DI!
www.nevernull.io © 2020 NEVERNULL® GmbH. All Rights Reserved.
9
DEPENDENCY INJECTION ON ANDROIDNEVERNULL
History of DI systems for mobile
• RoboGuice (2011 - 2016)
• RoboSpring (2012 - 2015)
• Dagger 1.0 (2012 - 2016)
• Dagger 2.0 (2012 - today)
• Micronaut (2018 - today)
www.nevernull.io © 2020 NEVERNULL® GmbH. All Rights Reserved.
10
DI WITH ANNOTATION PROCESSORSNEVERNULL
• Dagger
*.java + Annotations *.java
Generated *.java
*.class
Analyze & Generate
Compile
*.java + Annotations
Generated *.class
*.class
Analyze & Generate
Compile
• Micronaut
www.nevernull.io © 2020 NEVERNULL® GmbH. All Rights Reserved.
11
DAGGERNEVERNULL
• Google Project
• Complicated
• Components are obligatory.
• Modules are obligatory.
• Initialization is difficult.
• Documentation is incomplete.
• Hilt -> „standard Android way to Dagger“
Pro: It's fast.
Con: Developer experience is low.
www.nevernull.io © 2020 NEVERNULL® GmbH. All Rights Reserved.
12
MICRONAUTNEVERNULL
• Open Source project by OCI (2018)
• Works like Spring Boot.
• Just use Annotations like @Singleton.
• Use Configuration with Beans.
• Optionally use AOP.
Pro: It's fast.
Pro: Developer experience is high.
Con: But….
www.nevernull.io © 2020 NEVERNULL® GmbH. All Rights Reserved.
13
MICRONAUT & JAVA 8 NEVERNULL
• Lambdas & default interfaces supported
• Full Java 8 API support only on Android 7+
• Lower Version make up >25% market share.
• However: Java 8+ API desugaring support
(Android Gradle Plugin 4.0.0+, May 2020)
• Lambdas & default interfaces supported
• Minor quirk with default interfaces + abstract
classes
• Only Java 7 APIs available (Android 4.4)
• Java 8+ API desugaring support not applicable
iOS / RoboVM 2.3
Android
Android Dashboard as of September 2020
www.nevernull.io © 2020 NEVERNULL® GmbH. All Rights Reserved.
14
BRINGING MICRONAUT TO MOBILENEVERNULL
Retro Streaming
•Add streamsupport library (GPL + CE)
•replace all occurrences of Java 8 calls (automation welcome)
e.g. via gradle:
list.stream() -> java8.util.stream.StreamSupport.stream(list)
set.foreach() -> java8.lang.Iterables.forEach(set)
Arrays.stream(array) -> J8Arrays.stream(array)
...
dependencies { api “net.sourceforge.streamsupport:streamsupport:1.7.1“}
content.replaceAll(‚Arrays\\.stream\\(', "J8Arrays.stream(")
www.nevernull.io © 2020 NEVERNULL® GmbH. All Rights Reserved.
15
BRINGING MICRONAUT TO MOBILENEVERNULL
Micronaut as part of MobileUI App Framework
•MobileUI Inject = Micronaut for Android 5 & RoboVM
• solves all compatiblity issues
• adds additional platform support for Android & iOS
• automatically initialized with MobileUI
•Micronaut-based plugin system for iOS & Android
•Cross platform native UI system
Learn more on https://www.mobileui.dev/
www.nevernull.io © 2020 NEVERNULL® GmbH. All Rights Reserved.
16
USING MICRONAUT ON MOBILENEVERNULL
import javax.inject.Singleton;
@Singletonpublic class GreetingService { public String getGreeting() { return "Rock on!"; }}
import javax.inject.Inject;
public class MainController {
@Inject GreetingService greetingService;
public MainController() { MobileUI.inject(this); }
public String getGreeting() { return greetingService.getGreeting(); }
}
Defining & injecting a bean into an unmanaged object
www.nevernull.io © 2020 NEVERNULL® GmbH. All Rights Reserved.
17
USING MICRONAUT ON MOBILENEVERNULL
import javax.inject.Singleton;
@Singletonpublic class GreetingService { public String getGreeting() { return "Rock on!"; }}
import javax.inject.Inject;import io.micronaut.context.annotation.Prototype;
@Prototypepublic class MainController {
@Inject GreetingService greetingService;
public String getGreeting() { return greetingService.getGreeting(); }
}
Defining & injecting a bean into an managed object
www.nevernull.io © 2020 NEVERNULL® GmbH. All Rights Reserved.
18
USING MICRONAUT ON MOBILENEVERNULL
import javax.inject.Singleton;
@Singletonpublic class GreetingService { public String getGreeting() { return "Rock on!"; }}
import javax.inject.Inject;import io.micronaut.context.annotation.Prototype;
@Prototypepublic class MainController {
private GreetingService greetingService;
public MainController(GreetingService greetingService){ this.greetingService = greetingService; }
public String getGreeting() { return greetingService.getGreeting(); }
}
Defining & injecting a bean into an managed object using constructor injection
www.nevernull.io © 2020 NEVERNULL® GmbH. All Rights Reserved.
19
USING MICRONAUT ON MOBILENEVERNULL
Encapsulating platform specifica
Vibrator
VibratorIosImpl VibratorAndroidImpl
iOS
www.nevernull.io © 2020 NEVERNULL® GmbH. All Rights Reserved.
20
USING MICRONAUT ON MOBILENEVERNULL
public interface Vibrator { void vibrate();}
import org.robovm.apple.audiotoolbox.AudioServices;import javax.inject.Singleton;
@Singletonpublic class VibratorIosImpl implements Vibrator { @Override public void vibrate() { AudioServices.playAlertSound(AudioServices.SystemSoundVibrate); }}
@Singletonpublic class VibratorAndroidImpl implements Vibrator {
@Inject private Context context;
@Override public void vibrate() { android.os.Vibrator v =
(android.os.Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE); // Vibrate for 500 milliseconds if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { v.vibrate(VibrationEffect.createOneShot(500, VibrationEffect.DEFAULT_AMPLITUDE)); } else { //deprecated in API 26 v.vibrate(500); } }}iOS
www.nevernull.io © 2020 NEVERNULL® GmbH. All Rights Reserved.
21
USING MICRONAUT ON MOBILENEVERNULL
Encapsulating platform specifica
import io.micronaut.context.annotation.Prototype;import javax.inject.Inject;
@Prototypepublic class MainController {
@Inject public Vibrator vibrator;}
www.nevernull.io © 2020 NEVERNULL® GmbH. All Rights Reserved.
22
USING MICRONAUT ON MOBILENEVERNULL
A Micronaut-based cross-platform plugin system
allprojects { ext { //versions are examples mobileuiVersion = "0.5.1" roboVMVersion = "2.3.9"
mobileuiPlugins = [ "io.nevernull:mobileui-inject", "io.nevernull:mobileui-plugin-vibrator", ] }}
import io.micronaut.context.annotation.Prototype;import javax.inject.Inject;
@Prototypepublic class MainController {
@Inject public Vibrator vibrator;}
www.nevernull.io © 2020 NEVERNULL® GmbH. All Rights Reserved.
23
USING MICRONAUT ON MOBILENEVERNULL
A Micronaut-based cross-platform plugin system
allprojects { ext { //versions are examples mobileuiVersion = "0.5.1" roboVMVersion = "2.3.9"
mobileuiPlugins = [ "io.nevernull:mobileui-inject", "io.nevernull:mobileui-plugin-vibrator", ] }}
io.nevernull:mobileui-plugin-vibrator
io.nevernull:mobileui-plugin-vibrator-android
io.nevernull:mobileui-plugin-vibrator-ios
www.nevernull.io © 2020 NEVERNULL® GmbH. All Rights Reserved.
24
USING MICRONAUT ON MOBILENEVERNULL
A Micronaut-based cross-platform plugin system
allprojects { ext { //versions are examples mobileuiVersion = "0.5.1" roboVMVersion = "2.3.9"
mobileuiPlugins = [ "io.nevernull:mobileui-inject", "io.nevernull:mobileui-plugin-vibrator", ] }}
io.nevernull:mobileui-plugin-vibrator:0.5.1
io.nevernull:mobileui-plugin-vibrator-android:0.5.1
io.nevernull:mobileui-plugin-vibrator-ios:0.5.1
api platform("io.nevernull:mobileui-platform:$mobileuiVersion")
www.nevernull.io © 2020 NEVERNULL® GmbH. All Rights Reserved.
25
USING ORM ON MOBILENEVERNULL
OrmLite as Micronaut-based plugin allprojects { ext { //versions are examples mobileuiVersion = "0.5.1" roboVMVersion = "2.3.9"
mobileuiPlugins = [ "io.nevernull:mobileui-inject", "io.nevernull:mobileui-plugin-ormlite", ] }}
import io.micronaut.context.annotation.Factory;import io.nevernull.mobileui.ormlite.OrmLiteConfiguration;import javax.inject.Singleton;
@Factorypublic class Config {
@Singleton public OrmLiteConfiguration ormLiteConfiguration() { return OrmLiteConfiguration .entities(Person.class, Team.class, Project.class) .name("project.db") .version(1) .autoCreateTables(true) .onCreate(s -> { System.out.println("Created"); }) .onUpgrade((connection, oldVersion, newVersion) -> { System.out.println(oldVersion); System.out.println(newVersion); }); }}
www.nevernull.io © 2020 NEVERNULL® GmbH. All Rights Reserved.
26
USING ORM ON MOBILENEVERNULL
OrmLite in the Java Forum App (excerpt)
@Prototypepublic class ProgramController implements LifeCycleAware {
@Inject DaoFactory daoFactory;
List<Talk> talkList;
@Override public void onStart(Object o) { try { talkList = daoFactory.getDao(Talk.class).queryBuilder().orderBy(„timeSlot", true) .orderBy("id", true).query(); } catch (SQLException e) { logger.error("Error when loading talks: ", e); talkList = new ArrayList<>(); } }
…}
www.nevernull.io © 2020 NEVERNULL® GmbH. All Rights Reserved.
27
USING ORM ON MOBILENEVERNULL
OrmLite in the Java Forum App (excerpt) @DatabaseTablepublic class Talk {
@DatabaseField(id = true) public String id; @DatabaseField public String title; @DatabaseField public String abstr; @DatabaseField public String room; @DatabaseField public String topic; @DatabaseField public String timeSlot; @DatabaseField public String type; @DatabaseField public String keyWords; @DatabaseField public String audience; @DatabaseField public String slidesUrl; @DatabaseField public int favorite = 0;
@ForeignCollectionField public Collection<SpeakerTalk> speakers;…
}
www.nevernull.io © 2020 NEVERNULL® GmbH. All Rights Reserved.
28
USING ORM ON MOBILE & BACKENDNEVERNULL
OrmLite in the Java Forum Backend
void parseFile(File file) { ConnectionSource connectionSource = null; try { JfsHtmlParser parser = new JfsHtmlParser(); List<Talk> talks = parser.parse(file);
connectionSource = new JdbcConnectionSource("jdbc:sqlite:javaforum.db");
TableUtils.createTableIfNotExists(connectionSource, Speaker.class); TableUtils.createTableIfNotExists(connectionSource, Talk.class); TableUtils.createTableIfNotExists(connectionSource, SpeakerTalk.class);
Dao<Talk, String> talkDao = DaoManager.createDao(connectionSource, Talk.class); Dao<Speaker, String> speakerDao = DaoManager.createDao(connectionSource, Speaker.class); Dao<SpeakerTalk, String> speakerTalkDao = DaoManager.createDao(connectionSource, SpeakerTalk.class); for (Talk talk : talks) { talkDao.create(talk); for (SpeakerTalk speakerTalk : talk.speakers) { speakerDao.createIfNotExists(speakerTalk.speaker); speakerTalkDao.createIfNotExists(speakerTalk); } } connectionSource.close(); } catch (Exception e) {
... }}
Java Forum Sqlite DB
www.nevernull.io © 2020 NEVERNULL® GmbH. All Rights Reserved.
29
SQLITE-BASED SYNCNEVERNULL
Syncing Apps and Backend via SQLite & HTTP
javaforum.db
HTTP GET
Web Server
HTTP 200
Close DB
Replace DB File
Re-Open DB
Reload UI
ETag: "33a64df551425fcc55e4d42a148795d9f25f89d4"
www.nevernull.io © 2020 NEVERNULL® GmbH. All Rights Reserved.
30
SQLITE-BASED SYNCNEVERNULL
Syncing Apps and Backend via SQLite & HTTP
javaforum.db
HTTP GET
Web Server
HTTP 200
Close DB
Replace DB File
Re-Open DB
Reload UI
favStrategy.onBeforeMigration()
favStrategy.onAfterMigration()
ETag: "33a64df551425fcc55e4d42a148795d9f25f89d4"
www.nevernull.io © 2020 NEVERNULL® GmbH. All Rights Reserved.
31
SQLITE-BASED SYNCNEVERNULL
Syncing Apps and Backend via SQLite & HTTP
javaforum.db
HTTP GET If-None-Match: "33a64df551425fcc55e4d42a148795d9f25f89d4"
Web Server
HTTP 304 (Not Modified)
www.nevernull.io © 2020 NEVERNULL® GmbH. All Rights Reserved.
32
SQLITE-BASED SYNC WITH OKHTTPNEVERNULL
Syncing Apps and Backend via SQLite & HTTP
this.client = new OkHttpClient.Builder() .cache(new Cache(cacheDirectory, 4 * 1024 * 1024)) .build();
Request request = new Request.Builder().cacheControl(new CacheControl.Builder() .maxAge(0, TimeUnit.SECONDS) .build()) .url(url) .build();try (Response response = client.newCall(request).execute()) { if (response.networkResponse() != null && response.networkResponse().code() == HttpURLConnection.HTTP_OK) {
// do migrate!}
} catch (Exception e) { logger.warn("Request not successful: ", e);}
www.nevernull.io © 2020 NEVERNULL® GmbH. All Rights Reserved.
33
SAFELY SYNCING WITH RXJAVANEVERNULL
Using RxJava to safely manage resources on mobile
public Disposable startSync(Runnable updateListener);
private Disposable updateDisposable;
public void checkForUpdates() { if (System.currentTimeMillis() > (lastUpdate + UPDATETIME)) { lastUpdate = System.currentTimeMillis(); logger.debug("DataUpdate: Starting data update request."); updateDisposable = client.startSync(() -> { loadTalks(); MobileUI.firePropertyChanged(this, "talkList"); }); }}
@Overridepublic void onDestroy(Object o) { if (updateDisposable != null) { updateDisposable.dispose(); }}
www.nevernull.io © 2020 NEVERNULL® GmbH. All Rights Reserved.
34
CROSS PLATFORM UI WITH MOBILEUINEVERNULL
Displaying the data on iOS and Android
<?xml version="1.0" encoding="UTF-8"?><Layout xmlns="urn:nevernull:mobileui:layout"> <!-- @include{'app.mv'} --> <style>@include{'app.css'}</style> <LinearLayout height="fill_parent" orientation="vertical" padding="@{margin}dp" width="fill_parent"> <ImageView height="160dp" marginTop="16dp" href="#{'https://www.nevernull.io/download/mobileui-logo-for-template/'}" width="fill_parent" /> <TextView fontFamily="monospace" fontSize="18" marginTop="12dp" text="#{greeting}" textHorizontalAlign="center" width="fill_parent" /> <Button backgroundColor="#6111B1" horizontalAlign="center" marginTop="56dp" onClick="#{onOpenUrlClicked()}" paddingLeft="12dp" paddingRight="12dp" text="Yeah, take me to the candy shop" textColor="white" /> </LinearLayout></Layout>
www.nevernull.io © 2020 NEVERNULL® GmbH. All Rights Reserved.
35
CROSS PLATFORM UI WITH MOBILEUINEVERNULL
Displaying the Talk list with MobileUI ListView
<ListView width="fill_parent" height="fill_parent" items="#{talkList}" onItemClicked="#{onTalkSelected(item)}"> <Template>
<!— only and example —> <TextView text=“#{item.title}“ width="fill_parent" height="48dp" textHorizontalAlign="center" textVerticalAlign="center" fontSize="16" /> </Template></ListView>
@Prototypepublic class ProgramController extends AbstractProgramListController {
public List<Talk> talkList;
...
public void onTalkSelected(Talk talk) { openTalkController(talk);}
}
www.nevernull.io © 2020 NEVERNULL® GmbH. All Rights Reserved.
36
CHALLENGES & SOLUTIONSNEVERNULL
Using a single Java codebase for backends and mobile frontends:
•Stick with Java 8 (or consider Kotlin) and, for now, use Android 5/6
compatible libraries (they work on RoboVM as well).
•Use Micronaut as a fast and developer-friendly DI system.
•Consider SQLite as database system (maybe for sync as well).
•Combine it with ORMLite, if you want to go ORM on all platforms.
•For HTTP and REST consider OkHTTP and Retrofit
•Use RxJava for reactive programming (great compatibility).
•Use Gradle as build system (as of Android tooling).
•Consider MobileUI for high quality mobile user interfaces and
easy-to-use DI and plugins.
CODE SMARTER - NOT HARDER.
PHONE
+49 7034 9422225
+49 179 7389529
ADDRESS
NEVERNULL GmbH
Löwengasse 4
71134 Aidlingen
WEB
https://mobileui.dev
https://nevernull.io