Showing posts with label Gradle. Show all posts
Showing posts with label Gradle. Show all posts

Friday, 15 September 2023

Demystifying the Android Toolchain: Building Blocks of App Development.

In the world of mobile technology, Android applications have become ubiquitous, enhancing our lives in countless ways. But behind every app's sleek interface lies a complex process powered by the Android toolchain. In this exploration, we'll unveil the essential components of this toolchain, demystifying the process that turns code into the apps we rely on daily. Join us on a journey through the heart of Android app development.

Understanding the Android Toolchain

The Android toolchain is a set of tools and utilities used by developers to compile, build, test, and package Android applications. It transforms your source code into an APK (Android Package) file, which can be installed and run on Android devices. Let's delve into its primary components:

Java Compiler (javac): Java is the primary language for Android app development. The Java compiler converts your Java source code (.java files) into bytecode (.class files).
Dalvik or ART (Android Runtime): Android uses a virtual machine to run applications. Dalvik, the earlier runtime, was succeeded by ART (Android Runtime) in later versions. These runtimes convert bytecode into machine code that is executed by the device's CPU.
Android Package Manager (aapt): This tool helps package your app's resources (like images, layouts, and XML files) into the APK file. It also handles resource localization, density, and other configuration-related tasks.
Android Asset Packaging Tool (aapt2): A more modern version of aapt, aapt2 further improves resource management, making it more efficient and robust.
Dex Compiler (dx): The Dalvik or ART runtime doesn't directly execute Java bytecode. Instead, it converts it into a specialized bytecode format called Dalvik Executable (DEX). The dx tool performs this conversion.
Android Debug Bridge (ADB): ADB is a versatile command-line tool for interacting with Android devices and emulators. It allows you to install, debug, and manage apps on devices.
Gradle and Android Studio: While not part of the Android toolchain per se, Gradle and Android Studio are essential development tools. Gradle is a build automation tool, and Android Studio is the official Integrated Development Environment (IDE) for Android development. These tools simplify the build and development process.

The Build Process

Now that we've introduced the Android toolchain components, let's explore how they work together during the build process:

  1. Source Code: You start with your app's source code, typically written in Java or Kotlin. You also have XML files for layouts and resources like images, strings, and themes.
  2. Compilation: The Java compiler (javac) translates your Java/Kotlin source code into bytecode (.class files). These files contain your app's logic.
  3. Resource Packaging: The Android Asset Packaging Tool (aapt or aapt2) packages your app's resources and assets into a format that can be efficiently used by Android. This includes XML layout files, images, and other resources.
  4. Dex Conversion: The Dex Compiler (dx) converts the bytecode (.class files) into Dalvik Executable (DEX) files. These DEX files are optimized for execution on Android devices.
  5. APK Assembly: The Android Package Manager (aapt) takes the DEX files, resources, and other necessary assets, and assembles them into an APK file. This file is the heart of your Android application.
  6. Signing and Debugging: Before distribution, you may need to sign the APK with a digital certificate. During development, you can use the Android Debug Bridge (ADB) to install and debug your app on emulators or physical devices.
  7. Distribution: Once your app is fully tested and ready for release, you can distribute the signed APK via the Google Play Store or other distribution channels.

Conclusion

The Android toolchain is the backbone of Android app development, seamlessly transforming your source code and resources into a functional application. Understanding its components and how they work together is crucial for any Android developer.

As the Android ecosystem evolves, new tools and optimizations are continually introduced, making app development more efficient and user-friendly. Stay updated with the latest developments in the Android toolchain to ensure your apps are at the forefront of innovation in the mobile world. Happy coding!

Tuesday, 15 August 2017

HB Blog 142: Android Configure Using Build Variants - Part 2.

Hello friends, Thanks for reading my previous post on HB Blog 141: Android Configure Build Variants - Part 1.
This post will show you how you can configure build variants to create different versions of your app from a single project, and how to properly manage your dependencies and signing configurations.
Build variants are the result of Gradle using a specific set of rules to combine settings, code, and resources configured in your build types and product flavors. Although you do not configure build variants directly, you do configure the build types and product flavors that form them. Each build variant represents a different version of your app that you can build. For example, you might want to build one version of your app that's free, with a limited set of content, and another paid version that includes more. You can also build different versions of your app that target different devices, based on API level or other device variations.

