Skip to main content

113 posts tagged with "Android"

View All Tags

State Management in Flutter: Provider vs. BLoC vs. Redux

Published: · Last updated: · 6 min read
Appxiom Team
Mobile App Performance Experts

State management is one of the most important concepts in Flutter app development. Managing state effectively can make your app more efficient, faster, and easier to maintain.

In this article, we'll explore three popular state management solutions in Flutter: Provider, BLoC, and Redux.

State Management in Flutter

In Flutter, state management refers to the way in which data is managed and updated within an app. In general, there are two types of state: local state and global state.

Local state is data that is used only within a single widget. For example, if you have a button that changes color when clicked, the color of the button is a piece of local state.

Global state is data that needs to be accessed by multiple widgets within the app. For example, if you have a shopping app and you need to keep track of the user's cart across multiple screens, the contents of the cart are global state.

1. Provider

Provider is a state management solution that was introduced as an alternative to Flutter's built-in setState() method. Provider is a relatively new solution but has gained popularity among developers because of its simplicity and ease of use.

Provider works by creating a central data store that can be accessed by any widget within the app. This data store is known as a ChangeNotifier and is responsible for managing the app's global state.

Here is an example of how to use Provider in Flutter:

class CartModel extends ChangeNotifier {
List<Item> _items = [];

List<Item> get items => _items;

void addItem(Item item) {
_items.add(item);
notifyListeners();
}
}

class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return ChangeNotifierProvider(
create: (_) => CartModel(),
child: MaterialApp(
home: MyHomePage(),
),
);
}
}

class MyHomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
final cart = Provider.of<CartModel>(context);
return Scaffold(
appBar: AppBar(
title: Text('My App'),
),
body: Center(
child: FlatButton(
onPressed: () {
cart.addItem(Item(name: 'Item 1', price: 10));
},
child: Text('Add to Cart'),
),
),
);
}
}

In this example, we create a CartModel class that manages the app's global state. We then wrap our MyApp widget in a ChangeNotifierProvider, which provides access to the CartModel to any widget within the app. Finally, in the MyHomePage widget, we use the Provider.of<CartModel>(context) method to access the CartModel and add items to the cart when the user clicks the "Add to Cart" button.

2. BLoC

BLoC (Business Logic Component) is another popular state management solution in Flutter. BLoC separates the business logic of the app from the user interface, making it easier to manage complex state.

BLoC works by creating a stream of data that emits events whenever the state changes. Widgets can then subscribe to this stream and update themselves accordingly.

Here is an example of how to use BLoC in Flutter:

class CartBloc {
final _cart = BehaviorSubject&lt;List&lt;Item&gt;&gt;.seeded([]);

Stream&lt;List&lt;Item&gt;&gt; get cart =&gt; _cart.stream;

void addItem(Item item) {
final items = _cart.value;
items.add(item);
_cart.add(items);
}

void dispose() {
_cart.close();
}
}

class MyApp extends StatelessWidget {
final cart = CartBloc();

@override
Widget build(BuildContext context) {
return StreamProvider&lt;List&lt;Item&gt;&gt;.value(
value: cart.cart,
initialData: [],
child: MaterialApp(
home: MyHomePage(),
),
);
}

@override
void dispose() {
cart.dispose();
super.dispose();
}
}

class MyHomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
final cart = Provider.of&lt;List&lt;Item&gt;&gt;(context);

return Scaffold(
appBar: AppBar(
title: Text('My App'),
),
body: Center(
child: FlatButton(
onPressed: () {
Provider.of&lt;CartBloc&gt;(context, listen: false).addItem(Item(name: 'Item 1', price: 10));
},
child: Text('Add to Cart'),
),
),
);
}
}

In this example, we create a CartBloc class that manages the app's global state. We then use a StreamProvider to provide access to the cart stream to any widget within the app. Finally, in the MyHomePage widget, we use the Provider.of&lt;List&lt;Item&gt;&gt;(context) method to access the cart and add items to the cart when the user clicks the "Add to Cart" button.

3. Redux

Redux is a popular state management solution in the web development world, and has also gained popularity in the Flutter community. Redux works by creating a single data store that is responsible for managing the app's global state. This data store is modified by dispatching actions, which are then handled by reducers that update the state.

Here is an example of how to use Redux in Flutter:

enum CartAction { addItem }

class CartState {
final List&lt;Item&gt; items;

CartState({this.items});

CartState.initialState() : items = [];
}

CartState cartReducer(CartState state, dynamic action) {
if (action == CartAction.addItem) {
return CartState(items: List.from(state.items)..add(Item(name: 'Item 1', price: 10)));
}

return state;
}

class MyApp extends StatelessWidget {
final store = Store&lt;CartState&gt;(cartReducer, initialState: CartState.initialState());

@overrideWidget build(BuildContext context) {
return StoreProvider(
store: store,
child: MaterialApp(
home: MyHomePage(),
),
);
}
}

class MyHomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('My App'),
),
body: Center(
child: FlatButton(
onPressed: () {
StoreProvider.of&lt;CartState&gt;(context).dispatch(CartAction.addItem);
},
child: Text('Add to Cart'),
),
),
);
}
}

In this example, we create a CartState class that manages the app's global state. We then use a StoreProvider to provide access to the store to any widget within the app. Finally, in the MyHomePage widget, we use the StoreProvider.of<CartState>(context) method to access the store and dispatch an action to add an item to the cart when the user clicks the "Add to Cart" button.

Conclusion

There are several popular state management solutions in Flutter, including Provider, BLoC, and Redux. Each solution has its own strengths and weaknesses, and the best solution for your project will depend on a variety of factors, including the complexity of the app and the preferences of the development team.

