Associate Android Developer Study Guide
https://developers.google.com/training/certification/associate-android-developer/study-guide/
- Material design
- Localisation
- Device compatibility
- Lifecycle
- App components
- Working in the background
- Notification
- Accessibility Features
- Day/Night Mode
- Styles
- Jetpack
- Debugging
- Test
- Kotlin
Add the material dependency in build.gradle
implementation 'com.google.android.material:material:1.0.0'
Toast
vsSnackbar
Toast | Snackbar | |
---|---|---|
Overview | A toast provides simple feedback about an operation in a small popup | Snackbars provide lightweight feedback about an operation |
Interaction | Toasts automatically disappear after a timeout | Snackbar could either automatically disappear after a timeout, or be manually closed |
More info | 1. Having a CoordinatorLayout in your view hierarchy allows Snackbar to enable certain features 2. Snackbars can contain an action such as "undo" | |
Sample code | BaseActivity | BaseActivity |
Read more - Toasts
Read more - Snackbar
- AppBarLayout -> [Any activity in this app
- BottomSheet -> ContentProviderFragment
- Chip -> NotificationActivity
- ConstraintLayout -> rv_album_item
- FloatingActionButton -> MainActivity
- MaterialButton -> BackgroundServiceFragment
- MaterialCardView -> BackgroundActivity
- NavigationView -> MainActivity
- RadioGroup -> BackgroundActivity
- Seekbar (sliders) -> BackgroundActivity
- TabLayout -> AppComponentsActivity
- TextInputLayout, TextInputEditText -> NotificationActivity
- RecyclerView (grid) -> ArtistsFragment
- RecyclerView (linear) -> AlbumsFragment
- RecyclerView (drag + swipe) -> PlaylistFragment, PlaylistAdapter, RecyclerViewItemTouchHelper
- RecyclerView (SelectionTracker) -> ArtistsFragment, ArtistAdapter, ArtistItemKeyProvider, ArtistItemDetailsLookup
SelectionTracker
is another to dosetOnClickListener
, which is more powerful while multiple items need selecting - BottomNavigationView -> UIComponentsActivity
- TabLayout + ViewPager -> MusicFragment, UIComponentsActivity
- SearchView -> SearchSongsActivity, AndroidManifest
Believe if or not, a SearchView could be far more complicated than you've expected.
Before we get started, let's take a look at some features of SearchView:
- Search Interface
- Query suggestion (either recent searches or custom suggestions)
- Query history
- Searchable configuration (E.g. voice search)
First thing first, you need to create either Search Dialog (a SearchView inside NavigationView) or Search Widget (your custom search view, which could be an EditText placed anywhere in your layout).
Secondly, think about how you handle the search view. Here is my workflow:
- Start the
SearchSongsActivity
- User types something in
SearchView
and press enter - Start the
SearchSongsActivity
again - Handle searches in
onNewIntent()
You need the following:
- Add tags to your result activity in AndroidManifest.xml
<activity
android:name=".activities.SearchSongsActivity"
android:launchMode="singleTop">
<intent-filter>
<action android:name="android.intent.action.SEARCH" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
</intent-filter>
<intent-filter>
<action android:name="com.google.android.gms.actions.SEARCH_ACTION" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
</intent-filter>
<meta-data
android:name="android.app.searchable"
android:resource="@xml/searchable" />
</activity>
- In xml/searchable.xml
<?xml version="1.0" encoding="utf-8"?>
<searchable xmlns:android="http://schemas.android.com/apk/res/android"
android:hint="@string/search_hint"
android:label="@string/app_name"
android:searchSuggestAuthority="com.catherine.materialdesignapp.providers.SearchSuggestionProvider"
android:searchSuggestSelection=" ?"
android:voiceSearchMode="showVoiceSearchButton|launchRecognizer" />
android:searchSuggestAuthority"
: (Optional) Refer to your search content provider
android:searchSuggestSelection"
: (Optional) Pop up search suggestions, " ?" means query
android:voiceSearchMode"
: (Optional) To enable voice search
Read more search configuration here: https://developer.android.com/guide/topics/search/searchable-config
(Optional) Create your search suggestion content provider
public class SearchSuggestionProvider extends SearchRecentSuggestionsProvider {
public final static String AUTHORITY = "com.catherine.materialdesignapp.providers.SearchSuggestionProvider";
public final static int MODE = DATABASE_MODE_QUERIES | DATABASE_MODE_2LINES;
public SearchSuggestionProvider() {
setupSuggestions(AUTHORITY, MODE);
}
}
And register your content provider in AndroidManifest.xml
<provider
android:name=".providers.SearchSuggestionProvider"
android:authorities="com.catherine.materialdesignapp.providers.SearchSuggestionProvider" />
- Handle intents and create a search icon in your activity
public class SearchSongsActivity extends AppCompatActivity {
private final static String TAG = SearchSongsActivity.class.getSimpleName();
private SearchManager searchManager;
private SearchView searchView;
/**
* In this case, this onNewIntent will be called while
* user finishes searching, this activity will be relaunch.
* <p>
* Because
* 1. this activity launches in single top/task/instance mode
* 2. ACTION_SEARCH is defined in intent-filter
*
* @param intent
*/
@Override
protected void onNewIntent(Intent intent) {
handleIntent(intent);
}
private void handleIntent(Intent intent) {
if (intent == null)
return;
if (Intent.ACTION_SEARCH.equals(intent.getAction())) {
String query = intent.getStringExtra(SearchManager.QUERY);
// Handle the scenario that user submitted searches:
// 1. Fill in what user just typed in SearchView automatically
// 2. Dismiss search suggestions
// 3. Query
// 4. Save queries
searchView.setQuery(query, false);
searchView.clearFocus();
query(query);
saveQueries(query);
}
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.searchable_menu, menu);
searchManager = (SearchManager) getSystemService(Context.SEARCH_SERVICE);
searchView = (SearchView) menu.findItem(R.id.action_search).getActionView();
searchView.setSearchableInfo(searchManager.getSearchableInfo(getComponentName()));
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.action_clear:
// clear query history
SearchRecentSuggestions suggestions = new SearchRecentSuggestions(this,
SearchSuggestionProvider.AUTHORITY, SearchSuggestionProvider.MODE);
suggestions.clearHistory();
return true;
default:
return super.onOptionsItemSelected(item);
}
}
private void query(String text) {
// do something
}
// save queries while you've defined a search content provider.
private void saveQueries(String text) {
SearchRecentSuggestions suggestions = new SearchRecentSuggestions(this,
SearchSuggestionProvider.AUTHORITY, SearchSuggestionProvider.MODE);
suggestions.saveRecentQuery(text, null);
}
}
- In menu/ui_components_menu.xml
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/action_search"
android:actionLayout="@layout/searchview_layout"
android:icon="@drawable/ic_search_black_24dp"
android:title="@string/action_search"
app:actionViewClass="androidx.appcompat.widget.SearchView"
app:showAsAction="ifRoom|collapseActionView" />
</menu>
app:showAsAction="collapseActionView"
: Click the search icon and stretch the view
setIconified(false)
: Always show the search field
Build a custom view from scratch:
List all resource directories you should take care of:
- animator/
- anim/
- color/
- drawable/
- mipmap/
- layout/
- menu/
- raw/
- values/
- xml/
- font/
- Create alternative UI resources such as layouts, drawables and mipmaps
- Set layout files with the following rules:
- Avoid hard-coded layout sizes by using
wrap_content
,match_parent
andlayout_weight
, etc - Prefer
ConstraintLayout
- Redraw views when window configuration changes (multi-window mode or screen rotation)
- Avoid hard-coded layout sizes by using
- Define alternative layouts for specific screen sizes. E.g.
layout-w600dp
andlayout-w600dp-land
for 7” tablets and 7” tablets in landscape representative - Create stretchable nine-patch bitmaps
- Build a dynamic UI with fragments
- Test on all screen sizes
ConstraintLayout example: []
Fragments example: []
Pixel density is how many pixels within a physical area of the screen, dpi
is the basic unit.
dpi: Dots per inch
resolution: The total number of pixels on a screen
dp or dip: Instead of px (pixel), measure UI with dp (density-independent pixels) on mobile devices
ldpi | mdpi | hdpi | xhdpi | xxhdpi | xxxhdpi | |
---|---|---|---|---|---|---|
Scaling ratio | 0.75x | 1x | 1.5x | 2x | 3x | 4x |
Dpi | ~120dpi | ~160dpi | ~240dpi | 320dpi | 480dpi | 640dip |
App icon size | 36x36 px | 48x48 px | 72x72 px | 96x96 px | 144x144 px | 192x192 px |
nodpi: bitmaps in nodpi drawables look larger in xhdpi devices whereas it seems smaller on mdpi devices.
anydpi: These bitmaps in anydpi have priority when no bitmaps are found in other drawable directories. For instance, we havedrawable-hdpi/banner.9.png
anddrawable-anydpi/banner.xml
,banner.9.png
will be used on hdpi devices andbanner.xml
will be seen on other devices.
To see more details by automatically importing icons with Android Studio Image Asset tools and have a look at Grid and keyline shapes
res
directory example: res
ViewModel
onSaveInstanceState()
To testsavedInstanceState
, haveDo not keep activities
selected on system Settings page to testonSaveInstanceState
andonRestoreInstanceState
Code: LifecycleActivity- Persistent in local storage for complex or large data
-
Implement both
LifecycleObserver
andLifecycleOwner
Code: LifecycleActivity, LifecycleObserverImpl -
Associate with Jetpack
- Enable activities to handle configuration changes like screen rotation and keyboard availability change
<activity android:name=".MyActivity"
android:configChanges="orientation|keyboardHidden"
android:label="@string/app_name">
- In activities
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
// do something
}
Code: BaseActivity
Read more
There are four different types of app components:
- Activity
- Services
- Broadcast receivers
- Content providers
<activity
android:name=".SingleTaskActivity"
android:launchMode="singleTask">
Let's say we have 4 activities: A, B, C and D
- standard
- Default mode, it pushes new activities on the top of the stack.
- Example (how activities work in the stack): (bottom) A-B-B-D-A-C-C (top)
- singleTop
- No duplicate activities on the top, but there could be same activities in the stack.
- Example (how activities work in the stack): (bottom) A-B-A-C-D-C (top)
- Let's say C is the top activity, and you try to launch C again. Then this C won't be created, instead,
onNewIntent()
will be called in existed C.
- singleTask
- No duplicate activities in the stack.
- Example (how activities work in the stack): (bottom) A-B-C-D (top)
- Let's say C is in the stack, and you try to launch C again. Then this C won't be created, instead,
onNewIntent()
will be called in existed C. - Don't forget to set taskAffinity (
android:taskAffinity="your packageName"
) to map the activity to app.
- singleInstance
singleTask
+taskAffinity
is more recommended.- No duplicate activities in the system, which means if there is A running in App1, then we lunch A in App2, this running A in App1 will be killed.
- Example (how activities work in the stack): (bottom) A-B-C-D (top)
- Let's say C is in the stack, and you try to launch C again. Then this C won't be created, instead,
onNewIntent()
will be called in existed C. - You could set taskAffinity (
android:taskAffinity="your packageName"
) as well
NOTICE: You might need to handle both onCreate()
and onNewIntent()
lifecycle events in singleTop
- To build a multi-pane UI
- To reuse fragments in multiple activities.
- A fragment must always be hosted in an activity and the fragment's lifecycle is directly affected by the host activity's lifecycle.
Code (Activity + Fragment): UIComponentsActivity
Code (ViewPager + Fragment): MusicFragment
Read more
- Two services in Android - background service and foreground service
- Background services are no longer working since Android Oreo, you are suppose to use
JobScheduler
instead. - Foreground services and JobScheduler are alternatives to run app in the background, but
notification
is required while running a foreground service. JobScheduler
is only available on devices running API 21+. Fore devices running API 14+ including devices without Google Play serivces,WorkManager
let you schedule background tasks that need guaranteed completion, whether or not the app process is running.
Android API level | background service | foreground service | job scheduler |
---|---|---|---|
≤ 25 | O | X | X |
≥ 26 | X | O | O |
- Because setting this property is not compatible with persisted jobs, doing so will throw an IllegalArgumentException when
JobInfo.Builder.build()
is called. jobScheduler.cancel(JOB_ID)
orjobScheduler.cancelAll()
only works while jobs haven't started. For example, a job is scheduled to start in 5 seconds (setMinimumLatency(5000)
),cancel()
works right before the job actually runs.- Don't forget to finish jobs if the task is done. (
jobFinished(jobParameters, false)
)
Code: BackgroundServiceFragment, MusicPlayerService, MusicPlayerJobScheduler, AndroidManifest
Read more
You could either register receivers by dynamically extending BroadcastReceiver
or statically declaring an implementation with the <receiver>
tag in the AndroidManifest.xml
Code: NetworkHealthService, NetworkHealthJobScheduler, InternetConnectivityReceiver
Read more
Create your own content providers to share data with other applications or access existing content providers in another applications.
In order to get the uri path, we are going to have a look at android source code.
- Go to https://android.googlesource.com/platform/packages/providers/ContactsProvider/+/master/ and pick out needed providers
- Search
<provider>
tag in AndroidManifest, e.g.
<provider
android:name="CallLogProvider"
android:authorities="call_log"
android:syncable="false" android:multiprocess="false"
android:exported="true"
android:readPermission="android.permission.READ_CALL_LOG"
android:writePermission="android.permission.WRITE_CALL_LOG">
</provider>
To read call logs, ask for READ_CALL_LOG
permission.
3. Now we have the host name (android:authorities
), then go to CallLogProvider, which refers to android:name
, and get the table name
4. Search UriMatcher
in CallLogProvider, you will find a bunch of sURIMatcher.addURI()
.
static {
sURIMatcher.addURI(CallLog.AUTHORITY, "calls", CALLS);
sURIMatcher.addURI(CallLog.AUTHORITY, "calls/#", CALLS_ID);
sURIMatcher.addURI(CallLog.AUTHORITY, "calls/filter/*", CALLS_FILTER);
// Shadow provider only supports "/calls".
sURIMatcher.addURI(CallLog.SHADOW_AUTHORITY, "calls", CALLS);
}
- The completely path would be: scheme (content://) + table_name (authorities) + path (defined uriMatcher)
private final static Uri callLogsDB = Uri.parse("content://call_log/calls");
- Fetch call logs
You are able to query call logs, register observers to listen to fetch events like incoming calls, outgoing calls, etc.
Check all fields you can query: CallLogProvider, CallLog
Code: CursorLoaderActivity
A content provider uri should be scheme + authority + table + [id] + [filter]
. E.g. content://com.catherine.myapp/member/1/name
ContactsProvider + CallLogs code: ContentProviderFragment
Tasks on a background thread using AsyncTask
(for short or interruptible tasks) or AsyncTaskLoader
(for tasks that are high-priority, or tasks that need to report back to the user or UI).
- run on UI thread:
onPreExecute
,onProgressUpdate
andonPostExecute
- update progress to UI via
publishProgress
, handle data inonProgressUpdate
WeakReference
executeOnExecutor
Code: BackgroundActivity, SleepTask
When you want the data to be available even if the device configuration changes, use loaders
This getLoaderManager()
or getSupportLoaderManager()
is deprecated since Android P. Instead, we use ViewModels
and LiveData
- Call
getLoaderManager()
orgetSupportLoaderManager()
depends on whether you useSupport Library
initLoader
is supposed to be called inonCreate()
- Call
restartLoader
is equivalent toinitLoader
, butinitLoader
only works at the first time. onLoadFinished
is run on background thread, this may cause memory leak if updating UI here - use at your own risk.
Code: BackgroundActivity, SleepTaskLoader
Three style of notifications:
- Standard notification
- Notification with actions (one or two buttons)
- Replying notification
Long click app icons on Android O+ devices, notification badge will pop up.
Classify notifications by channels in the Settings app on Android O+ devices.
Code: NotificationActivity
Read more
- Set ContentDescription
- Make the views focusable
- Define your own style in styles.xml, notice your style must extend whatever styles contain ".DayNight" keyword.
<style name="AppTheme.NoActionBar" parent="Theme.MaterialComponents.DayNight.NoActionBar">
<item name="windowActionBar">false</item>
<item name="windowNoTitle">true</item>
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item>
</style>
or
<style name="AppTheme" parent="AppTheme.DayNight">
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item>
</style>
In Manifest, update the theme
android:theme="@style/AppTheme.NoActionBar"
- Initialise night mode programmatically if you want
public class MainActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
initNightMode();
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
// We set the theme, immediately in the Activity’s onCreate()
private void initNightMode() {
Storage storage = new Storage(this);
int nightMode = storage.retrieveInt(Storage.NIGHT_MODE);
if (nightMode == AppCompatDelegate.MODE_NIGHT_YES) {
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES);
} else {
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO);
}
}
}
- Switch day/night mode programmatically if you want
SharedPreferences sharedPreferences = getSharedPreferences("main", Context.MODE_PRIVATE);
button.setOnClickListener(
v -> {
int nightMode = AppCompatDelegate.getDefaultNightMode();
if (nightMode == AppCompatDelegate.MODE_NIGHT_YES) {
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO);
button.setTitle(getString(R.string.action_night_mode));
sharedPreferences.edit().putInt("night_mode", AppCompatDelegate.MODE_NIGHT_NO).apply();
} else {
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES);
item.setTitle(getString(R.string.action_day_mode));
sharedPreferences.edit().putInt("night_mode", AppCompatDelegate.MODE_NIGHT_YES).apply();
}
// Recreate the activity for the theme change to take effect.
recreate();
}
);
Code: BaseActivity Read more
To inherit styles from Android support library by using parent
<style name="GreenText" parent="TextAppearance.AppCompat">
<item name="android:textColor">#00FF00</item>
</style>
And to inherit user-defined styles, you could use a dot notation
<style name="GreenText.Large">
<item name="android:textSize">22dp</item>
</style>
Code: styles
A robust SQLite object mapping library.
WorkManager providers APIs for deferrable, one-off and recurring background tasks that need guaranteed execution
ViewModel is constructed when app configuration changes such as screen rotation
LiveData is an observable data holder for the data is meant to be shown on screens.
Paging library integrates directly with endless data
Navigation library simplifies implementation of complex but common navigation requirements.
Go to google tutorial to see how Room
+ ViewModel
+ LiveData
works.
Code(Room + ViewModel + LiveData): PlaylistFragment, AlbumsFragment, ArtistsFragment and jetpack
- In order to activate AndroidX, add two flags in gradle.properties
android.useAndroidX=true
android.enableJetifier=true
-
Remove android support libraries
com.android.support...
andandroid.arch...
, all the changes must be implemented to both classes and layouts. -
Update test options in build.gradle
android {
defaultConfig {
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
testOptions {
execution 'ANDROIDX_TEST_ORCHESTRATOR'
}
}
dependencies {
androidTestImplementation 'androidx.test:runner:1.1.1'
androidTestUtil 'androidx.test:orchestrator:1.1.1'
}
- Set the debuggable variable (Some 3rd party dependencies might use this variable as well)
android {
buildTypes {
debug {
debuggable true
}
release {
debuggable false
}
}
}
-
Add breakpoints if needed (Click the Debug button instead of the Run button)
step over
: go to next line of codestep into
: go into the function
-
Add watchpoints for C/C++ code
First thing first, install LLDB or LLDB navigator won't appear on Android Studio.
Then debug C/C++ code as if debugging Java/Kotlin.
To create modules in apps and publish which as individual APKs, you need Android Bundle
Min SDK version: Android 5.0+ (API level 21)
Dynamic delivery: An android bundle, which contains features that you suppose the user won't use at install, is basically a bunch of code and resources. Users download bundles on demand.
APKs you will publish:
Base APK
is where contains basic functionality and must-have resources at first download and install.Configuration APKs
are modules for specific screen density, CPU architecture or language. Google Play automatically generates configuration APKs for you.- (Optional)
Dynamic feature APKs
provide modularize features as the user requests
- Install Android Studio Canary 14
- In AndroidManifest.xml
<application
android:extractNativeLibs="false"
/>
- In build.gradle
android {
bundle {
language {
enableSplit = true
}
density {
enableSplit = true
}
abi {
enableSplit = true
}
}
dependencies {
// dynamic delivery
implementation "com.google.android.play:core:${rootProject.ext.playcore}"
// bundle's dependencies
implementation 'com.android.support:customtabs:28.0.0'
}
// bundles
dynamicFeatures = [":bbc_news"]
}
- Create new Dynamic Feature Module (Edit -> New Module -> Dynamic Feature Module) and Fill in bbc_news as module name
- Create NewsPageActivity in bbc_news module
public class NewsPageActivity extends AppCompatActivity {
private final static String TAG = NewsPageActivity.class.getSimpleName();
private final static String BBC_NEWS_URL = "https://www.bbc.com/news";
private final static int CUSTOM_TABS_REQUEST_CODE = 100;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_news_page);
CustomTabsIntent customTabsIntent = new CustomTabsIntent.Builder()
.setToolbarColor(getResources().getColor(R.color.colorPrimary))
.build();
customTabsIntent.intent.setData(Uri.parse(BBC_NEWS_URL));
startActivityForResult(customTabsIntent.intent, CUSTOM_TABS_REQUEST_CODE);
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == CUSTOM_TABS_REQUEST_CODE) {
finish();
}
}
}
- Update AndroidManifest.xml in bbc_news module
<application>
<activity android:name=".NewsPageActivity">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
</intent-filter>
</activity>
</application>
- Install and launch this bbc_news module See DynamicDeliveryActivity, bbc_news module
Code: DynamicDeliveryActivity, bbc_news module, tour_guide module, assets module, open_weather module
Google sample code
Read More
- bundletool (Generate signed Android bundles)
Before you publish your Android bundles and APK to Google Play, you needbundletool
, a command line tool, to test those bundles locally.
Download bundletool and move to AAD-Preparation/
- In Android Studio, go to Build -> Generate Signed Bundle/APK... -> generate a .aab file
- Generate a set of APKs
java -jar ../bundletool-all-0.9.0.jar build-apks --bundle=app/debug/app-debug.aab --output=app/debug/MaterialDesign.apks
- Deploy APKs to a connected device
java -jar ../bundletool-all-0.9.0.jar install-apks --apks=app/debug/MaterialDesign.apks
- Unzip and see all APKs if you like
mkdir app/debug/apks
unzip app/debug/MaterialDesign.apks -d app/debug/apks
Sign APKs: apksigner
Sign .jar files, .aar files or Android bundles: jarsigner
- Tests give you rapid feedback on failures, which are spotted earlier on development cycle are far easier to fix than once that have gone live.
- You are able to maintain a stable velocity throughout the lifetime of your project.
- Avoid the boom bust cycle of crunch feature time and the aggregation of technical debt.
- (small tests) Unit test
- Validate the functionality and contracts of each class within your app
- Run on your own local machine
- fast and focus
- 70%
- (medium tests) Integration test
- Interaction between a view and view model and run on particular screens, DAOs and multi-fragment tests
- Run on real devices or emulators
- 20%
- (large tests) End-to-end test
- validate end-to-end workflows that guide users through multiple modules and features
- Run on real devices or emulators
- 10%
- Through: Test failure conditions, invalid inputs and boundary conditions.
- Repeatable: Return the same results every time.
- Focused: Focus one specific aspect of the code.
- Verifies behaviour: Avoid writting too many assumption in the actual implementation of your code.
- Fast
- Concise
You could run your unit tests on any of them:
- Real devices
- Emulator
- Simulator. E.g. Roboletric
Robolectric supports testing on JVM-powered development machines
Espresso
AndroidJUnit4
Google sample
Google testing blog
Google doc
The exam is only available in Java at this time (4/1/2019)
Read more
- Basic Types
- Bitwise operators
- == vs ===
- Numbers
- Characters
- Strings
- Array
- Control Flow
when
if
while
- Returns and Jumps
break
xxx@ for
orxxx@ while
return
- Classes and Inheritance
- Class with multiple constructors
- Inheritance
interface
override
inner class
super@xxx.f()
abstract class
- Properties and Fields
- getter and setter
lateinit
::
- Visibility Modifiers
open
public
internal
protected
private
- BaseClass, BaseClassExtensions
- Extension functions
- Extension properties
- Companion objects (which is similar to
static
) - Call extension functions of the base class declared other class
- Call functions both declared in the base class and self class inside extension functions (check BaseClassExtensions)
- Data Class
- Sealed Class, Enum Classes
- enum vs sealed class
- [Generics]
- Declaration-site variance
- Type projections
- Nested and Inner Classes
- Nested Class
- Inner Class
- Anonymous Inner Class [NOT YET]
- Enum classes
- Basic usage of enum classes
- Another way to initialise the enum
- Enum constants can also declare their own anonymous classes
- Print all values of enum class
- Objects
- Object expressions - class
- Object expressions - class + interface
- Anonymous objects
- Object declarations (Singleton)
- Companion objects
- Type aliases
- Shorten types declaration
- Inline classes
- An inline class must have a single property initialised in the primary constructor
- Inline classes cannot have properties with backing fields, ie, your code would be:
val length: Int get() = s.length
- Representation: Inline classes could be as inline, generic, interface or nullable
- Inline classes vs type aliases
- Enable inline classes in Gradle:
compileKotlin { kotlinOptions.freeCompilerArgs += ["-XXLanguage:+InlineClasses"] }
- Delegation
- Implementation by delegation
by
// The last "b" (from ": Base by b") is implemented by the "b" in "Derived(b: Base)"
class Derived(b: Base) : Base by b
- Overriding functions and variables is optional
- Declare standard Delegates(
Lazy
,Observable
and storing properties in a Map) viaby
- Implement properties including standard delegates once for all
- Local delegated properties
- Basic functions
- Functions with default arguments
- Override functions
- Lambda
- variable number of arguments (
vararg
) - Unit-returning functions
infix fun
- Projections
- in-projections
- out-projections
- star-projections
- Higher-Order Functions is a function that takes functions as parameters, or returns a function.
- Compare callbacks in Java and Kotlin (SAM for Kotlin classes)
- Lambda functions
- Pass functions as arguments to another function (
this::func
) - Passing a lambda to the last parameter
- Implement a function type as an interface (You can either override
invoke()
or runinvoke()
) invoke()
::
- Inline Functions