Android Interview Note

Homan Huang
30 min readMar 3, 2021

Update:

Here is my interview collection. It’s growing up because you🤔 have no clue who's hiring manager🐱 will ask 🐮. I guess that you have the same face as the cow does.

— === Menu === —

👤 0. Personal + Software Design(Agile)
🕊……….✔️ Android Experience
🕊……….❓ Questions at the end
🕊……….🐣 Advantages of Android 11
🕊……….🌿 Android Methodologies

🏗️ 1. Android Framework
⏱……….©️ Application components
⏱……….💠 Android Framework
⏱……….🏭 Android Architecture
⏱……….🗼Android Architecture Components
⏱……….🚀Android Jetpack
⏱……….❓ Simple Q: Context, Activity, Fragment
⏱……….👁‍🗨 ViewModel
⏱……….♐️ Project Structure
⏱……….©️ Components → New Android project
⏱……….🔧 Android SDK (compile, min, &. target)
⏱……….🆙 App Launched
⏱……….🏁 Android Flag of Activity
⏱……….⏱……….♌️ Intent
⏱……….⏱……….⏳ PendingIntent
⏱……….📑 Android Manifest
⏱……….♨️ Gradle Build System
⏱……….🎢 Activities and Fragments
⏱……….⏱……….💱 App Lifecycle
⏱……….⏱……….📰 Fragment Lifecycle
⏱……….💢 Configuration Changes
⏱……….📣 Broadcast Receiver

📝2. Design and View
🖱……….🏺 What Is Material Design?
🖱……….📲 Different Screen Sizes
🖱……….🖌 Layouts in Android
🖱……….🔗 ConstraintLayout
🖱……….✨ ConstraintLayout vs RelativeLayout vs LinearLayout
🖱……….🎑 MotionLayout
🖱……….♻️ RecyclerView
🖱……….🖱……….🖱️ RecyclerView Click Listener
🖱……….♻️RecyclerView Vs. 📜 ListView
🖱……….🀄️ Localization
🖱……….🌐 Webview
🖱……….🖱……….📌 URL →Activity
🖱……….💐 ViewBinding
🖱……….🖱……….♻️ RecyclerViewAadapter
🖱……….🍛 Databinding
🖱……….🖱……….🍡 Binding adapters
🖱……….⛧ XML Design
🖱……….🖱……….🌈Gradient
🖱……….🖱……….🚄 Round Corner

💣 3. Test
☠……….📁 test &. androidTest
☠……….💊 Unit Tests
☠……….💉 Test-Driven Development
☠……….📱 Instrumentation Tests
☠……….🕷 Mockito
☠……….⏱ Coroutines Test

ⓚ 4. Kotlin
🎖……….😎 Visibility Modifiers
🎖……….🤔 Null Safety
🎖……….🎎 Kotlin Vs Java
🎖……….🍎 Basic
🎖………. ⚖ Comparable interface
🎖……….🚻 Iterable
🎖……….🐼 Simple Problems
🎖……….🎖……….👯 Pair Sum
🎖……….🎖……….🐹 Unique Array
🎖……….🎖……….🅰️ Palindrome
🎖……….🎖……….🃏 Binary Search
🎖……….🎖……….⛓ Linked List
🎖……….🎖……….🎄Binary Search Tree
🎖……….🔄 Coroutines
🎖……….🛐 RunningBlock
🎖……….🛠 Job
🎖……….🔭 coroutineScope
🎖……….🔜 Suspend fun
🎖……….❌ Cancellation and timeouts
🎖……….🔀 Non-cancellable block
🎖……….🔩 Higher Order Function
🎖……….❇️ Coroutines with ArchComps

💾 5. Storage
🎚……….📏 Type of Storage
🎚……….🔰 SharedPreferences
🎚……….📦 Custom Data Store
🎚……….🏠 Room Database
🎚……….🎚……….🚑 Migration

🌐 6. Clouse Service: RestApi + JSON
🕹……….📄 JSON
🕹……….♋️ Retrofit

😎7. Server
🌥……….📄 Jenkins
🌥……….♨️ Spring Boot

👤 0. Personal + Software Design(Agile) …… → Menu

✔️ Android Experience …… → Menu

How are you doing?
When
do you work on Android?
What kind of apps did you create?
What are the benefits of your app?

❓ Questions at the end …… → Menu

What is your expectation?
What is your future goal?
Can you relocate?

🐣 Advantages of Android 11 …… → Menu

Android 11:

* Separate the Notification into categories: Conversations, Alerting, and Silent.

  • Add 💬 Chat bubbles
  • Screen 📳 Recorder
  • Media 🎶 Controls
  • Smart device 🎛️ controls
  • One-time👮 permissions and auto-reset

🌿 Android Methodologies …… → Menu

Q1: Chose your favor and explain why.
Q2: How do you manage the team?

1️⃣ Waterfall Methodology

  • Complete ❗️one phase entirely before moving to the next stage.

2️⃣Prototype Methodology

  • Allow making changes📝 during the design phase + use of prototypes as the name suggests
  • Managed to reduce the risk of failure through the use of prototyping ➕ client feedback.

3️⃣Spiral Methodology

  • The framework relies on the risk pattern of the project and uses more than one method in its process. ( Waterfall ➕ iteration )
  • Set requirements for each phase of the framework.
  • The number of stages in the spiral the model highly depend on the project risk.

4️⃣ Agile Methodology

  • Adopted for the development of sophisticated software
  • The framework allows for iterations, which helps a lot in minimizing mistakes and errors that commonly occur.
  • Types:
    # Feature Driven Development (FDD): Focus on features as the name suggests
    #
    Lean software development: Aim at optimizing time and reducing ♻️waste, 💰cost, and 👷🏻effort.
    #
    Scrum: Focus on the management aspects of software development.
    # Crystal Methods: It focuses on 🚻team member talent skills, interactions, and communication.
    #Rapid Application Development (RAD)/ Rapid-application building (RAB): Focus on ⏰timely delivery in a 🚴🏻fast-paced environment with the use of prototyping and iterative development.
    # Adaptive Software Development (ASD): Provides continuous adaptation to change in project requirements or market needs.
    # Dynamic Systems Development Method (DSDM): It is an iterative and incremental Agile approach based on RAD, but with governance and ⛔️strict guidelines.
    #Extreme Programming (XP): High-level collaboration with minimal documentation → on the 🐵changing needs of the client
    #Kanban: The aim is to 💼manage and 🌊improve flow systems in mobile app development.

5️⃣Lean Startup Methodology

It offers a scientific solution for startups. Since it is a principled approach to new product development, the framework has found application in mobile app development.

🏗️ 1. Android Framework …… → Menu1

©️ Application components …… → Menu1

App components == building blocks of an Android app

4️⃣ types:

Activities — A single screen with a user interface

Services — Keeping an app running in the background; No UI.

Broadcast receivers — Deliver events to the app outside of a regular user flow.

Content providers — Manages a shared set of app data that you can store in the 1️⃣ file system, 2️⃣SQLite database, 3️⃣Web, 4️⃣ persistent storage location.

💠 Android Framework …… → Menu1

The Android framework is the set of APIs that allow developers to 🏃🏼quickly and 👍easily write apps for android phones. It consists of tools for designing UIs like buttons, text fields, image panes, and system tools like intents (for starting other apps/activities or opening files), phone controls, media players, etc. Essentially an android app consists of Activities (programs that the user interacts with), services (programs that run in the background or provide some function to other apps), and broadcast receivers (programs that catch information important to your app).