When choosing a state management solution, it's important to consider factors such as the ease of use, the level of abstraction, the performance, and the scalability of the solution. It's also important to consider the trade-offs between different solutions in terms of code complexity, maintenance, and the ability to integrate with other tools and libraries.

Provider is a great choice for simple apps with straightforward state management needs. It is easy to use and has a low learning curve, making it a popular choice for beginners.

BLoC is a more complex solution that offers a high level of abstraction, making it a good choice for complex apps with complex state management needs.

Redux is a mature and battle-tested solution that is widely used in the web development world and offers excellent scalability and performance.

The best state management solution for your project will depend on a variety of factors, including the size and complexity of your app, your team's preferences and skill level, and your performance and scalability requirements.

Regardless of which solution you choose, it's important to follow best practices for state management, such as separating UI logic from business logic, minimizing unnecessary state changes, and keeping state management code as simple and modular as possible. With the right approach and the right tools, you can build robust and scalable Flutter apps that deliver great user experiences and meet your business goals.

How to Improve Performance of Android Apps Built Using Kotlin

Published: · Last updated: · 3 min read
Appxiom Team
Mobile App Performance Experts

Kotlin is a popular programming language used by Android developers to build high-quality, performant mobile apps. However, even with the benefits of Kotlin, app performance can still be a concern for developers. In this blog post, we'll explore some tips and techniques to help improve the performance of Android apps built using Kotlin.

1. Use Kotlin's null safety features

One of Kotlin's significant advantages is its null safety feature, which helps prevent null pointer exceptions. However, if not used correctly, null safety can result in performance issues. To avoid this, use the Elvis operator (?:) instead of null checks, as it performs better at runtime.

val result = nullableValue ?: defaultValue

2. Optimize memory usage

Memory usage can significantly impact app performance. To optimize memory usage, avoid creating new objects unnecessarily, as it can lead to memory leaks and affect app performance. Additionally, use data classes instead of regular classes, as data classes are more efficient in terms of memory usage.

3. Use lazy initialization

Lazy initialization is a technique that delays the creation of objects until they are needed. Using lazy initialization can help reduce app startup time and improve overall performance. You can use the by lazy keyword to implement lazy initialization.

val myObject: MyObject by lazy { MyObject() }

4. Use inline functions

Inline functions can help improve app performance by reducing the function call overhead. Use the inline keyword to declare a function as inline.

inline fun performTask() {
// function code here
}

5. Use coroutines

Coroutines are a powerful feature in Kotlin that can help improve app performance by executing tasks asynchronously. Coroutines can perform tasks without blocking the main thread, reducing the risk of UI freezes and improving app performance.

To use coroutines, you need to add the kotlinx-coroutines-android library to your app.

GlobalScope.launch {
// Coroutine code here
}

6. Use Kotlin extensions

Kotlin extensions are a convenient feature that can help reduce boilerplate code and improve app performance. You can use Kotlin extensions to add functionality to existing classes without creating subclasses. For example, you can use an extension function to simplify view binding code.

fun &lt;T : ViewBinding&gt; AppCompatActivity.bind(viewBindingFactory: (LayoutInflater) -&gt; T): T {
return viewBindingFactory.invoke(layoutInflater).apply {
setContentView(root)
}
}

7. Use performance monitoring tools

Integrate SDKs that monitors and reports performance issues and bugs in realtime. Some of the tools available are Firebase Crashlytics, Bugsnag, New Relic, Appxiom and Instabug.

8. Use the latest Kotlin version

Finally, make sure you are using the latest version of Kotlin. Newer versions of Kotlin often include performance improvements and bug fixes that can help improve app performance. You can check the latest version of Kotlin on the official Kotlin website.

Conclusion

Kotlin is a powerful programming language that can help you build high-quality, performant Android apps. By using Kotlin's null safety features, optimizing memory usage, using lazy initialization, using inline functions, using coroutines, using Kotlin extensions, using performance monitoring tools and using the latest Kotlin version, you can further improve app performance and provide a seamless user experience.

Getting Started with Flutter: A Beginner's Guide

Published: · Last updated: · 3 min read
Appxiom Team
Mobile App Performance Experts

Flutter is an open-source mobile application development framework created by Google. It allows developers to build high-quality, natively compiled applications for mobile, web, and desktop from a single codebase. Flutter uses the Dart programming language, which was also created by Google, and provides a rich set of pre-built widgets, tools, and libraries to simplify the development process.

In this guide, we will cover the basics of getting started with Flutter, including setting up your development environment, creating a new project, and building a simple user interface.

Prerequisites

Before we get started, you'll need to have the following software installed on your computer:

  • Flutter SDK

  • Android Studio or Visual Studio Code (with the Flutter extension)

  • Xcode (if you're developing for iOS)

You can download the Flutter SDK from the official Flutter website: https://flutter.dev/docs/get-started/install

Creating a New Project

Once you have Flutter installed, you can create a new project by running the following command in your terminal:

flutter create my_app

This will create a new Flutter project named my_app in your current directory.

Running the App

To run the app, you'll need to have an emulator or a physical device connected to your computer. To start the app on an emulator, run the following command:

flutter run

This will build the app and launch it on the default emulator.

Building the User Interface

Flutter provides a wide range of pre-built widgets that you can use to build your app's user interface. In this example, we will create a simple app that displays a list of items.

First, open the lib/main.dart file in your project directory. This is the main entry point of your app.

Next, remove the existing code and replace it with the following:

import 'package:flutter/material.dart';

void main() =&gt; runApp(MyApp());

class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'My App',
home: Scaffold(
appBar: AppBar(
title: Text('My App'),
),
body: ListView(
children: [
ListTile(
leading: Icon(Icons.ac_unit),
title: Text('Item 1'),
),
ListTile(
leading: Icon(Icons.access_alarm),
title: Text('Item 2'),
),
ListTile(
leading: Icon(Icons.accessibility),
title: Text('Item 3'),
),
],
),
),
);
}
}