Refer the below link for complete sample code:-

Download Sample Code

Have a look on few code snippets,

//build.gradle
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
apply plugin: 'com.android.application'

android {
    compileSdkVersion 25
    buildToolsVersion "25.0.2"
    defaultConfig {
        applicationId "com.harshalbenake.buildvariant"
        minSdkVersion 15
        targetSdkVersion 25
        versionCode 1
        versionName "1.0"
    }

    /**
     * The following sample specifies an applicationIdSuffix for the debug build type,
     * and configures a "jnidebug" build type that is initialized using settings from
     * the debug build type.
     */
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }

        debug {
            applicationIdSuffix ".debug"
        }

        /**
         * The 'initWith' property allows you to copy configurations from other build types,
         * so you don't have to configure one from the beginning. You can then configure
         * just the settings you want to change. The following line initializes
         * 'jnidebug' using the debug build type, and changes only the
         * applicationIdSuffix and versionNameSuffix settings.
         */
        jnidebug {
            // This copies the debuggable attribute and debug signing configurations.
            initWith debug

            applicationIdSuffix ".jnidebug"
            jniDebuggable true
        }
    }

    // Specifies the flavor dimensions you want to use. The order in which you
    // list each dimension determines its priority, from highest to lowest,
    // when Gradle merges variant sources and configurations. You must assign
    // each product flavor you configure to one of the flavor dimensions.
    flavorDimensions "api", "mode"

    /**
     * The following code sample creates "demo" and "full" product flavors
     * which provide their own applicationIdSuffix and versionNameSuffix.
     * The following code sample uses the flavorDimensions property
     * to create a "mode" flavor dimension to group the "full" and "demo" product flavors,
     * and an "api" flavor dimension to group product flavor configurations based on API level
     */
    productFlavors {
        demo {
            applicationIdSuffix ".demo"
            versionNameSuffix "-demo"
            // Assigns this product flavor to the "mode" flavor dimension.
            dimension "mode"
            resValue "string", "app_name", "HB Demo"
        }
        full {
            applicationIdSuffix ".full"
            versionNameSuffix "-full"
            dimension "mode"
            resValue "string", "app_name", "HB Full"
        }

        // Configurations in the "api" product flavors override those in "mode"
        // flavors and the defaultConfig block. Gradle determines the priority
        // between flavor dimensions based on the order in which they appear next
        // to the flavorDimensions property above--the first dimension has a higher
        // priority than the second, and so on.
        minApi24 {
            dimension "api"
            minSdkVersion '24'
            // To ensure the target device receives the version of the app with
            // the highest compatible API level, assign version codes in increasing
            // value with API level. To learn more about assigning version codes to
            // support app updates and uploading to Google Play, read Multiple APK Support
            versionCode 30000 + android.defaultConfig.versionCode
            versionNameSuffix "-minApi24"
        }

        minApi23 {
            dimension "api"
            minSdkVersion '23'
            versionCode 20000  + android.defaultConfig.versionCode
            versionNameSuffix "-minApi23"
        }

        minApi21 {
            dimension "api"
            minSdkVersion '21'
            versionCode 10000 + android.defaultConfig.versionCode
            versionNameSuffix "-minApi21"
        }
}

    /**
     * Using the build configuration from the previous section as an example,
     * suppose you plan to support only API levels 23 and higher for the demo version of the app.
     * You can use the variantFilter block to filter out all build variant configurations that
     * combine the "minApi21" and "demo" product flavors.
     */
    variantFilter { variant ->
        def names = variant.flavors*.name
        // To check for a certain build type, use variant.buildType.name == "<buildType>"
        if (names.contains("minApi23") && names.contains("full")) {
            // Gradle ignores any variants that satisfy the conditions above.
            setIgnore(true)
        }
    }
}

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    compile 'com.android.support:appcompat-v7:25.3.1'
}

Tuesday, 1 August 2017

HB Blog 141: Android Configure Using Build Variants - Part 1.