🏭Android Architecture …… → Menu1

Application framework.
Binder IPC. The Binder Inter-Process Communication (IPC) mechanism allows the application framework to cross process boundaries and call into the Android system services code.
System services. System services are modular, focused components such as 🔹Window Manager, 🔹Search Service, or 🔹Notification Manager.
Hardware abstraction layer: A HAL defines a standard interface for hardware vendors to implement, which enables Android to be agnostic about lower-level driver implementations. Using a HAL allows you to implement functionality ⛔️without affecting or modifying the higher-level system.
Linux kernel. Developing your device drivers is similar to developing a typical Linux device driver. Additions: ➕Low Memory Killer ➕Wake locks ➕Binder IPC driver

🗼Android Architecture Components …… → Menu1

Architecture Components are a collection of libraries included in Jetpack, based primarily on the MVVM pattern.

Data Binding — It replaces findViewById with binding classes.

LiveData — It is an observable class like Rx with lifecycle-aware. Updates data in onStart() / onResume().

Room — It is a persistence library built over SQLite.

ViewModel —

Navigation Component —It is a framework that controls navigation within an Android app. It manages the back stack. It also handles functions with different controls, such as the app bar or drawers.

🚀Android Jetpack …… → Menu1

Jetpack is a suite of libraries released by Android in 2018. Jetpack’s libraries provide code that works across the many Android devices currently in use. This makes it easy for developers to adhere to best practices!

❓ Simple Q: Context, Activity, Fragment …… → Menu1

Context …… → Menu1

  • The current ⭕️ state of the application
  • Get ❗️ information regarding the activity and application
  • Get 🔑 access to resources, databases, and shared preferences, and etc.
  • the Activity and Application classes ⬇️extend the Context class

Activity…… → Menu1

  • In C/C++, the program starts at main(). The Android system initiates code in an Activity instance by invoking specific callback methods that correspond to specific stages of its lifecycle.

Please jump to 💱 App Lifecycle

Fragment…… → Menu1

A Fragment represents a reusable portion of your app's UI. A fragment defines and manages its 1️⃣ own layout, has its 2️⃣ own lifecycle, and can handle its 3️⃣ own input events. Fragments cannot ⛔️ live on their own--they must be hosted by ✔️an activity or ✔️another fragment.

Please jump to 📰Fragment Lifecycle

👁‍🗨 ViewModel …… → Menu1

It is a class that stores the data from your UI. Because it’s aware of the app’s lifecycle, ViewModel is able to persist UI data through configuration changes, such as screen rotations. The ViewModel remains in memory until the Lifecycle it's scoped to goes away permanently: in the case of an activity, when it finishes, while in the case of a fragment, when it's detached.

Share data between activity and fragment: activityViewModels()

Benefit:

  • The activity does not need to do anything, or know anything about this communication.
  • Fragments don’t need to know about each other besides the SharedViewModel contract. If one of the fragments disappears, the other one keeps working as usual.
  • Each fragment has its own lifecycle, and is not affected by the lifecycle of the other one. If one fragment replaces the other one, the UI continues to work without any problems.

♐️ Project Structure …… → Menu1

project == workspace for an app

Modules — a collection of source files and build settings

Android app module: 1️⃣Phone & Tablet Module, 2️⃣Wear OS Module, 3️⃣Android TV Module, 4️⃣Glass Module

Feature module — a modularized feature of your app

Library module — Android Lib+Java Lib

Google Cloud module Provides a container for your Google Cloud backend code

©️ Components → New Android project …… → Menu1

Required:
✔️ manifest: It contains an XML file.
✔️ build/: It contains build output.
✔️ src/: It contains the code and resource files.
✔️res/: It contains bitmap images, UI Strings &. XML Layout, i.e. all non-code resources.
✔️assets/: It contains a file that should be compiled into a .apk file.

🔧 Android SDK …… → Menu1

“Android SDK” is a set of tools that are used for developing or writing apps.

🚩Visual layout editor, 🚩APK Analyzer, 🚩Fast emulator, 🚩Intelligent code editor, 🚩Flexible build system, 🚩Realtime profilers

compileSdkVersion

→ What version of the Android SDK to compile your app with.

minSdkVersion

→ Lower bound for your app

targetSdkVersion

→ Provides forward compatibility

🆙 App Launched …… → Menu1

startActivity with an intent:

Intent init = new Intent( this, MainActivity.class );
init.putExtra( Put whatever you want to put );
init.setFlags( Intent.FLAG_ACTIVITY_CLEAR_TOP );
startActivity( init );

🏁 Android Flag of Activity …… → Menu1

FLAG_ACTIVITY_NEW_TASK → An already running activity? Yes, deny and the current task will be brought to the front / No, create a new activity.

FLAG_ACTIVITY_NO_ANIMATION → no transition animation to go to the next activity state

FLAG_ACTIVITY_NO_HISTORY → The new activity is not kept in the history stack

FLAG_ACTIVITY_NO_USER_ACTION → It will prevent the normal Activity.onUserLeaveHint() callback from occurring on the current frontmost activity before it is paused as the newly-started activity is brought to the front.

FLAG_ACTIVITY_PREVIOUS_IS_TOP → Launch a new activity from an existing one.

FLAG_ACTIVITY_REQUIRE_DEFAULT → It will only launch the intent if it resolves to a single result.

FLAG_ACTIVITY_REQUIRE_NON_BROWSER → It will only launch the intent if it resolves to a result that is not a browser.

FLAG_ACTIVITY_SINGLE_TOP → The activity will not be launched if it is already running at the top of the history stack.

♌️ Intent …… → Menu1

Intents == MsgObj
You can use it to request an action from another app component.

Functions

startActivityActivity
broadcastIntent
→ any interested BroadcastReceiver
Context.startService(Intent)
or Context.bindService(Intent, ServiceConnection, int)Service

Structure

  • action: such as ACTION_VIEW, ACTION_EDIT, ACTION_MAIN, etc.
  • data: such as person record as Uri

action+data example:
ACTION_VIEW tel:123,
ACTION_EDIT content://contacts/people/1

  • Secondary attributes: category, type, component, extras

Resolution

Explicit Intents + specified a component → exact class to be run

Implicit Intents + NO specified a component → declare a general action to perform

⏳ PendingIntent …… → Menu1

Object < intent > and it specifies an action to be taken place in the future.

Cases: 1. AlarmManager; 2. Notification

Functions: getActivity(), getBroadcast(), getService()

Example:

  • AlarmManager
fun setAlarm(context: Context) {
val am = context.getSystemService(Context.ALARM_SERVICE) as
AlarmManager
val alarmIntent = Intent(context, Alarm::class.java)
val pending = PendingIntent.getBroadcast(
context, 0, alarmIntent, 0)
am.setRepeating(
AlarmManager.RTC_WAKEUP,
System.currentTimeMillis(),
(1000 * 60 * 10).toLong(),
pending
) // Millisec * Second * Minute
}
  • Notification
val pending = PendingIntent.getActivity(this,
0, intent, PendingIntent.FLAG_UPDATE_CURRENT)
builder = Notification.Builder(this,channelId)
.setContent(contentView)
.setSmallIcon(R.drawable.ic_launcher_background)
.setLargeIcon(
BitmapFactory.decodeResource(this.resources,
R.mipmap.ic_stat_assignment_returned))
.setContentIntent(pending)

