💼📝⚡️Mobile App Developer Interviews Q&A Cheat Sheet - An awesome curation of Q&A, code examples, resources - categorized based on expertise (Continuously Updating)
1 | 2 | 3 | 4 | 5 | 6 |
---|---|---|---|---|---|
Android Basics | Android Specifics: Native | Android Specifics: Cross-Platform | Android Intermediate | Android Advanced | Resources & Learning Guides |
There are four different types of app components:
To declare all app components, we use the following elements:
<activity>
elements for activities.<service>
elements for services.<receiver>
elements for broadcast receivers.<provider>
elements for content providers.An Intent is a messaging object you can use to request an action from another app component.
Use Cases: Starting an Activity or a Service, Delivering a broadcast.
A PendingIntent object is a wrapper around an Intent object. The primary purpose of a PendingIntent is to grant permission to a foreign application to use the contained Intent as if it were executed from your app's own process.
Use Cases:
This element is used in the Manifest file to declare the capabilities of an app component (i.e. an Activity)
Use Case: When we declare an Activity in the Manifest, we can include intent filters so it can respond to intents from other applications like the following:
<manifest>
...
<application>
<activity android:name="com.example.SendEmailActivity">
<intent-filter>
<action android:name="android.intent.action.SEND" />
<data android:type="*/*" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
</application>
</manifest>
Link to the resources with graphical explanation:
There are 4 visibility modifiers in Kotlin:
class EventViewModel internal constructor()
In the previous code snippet, internal makes class available to public, but constructor only to inside module
If you need a function or a property to be tied to a class rather than to instances of it, you can declare it inside a companion object. If you declare a companion object inside your class, you'll be able to call its members with the same syntax as calling static methods in Java/C#, using only the class name as a qualifier. It is similar to static
keyword in Java or @staticmethod
in Python.
companion object {
@Volatile private var instance: EventRepository? = null
fun getInstance(eventDao:EventDao) =
instance ?: synchronized(this) {
instance ?: EventRepository(eventDao).also { instance = it }
}
}
var a: String = "abc"
a = null
var b: String? = "abc"
b = null
print(b)
a =null
will produce a compilation errorb = null
will not produce any errorval a = "Kotlin"
val b: String? = null
println(b?.length) //prints null
println(a?.length) //prints 6
A quick Overview of some possible cases:
1 | 2 | 3 | 4 |
---|---|---|---|
a: String? | a.length | a?.length | a!!.length |
"cat" | Compile time error | 3 | 3 |
null | Compile time error | null | NullPointerException |
!! is an option for NPE-lovers. a!!.length
will return a non-null value of a.length or throw a NullPointerException if a is null. And a?.length
returns a.length if a is not null, and null otherwise:
val a: String? = null
print(a!!.length) // >>> NPE: trying to get length of null
val a: String? = null
print(a?.length) // >>> null is printed in the console
val list = mutableList ?: mutableListOf()
is a shorter form of
val list = if (mutableList != null) mutableList else mutableListOf()
The name,howver, comes from the famous American singer Elvis Presley. His hairstyle resembles a Question Mark Ref
1st Code Snippet:
return if (x) foo() else bar()
return when(x) {
0 -> "zero"
else -> "nonzero"
}
2nd Code Snippet:
if (x)
return foo()
else
return bar()
when(x) {
0 -> return "zero"
else -> return "nonzero"
}
The above is preferable to the latter one.
Out Initialization code can be placed in initializer blocks, which are prefixed with the init
keyword:
class InitOrderDemo(name: String) {
val firstProperty = "First property: $name".also(::println) //inits a val & also, prints the name
init {
println("First initializer block that prints ${name}")
println("Second initializer block that prints ${name.length}")
}
}
fun main() {
//both of the ways are correct!
test( { println(it) } )
test(::println)
}
fun test(block: (String) -> Unit ) {
block("okay")
}
@Volatile
before a field means that writes to this field are immediately made visible to other threads.
In Kotlin, there's no way to check the generic parameters at runtime in general case (like just checking the items of a List<T>
or here in this ViewModelFactory, modelClass: Class<T>
which is only a special case), so casting a generic type to another with different generic parameters will raise a warning, which needs to be suppressed
@Suppress("UNCHECKED_CAST")
override fun <T : ViewModel?> create(modelClass: Class<T>) = EventViewModel(eventRepository, lifecycleOwner) as T
val andResult = a and b
val orResult = a or b
val xorResult = a xor b
val rightShift = a shr 2
val leftShift = a shl 2
Generic parameters are a great way to provide type safety, but can be limiting when accessing class info type.
The reified
keyword allows get type of generic variable in inline function.
inline fun <reified T> printType() {
print(T::class.java)
}
fun printStringType() {
//calling with String type
printType<String>()
}
Some rules to follow:
More on Reified and Inline functions on Kotlin Vocabulary
flatMap()
merges two collections into a single onemap()
results in a list of listsflatten()
produces the same result as flatMap()
. So flatMap()
is a combination of the two functions: map()
and then flatten()
The following code example with commented output illustrates their use cases:
class Data(val items : List<String>)
val dataObjects = listOf(
Data(listOf("a", "b", "c")),
Data(listOf("1", "2", "3"))
)
val items: List<String> = dataObjects.flatMap { it.items } //[a, b, c, 1, 2, 3]
val items2: List<List<String>> = dataObjects.map { it.items } //[[a, b, c], [1, 2, 3]]
val nestedCollections: List<Int> = listOf(listOf(1,2,3), listOf(5,4,3)).flatten() //[1, 2, 3, 5, 4, 3]
final
No. In Java, a static variable is a class variable (for whole class). So if we have static local variable (a variable with scope limited to function), it violates the purpose of static. Hence compiler does not allow static local variable. So the following code throws a Error: Static local variables are not allowed
:
class Test {
public static void main(String args[]) {
System.out.println(fun());
}
static int fun()
{
static int x= 10; //throws an error here
return x--;
}
}
Using Java keyword transient
. This keyword, when applied to a field, tells the Java object serialization subsystem to exclude the field when serializing an instance of the class
class Test<T>
{
T obj;
Test(T obj) { this.obj = obj; }
public T getObject() { return this.obj; }
}
class Main
{
public static void main (String[] args)
{
Test <Integer> iObj = new Test<Integer>(15);
System.out.println(iObj.getObject());
Test <String> sObj = new Test<String>("GeeksForGeeks");
System.out.println(sObj.getObject());
}
}
equals()
method compares two strings, character by character, to determine equality==
operator checks to see whether two object references refer to the same instance of an objectStringBuffer and StringBuilder are classes used for String manipulation. These are mutable objects, which provide methods such as substring()
, insert()
, append()
, delete()
for String manipulation. They are similar, but StringBuilder is faster and preferred over StringBuffer for single threaded program. However, StringBuilder operations are not thread-safe are not-synchronized. So StringBuffer is preferred over StringBuilder when thread safety is required.
There are two types of polymorphism in Java:
If a widget can change when a user interacts with it, it’s stateful. If it can't, it's stateless.
Icon
, IconButton
, and Text
Checkbox
, Radio
, Slider
, InkWell
, Form
, and TextField
Hot Restart takes a bit longer than Hot Reload, in comparison
BuildContext
is the widget's element in the Element tree. Every widget has its own BuildContext. It's used to get a reference to the theme or to another widget.
const
widgets to cache and reuseMore technique on official doc on Perfomance Considerations on Flutter
More in-depth analysis of every step on the following aticle
onPause()
method executes.onCreate()
, onStart()
, and onResume()
methods execute in sequence. (Activity B now has user focus.)onStop()
method executes.Android manages tasks and the back stack, by placing all activities started in succession in the same task and in a Last In First Out (LIFO) stack
FLAG_ACTIVITY_NEW_TASK
FLAG_ACTIVITY_CLEAR_TOP
FLAG_ACTIVITY_SINGLE_TOP
By displaying notifications. But there are some exceptions and apps running on Android 10 or higher can start activities only when one or more of these conditions are met.
You can retrieve a NavController by using one of the following methods:
Fragment.findNavController()
View.findNavController()
Activity.findNavController(viewId: Int)
NavHostFragment.findNavController(Fragment)
Navigation.findNavController(Activity, @IdRes int viewId)
Navigation.findNavController(View)
3 major components in Room:
Apps that use scoped storage have access only to their app directory on external storage plus any media the app created. More details on this article.
Sealed classes are used for representing restricted class hierarchies, when a value can have one of the types from a limited set, but cannot have any other type.
They are, in a sense, an extension of enum classes: the set of values for an enum type is also restricted, but each enum constant exists only as a single instance, whereas a subclass of a sealed class can have multiple instances which can contain state.
A use case can be when to display a list of a specific type of object in a RecyclerView, where each object has its own ViewHolder.
sealed class Fruit {
class Apple : Fruit()
class Orange : Fruit()
}
class FruitAdapter : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
private val fruits = arrayListOf<Fruit>()
override fun getItemCount() = fruits.size
override fun getItemViewType(position: Int): Int {
return when (fruits[position]) {
is Fruit.Apple -> R.layout.item_apple
is Fruit.Orange -> R.layout.item_orange
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
val layoutInflater = LayoutInflater.from(parent.context)
return when (viewType) {
R.layout.item_apple -> {
val itemView = layoutInflater.inflate(R.layout.item_apple, parent, false)
AppleViewHolder(itemView)
}
R.layout.item_orange -> {
val itemView = layoutInflater.inflate(R.layout.item_orange, parent, false)
OrangeViewHolder(itemView)
}
else -> throw UnknownViewTypeException("Unknown view type $viewType")
}
}
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
val fruit = fruits[position]
when (fruit) {
is Fruit.Apple -> (holder as AppleViewHolder).bind(fruit)
is Fruit.Orange -> (holder as OrangeViewHolder).bind(fruit)
}
}
}
onCreateViewHolder()
method. It then binds the View via onBindViewHolder()
if necessary, and finally returns itIf a ViewHolder can not be found in Scrap, or Pool, or View Cache, RecyclerView calls its adapter's onCreateViewHolder() and then binds the created view by calling onCreateViewHolder()
method.
When a ViewHolder is in the view cache, we hope to to reuse it “as-is”, without rebinding, at the same position as the one it was at before it got into the cache.
With the given scenario, it's assumable that users would not scroll up/go back to previously read articles too often. So we may need to extend the capacity of the cache in the latter as the grid needs to be updated more frequently.
onViewAttachedToWindow()
and onViewDetachedFromWindow()
callbacks of the RecyclerView's Adapter
There's no difference becaused multi-window mode does not change the activity lifecycle.
When apps are running simultaneously in a multi-windowed environment, supported in Android 7.0 (API level 24) and higher, the system manages tasks separately for each window; each window may have multiple tasks. Since it's managed by the System as a per-window basis, 2 applications in 2 windows are not in the same back stack of tasks in any way.
For the specific case of savedInstanceState
, the amount of data should be kept small because the system process needs to hold on to the provided data for as long as the user can ever navigate back to that activity (even if the activity's process is killed). Less than 50k of data in saved state is recommended. Ref
Types of Observables in RxJava:
Types of Observers in RxJava:
Backpressure is when your observable (publisher) is creating more events than your subscriber can handle. So you can get subscribers missing events, or you can get a huge queue of events which just leads to out of memory eventually.
Flowable
takes backpressure into consideration. Observable
does not.
Comparison:
by lazy {...}
delegate can only be used for val
properties, whereas lateinit
can only be applied to var
typeslateinit
var can be initialized from anywhere the object is seen from, wherease by lazy {...}
can only be initialized from its initializer lambdaby lazy {...}
is thread safe by default, it guarantees that the initializer is invoked at most once, whereas lateinit
is not thread safe, the user is responsible for initialize in multi-thread inveronmentsUse Cases:
lateinit
.by lazy {...}
Code example:
A code snippet where lateinit
is allowed or not:
lateinit var name: String //Allowed
lateinit val name: String //Not Allowed in val
lateinit var name: String? //Not Allowed if nullable
Here's a resourceful thread on Stack Overflow