The Android build system compiles app resources and source code, and packages them into an Android Application Package (APK), that you can test, deploy, sign, and distribute.  Android Studio uses Gradle, an advanced build toolkit, to automate and manage the build process, while allowing you to define flexible custom build configurations.
 The build process for a typical Android app module follows these general steps:
  1.     The compilers convert your source code into DEX (Dalvik Executable) files, which include the bytecode that runs on Android devices, and everything else into compiled resources.
  2.     The APK Packager combines the DEX files and compiled resources into a single APK. Before your app can be installed and deployed onto an Android device, however, the APK must be signed.
  3.     Before generating your final APK, the packager uses the zipalign tool to optimize your app to use less memory when running on a device.
At the end of the build process, you have either a debug APK or release APK of your app that you can use to deploy, test, or release to external users.

Gradle and the Android plugin help you configure the following aspects of your build:

 Build Types :-
    Build types define certain properties that Gradle uses when building and packaging your app, and are typically configured for different stages of your development lifecycle. For example, the debug build type enables debug options and signs the APK with the debug key, while the release build type may shrink, obfuscate, and sign your APK with a release key for distribution. You must define at least one build type in order to build your app—Android Studio creates the debug and release build types by default.

 Product Flavors :-
    Product flavors represent different versions of your app that you may release to users, such as free and paid versions of your app. You can customize product flavors to use different code and resources, while sharing and reusing the parts that are common to all versions of your app. Product flavors are optional and you must create them manually.

 Build Variants :-
    A build variant is a cross product of a build type and product flavor, and is the configuration Gradle uses to build your app. Using build variants, you can build the debug version of your product flavors during development, or signed release versions of your product flavors for distribution. Although you do not configure build variants directly, you do configure the build types and product flavors that form them. Creating additional build types or product flavors also creates additional build variants.

 Manifest Entries :-
    You can specify values for some properties of the manifest file in the build variant configuration. These build values override the existing values in the manifest file. This is useful if you want to generate multiple APKs for your modules where each of the apk files has a different application name, minimum SDK version, or target SDK version.

 Dependencies :-
    The build system manages project dependencies from your local filesystem and from remote repositories. This prevents you from having to manually search, download, and copy binary packages of your dependencies into your project directory.

 Signing :-
    The build system enables you to specify signing settings in the build configuration, and it can automatically sign your APKs during the build process. The build system signs the debug version with a default key and certificate using known credentials to avoid a password prompt at build time. The build system does not sign the release version unless you explicitly define a signing configuration for this build.
For more details, visit my post, HB Blog 120: How To Sign Your APKs Using Android Studio.

 ProGuard :-
    The build system enables you to specify a different ProGuard rules file for each build variant. The build system can run ProGuard to shrink and obfuscate your classes during the build process.
For more details, visit my post, HB Blog 139: ProGuard - Shrinks, Optimizes, And Obfuscates Your Code.

 Multiple APK Support :-
    The build system enables you to automatically build different APKs that each contain only the code and resources needed for a specific screen density or Application Binary Interface (ABI).

Custom Build Configurations :-

Creating custom build configurations requires you to make changes to one or more build configuration files, or build.gradle files. These plain text files use Domain Specific Language (DSL) to describe and manipulate the build logic using Groovy, which is a dynamic language for the Java Virtual Machine (JVM).

When starting a new project, Android Studio automatically creates some of these files for you, as shown in below image, and populates them based on sensible defaults.
 ......Continue, to read more on Build Variant gradle in my next post, Android Configure Using Build Variants - Part 2.

Saturday, 1 July 2017

HB Blog 139: ProGuard - Shrinks, Optimizes, And Obfuscates Your Code.

The ProGuard tool shrinks, optimizes, and obfuscates your code by removing unused code and renaming classes, fields, and methods with semantically obscure names. The result is a smaller sized .apk file that is more difficult to reverse engineer. Because ProGuard makes your application harder to reverse engineer, it is important that you use it when your application utilizes features that are sensitive to security like when you are Licensing Your Applications.