📑 Android Manifest …… → Menu1

  1. Package name
  2. Components of the app, such as activities, fragments, and services
  3. Permissions needed from the user

Top-level activity —

“android.intent.action.MAIN” is the main activity for this application.

android.intent.category.LAUNCHER” should appear in the Launcher as a top-level application.

<intent-filter>
<action android:name=”android.intent.action.MAIN” />
<category android:name=”android.intent.category.LAUNCHER” />
</intent-filter>

♨️ Gradle Build System …… → Menu1

Android Studio → Gradle → building apps. Gradle files include dependencies and their versions.

🎢 Activities and Fragments …… → Menu1

An activity is a single screen + UI.
A fragment is a small piece of functionality → activity.

💱 App Lifecycle …… → Menu1

C s r P s d →

  • onCreate() — bind data+ViewModel; instantiate some class-scope variables; savedInstanceState; XML→ setContentView()
  • onStart() — makes the activity visible to the user
  • onResume() — Interacts with the user; stays until something takes focus away 🏃💨 the app.
  • onPause() — pause or adjust operations: 1️⃣Some event interrupts app execution; 2️⃣ X apps run in X-window mode — one focus == all other paused; 3️⃣ A new, semi-transparent activity (such as a dialog) opens==activity: partially visible → paused.
  • onStop() — Activity is no longer visible to the user: release or adjust resources / shutdown operations / save to database or persistent storage
  • onDestroy() — activity is destroyed: 1️⃣ finish(); 2️⃣ configuration change

📰Fragment Lifecycle …… → Menu1

Controlled by FragmentManager.

onCreated(): 4 sections

onCreated(): After onAttach()←It →onCreatedView(), system initials creation of a fragment.

onCreateView() — Optional: The fragment instantiates its user interface view.

onViewCreated(): Called before any saved state has been restored into the view.

onViewStateRestored(): Called when all saved state has been restored into the view hierarchy of the fragment.

onStart()

Called when the Fragment is visible👁‍🗨 to the user.

onResume()

Called when the fragment is visible👁‍🗨 to the user and actively running🏃🏼.

onPause()

Called when the Fragment is no longer resumed®️.

onStop()

Called when the Fragment is no longer started.

onSaveInstanceState()

Called to ask the fragment to save its current dynamic state, so it can later be reconstructed in a new instance if its process is restarted.

onDestroyView()

Called when the view previously created by onCreateView(LayoutInflater, ViewGroup, Bundle) has been detached from the fragment.

onDestroy()

Called when the fragment is no longer in use.

💢 Configuration Changes …… → Menu1

Restart the activity: 📳screen orientation, ⌨️keyboard availability and 💠multi-window mode. Must call onDestroy() → onCreate().

📣 Broadcast Receiver …… → Menu1

A broadcast receiver (receiver) is an Android component that allows you to register for system or application events. Android apps can send or receive broadcast messages from the Android system and other Android apps.

Register Receiver

Android Manifest:

<receiver android:name=".MyBroadcastReceiver"  android:exported="true">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED"/>
<action android:name="android.intent.action.INPUT_METHOD_CHANGED" />
</intent-filter>
</receiver>

Code:

private const val TAG = "MyBroadcastReceiver"

class MyBroadcastReceiver : BroadcastReceiver() {

override fun onReceive(context: Context, intent: Intent) {
StringBuilder().apply {
append("Action: ${intent.action}\n")
append("URI: ${intent.toUri(Intent.URI_INTENT_SCHEME)}\n")
toString().also { log ->
Log.d(TAG, log)
Toast.makeText(context, log, Toast.LENGTH_LONG).show()
}
}
}
}
...val br: BroadcastReceiver = MyBroadcastReceiver()...val filter = IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION)
.apply {
addAction(Intent.ACTION_AIRPLANE_MODE_CHANGED)
}
registerReceiver(br, filter)

Receiving + permission

<receiver android:name=".MyBroadcastReceiver"
android:permission="android.permission.SEND_SMS">
<intent-filter>
<action android:name="android.intent.action.AIRPLANE_MODE"/>
</intent-filter>
</receiver>

or

var filter = IntentFilter(Intent.ACTION_AIRPLANE_MODE_CHANGED)
registerReceiver(
receiver, filter, Manifest.permission.SEND_SMS, null )

Unregister Receiver

unregisterReceiver (BroadcastReceiver receiver)

onPause() or onDestory()

Send Boardcast

Intent().also { intent ->
intent.setAction("com.example.broadcast.MY_NOTIFICATION")
intent.putExtra("data", "Nothing to see here, move along.")
sendBroadcast(intent)
}

Send with permission

sendBroadcast(
Intent("com.example.NOTIFY"), Manifest.permission.SEND_SMS)

📝2. Design and View …… → Menu2

🏺 What Is Material Design?…… → Menu2

Building blocks → UI components

📲 Different Screen Sizes …… → Menu2

  1. Use view dimensions.
  2. Create several layouts, depending on the screen.
  3. Provide your images and resources as bitmaps.

🖌 Layouts in Android …… → Menu2

Layouts define the structure of the UI of an app: View + ViewGroup, arranged hierarchically.

🔗 ConstraintLayout …… → Menu2

ConstraintLayout allows you to create large and complex layouts with a flat view hierarchy.

ConstraintLayout vs RelativeLayout vs LinearLayout …… → Menu2

Following are the differences/advantages:

  1. 2 x Power: RelativeLayout + Linear layout: Set relative positions of views ( like Relative layout ) and also set weights for dynamic UI (which was only possible in Linear Layout).
  2. Chain elements to form another group of views.
  3. Apply horizontal and vertical bias which is nothing but the percentage of displacement from the center.
  4. Provides the functionality to handle the GONE views so that layouts do not break if some view is set to GONE through java code.
  5. Automatic constraint applying by the use of Blue print and Visual Editor tool which makes it easy to design a page.
  6. The view hierarchy == improves performance / dynamic UI == adapt to different screen sizes and densities.

🎑 MotionLayout …… → Menu2

MotionLayout is a layout type that helps you manage motion and widget animation in your app. MotionLayout is a subclass of 📌ConstraintLayout and builds upon its rich layout capabilities.

♻️ RecyclerView …… → Menu2

A widget to display a list of elements that don’t fit on one screen and require scrolling.

RecyclerAdapter: It tells RV what to display in each ViewHolder.

🖱️ RecyclerView Click Listener …… → Menu2

Live Template:

interface $class$ClickListener {
fun $fun$_item_click($var$: $type$)
}

Adapter:

class $revItem$Adapter(
private val dataSet: List<$dataType$>,
private val $tapVar$ClickListener: $ClickListener$
): RecyclerView.Adapter<$revItem$Adapter.ViewHolder>() {
//region view binding
class ViewHolder(
val binding: $viewBinding$
): RecyclerView.ViewHolder(binding.root)
//endregion view binding
override fun onCreateViewHolder(
parent: ViewGroup, viewType: Int
): ViewHolder =
ViewHolder(
$viewBinding$.inflate(
LayoutInflater.from(parent.context),
parent,
false
)
)
// Replace the contents of a view
override fun onBindViewHolder(
holder: ViewHolder, position: Int
) {
//val schoolName = holder.binding.schoolName
//schoolName.text = dataSet[position].schoolName
holder.binding.$tapViewItem$.setOnClickListener {
$tapVar$ClickListener.$click_fun$(dataSet[position])
}
}
// Return the size of your dataset
override fun getItemCount() = dataSet.size
}