Let's break down the code.

The import 'package:flutter/material.dart'; statement imports the Material package, which provides the MaterialApp widget that we'll use to define our app's theme and navigation structure.

The MyApp class is a stateless widget that defines the structure of our app. In this example, we've defined a simple MaterialApp with a Scaffold widget as its home screen.

The Scaffold widget provides a basic framework for our app's layout, including an app bar and a body. We've set the app bar's title to "My App" and the body to a ListView widget that displays a list of ListTile widgets.

Each ListTile widget displays an icon and a title. We've used three different icons (Icons.ac_unit, Icons.access_alarm, and Icons.accessibility) and three different titles ("Item 1", "Item 2", and "Item 3").

Conclusion

In this beginner's guide to Flutter, we covered the basics of setting up your development environment, creating a new project, and building a simple user interface. We used pre-built widgets from the Material package to create a basic layout, and we explored some of the basic concepts of Flutter app development.

As you continue to learn and explore Flutter, you'll discover many more powerful widgets, tools, and libraries that can help you create beautiful and highly functional apps. With its rich set of features and excellent documentation, Flutter is a great choice for developers who want to build high-quality, cross-platform applications quickly and efficiently.

10 Android Libraries Every Developer Should Know

Published: · Last updated: · 3 min read
Appxiom Team
Mobile App Performance Experts

Android development can be complex and time-consuming, but luckily, there are many libraries available that can help make the process easier and faster. In this blog post, we'll explore 10 essential Android libraries that every developer should know and consider using in their projects.

  • Retrofit: Retrofit is a type-safe REST client for Android that makes it easy to retrieve and upload data to a server. It's a popular library that supports several data formats such as JSON, XML, and Protocol Buffers. With Retrofit, you can define API endpoints, request parameters, and response types in an interface, making it easy to create a robust REST client.

  • Glide: Glide is an image loading and caching library for Android that can load images from a variety of sources, including URLs, local files, and content providers. It's easy to use and can automatically scale and crop images to fit different device sizes and aspect ratios. Glide also provides advanced features like memory and disk caching, placeholder images, and animated GIF support.

  • Room: Room is an object-relational mapping (ORM) library that makes it easy to work with SQLite databases on Android. It provides an abstraction layer over raw SQL queries, allowing you to easily perform CRUD (create, read, update, delete) operations on database entities. Room also supports reactive programming with LiveData and RxJava, making it easy to create responsive UIs.

  • Dagger 2: Dagger 2 is a dependency injection (DI) library that helps manage the dependencies between different components of an Android app. It uses annotations to generate boilerplate code for injecting dependencies, making it easy to maintain a clean, modular architecture. Dagger 2 also supports compile-time validation of dependencies, reducing the risk of runtime errors.

  • OkHttp: OkHttp is an HTTP client library for Android that can handle both synchronous and asynchronous network requests. It provides a simple API for making requests and supports features like caching, authentication, and encryption. OkHttp is also highly customizable, allowing you to add interceptors, configure timeouts, and handle error responses.

  • Timber: Timber is a logging library for Android that makes it easy to debug and troubleshoot your app. It provides a simple API for logging messages with different levels of severity and can automatically tag log messages with useful information like the class name and line number. Timber also supports custom loggers, making it easy to integrate with third-party logging services.

  • Gson: Gson is a JSON serialization and deserialization library for Android that can convert JSON strings to Java objects and vice versa. It provides a simple API for defining custom serialization and deserialization rules and supports advanced features like nested objects, arrays, and polymorphic types. Gson can also handle malformed JSON input, making it robust and flexible.

  • Firebase: Firebase is a suite of tools and services for developing Android apps, which includes features like real-time database, authentication, hosting, cloud messaging along with support for serverless functions that run in response to events that are automatically scaled and managed.

  • Appxiom: Appxiom is a lightweight SDK that detects performance issues and bugs like memory leaks, ANR, Frozen Frames, screen load delays, crashes, network call failures, exceptions, function failures and more. The tool works seamlessly in development, testing and live phases.

  • Espresso: Espresso is a testing framework for Android that helps automate UI testing and ensure app quality. It provides a simple API for interacting with UI elements and simulating user actions like clicks

Achieving Concurrency and Parallelism in Kotlin Using Threads and Coroutines.

Published: · Last updated: · 9 min read
Appxiom Team
Mobile App Performance Experts

Concurrency and parallelism are two essential concepts in software development that allow you to execute multiple tasks simultaneously. Although these terms are often used interchangeably, they are distinct concepts.

In this blog post, we will explore concurrency and parallelism in Kotlin and how to implement them using threads and coroutines with some code samples.

Concurrency vs. Parallelism

Concurrency refers to the ability of a program to execute multiple tasks simultaneously, regardless of whether they are running on different processors or not. It involves breaking up a task into smaller pieces and executing them independently of each other. However, concurrency does not guarantee that the tasks will be executed in parallel.

Parallelism, on the other hand, refers to the ability of a program to execute multiple tasks simultaneously using multiple processors. It involves breaking up a task into smaller pieces and distributing them across multiple processors for simultaneous execution.

Threads

Threads are the most basic mechanism for achieving concurrency in Kotlin. A thread is a lightweight unit of execution that can run concurrently with other threads within a program. Each thread can execute a separate task, allowing multiple tasks to be executed simultaneously.

Threads achieve concurrency by allowing multiple threads to run concurrently on a single CPU. The CPU switches between threads, allowing each thread to execute a portion of its code. This switching happens so fast that it appears as if all threads are running simultaneously.