ProGuard is integrated into the Android build system, so you do not have to invoke it manually. ProGuard runs only when you build your application in release mode, so you do not have to deal with obfuscated code when you build your application in debug mode. Having ProGuard run is completely optional, but highly recommended.
1. Shrinking – detects and removes unused classes, fields, methods, and attributes.
2. Optimization – analyzes and optimizes the bytecode of the methods.
3. Obfuscation – renames the remaining classes, fields, and methods using short meaningless names.

How to Enable Proguard in Android Studio ?
  • In Android Studio project, the minifyEnabled  property in the build.gradle file enables and disables Proguard for release builds.
  • The minifyEnabled property is part of the buildTypes release block that controls the settings applied to release builds.
  • The getDefaultProguardFile(‘proguard-android.txt’) method obtains the default Proguard settings from the Android SDK tools/proguard folder.
  • Android Studio adds the proguard-rules.pro file at the root of the module, which helps to add custom Proguard rules.

Thursday, 15 December 2016

HB Blog 125: AndroidTreeView:- TreeView Implementation For Android.

A Listview is a view that shows items in a vertically scrolling list. The items come from the ListAdapter associated with this view.

An ExpandableListView is a view that shows items in a vertically scrolling two-level list. This differs from the ListView by allowing two levels: groups which can individually be expanded to show its children. The items come from the ExpandableListAdapter associated with this view.
But, we need a view which will not have limit for levels. We can think of a view that presents a hierarchical view of information.

AndroidTreeView is a TreeView implementation for android that has features such as,
    1. N - level expandable/collapsable tree
    2. Custom values, views, styles for nodes
    3. Save state after rotation
    4. Selection mode for nodes
    5. Dynamic add/remove node
Refer the below link for complete sample code:-

Download Sample Code

Have a look on few code snippets,

Integration:-

1) Add library as a dependency to your project
1
compile 'com.github.bmelnychuk:atv:1.2.+'

2) Create your tree starting from root element. TreeNode.root() element will not be displayed so it doesn't require anything to be set.
1
TreeNode root = TreeNode.root();
Create and add your nodes (use your custom object as constructor param)
1
2
3
4
5
 TreeNode parent = new TreeNode("MyParentNode");
 TreeNode child0 = new TreeNode("ChildNode0");
 TreeNode child1 = new TreeNode("ChildNode1");
 parent.addChildren(child0, child1);
 root.addChild(parent);

3) Add tree view to layout
1
2
 AndroidTreeView tView = new AndroidTreeView(getActivity(), root);
 containerView.addView(tView.getView());
The simplest but not styled tree is ready. Now you can see parent node as root of your tree

4) Custom view for nodes
Extend TreeNode.BaseNodeViewHolder and overwrite createNodeView method to prepare custom view for node:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
public class MyHolder extends TreeNode.BaseNodeViewHolder<IconTreeItem> {
    ...
    @Override
    public View createNodeView(TreeNode node, IconTreeItem value) {
        final LayoutInflater inflater = LayoutInflater.from(context);
        final View view = inflater.inflate(R.layout.layout_profile_node, null, false);
        TextView tvValue = (TextView) view.findViewById(R.id.node_value);
        tvValue.setText(value.text);

        return view;
    }
    ...
    public static class IconTreeItem {
        public int icon;
        public String text;
    }
}

5) Connect view holder with node
1
2
 IconTreeItem nodeItem = new IconTreeItem();
  TreeNode child1 = new TreeNode(nodeItem).setViewHolder(new MyHolder(mContext));

6) Consider using
1
2
3
4
TreeNode.setClickListener(TreeNodeClickListener listener);
AndroidTreeView.setDefaultViewHolder
AndroidTreeView.setDefaultNodeClickListener
...

Thursday, 1 December 2016

HB Blog 124: Android Multi-Window Mode.

Android 7.0 allows several apps to share the screen at once. For example, a user could split the screen, viewing a web page on the left side while composing an email on the right side. The user experience depends on the device:
  • Handheld devices running Android 7.0 offer split-screen mode. In this mode, the system fills the screen with two apps, showing them either side-by-side or one-above-the-other. The user can drag the dividing line separating the two to make one app larger and the other smaller.
  • On TV devices, apps can put themselves in picture-in-picture mode, allowing them to continue showing content while the user browses or interacts with other apps.
  •  Manufacturers of larger devices can choose to enable freeform mode, in which the user can freely resize each activity. If the manufacturer enables this feature, the device offers freeform mode in addition to split-screen mode.