♻️RecyclerView Vs. 📜 ListView …… → Menu2

1️⃣. ViewHolder(findViewById()) → onCreateViewHolder() + onBindViewHolder(); L: inefficient scrolling;
2️⃣. LayoutManager: layouting row views — LinearLayoutManager / GridLayoutManager;
3️⃣. R-ItemAnimator: adding or removing particular views
4️⃣. R-ItemDecoration vs L-rows decorations
5️⃣. Notifying adapter: R-notifyDataSetChanged() / otifyItemInserted() / notifyItemRemoved() / notifyItemChanged(); L-notifyDataSetChanged()
6️⃣. R-DiffUtil.Callback: update data in difference.

 fun renderItems(newItems: List){
val diffResult: DiffUtil.DiffResult =
DiffUtil.calculateDiff(
DiffUtilCallback(oldItems, newItems))
diffResult.dispatchUpdatesTo(this)
}

🀄️ Localization …… → Menu2

Localization is the ability of an app to support multiple languages, time zones, currencies, number formats, etc.

🌐 Webview …… → Menu2

Def: Deliver a web application (or just a web page) as a part of a client application.

val myWebView: WebView = findViewById(R.id.webview)
myWebView.loadUrl("http://www.example.com")

🔑Permission:

“android.permission.INTERNET

Hierarchy:

kotlin.Any
android.view.View
android.view.ViewGroup
android.widget.AbsoluteLayout
android.webkit.WebView

Zoom Support

webView.settings.setSupportZoom(true)

Enabling JavaScript

myWebView.settings.javaScriptEnabled = true

Create MyAndroid Interface for Javascript

val webView: WebView = findViewById(R.id.webview)
webView.addJavascriptInterface(WebAppInterface(this), "MyAndroid")

Replace Javascript way with Android way

@JavascriptInterface
fun showToast(toast: String) {
Toast.makeText(mContext, toast, Toast.LENGTH_SHORT).show()
}

HTML: MyAndroid Interface + showToast()

<script type="text/javascript">
function showAndroidToast(toast) {
MyAndroid.showToast(toast);
}
</script>

📌 URL →Activity …… → Menu2

myWebView.webViewClient = WebViewClient()

class WebToActivity : WebViewClient()

private class WebToActivity : WebViewClient() {

override fun shouldOverrideUrlLoading(
view: WebView?,
url: String?
): Boolean {
val targetUrl = "www.my_package_my_link.com"

if (targetUrl.equals(url) {
// Implicit call
Intent(Intent.ACTION_VIEW, Uri.parse(url)).apply {
startActivity(this)
}
} else {
view.loadUrl(url)
}
return true
}
}

Internal Html file into WebView

Everything will be put in the asset folder.

webview.loadUrl("file:///android_asset/web/city.html")

💐 ViewBinding …… → Menu2

Build.Gradle — Module:

buildFeatures {
viewBinding true
}

Generate Biding class:

type: ActivityName(Ex: ActivityBlur)+Binding

private lateinit var binding: ActivityBlurBinding

onCreate():

binding = ActivityBlurBinding.inflate(layoutInflater)
setContentView(binding.root)

ActivityBlurBinding looks for acitivity_blur.xml automatically.

Fragment, ListView, or RecyclerView adapter:

// view binding
private var _binding: FragmentImagePickBinding? = null
private val binding get
() = _binding!!
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// view binding
_binding = FragmentImagePickBinding.inflate(
inflater, container, false)
val root = binding.root

return root
}

♻️ RecyclerViewAadapter …… → Menu2

Live Template:

class $NAME$Adapter(private val dataSet: ArrayList<String>) :
RecyclerView.Adapter<$NAME$Adapter.ViewHolder>() {

inner class ViewHolder(val binding: $NAME$Binding) :
RecyclerView.ViewHolder(binding.root)

override fun onCreateViewHolder(
parent: ViewGroup, viewType: Int
): ViewHolder =
ViewHolder(
$NAME$Binding.inflate(
LayoutInflater.from(parent.context),
parent,
false
)
)

// Replace the contents of a view
override fun onBindViewHolder(
holder: ViewHolder, position: Int
) {
holder.binding.EXAMPLE.text = dataSet[position]
}

// Return the size of your dataset
override fun getItemCount() = dataSet.size

}

🍛 Databinding …… → Menu2

Build.Gradle — Module:

buildFeatures {
dataBinding true
}

Views with IDs

Declare variables: name + type → classpath

<layout ...>
<data>
<import type="android.graphics.drawable.Drawable"/>
<variable name="user" type="com.example.User"/>
<variable name="image" type="Drawable"/>
<variable name="note" type="String"/>
</data>

Custom binding class names

This will generate a binding class name as ContactItem.

<data class="ContactItem"> 

Different name: with “.”

<data class=".ContactItem">

One-way binding: @{…}

android:text="@{user.firstName}"

🍡 Binding adapters …… → Menu2

Binding adapters are responsible for making the appropriate framework calls to set values.

Specify a custom method name

@BindingMethods(value = [
BindingMethod(
type = android.widget.ImageView::class,
attribute = "android:tint",
method = "setImageTintList")])

Receive multiple attributes

@BindingAdapter("imageUrl", "error") // XML
fun loadImage(view: ImageView, url: String, error: Drawable) {
Picasso.get().load(url).error(error).into(view)
}

XML:

<ImageView 
app:imageUrl="@{venue.imageUrl}"
app:error="@{@drawable/venueError}" />

Object conversions

XML:

<View
android:background="@{isError ? @color/red : @color/white}"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>

fun:

@BindingConversion
fun convertColorToDrawable(color: Int) = ColorDrawable(color)

⛧ XML Design …… → Menu2

🌈Gradient …… → Menu2

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<gradient android:startColor="@color/dark_red_200"
android:endColor="#eb503c"
android:angle="360"
/>
</shape>

startColor + endColor

angle: 0 = 360, no change → left: start → right: end
90, → up:end → down: start
180, → left: end → right: start
270, → up: start → down: end

🚄 Round Corner …… → Menu2

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:tools="http://schemas.android.com/tools"
xmlns:android="http://schemas.android.com/apk/res/android"
tools:ignore="MissingDefaultResource"
>

<solid android:color="@color/white" />
<corners android:topLeftRadius="70dp"
android:topRightRadius="70dp"
android:bottomLeftRadius="70dp"
android:bottomRightRadius="70dp"
/>

</shape>

Use radius to bent the corners.

💣 3. Test…… → Menu3

📁 test &. androidTest …… → Menu3

test → local unit test for JVM

androidTest → tests run on the devices, such as integration tests and end-to-end tests → Instrument Test + UI Test

💊 Unit Tests …… → Menu3

It does not run on a device &. don’t have access to any Android framework library.

JUnit is the standard java library for testing, which is usually coupled with AndroidX Test.

Mockito is another popular open-source testing framework.

💉 Test-Driven Development …… → Menu3

Benefits: 1️⃣Faster development time, 2️⃣Automatic, up-to-date documentation, 3️⃣Greater confidence in your code, 4️⃣Higher test coverage

Steps: 1️⃣Add a test. 2️⃣Run it and watch it fail. 3️⃣Write the code to make the test pass. 4️⃣Run the tests and see them pass. 5️⃣Do any refactoring

📱 Instrumentation Tests …… → Menu3

They are run on the device, you have access to the Android device libraries.

UI Tests → It simulates a user’s interactions with your UI.

🕷 Mockito …… → Menu3