Threads also enable parallelism by allowing multiple threads to run on separate CPUs. In this case, each thread is assigned to a different CPU core, allowing multiple threads to be executed simultaneously.

Threads are created using the Thread class, which takes a function or lambda expression as an argument. The function or lambda expression contains the code that the thread will execute. The following code snippet demonstrates how to create a new thread:

val thread = Thread {
// code to be executed in the thread
}

Once the thread is created, it can be started using the start() method. The start() method launches the thread and begins executing the code in the thread.

thread.start()

Threads can communicate with each other and share data using synchronization mechanisms like locks, semaphores, and monitors. However, this can be a challenging task, and incorrect synchronization can lead to race conditions and other concurrency bugs.

Coroutines

Coroutines are a more advanced mechanism for achieving concurrency and parallelism in Kotlin. Coroutines are lightweight, and they provide a more flexible and scalable approach to concurrency than threads. Coroutines enable asynchronous, non-blocking code execution, making them ideal for use cases like network programming or graphical user interfaces.

Coroutines achieve concurrency by allowing multiple coroutines to be executed on a single thread. This is possible because coroutines are cooperative, meaning that they suspend their execution voluntarily, allowing other coroutines to run. This cooperative nature enables a single thread to execute multiple coroutines simultaneously, resulting in highly efficient and performant code.

Coroutines also enable parallelism by allowing multiple coroutines to be executed on separate threads or even separate CPUs. This is achieved by using coroutines with different coroutine contexts, which specify the thread or threads on which the coroutine should execute.

Coroutines are created using the launch or async functions provided by the GlobalScope object in kotlinx.coroutines library. The launch function creates a new coroutine that runs in the background, while the async function creates a new coroutine that returns a result.

val job = GlobalScope.launch {
// code to be executed in the coroutine
}

val deferred = GlobalScope.async {
// code to be executed in the coroutine and return a result
}

Communicating between Coroutines using Channel

Coroutines can communicate with each other and share data using channels and suspending functions. Channels provide a way for coroutines to send and receive data asynchronously, while suspending functions enable coroutines to suspend their execution until a specific condition is met.

val channel = Channel&lt;Int&gt;()
val job = GlobalScope.launch {
for (i in 1..5) {
channel.send(i)
}
}

val deferred = GlobalScope.async {
var sum = 0
for (i in 1..5) {
sum += channel.receive()
}
sum
}

Coroutine Context

Coroutine context is a key concept in coroutines, and it provides a mechanism for managing the execution of coroutines. The coroutine context is a set of rules and properties that define how a coroutine should be executed. It includes information like the dispatcher, which specifies the thread or threads on which the coroutine should execute, and the job, which represents the lifecycle of the coroutine.

The dispatcher is responsible for assigning coroutines to threads. Different dispatchers are available, each with a different execution strategy. For example, the Dispatchers.Default dispatcher assigns coroutines to a thread pool, while the Dispatchers.IO dispatcher assigns coroutines to a pool of threads optimized for I/O operations.

The CoroutineContext interface represents a context for a coroutine, which includes information like the coroutine dispatcher and job. The coroutine context provides a way to control the execution of coroutines, including where they run, how they are executed, and how they are cancelled.

Let's explore how to use the coroutine context in Kotlin with a sample code.

import kotlinx.coroutines.*

fun main() = runBlocking&lt;Unit&gt; {
launch(Dispatchers.Default) {
println("Running in the Default dispatcher")
println("Current thread: ${Thread.currentThread().name}")
}

launch(Dispatchers.IO) {
println("Running in the IO dispatcher")
println("Current thread: ${Thread.currentThread().name}")
}

launch(newSingleThreadContext("MyThread")) {
println("Running in a single-threaded context")
println("Current thread: ${Thread.currentThread().name}")
}
}

In the above code, we are launching three coroutines with different dispatchers: Dispatchers.Default, Dispatchers.IO, and a new single-threaded context created with newSingleThreadContext("MyThread").

The runBlocking coroutine builder is used to create a scope where coroutines can be launched and executed synchronously. It is similar to the Thread.join() method in Java, which blocks the current thread until the specified thread completes.

When a coroutine is launched with a dispatcher, it is assigned to a thread pool managed by that dispatcher. In the above code, the first coroutine is launched with the Dispatchers.Default dispatcher, which assigns it to a thread pool optimized for CPU-bound tasks. The second coroutine is launched with the Dispatchers.IO dispatcher, which assigns it to a thread pool optimized for I/O-bound tasks. Finally, the third coroutine is launched with a new single-threaded context, which creates a new thread on which the coroutine runs.

When the coroutines run, they print out a message indicating which dispatcher or context they are running in, as well as the name of the current thread. The output might look something like this:

Running in the IO dispatcher
Current thread: DefaultDispatcher-worker-1
Running in a single-threaded context
Current thread: MyThread
Running in the Default dispatcher
Current thread: DefaultDispatcher-worker-2

In this example, we can see that the coroutines are running on different threads depending on the dispatcher or context they are launched with.

Example: Downloading Images

Using Thread

import java.net.URL

fun main() {
val urls = listOf(
"https://example.com/image1.jpg",
"https://example.com/image2.jpg",
"https://example.com/image3.jpg",
)
val threads = urls.map {
Thread {
val url = URL(it)
val stream = url.openStream()
// Code to process the downloaded image
}
}
threads.forEach { it.start() }
threads.forEach { it.join() }
}

In the code above, we define a list of URLs and use the map() function to create a list of threads that download each image in parallel. We then start each thread and wait for them to finish using the join() function.

Using Coroutine

import kotlinx.coroutines.*
import java.net.URL