The user can switch into multi-window mode in the following ways:
  • If the user opens the Overview screen and performs a long press on an activity title, they can drag that activity to a highlighted portion of the screen to put the activity in multi-window mode.
  • If the user performs a long press on the Overview button, the device puts the current activity in multi-window mode, and opens the Overview screen to let the user choose another activity to share the screen.
Users can drag and drop data from one activity to another while the activities are sharing the screen. 

Multi-window mode does not change the activity lifecycle. In multi-window mode, only the activity the user has most recently interacted with is active at a given time. This activity is considered topmost. All other activities are in the paused state, even if they are visible. However, the system gives these paused-but-visible activities higher priority than activities that are not visible. If the user interacts with one of the paused activities, that activity is resumed, and the previously topmost activity is paused. When the user puts an app into multi-window mode, the system notifies the activity of a configuration change, as specified in Handling Runtime Changes. This also happens when the user resizes the app, or puts the app back into full-screen mode. Essentially, this change has the same activity-lifecycle implications as when the system notifies the app that the device has switched from portrait to landscape mode, except that the device dimensions are changed instead of just being swapped. As discussed in Handling Runtime Changes, your activity can handle the configuration change itself, or it can allow the system to destroy the activity and recreate it with the new dimensions. If the user is resizing a window and makes it larger in either dimension, the system resizes the activity to match the user action and issues runtime changes as needed. If the app lags behind in drawing in newly-exposed areas, the system temporarily fills those areas with the color specified by the windowBackground attribute or by the default windowBackgroundFallback style attribute.

Tuesday, 1 March 2016

HB Blog 105: Overview Of Build Automation Tools.

Build automation is the process of automating the creation of a software build and the associated processes including: compiling computer source code into binary code, packaging binary code, and running automated tests.

Basically, a build automation was accomplished through makefiles. A makefile is a file that contains a set of directives used with the make build automation tool. Build automation tool are divided into 2 categories namely,
  1. Build automation utility: - Whose primary purpose is to generate build artifacts through activities like compiling and linking the source code.
  2. Build automation servers: - These are general web based tools that execute build automation utilities on a scheduled or triggered basis; a continuous integration server is a type of build automation server. 
There are various tools that automates the process of compiling computer source code into binary code based on creation of make file.
Make-based tools such as GNU make, make, mk, etc.
Non-Make-based tools such as Apache Ant, Apache Maven, Gradle, etc.
Earlier, we used to use Eclipse for our Android development where, we used dx, aapt, etc. tools for Apk creation. Now, have you ever wondered why the res folder is in the same directory as your src folder? This is where the build system enters the picture. The build system automatically takes all the source files (.java or .xml), then applies the appropriate tool (e.g. takes java class files and converts them to dex files), and groups all of them into one compressed file, our beloved APK. This build system uses some conventions: an example of one is to specify the directory containing the source files (in Eclipse it is \src folder) or resources files (in Eclipse it is \res folder). Now, in order to automate all these tasks, there has to be a script; you can write your own build system using shell scripting in linux or batch files syntax in windows.

Gradle is another build system that takes the best features from other build systems and combines them into one. It is improved based off of their shortcomings. It is a JVM based build system, what that means is that you can write your own script in Java, which Android Studio makes use of. One more thing about gradle is that it is a plugin based system. This means if you have your own programming language and you want to automate the task of building some package (output like a JAR for Java) from sources then you can write a complete plugin in Java or Groovy, and distribute it to rest of world.

Have a look on few code snippets,

//build.gradle 
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
apply plugin: 'com.android.application'

android {
    compileSdkVersion 23
    buildToolsVersion "23.0.3"

    defaultConfig {
        applicationId "com.example.user.myapplication"
        minSdkVersion 15
        targetSdkVersion 23
        versionCode 1
        versionName "1.0"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
}

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    testCompile 'junit:junit:4.12'
    compile 'com.android.support:appcompat-v7:23.3.0'
}