Mocking → Test the functionality of a class in isolation
……⛔️ database connection
……⛔️ properties file read
……⛔️ file server.
Dummy input → Mock objects ( mocking of the real service ) → Dummy data

Mockito = Mocking Framework → Mock objects seamlessly

Benefits:

✔️No need to write mock objects on your own
✔️Refactoring Safe: no break o test code
✔️Return value support
✔️Exception support
✔️Supports check on the order of method calls
✔️Annotation support

Limitations of Mockito:

❌ Constructors, private methods, and final classes
❌ Static methods except for Mockito 3.4.0+Junit-5
MockedStatic<Type>

Runner:

@RunWith(MockitoJUnitRunner.class) public class MockitoAnnotationTest {

⏱ Coroutines Test …… → Menu3

Rule for single Task →

Gradle:

def arch_version = "2.1.0"
// for InstantTaskExecutorRule()
testImplementation "androidx.arch.core:core-testing:$arch_version"
androidTestImplementation "androidx.arch.core:core-testing:$arch_version"

Rule:

@get:Rule
var instantTaskExecutorRule = InstantTaskExecutorRule()

Coroutines Test →

runBlocking():

@Test
fun insertTest() = runBlocking {

runBlockingTest(): Time-controlled test

@Test
fun test2() = runBlockingTest {

ⓚ 4. Kotlin …… → Menu4

😎 Visibility Modifiers …… → Menu4

There are four visibility modifiers in Kotlin: private, protected, internal and public. The default visibility is public.

private → visible in the class

protected → visible in the subclass

internal → visible in the same module and its member → classes

public → any client

Functions, properties and classes, objects and interfaces → Package

Constructors → public, can be set as private

🤔 Null Safety …… → Menu4

Kotlin has tried to eliminate NPE. In Kotlin, all variables are non-nullable by default. It can’t assign a null value to a variable.

We have to add (?) to a nullable variable.

🎎 Kotlin Vs Java …… → Menu4

Benefits:

1️⃣ Faster to compile, lightweight
2️⃣ Less code than Java == less bug
3️⃣ Can be executed in the JVM, so Java can run in Kotlin project.
4️⃣ Kotlin script can be used to configure projects.
5️⃣ Safe against NullPointerException with the safe operator(?)

Java 
if (text != null) {
int length = text.length();
}

Kotlin
text?.let {
val length = text.length
}
// or simply
val length = text?.length

6️⃣ Kotlin incorporates coroutines, Javascript for web development.

🍎 Basic …… → Menu4

String templates

println("sum of $a and $b is ${a + b}")

Conditional expressions

fun maxOf(a: Int, b: Int) = if (a > b) a else b

for loop & whil loop

for (item in items) {}
for (index in items.indices) {}
while (index < items.size) {}

when

when (obj) {
1 -> "One"
...
else -> "Unknown"
}

Range

if (x in 1..y+1) {}
if (-1 !in 0..list.lastIndex) {}
for (x in 1..10 step 2) {}
for (x in 9 downTo 0 step 3) {}

Collections

fruits
.filter { it.startsWith("a") }
.sortedBy { it }
.map { it.toUpperCase() }
.forEach { println(it) }

⚖ Comparable interface …… → Menu4

The Comparable interface provides a compareTo() function to compare two objects.

compareTo( k ):0(it==k), -1(it<k), +1(it>k)
coerceAtLeast( min ): →
(it > min) it else min
coerceAtMost( max ): → (it < max) it else max
coerceIn(min, max): (it in min..max) it else min(it<min) or max(it>max)

    override fun compareTo(other: MyDate) = when {
year != other.year -> year - other.year
month != other.month -> month - other.month
else -> dayOfMonth - other.dayOfMonth
}

🚻 Iterable

class DateRange(val start: MyDate, val end: MyDate) : Iterable<MyDate> {
override fun iterator(): Iterator<MyDate> {
return object : Iterator<MyDate> {
var current: MyDate = start
override fun next(): MyDate {
if (!hasNext()) throw NoSuchElementException()
val result = current
current = current.followingDate()
return result
}
override fun hasNext(): Boolean = current <= end
}
}
}

🐼 Simple Problems …… → Menu4

👯 Pair Sum …… → Menu4

fun findPair(arr: IntArray, n: Int, sum: Int) {
// consider each element except the last
for (i in 0 until n - 1) {
for (j in i + 1 until n) {
// if the desired sum is found, print it
if (arr[i] + arr[j] == sum) {
arr[i], arr[j]);
return
}
}
}

// we reach here if the pair is not found
println("Pair not found")
}

🐹 Unique Array …… → Menu4

val mArr = arrayOf<Int>(1,1,1,1,1,2,5,5,5,5,6,6,6,6,7,7,7,7,3,3,3,9,9,8,8,4,10,10,10)
val someSet = mutableSetOf<Int>()

for (item in mArr)
someSet.add(item)

println("$someSet")

🅰️ Palindrome …… → Menu4

Number Type:

//Function to check Palindrome Number
fun isPalindrome(number: Int): Boolean {
var isPalindromeNumber = false
var sum = 0
var tempNum = number

while (tempNum > 0) {
val r = tempNum % 10
sum = sum * 10 + r
tempNum /= 10
}
if (sum == number) {
isPalindromeNumber = true
}
return isPalindromeNumber
}

//Main Function, Entry Point of Program
fun main(arg: Array<String>) {
val sc = Scanner(System.`in`)

// Input Number
println("Enter Number : ")
val num: Int = sc.nextInt()

//Call Function to check Number
if (isPalindrome(num))
println("$num is a Palindrome Number")
else
println("$num is not a Palindrome Number")
}

String Type:

//function to check string is palindrome or not
fun isPalindromeString(inputStr: String): Boolean {
val sb = StringBuilder(inputStr)

//reverse the string
val reverseStr = sb.reverse().toString()

//compare reversed string with input string
return inputStr.equals(reverseStr, ignoreCase = true)
}

//Main function, Entry Point of Program
fun main(args: Array<String>) {
//Input Stream
val sc = Scanner(System.`in`)

//Input String Value
println("Enter String : ")
val inString: String = sc.nextLine()

//Call function to check String
if (isPalindromeString(inString)) {
println("$inString is a Palindrome String")
} else {
println("$inString is not a Palindrome String")
}
}

🃏 Binary Search …… → Menu4

object BinarySearch {
fun search(list: List<Int>, item: Int) = list
.binarySearch(item)
.let {
if (it < 0)
throw NoSuchElementException("Item cannot be found")
else it }
}

⛓ Linked List: L←push; L →pop; …… → Menu4

class Deque<T> {
private val nodeList = mutableListOf<T>()

fun push(value: T) { // insert value at back
nodeList.add(value)
}

fun pop(): T? { // remove value at back
val value = nodeList.last()
nodeList.removeAt(nodeList.lastIndex)
return value
}

fun shift(value: T) { // insert value at front
nodeList.add(0, value)
}

fun unshift(): T? { // remove value at front
val value = nodeList.first()
nodeList.removeAt(0)
return value
}
}

🎄 Binary Search Tree …… → Menu4

class Circle(val radius: Int): Comparable<Circle> { 

override fun compareTo(other: Circle): Int =
when {
radius == other.radius -> 0
radius < other.radius -> -1
else -> 1
}

override fun toString(): String = "Circle($radius)"
}
class BinarySearchTree<T : Comparable<T>> { data class Node<T: Comparable<T>>(
var data: T,
var left: Node<T>? = null,
var right: Node<T>? = null
) {

override fun toString(): String =
"\tdata($data),\n<-($left),\t->($right)\n"

fun insert(value: T) {
if (value <= data) {
left?.insert(value) ?: run { left = Node(value) }
} else {
right?.insert(value) ?: run { right = Node(value) }
}
}

fun contains(value: T): Boolean =
when {
value < data && left != null ->
left!!.contains(value)
value > data && right!=null ->
right!!.contains(value)
else ->
value.compareTo(data) == 0
}


val minNode: Node<T>?
get() = left?.minNode ?: this

fun toList(): List<T> = listOfNotNull(
left?.toList(),
listOf(data),
right?.toList()).flatten()

}
var root: Node<T>? = null

override fun toString() = root?.toString() ?: "empty tree"
fun insert(value: T) {
root?.apply { insert(value) } ?:
run { root = Node(value) }
}

fun contains(value: T): Boolean {
var found = false;
root?.apply {
found = contains(value)
}
return found
}

fun remove(value: T): Boolean {
root = remove(value, root)
return !contains(value)
}

fun remove(value: T, parent: Node<T>?): Node<T>? {
parent ?: return null

when {
parent.data.compareTo(value) == 0 -> {
when {
// no left child
parent.left == null -> {
return parent.right
}
// no right child
parent.right == null -> {
return parent.left
}
}
// two children
// val rightMin = parent.right?.minNode!!
// println("Right minimum: ${rightMin.data}")
// // move value of rightMin up
parent.right?.minNode!!.data?.let {
parent.data = it
}
// remove rightMin
parent.right = remove(parent.data, parent.right)
}
// less to left
value < parent.data ->
parent.left = remove(value, parent.left)
// most to right
else -> parent.right = remove(value, parent.right)
}
return parent
}

fun asSortedList(): List<T> = root?.toList() ?: emptyList()

fun asLevelOrderList(): List<T> {
val output = mutableListOf<T>()
val queue = mutableListOf(root)
while (queue.isNotEmpty()) {
val node = queue.removeAt(0)!!
output.add(node.data)
if (node.left != null) { queue.add(node.left) }
if (node.right != null) { queue.add(node.right) }
}
return output
}
}fun main(){

val cirs: List<Circle> = listOf(
Circle(8), Circle(10), Circle(9),
Circle(2), Circle(1), Circle(5),
Circle(4), Circle(6), Circle(7),
Circle(3)
)

val bt: BinarySearchTree<Circle> = BinarySearchTree<Circle>()
for (c in cirs) { bt.insert(c) }
// bt.insert(Circle(8))
// bt.insert(Circle(7))

// println("BST: $bt")
println("Removed circle(12): ${bt.remove(Circle(12))}")
// println("Removed circle(3): ${bt.remove(Circle(3))}")
// println("Removed circle(10): ${bt.remove(Circle(10))}")
// println("BST: $bt")
val mlist = bt.asSortedList()
println("sorted list: ${mlist}")

val revlist = mlist.asReversed()
println("Reversed Order list: ${revlist}")



// println("Search for Circle(8): ${bt.contains(Circle(8))}")


// println("Is cir1 > cir2? ${cirs[0] > cirs[1]}")
// println("Is cir3 > cir1? ${cirs[2] > cirs[1]}")
// println("Is cir3 == cir1? ${cirs[2] == cirs[1]}")

// var min = Circle(2)
// var max = Circle(9)

// println("\nmin: ${min}\ncir2: ${cirs[1]}")
// println("Find bigger: cir1 > min? Bigger is ${cirs[1].coerceAtLeast(min)}")

// println("\ncir1: ${cirs[0]}\ncir2: ${cirs[1]}")
// println("Find bigger: cir1 > cir2? Bigger is ${cirs[0].coerceAtLeast(cirs[1])}")
// println("Find bigger: cir2 > cir1? Bigger is ${cirs[1].coerceAtLeast(cirs[0])}")

// println("\ncir5: ${cirs[4]}\nmax: ${max}")
// println("Find bigger: cir5 > max? Bigger is ${cirs[4].coerceAtMost(max)}")
// println("Find bigger: cir5 > max? Bigger is ${max.coerceAtMost(cirs[4])}")

// min = Circle(6)
// println("\ncir6: ${cirs[5]}\nmin: ${min}\nmax: ${max}")
// println("Is cir6 in min and max? ${cirs[5].coerceIn(min, max)}")
}

🔄 Coroutines …… → Menu4

Coroutines are pieces of code that convert asynchronous callbacks for long-running tasks into sequential code.

fun main() {
GlobalScope.launch { // bkg coroutine
delay(1000L) // non-blocking delay for 1 second
println("World!") // print after delay
}
println("Hello,") // main thread continues
Thread.sleep(2000L) // block main thread for 2 seconds
}

Light-weight:

fun main() = runBlocking {
repeat(100_000) { // launch a lot of coroutines
launch {
delay(5000L)
print(".")
}
}
}

It launches 100K coroutines and, after 5 seconds, each coroutine prints a dot. Thread will produce some sort of out-of-memory error.

🛐 RunningBlock …… → Menu4

The main thread invoking runBlocking blocks until the coroutine inside runBlocking completes.

runBlocking { // but this expression blocks the main thread
delay(2000L) // … while we delay for 2 seconds to keep JVM alive
}

🛠 Job …… → Menu4

Let’s explicitly wait (in a non-blocking way) until the background Job that we have launched is complete.

val job = GlobalScope.launch { // launch a new coroutine
delay(1000L)
println("World!")
}
println("Hello,")
job.join() // wait until child coroutine completes

🔭 coroutineScope …… → Menu4

runBlocking Vs coroutineScope :

Similar → They both wait for their body and all its children to complete.

Different → runBlocking blocks the current thread for waiting. coroutineScope just suspends, releasing the underlying thread for other usages

fun main() = runBlocking { // this: CoroutineScope
launch {
delay(200L)
2️⃣println("Task from runBlocking")
}

coroutineScope { // Creates a coroutine scope
launch {
delay(500L)
3️⃣println("Task from nested launch")
}

delay(100L)
1️⃣println("Task from coroutine scope")
}

4️⃣println("Coroutine scope is over")
}

🔜 Suspend fun …… → Menu4

Suspending functions can be used inside coroutines just like regular functions, but their additional feature is that they can, in turn, use other suspending functions (like delay in this example) to suspend execution of a coroutine.

fun main() = runBlocking {
launch { doWorld() 1️⃣}
println("Hello,")0️⃣
}
// this is your first suspending function
suspend fun doWorld() {
delay(1000L)
println("World!")1️⃣
}

❌ Cancellation and timeouts …… → Menu4

  • Repeat is cancellable.
val job = launch {
repeat(1000) { i ->
println("job: I'm sleeping $i ...")0️⃣ 1️⃣ 2️⃣
delay(500L)
}
}
delay(1300L) // delay a bit
println("main: I'm tired of waiting!") 3️⃣
job.cancel() // cancels the job
job.join() // waits for job's completion
println("main: Now I can quit.") 4️⃣
  • Cancellation is cooperative → computation do not check for cancellation.
val startTime = System.currentTimeMillis()
val job = launch(Dispatchers.Default) {
var nextPrintTime = startTime
var i = 0
while (i < 5) { // computation loop, just wastes CPU
// print a message twice a second
if (System.currentTimeMillis() >= nextPrintTime) {
println("job: I'm sleeping ${i++} ...") 0️⃣ 1️⃣ 2️⃣ 4️⃣ 5️⃣
nextPrintTime += 500L
}
}
}
delay(1300L) // delay a bit
println("main: I'm tired of waiting!") 3️⃣
job.cancelAndJoin() // cancels the job and waits for its completion
println("main: Now I can quit.") 6️⃣
  • Making computation code cancellable: isActive
val startTime = System.currentTimeMillis()
val job = launch(Dispatchers.Default) {
var nextPrintTime = startTime
var i = 0
while (isActive) { // cancellable computation loop
// print a message twice a second
if (System.currentTimeMillis() >= nextPrintTime) {
println("job: I'm sleeping ${i++} ...")0️⃣ 1️⃣ 2️⃣
nextPrintTime += 500L
}
}
}
delay(1300L) // delay a bit
println("main: I'm tired of waiting!")3️⃣
job.cancelAndJoin() // cancels the job and waits for its completion
println("main: Now I can quit.")4️⃣
  • Closing resources with finally
val job = launch {
try {
repeat(1000) { i ->
println("job: I'm sleeping $i ...")0️⃣ 1️⃣ 2️⃣
delay(500L)
}
} finally {
println("job: I'm running finally")
4️⃣
}

}
delay(1300L) // delay a bit
println("main: I'm tired of waiting!")3️⃣
job.cancelAndJoin() // cancels the job and waits for its completion
println("main: Now I can quit.")5️⃣

Timeout: with Timeout Exception

withTimeout(1300L) {
repeat(1000) { i ->
println("I'm sleeping $i ...")
delay(500L)
}
}

Without Exception:

val result = withTimeoutOrNull(1300L) {
repeat(1000) { i ->
println("I'm sleeping $i ...")
delay(500L)
}
"Done" // will get cancelled before it produces this result
}
println("Result is $result")

🔀 Non-cancellable block …… → Menu4

Use withContext(NonCancellable) to continue running.

val job = launch {
try {
repeat(1000) { i ->
println("job: I'm sleeping $i ...")0️⃣ 1️⃣ 2️⃣
delay(500L)
}
} finally {
withContext(NonCancellable) {
println("job: I'm running finally")4️⃣
delay(1000L)
println("job: And I've just delayed for 1 sec because I'm non-cancellable")5️⃣
}
}
}
delay(1300L) // delay a bit
println("main: I'm tired of waiting!")3️⃣
job.cancelAndJoin() // cancels the job and waits for its completion
println("main: Now I can quit.")6️⃣

🔩 Higher Order Function …… → Menu4

Structure

The parameter in function header:

fun exeFun( funName: ( parameters ) -> returnType ) { ... }

Definition

A higher-order function is a function that takes another function as a parameter

// lambda expression 
var lambda = {a: Int , b: Int -> a + b }
// higher order function
fun higherfunc( lmbd: (Int, Int) -> Int) {

var result = lmbd(2,4) // invokes the lambda
println("The sum of two numbers is: $result")
}

fun main(args: Array<String>) {
higherfunc(lambda) //passing lambda as parameter
}

Returns Unit:

// regular function definition 
fun printMe(s:String): Unit{
println(s)
}
// higher-order function definition
fun higherfunc( str : String, myfunc: (String) -> Unit){
// invoke regular function using local name
myfunc(str)
}
fun main(args: Array<String>) {
// invoke higher-order function
higherfunc("Higher Order Example", ::printMe)
}

Returns an integer value:

// regular function definition 
fun add(a: Int, b: Int): Int{
var sum = a + b
return sum
}
// higher-order function definition
fun higherfunc( addfunc:(Int,Int)-> Int ) {
// invoke regular function using local name
var result = addfunc(3,6)
print("The sum of two numbers is: $result")
}
fun main(args: Array<String>) {
// invoke higher-order function
higherfunc(::add)
}

Returning another function:

// function declaration 
fun mul(a: Int, b: Int): Int{
return a*b
}
//higher-order function declaration
fun higherfunc() : ((Int,Int)-> Int){
return ::mul
}
fun main(args: Array<String>) {
val multiply = higherfunc()
// invokes the mul() function by passing arguments
val result = multiply(2,4)
println("The multiplication of two numbers is: $result")
}

❇️ Coroutines with ArchComps …… → Menu4

ViewModelScope: It’s is defined for each ViewModel.

viewModelScope.launch {
// Will be canceled when the ViewModel is cleared.
}

LifecycleScope is defined for each Lifecycle object.

class MyFragment: Fragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
viewLifecycleOwner.lifecycleScope.launch {
val params = TextViewCompat.
getTextMetricsParams(textView)
val precomputedText = withContext(Dispatchers.Default) {
PrecomputedTextCompat.create(
longTextContent, params)
}
TextViewCompat.setPrecomputedText(
textView, precomputedText)
}
}
}