fun main() = runBlocking {
val urls = listOf(
"https://example.com/image1.jpg",
"https://example.com/image2.jpg",
"https://example.com/image3.jpg",
)
val deferred = urls.map {
async {
val url = URL(it)
val stream = url.openStream()
// Code to process the downloaded image
}
}
deferred.awaitAll()
}

In the code above, we define a list of URLs and use the map() function to create a list of coroutines that download each image in parallel. We then wait for all the coroutines to finish using the awaitAll() function.

Comparison: Thread vs Coroutine

When comparing coroutines and threads in Kotlin, there are several factors to consider that can affect performance. Here are some of the key differences between coroutines and threads in terms of performance:

  • Memory usage: Coroutines typically use less memory than threads because they are not tied to a specific thread and can reuse threads from a thread pool. This means that coroutines can potentially support a larger number of concurrent tasks without running out of memory.

  • Context switching: Context switching is the process of switching between different threads or coroutines. Context switching can be a performance bottleneck, as it involves saving and restoring the state of the thread or coroutine. Coroutines typically have a lower context switching overhead than threads because they use cooperative multitasking, where the coroutine decides when to suspend and resume execution, rather than relying on the operating system to schedule threads.

  • Scheduling: Coroutines are scheduled by a coroutine dispatcher, which determines which coroutine runs on which thread. This allows for more fine-grained control over how coroutines are executed and can improve performance by minimizing the number of context switches. Threads, on the other hand, are scheduled by the operating system, which can result in less control over scheduling and potentially more context switching.

  • Scalability: Coroutines can be more scalable than threads because they can be launched and cancelled more quickly, allowing for more dynamic allocation of resources. Coroutines can also be used with non-blocking I/O libraries, which can improve scalability by reducing the number of threads needed to handle I/O operations.

In general, coroutines can provide better performance for concurrent and asynchronous tasks due to their lower memory usage, lower context switching overhead, and more fine-grained control over scheduling.

Conclusion

In summary, concurrency and parallelism are essential concepts in software development, and Kotlin provides two mechanisms for achieving these goals: threads and coroutines. Threads achieve concurrency and parallelism by allowing multiple threads to run on a single or multiple CPUs. Coroutines achieve concurrency and parallelism by allowing multiple coroutines to be executed on a single or multiple threads, with each coroutine being cooperative and suspending its execution voluntarily.

With a solid understanding of threads and coroutines, developers can write highly efficient and performant applications that can execute multiple tasks simultaneously.

Why Mobile App Testers and Developers Should Use APM Tools for Performance Monitoring.

Published: · Last updated: · 2 min read
Appxiom Team
Mobile App Performance Experts

Performance monitoring and continues bug monitoring are critical parts of the Mobile App development lifecycle. As mobile devices become more powerful and users expect more from their apps, it is essential to ensure that apps are performing well and are free of bugs. One way to achieve this is by using Application Performance Management (APM) tools.

APM tools are designed to help mobile app testers and developers detect and diagnose performance issues and bugs in their apps. These tools provide a wide range of information about an app's performance, including memory usage, CPU usage, network activity, and more. This information can be used to identify bottlenecks, memory leaks, and other issues that can negatively impact an app's performance.

One of the main benefits of using APM tools is that they can help app developers and testers find and fix performance issues before they become a problem for users. By identifying issues early in the development process, teams can make changes to improve performance and ensure that the app is stable and reliable. This can help reduce the number of crashes and improve the overall user experience.

Another benefit of using APM tools is that they can help developers and testers understand how users are interacting with their apps. This can be especially useful for understanding how different user segments are interacting with the app, which can help teams optimize the user experience and make improvements that will have the biggest impact.

In short, APM tools are an essential tool for mobile app testers and developers. They help teams identify and fix performance issues and bugs, improve the user experience, and ensure that apps are stable and reliable. By using APM tools, teams can deliver better quality apps and create a more positive user experience.

Visit appxiom.com to know more about how Appxiom can help you with monitoring performance and bugs in mobile apps.

Tips from ChatGPT for Reducing Bug Fixing Time.

Published: · Last updated: · 2 min read
Appxiom Team
Mobile App Performance Experts

ChatGPT is brilliant, and awesome. I asked what I should do to reduce bug fixing time. This is the reply 👇.

  •  There are several ways that you can reduce the time required to fix bugs, including the following:

Use automated testing tools: Automated testing tools can help to quickly identify and diagnose bugs, allowing developers to focus their efforts on fixing the issues rather than manually testing for them.

  • Prioritize bug fixes: Prioritizing the most important or severe bugs can help to ensure that the most critical issues are addressed first, saving time and effort in the long run.

  • Use debugging tools: Debugging tools can help to quickly identify the source of a bug, allowing developers to fix the issue more efficiently.

  • Collaborate and communicate: Effective collaboration and communication among team members can help to identify and resolve bugs more quickly, saving time and effort.

  • Adopt agile development methodologies: Agile development methodologies, such as Scrum and Kanban, can help teams to quickly identify and fix bugs, as well as adapt to changing requirements and priorities.

Overall, using a combination of these strategies can help to reduce the time and effort required to fix bugs.

 I'm like .. Impressed !!!

Clear, and to the point.

Do as ChatGPT suggests, and you will indeed reduce bug fixing time.

On a side note, Appxiom helps you with first 3 points, and 4th point to an extend.

Now that you are here, I recommend to you this blog post. How to reduce bug fixing time in mobile apps.

How to Reduce Bug Fixing Time in Mobile Apps.

Published: · Last updated: · 2 min read
Appxiom Team
Mobile App Performance Experts

Bugs in physical world are beautiful. But in digital world, bugs are unwanted, but they do come uninvited.

We hate bugs in our apps because we need to put extra efforts to fix them.

Let's look at some basic numbers.

A ballpark optimistic estimate of the effort involved in fixing a medium severity bug is as follows.

Activity

Hours

​Collecting data

1.5

Reproducing the bug

0.5

Coding the fix

0.5

Testing

0.5

Total

3

If the effort required can be cut by 1/3rd, for each bug we will save one hour.

With a tool which can auto-detect and report bugs, the data collection can be cut short to 30 minutes from 90 minutes. That's a 1/3rd cut in total effort.

Effort saved => One hour.

If the time required to reproduce the bug and test the fix are reduced by 15 minutes each, that will be half an hour saved. For now let's not add that into this calculation.

Even with a very low estimate of 10 medium severity bugs per month, and with the saving of one hour in data collection alone, we will be saving 10 man-hours. This is equivalent to 1.25 man-days, assuming one man-day is equal to 8 man-hours. So we will save more than one man-day every month.

The actual can only go north by many multiples as the numbers considered here are much below realistic, and that's what we gather from our customers.

Try Appxiom to reduce bug fixing time in Android and iOS apps. Visit https://appxiom.com for more details.

Happy Bug Fixing.

Detecting ANR in Android Apps Using Firebase Crashlytics and Appxiom.

Published: · Last updated: · 2 min read
Appxiom Team
Mobile App Performance Experts

Firebase Crashlytics is used in most of the mobile apps to detect crashes. It also reports ANRs in Android with a detailed stacktrace.

Appxiom is a bug detection tool for Android and iOS apps. It captures a range of bugs including Crashes, ANRs, Memory Leaks, Memory Spikes, Abnormal Memory Usage, Frozen Frames, Slow Frames, HTTP API call issues, Screen Load Delays, and much more.

By definition ANR is triggered when the UI Thread gets blocked for 5 seconds or more. Appxiom detects and reports ANRs as and when the issue happens. It provides a chronologically ordered Activity Trail and a detailed Stacktrace that helps in locating where the ANR was triggered.

In Firebase ANRs get captured only when the user opts to Force Quit the app and then reopen it. This selection is done when the dialog message pops up asking if the app should Force Quit or it should wait to see if the UI Thread comes back. That means if the user waits and the UI Thread comes back to normalcy the ANR will not be reported. Also if the user decides to not come back the Firebase will not report the ANR.

ANR detection in Appxiom & FirebaseA detailed write up on how to use Appxiom to detect the root cause of ANR is available here https://www.blog.appxiom.com/post/detecting-and-fixing-anr-in-android-apps.

To know more about how Appxiom can help you in detecting bugs, visit https://appxiom.com. BTW, the tool works seamlessly in development, testing and live phases.

Our Customer Showreel, the Video Based Professional Networking App, and Its Super Star Founder.

Published: · Last updated: · 2 min read
Appxiom Team
Mobile App Performance Experts

One of the important advice from my mentor is to keep collecting as much publicly available information about apps that use our platform, connect with key people, and collect feedback about Appxiom. Appxiom helps mobile app developers to detect bugs and performance issues in Android and iOS apps, and to collect all relevant data for reproducing them.

And thus early last year we came across an app named Showreel.

Video for professional networking

Showreel is Tiktok for professional networking. You build your profile in Showreel as a well stitched series of short videos where you talk about yourself and what you do. It’s ideal for pitching for new jobs, or just to present what you do in your professional life. They also have a section for startup’s where founders can create short video pitches.

Showreel appVideo content has become the maximum consumed content format in our personal digital life, Bringing that to professional life is a great idea, isn’t it?

The Super Star founder

We explored further. And me and Don Peter were elevated to Cloud 9 when we realised who the founder is. Remember Hotmail? And its founder Sabeer Bhatia? Yes, Showreel is the new venture founded by Mr. Bhatia.

Sabeer Bhatia, founder of Hotmail and ShowreelHotmail was my first email id. I’m sure so is the case with almost everyone who got exposed to digital world in late 90’s. It was probably the first SaaS product that was adopted by masses across the globe. Mr. Bhatia became our hero and manifestation of what we all could dream to become. Selling Hotmail to Microsoft for a whopping $400M in 1998 made us all realize the scale of opportunity that the new world offered. He became a Super Star in the Web 1.0 era.

More than a customer

Coming back to the present, we connected with the engineering team of Showreel, and requested their feedback. They obliged and gave very valuable inputs and insights regarding how they use the product. The engagement is still continuing strong and they keep sharing their suggestions. Long story short, their inputs have contributed much to our product roadmap and many of their suggestions are included in Appxiom 6.0.

Thank you Team Showreel for choosing Appxiom, and supporting us with your thoughts and suggestions on improving the product. We value that much.

Check out Showreel here. https://www.showreelapp.com.

The Advantages and Disadvantages of Defensive Programming, and How Appxiom Finds the Balance.

Published: · Last updated: · 3 min read
Appxiom Team
Mobile App Performance Experts

While architecting any software solution, it’s important to focus on below three principles.

  • Incoming data is impure unless proven otherwise.

  • All data is important unless proven otherwise.

  • All code is insecure unless proven otherwise.

To incorporate these principles while building software applications, programmers tend to rely almost entirely on defensive programming, as if it is a divine universal solution. So wrapping entire code in multiple layers of try / catch blocks, verifying the same data at multiple points in the call flow, keeping unused data in memory because ‘all data is important’, and ignoring the 'proven otherwise' part of the principles, are common features in codebase these days. Throwing and catching Exceptions, as the name indicates, are to handle 'exceptions', and using them all over the place is an expensive bad idea.

I (almost) hate defensive programming the way it is done today.