Suspend Lifecycle-aware coroutines: lifecycle.whenCreated, lifecycle.whenStarted, and lifecycle.whenResumed. Any coroutine run inside these blocks is suspended if the Lifecycle isn't at least in the minimal desired state.

class MyFragment: Fragment {
init { // Safely launch in the constructor of the Fragment.
lifecycleScope.launch {
whenStarted {

Use coroutines with LiveData

val user: LiveData<User> = liveData {
val data = database.loadUser() // loadUser is a suspend fun
emit(data)
}

💾 5. Storage …→Menu5

📏 Type of Storage …… →Menu5

1️⃣App-specific storage: internal storage(sensitive information) ➕ external storage. ♻️Remove by uninstall.

  • From internal storage, getFilesDir() or getCacheDir()
  • From external storage, getExternalFilesDir() or getExternalCacheDir()

2️⃣Shared storage: Share with other apps, including media(img/aud/vdo), documents, and other files. Accessed by MediaStore API.

— Permission needed:

READ_EXTERNAL_STORAGE > Android 11 (API level 30)

READ_EXTERNAL_STORAGE or WRITE_EXTERNAL_STORAGE = Android 10 (API level 29)

Permissions are required < Android 9 (API level 28)

3️⃣Preferences: Store private, primitive data in 🔑key-value pairs(Jetpack Preferences).

4️⃣Databases: Store structured data in a private database using the Room persistence library.

🔰 SharedPreferences …… →Menu5

Preferences (not share) / SharedPreferences(shared by default)

The SharedPreferences API allows for reading and writing simple 🔑key-value pairs from a file that is saved across application sessions.

val sharedPreferences = PreferenceManager
.getDefaultSharedPreferences(this)
val name = sharedPreferences.getString("signature", "")

From file:

private val sharedPreferences = context
.getSharedPreferences(fileName, Context.MODE_PRIVATE)

get/set: ✔️String/Set<String>, ✔️Int/Long, ✔️Float, ✔️Boolean

// get
sharedPreferences.getString(key, "")!!
// set
with(sharedPreferences.edit()) {
putString(key, value)
apply()
}

Delete:

with(sharedPreferences.edit()) {
remove(key)
apply()
}

Clear:

with(sharedPreferences.edit()) {
clear()
apply()
}

Listeners: 1️⃣Preference.OnPreferenceChangeListener and 2️⃣SharedPreferences.OnSharedPreferenceChangeListener

override fun onSharedPreferenceChanged(
sharedPreferences: SharedPreferences, key: String) {
if (key == "signature") {
Log.i(TAG, "Preference value was updated to: " + sharedPreferences.getString(key, ""))
}
}

Register/Unregister:

preferenceManager.sharedPreferences.registerOnSharedPreferenceChangeListener(...)override fun onResume() {
super.onResume()
preferenceManager.sharedPreferences.
registerOnSharedPreferenceChangeListener(this)
}

override fun onPause() {
super.onPause()
preferenceManager.sharedPreferences.
unregisterOnSharedPreferenceChangeListener(this)
}

📦 Custom Data Store …… →Menu5

Data Store access 🔑key-value pairs file.

Custom:

// specific preference
val preference: Preference? = findPreference("key")
preference?.preferenceDataStore = dataStore
//To enable a custom data store for an entire hierarchyval preferenceManager = preferenceManager
preferenceManager.preferenceDataStore = dataStore

🏠 Room Database …… →Menu5

The Room persistence library provides an abstraction layer over SQLite to allow fluent database access while harnessing the full power of SQLite.

  • Compile-time verification of SQL queries.
  • Convenience annotations that minimize repetitive and error-prone boilerplate code.
  • Streamlined database migration paths.

Entity(similar with POJO):

@Entity
data class User(
@PrimaryKey val uid: Int,
@ColumnInfo(name = "first_name") val firstName: String?,
@ColumnInfo(name = "last_name") val lastName: String?
)

Data access object == DAO: Room← →RestApi

@Dao
interface UserDao {
@Query("SELECT * FROM user") 🔺SQLite Query
fun getAll(): List<User>

@Query("SELECT * FROM user WHERE uid IN (:userIds)")
fun loadAllByIds(userIds: IntArray): List<User>

@Query("SELECT * FROM user WHERE first_name LIKE :first AND " +
"last_name LIKE :last LIMIT 1")
fun findByName(first: String, last: String): User

@Insert 🔺SQLite Query
fun insertAll(vararg users: User)

@Delete 🔺SQLite Query
fun delete(user: User)
}
// Coroutines
@Dao
interface UserDao {
@Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun insertUsers(vararg users: User)

@Update
suspend fun updateUsers(vararg users: User)
// RxJava
@Dao
public interface UserDao {
@Insert(onConflict = OnConflictStrategy.REPLACE)
public Completable insertUsers(List<User> users);

@Update
public Completable updateUsers(List<User> users);

@Delete
public Completable deleteUsers(List<User> users);

@Query("SELECT * FROM user WHERE id = :id")
public Single<User> loadUserById(int id);

Declare Database:

@Database(entities = arrayOf(User::class), version = 1)
abstract class AppDatabase : RoomDatabase() {
abstract fun userDao(): UserDao
}

Usage — databaseBuilder()

val db = Room.databaseBuilder(
applicationContext,
YourDatabase::class.java, "Your-database-name"
).build()
//access
val userDao = db.userDao()
val users: List<User> = userDao.getAll()

View → Class

@DatabaseView("SELECT user.id, user.name, user.departmentId," +
"department.name AS departmentName FROM user " +
"INNER JOIN department ON user.departmentId = department.id")

data class UserDetail(
val id: Long,
val name: String?,
val departmentId: Long,
val departmentName: String?
)

👁‍🗨 View included Database:

@Database(entities = arrayOf(User::class),
views = arrayOf(UserDetail::class), version = 1)
abstract class AppDatabase : RoomDatabase() {
abstract fun userDao(): UserDao
}

🏧 Packed Database to Local Database:

// from asset
Room.databaseBuilder(appContext, YourDatabase.class, "YourFile.db")
.createFromAsset("database/myapp.db")
.build()
// from file
Room.databaseBuilder(appContext, YourDatabase.class, "YourFile.db")
.createFromFile(File("mypath"))
.build()

🚑 Migration: …… →Menu5

// Database class definition declaring version 3.
@Database(version = 3)✔️
abstract class YourDatabase : RoomDatabase() {
...
}

// Destructive migrations are enabled and a prepackaged database
// is provided.
Room.databaseBuilder(appContext, YourDatabase.class, "YourFile.db")
.createFromAsset("database/myapp.db")🏧
.fallbackToDestructiveMigration()⛔️
.build()

Add 🚑Migration:1️⃣update version2️⃣Migration SQL3️⃣update builder

// Database class definition declaring version 3.
@Database(version = 3)✔️
abstract class AppDatabase : RoomDatabase() {
...
}

// Migration path definition from version 2 to version 3.
val MIGRATION_2_3 = object : Migration(2, 3) {✔️
override fun migrate(database: SupportSQLiteDatabase) {
...
}
}

// A prepackaged database is provided.
Room.databaseBuilder(appContext, AppDatabase.class, "Sample.db")
.createFromAsset("database/myapp.db")
.addMigrations(MIGRATION_2_3)✔️
.build()

🌐 6. Cloud Service: RestApi + JSON …… → Menu6

📄 JSON …… → Menu6

JSON: JavaScript Object Notation → a syntax for storing and exchanging data.

// send
var myObj = {name: "John", age: 31, city: "New York"};
var myJSON = JSON.stringify(myObj);
window.location = "demo_json.php?x=" + myJSON;
// received
var myObj = JSON.parse(myJSON);
document.getElementById("demo").innerHTML = myObj.name;
// array
{
"name":"John",
"age":30,
"cars":[ "Ford", "BMW", "Fiat" ]🔸
}

Why use JSON?

Text only = Easily be sent to and from a server, and used as a data format by any programming language.

Data type: ◾️string ◾️number ◾️boolean ◾️null/empty ◾️object ◾️array

backslash-escaped characters: \" – Double quote, \\ – Backslash, \/ – Forward slash, \b – Backspace, \f – Form feed, \n – Newline, \r – Carriage return, \t – Tab, \u – Trailed by four hex digits

Number:

{
"number_1" : 210,
"number_2" : -210,
"number_3" : 21.05,
"number_4" : 1.0E+2
}

♋️ Retrofit …… → Menu6

Retrofit is a REST Client for Java and Android → Retrieve and upload JSON/GSON via a REST-API. Retrofit uses the 👌OkHttp library for HTTP requests.

1️⃣URL:

private const val BASE_URL = "https://RestService.com"

2️⃣Log with OKHttp:

val logger = HttpLoggingInterceptor()
logger.level = HttpLoggingInterceptor.Level.BODY
OkHttpClient.Builder()
.addInterceptor(logger)
.readTimeout(100, TimeUnit.SECONDS)
.connectTimeout(100, TimeUnit.SECONDS)
.build()

3️⃣Retrofit:

Retrofit = Retrofit.Builder()
.baseUrl(BaseURL)
.client(okHttpClient)
.addConverterFactory(GsonConverterFactory.create())
.build()

4️⃣API Service:

YourApiService = retrofit.create(YourApiService::class.java)

5️⃣YourApiService.kt → Http Requests

interface YourApiService {
@Headers(
You May Have Header
)
@GET(Http Query)
suspend fun getStory(@Path("type") type: String): Story
}

😎7. Server …… → Menu7

📄 Jenkins…… → Menu7

Jenkins — An java based automation server(Open Source) enables developers to reliably build, test, and deploy their software.

♨️ Spring Boot…… → Menu7

Spring Boot is a Java-based framework(Open Source) used to create a micro Service.

Spring Boot stand-alone and production-ready spring applications.

Micro Service → Each service running has its own process and this achieves the lightweight model to support business applications.

--

--

Homan Huang

Computer Science BS from SFSU. I studied and worked on Android system since 2017. If you are interesting in my past works, please go to my LinkedIn.