No, I am not at all advocating not to take precautions against failure scenarios and potential security loopholes. We should. But over doing the precautionary measures do have a negative impact on the performance. Many programmers end-up doing exactly that in the name of defensive programming, and screwing up the performance as a result.

Another problem is that the defensive coding techniques end up preventing some bugs to manifest, and such bugs go unnoticed. The bugs will continue to exist, but they are not conspicuous enough. They may end up manipulating the end result, or affect the performance, but still go unnoticed. Even security bugs, though might get prevented from executing, would continue to exist as potential threats in the code. As the developer remains unware of the existence of these bugs, they go unfixed.

Of course, defensive programming has its advantages too. It helps the application much in gracefully handling unpleasant situations arising due to bugs. Also it helps in writing cleaner logging code.

So I very well understand why programmers tend to go for it.

How to make use of defensive programming while still enabling active reporting of bugs and errors? This has been one of the thought processes when we started building Appxiom for detecting and reporting bugs in mobile apps. The bugs have to be reported with as much data points as possible. And there should be a call back mechanism when bugs occur so that the developer can gracefully handle the situation. The entire process should happen in a resource efficient manner utilising less of CPU and Memory. Appxiom is architected based on these concepts.

The way Appxiom is architected helps mobile app developers to handle the buggy situation and also get notified as and when they occur. So they get the advantage of defensive programming and also get enough information to reproduce and fix the bugs.

If you are an android app developer, and use Java or Kotlin, or if you are an iOS developer, and if you use Objective-C or Swift, do check appxiom.com.Happy bug fixing.

Analysis of Impact of Bugs in Android, iOS and watchOS Apps is Now Available in Appxiom.

Published: · Last updated: · One min read
Appxiom Team
Mobile App Performance Experts

Performance Analytics has been a demand from our customers since Appxiom 1.0. Now with the release of Appxiom 6.0 the mobile app developers will get a clear understanding about how the bugs are impacting the installation base. The data is presented graphically and provides insights into the percentage and absolute number of android, watchOS and iOS devices that got affected.

Fig 1: Analysis of bugs and the impact on mobilesThe impact of memory leaks, abnormal memory usages, slow frames and frozen frames, ANR and App Hang, UI thread blocks, network issues, and all other bugs are now presented in a tangible way. Also bugs can be ordered based on the number of devices that are affected. This helps mobile app developers to prioritise bugs and to understand where they need to focus on to improve the performance of the app.

Appxiom 6.0 comes as Android SDK, iOS framework and watchOS framework. It's currently in closed beta, and is expected to go live by 21st of February, 2022. If you would like to request access for Appxiom 6.0, click here.

Detecting and Fixing ANR in Android Apps.

Published: · Last updated: · 4 min read
Don Peter
Cofounder and CTO, Appxiom

Any developer who has ever written an Android app must have faced the age old issue of 'App Not Responding' popularly known by the acronym ANR. ANRs are UI/Main thread blocks that prevail for more than 5000 milliseconds. Fixing ANRs require three steps much like any other issue.

  1. Get to know the existence of the issue.

  2. Get accurate data points to identify the root cause of the issue.

  3. Get the issue fixed and release a patch version fast.

Unlike crashes, the problem that we developers face here is in step 2. We at Appxiom figured out a method that will help us get accurate data points to fix ANRs in Android apps.

Let us get started.

Integrate Appxiom.

The first step is to add Appxiom SDK to the android application. SDK is capable of automatically detecting and reporting UI thread blocks in Android apps out of the box. SDK comes as a Gradle library, and the integration will take only couple of minutes.

Steps to integrate

  1. Add maven tag to your project level build.gradle file.
buildscript {
repositories {
}
dependencies {
classpath 'com.android.tools.build:gradle:x.x.x'
}
}
allprojects {
repositories {
jcenter()
maven {
url "https://appxiomcoreandroid.s3.amazonaws.com/release"
}
}
}
  1. Add dependencies to the app level build.gradle file and sync the project with gradle files.
releaseImplementation('com.appxiom:appxiomcore:x.x.x@aar') {
transitive = true;
}

debugImplementation('com.appxiom:appxiomdebug:x.x.x@aar') {
transitive = true;
}
  1. Finally, initialize the SDK in the onCreate function of Application class.
Ax.init(this, appKey, platformKey);

Run the app using Exerciser Monkey.

Exerciser Monkey is an adb command. It executes random touches and gestures in the app, just as if a monkey is using the app. This will help in detecting abnormalities in the app that regular tests may miss.

It is easy to execute Monkey tool. Run this command from the terminal and wait for the execution to complete. Please feel free to customize these command options.

$ ./adb shell monkey -v-v --ignore-crashes --ignore-timeouts --ignore-security-exceptions --throttle 100 --pct-touch 35 --pct-motion 40 --pct-nav 0 --pct-majornav 0 --pct-appswitch 5 --pct-anyevent 5 --pct-trackball 5 --pct-syskeys 0 --pct-pinchzoom 5 --bugreport 11000

Before executing the command, it is a good practise to keep the app pinned to the screen. This is to make sure that the monkey command does not accidentally close your app.

Once the execution is complete you will get detailed report with the number of ANRs encountered. But as I said at the beginning, it is real hard to identify the exact reason for the ANRs purely from these bug reports.

Appxiom reports ANR as and when they occur, unlike Firebase Crashlytics which report only those ANRs which resulted in the user Force Quitting the app and restarting it.

Identifying root cause of ANR

So, how do we identify the root cause of these issues. Head over to Appxiom dashboard and look for all issues reported.

ANR reported in Appxiom dashboardStacktrace available with each ANR issue report will help us to identify the exact line that triggered the ANR and to fix them.

Custom ANR threshold to detect shorter UI thread blocks.

Now, let us take this to the next level. We can use the @AX annotation provided by the SDK to set a custom threshold in detecting UI thread blocks. The value can be anywhere between 500 and 5000 milliseconds. Once set, SDK will report UI thread blocks detected above the set threshold. The default value is 3000 milliseconds.

@AX(ANRThresholdInMilliseconds = 1000)
public class BlogApp extends Application {
...

It is important to note that, monkey tool is not mandatory for Appxiom to detect ANRs. We run the app in any device or simulator and it will detect issues.

One of our customers tells us on how the new improved ANR detection feature helped them identify the root cause. Read it here https://www.blog.appxiom.com/post/identifying-root-cause-of-anr-in-android-apps.

Visit https://docs.appxiom.com for detailed documentation. To know more about Appxiom, visit https://appxiom.com.

Watch this space for more updates.

What's New for Developers in Android 12

Published: · Last updated: · 3 min read
Don Peter
Cofounder and CTO, Appxiom

Android released its OS version 12 developer preview back in February and is currently in beta 3. The expected date of stable release is in August or September.

Let us take a look at the new capabilities and features that developer can leverage.

Material UI gets an upgrade - Material You

Material You helps developers make design personal for app users. App users can tailor UI and design elements like color to their choice. This revolutionizes the entire concept of app design. Irrespective of the type of device, users will be able to interact and use apps in a familiar User Interface.

"Material You" Image Copyright: material.ioGoogle is yet to open up the APIs for developers. Hence the actual implementation and backward compatibility details are not yet verified.

New API for In-App searching

In Android 12, A document based NoSQL implementation has been introduced. Developers are provided with AppSearch API to store, index, search, share data.

Basic structure of LocalStorage and PlatformStorage in AppSearchAppSearch comes bundled as part of androidx jetpack library which makes it backward compatible.

  • AppSearch comes with LocalStorage and PlatformStorage support.

  • LocalStorage is an in-app NoSQL implementation.

  • PlatformStorage is a centralized NoSQL implementation that can be used to share data across apps.

  • LocalStorage supports Android 4 and above.

  • PlatformStorage supports Android 12 and future versions.

Privacy Indicator API in WindowInsets

OS will display privacy indicators in status bar of the device. The indicators are shown whenever the device camera or microphone is active. Developers can make use of getPrivacyIndicatorBounds API in WindowInsets to adjust the app interface accordingly so as not to overlap with privacy indicators.

Foreground permission for CDM paired apps

A new foreground permission is added to enable CDM or Companion Device Pairing apps to start foreground services. The name of the permission is REQUEST_COMPANION_START_FOREGROUND_SERVICES_FROM_BACKGROUND.

Game Mode API

Game developers can adjust game play settings based on the value from game mode API. Game Mode API is available in GameManager. It has the following game modes.

UNSUPPORTED mode

The game does not support Game Mode API.

STANDARD mode

The user has not yet selected any game modes. The game settings may remain as it is.

PERFORMANCE mode

User has selected performance mode. Game may adjust its settings to provide lowest latency frame rates even though the battery life may be affected.

BATTERY mode

User has opted for better battery life mode. Game need to adjust its settings to provide longest battery life possible during game play.

Conclusion

One of the impactful feature added is NoSQL based AppSearch. This gives developers the ability to implement efficient search functionality.

Why don't you give it a try?

Product Architecture Failure - Story of How I Made One, and Looked Stupid.

Published: · Last updated: · 3 min read
Don Peter
Cofounder and CTO, Appxiom

Friends, I would like to share an incident where I failed as a programmer.

I got my first freelance project

After my college days, I began working as a freelance developer. I got my first project from my LinkedIn network. I was in charge of developing both the mobile app, and the backend. The core feature of the app was implemented with a periodic data fetch from the server and displaying it to the user.

And the dreaded moment came knocking

A month of development work, and the application went live. We got some traction. And negative comments started appearing in the Play Store. A good number of users said they were not able to view the data !!!

Tested the app multiple times only to find out that it was working as per the expected workflow. This made us scratch our heads as feedback suggested our core feature was failing. Client started yelling at me during the project calls.

Many days of testing and in-depth analysis, we concluded that the data loss would have occurred due to some raise-condition in fetching and deleting data. The client had suggested removing the data from the server after it was consumed by the mobile app. I communicated the same to the server team and they made sure data is removed as soon as the data is retrieved by the app.

So this is what was happening. App sends a GET request to fetch data. After sending the response, the server then goes ahead and deletes the data. As per plan, yes !!! Now, if there is a network issue or any delay in receiving the response, the app sends the GET request again only to find the data is already deleted.

The app lost customers, and our client dropped the project before we could fix it.

Years passed, And I became a better developer

As time progressed, I started working on multiple programming languages, read RFC specifications and got experienced on software architectural patterns. Working on different products at scale enabled me to understand the need to properly architect a product. As I began to learn more about API design, It made me rethink what actually went wrong in my project.

The first mistake that I made was not recognizing the lifecycle of data that is to be deleted. Once the app receives data from the server, the data should be deleted only after the app sends a confirmation back to the server. The second mistake was that I executed multiple tasks in a single API call. Here, a single HTTP GET call was used to fetch and delete the data at the same time. Using GET requests to delete data goes against the intention of GET in HTTP spec. The app would have functioned smoothly and data loss would not have occurred if one HTTP call was assigned to fetch the data and another call was made to delete the data.

This is just one among the many instances where poor product architecture messed up the product.

Why am I writing this post? Because I see some of the product companies rushing through product development without focusing on the architecture. Spending quality time to understand the lifecycle of data and architecting the solution accordingly helps much. As a developer it will save you from embarrassment.