Pages

Tuesday, October 18, 2016

Android's bad company: IntentService, ResultReceiver and Configuration Changes

| Intro |

I has a simple task: convert the given location (latitude and longitude coordinates) into the human-readable address strings (city, street, post code etc.). On iOS platform this could be solved with the help of just several lines of code. On Android it turned out to be a complicated and error-prone problem with a lot of pitfalls.

At the first glance, this task can be very easily accomplished using the official code sample. However, since we don't live in a perfect world, this sample is not suitable for the production code. It may look good for some Android newcomers but if we have a goal to develop a real-world app a lot of different edge-cases (as much as possible) must be taken into account.

Here are just some issues we would encounter in case of blind copy-pasting the Google's code:
  • As all we know, after the configuration changes your Activities (or non-retained Fragments) are recreated (I suppose you don't use this nasty hack android:configChanges="orientation|screenSize"). When they have been recreated the old ones must be garbage collected and if not - say hello to a memory leak. As will be discussed on the next paragraphs, the IntentService from the Google's sample during ongoing operation holds the indirect reference to the Activity associated with the ResultReceiver and hence prevents its deconstruction. Check also this StackOverflow question.
  • What if the IntentService sends the result at the time when the activity has been temporarily destroyed (e.g. after the device rotation)? Yes, it will go to nowhere and that means the whole work is simply lost. 
  • If the device quickly changes the location then each time it needs to obtain the new address data. But the IntentService doesn't provide us opportunity to cancel previous or pending tasks. So the device must waste resources for the operations which are completely useless now.
Ok, let's go ahead. I'll show you what I've found regarding this subject after some research.

How does IntentService send the results

To obtain the results from the IntentService Google suggests us to use a ResultReceiver subclass. The ResultReceiver implements Parcelable interface that's why it can be easily added to the Intent that is used to start the service. The following code snippet depicts this process:

1
2
3
4
5
6
7
8
        // Create an intent for passing to the intent service responsible for fetching the address.
        Intent intent = new Intent(this, FetchAddressIntentService.class);

        // Pass the result receiver as an extra to the service.
        intent.putExtra(Constants.RECEIVER, mResultReceiver);

        // Start the service
        startService(intent);

Every Android developer must be familiar with such procedure - it's a common task to start services or activities using intents containing some data. Usually we put data to the intent using Intent class putExtra method. For example, it can be some primitive type (int, boolean etc.) or an object that implements Parcelable interface. The data then can be extracted using such code:

mReceiver = intent.getParcelableExtra(Constants.RECEIVER);

Intuitively we expect to get a new copy of the parcelable object from the getParcelableExtra method. And it worth noting that this object should not influence the life-cycle of the original object we put in to the parcel. There are even answers on StackOverflow which suggest to use Parcelable interface as a deep copying method: link1, link2.

But it turns out this is not always the case. After a closer look at the Parcel docs I found a very interesting paragraph, here is the quote:
An unusual feature of Parcel is the ability to read and write active objects. For these objects the actual contents of the object is not written, rather a special token referencing the object is written. When reading the object back from the Parcel, you do not get a new instance of the object, but rather a handle that operates on the exact same object that was originally written. 
Wow! And how is that possible we do not get a new instance of the object? Does it mean we obtain just another reference to the original object and hence this reference will prevent it from being garbage collected? After a lot of searching, googling, stackoverflowing I simply didn't find any comprehensive answer.

As a last resort, I decided to look at the ResultReceiver.java source to find out what's really going on here. Of course, you can analyse it yourself, but here are the most important parts. According to the parcelable interface, we should implement writeToParcel and static CREATOR field which in turn requires a constructor taking a Parcel as an argument. So how does our ResultReceiver is getting unparceled? Look at the snippet:

1
2
3
4
5
    ResultReceiver(Parcel in) {
        mLocal = false;
        mHandler = null;
        mReceiver = IResultReceiver.Stub.asInterface(in.readStrongBinder());
    }

As you can see, the readStrongBinder() method is used to extract the original object from the parcel. And what the parcel docs state about it? Yay! That method deals with the aforementioned active objects which means we do not get a new instance!

To understand what is the IResultReceiver.Stub.asInterface line for look at the official docs regarding AIDL technology. Take a closer look at this statement:
  • Objects are reference counted across processes.
Finally I found the info I was looking for. It's easy to see now that our friend IntentService does not make a deep copy of the result receiver. Instead the rather complicated approach called AIDL is used here.

In simple words, what all this means is that as long as the IntentService is doing its job it will hold the indirect reference to the original result receiver object. And if the activity which started the service also points to the same object after the configuration change we have a memory leak. Nice.

Weak References and Retained Fragment

There are different methods you can find in the internet to overcome this issue. But most of them have disadvantages that are not suitable for the production code. For example, some guys suggest to save the ResultReceiver subclass into the Bundle and then extract it after the configuration change. But in this case only the base class ResultReceiver will be extracted and all our callbacks will be lost (callbacks are just interfaces, they cannot be added to a parcel!).

The only good advise I've found is to use LocalBroadcastManager and forget about this IntentService + ResultReceiver company.

Nonetheless, I finally managed to solve this problem using the good old weak references and a retained fragment. Here is the step-by-step instruction:
  • To allow the old activity garbage collection it must hold a weak reference to the result receiver object that's why the result receiver is stored in the retained fragment like this:

  •  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
    /**
     * A fragment that retains the stateful objects during a configuration change.
     */
    public final class MainRetainedFragment extends Fragment {
        /**
         * A key used by the fragment manager to set/retrieve the fragment.
         */
        public static final String FRAGMENT_TAG = "MAIN_RETAINED_FRAGMENT_TAG";
    
        /**
         * An object used to receive the result of the intent service work.
         */
        @Nullable
        private ResultReceiver mResultReceiver;
    
        /**
         * A factory method used to create a fragment in accordance with Android development best practices.
         * @return A new instance of MainRetainedFragment.
         */
        public static MainRetainedFragment newInstance() {
            return new MainRetainedFragment();
        }
    
        @Override
        public void onCreate(@Nullable Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
    
            // Retain this fragment.
            setRetainInstance(true);
        }
    
        @Override
        public String toString() {
            return "MainRetainedFragment{" +
                    "mResultReceiver=" + mResultReceiver +
                    '}';
        }
    
        /**
         * Gets the result receiver object.
         *
         * @return ResultReceiver object.
         */
        @Nullable
        public ResultReceiver getResultReceiver() {
            return mResultReceiver;
        }
    
        /**
         * Set the result receiver object.
         *
         * @param resultReceiver An instance of the ResultReceiver object.
         */
        public void setResultReceiver(@NonNull ResultReceiver resultReceiver) {
            mResultReceiver = resultReceiver;
        }
    }
    

  • Now the result receiver survives during the configuration changes and, moreover, it has a chance to cache the results obtained from the intent service and send them back to the activity when it is available again. The last code snippet depicts how we can access the result receiver object from the activity:

  •  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
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
    
            final FragmentManager fragmentManager = getSupportFragmentManager();
    
            MainRetainedFragment mainRetainedFragment = (MainRetainedFragment) fragmentManager.
                    findFragmentByTag(MainRetainedFragment.FRAGMENT_TAG);
    
            AddressResultReceiver addressResultReceiver;
    
            if (mainRetainedFragment == null) {
    
                // Create a new fragment if it is null (in case it is accessed for the first time).
                mainRetainedFragment = MainRetainedFragment.newInstance();
                fragmentManager.beginTransaction().
                        add(mainRetainedFragment, MainRetainedFragment.FRAGMENT_TAG).commit();
    
                // Create a new result receiver.
                addressResultReceiver = new AddressResultReceiver(new Handler());
    
                 // Save the result receiver in the retained fragment.
                mainRetainedFragment.setResultReceiver(addressResultReceiver);
    
                // Set the activity as a callback handler.
                // Note: this setter must set the callback handler as a weak reference.
                addressResultReceiver.setReceiverCallbackHandler(this);
            } else {
                // Use the retained result receiver.
                try {
                    addressResultReceiver = (AddressResultReceiver) mainRetainedFragment.getResultReceiver();
                } catch (ClassCastException e) {
                    e.printStackTrace();
                    throw new ClassCastException(" The mResultReceiver must be of AddressResultReceiver type.");
                }
            }
    
            // Set the result receiver as a weak reference.
            mResultReceiverWeakRef = new WeakReference<>(addressResultReceiver);
        }
    

I have tested this approach using LeakCanary - a memory leak detection library and everything seems to work fine.

As always, I hope this will help someone. If something is still unclear, please feel free to leave a comment.

Friday, October 7, 2016

Android Studio 9-Patch Images

| Intro |

Developing my new App which targets Android platform and currently has a codename MyPlaceApp (I'm pondering about changing it to something more cool) I have encountered a lot of different issues and some of them were not so easy resolvable. Obviously, due to time constraints I won't discuss every solution I used in my project in this blog post. Today I'm gonna talk about an amazing way of preparing and integrating with Android Studio a bunch of the raster images that can be resized during runtime preserving the original quality.

Image scaling

This is a common task during mobile development to scale the image resources for the different screen sizes/densities regardless of the platform (Android or iOS). There are several techniques with the help of which we can obtain the required scaling. In particular, Android Developer Portal recommend us to use the following options:
  • Save the resized .png images into folders which define the abstract generalised screen densities: ldpi, mdpi, hdpi, xhdpi etc. By following this link you can check matching between the popular devices and the screen density groups. Android system automatically detects the current screen properties and loads the resources from the proper folders;
  • Import vector drawable into app/src/main/res/drawable/ folder. In this case images will be automatically resized without degrading the quality.

    Note: Android Support Library since version 23.2 provides support of vector drawables for the older API levels.

  • Draw custom shapes which can be defined in XML (e.g., res/drawable/filename.xml) with the specified colours or gradients.

Every option has its own pros and cons. Providing an image for each possible screen density can be very time consuming. At the same time, as the docs state
the initial loading of a vector drawable can cost more CPU cycles than the corresponding raster image
As per my case, I needed to find the solution that would allow me to use the raster images and, on the other hand, resize them without quality loss at the runtime (in accordance with the text content). At the first glance, this seems like magic. But finally I've found such solution. And it is called 9-Patch Images.

9-Patch Images

Here is the step-by-step instruction on how you can create and use the 9-Patch Images in your Android Studio projects:
  1. Using your favourite graphic editor create an image you want to use in your project. Here is what I got with the help of Affinity Designer:

  2. Background image created in Affinity Designer

    Since this is a vector editing program the image can be easily resized to .png with any resolution. But keep in mind that 9-Patch Images can be only stretched (not shrunk) so you should start with the smallest version of your icon.

  3. When you have the image ready copy it to the specific drawable folder. For example, this can be app/src/main/res/drawable-mdpi/.

    Note: For best results you should repeat this process for each screen density (xhdpi, xxhdpi etc.).

    Now right click the imported .png file in Android Studio and choose "Create 9-Patch File" option. You should see the open built-in 9-Patch editor like in the image below:

    Image in the built-in 9-Patch editor

  4. And now the most important part. To make it possible to scale this image while preserving the good quality you must draw 1px width black lines which would define the stretchable areas. The editor should already have added the 1px space around each image side. The topmost and leftmost lines specify the part of the original image that must be stretched when needed. Feel free to divide this line into multiple segments if you want to prevent scaling some specific elements (e.g., rounded corners) to save image quality. During the stretching process the Android platform just repeats the same colour pixels so the complex image parts must be excluded from this with the help of the aforementioned black lines.The right and bottom 1px black lines define the content area. For instance, if you have some dynamically generated text these lines would specify the exact rectangle where this text should be placed. If it cannot fit the defined area the image will be stretched in accordance with the specified stretchable parts.

    Let's look closer at the options which the built-in editor propose us:

    9-Patch editor with all checkbox selected

    Show lock - If this is selected on mouse hovering you will see the whole image covered by the diagonal red lines and only the 1px width lines on each side will remain transparent. This is just a reminder of where you are allowed to draw the meta black lines.

    Show patches - This option allows you to see the stretchable areas of your image. The green regions define the patches that will stretch either horizontal or vertical (not both). The pink define the fully stretchable patch.

    Show content - Shows the rectangle area where the content will be placed above the image.

    Show bad patches - This is the interesting one. When it is selected the problem parts of the image will be highlighted with the red rectangles. The term "problem parts" in this case means such image segments that can be distorted during scaling. As I said earlier, Android just repeats the same pixels when stretching the 9-Patch image. So if you have defined such stretchable areas where the adjacent pixels are different (even a little bit) the 9-Patch will doughtily report you that it is something wrong with your current image configuration. To ensure that the image will look good in all possible scenarios just select the stretchable areas where all the adjacent pixels are the same (have the same colour).


Basically, that's it. If you want to simplify the process of creating multiple 9-Patch images check out this nice third-party tool - Nine-patch Generator.

Hope this info will help someone.

Thursday, August 11, 2016

Private CocoaPods - Practical Advise

| Intro |

Recently I was faced with a challenge of sharing a source code of a privately developed iOS app with a group of customers. In this app I used several helper pods which I found using the official CocoaPods search engine and also the pods which were developed privately. All privately developed pods were synchronized with a BitBucket repository and I didn't want to give every customer access to it.

As far as I can see, there are such options that could be used in this case:

  • Distribute the source code as is and ask the customers to reconfigure the project in accordance with their own private repository;
  • Give every customer read access to my private repository
  • Copy private pods into the main project folder and use the relative references in a podfile
  • Use some other mechanism instead of CocoaPods 

I wanted to make the process of app configuration for customers as simple as possible so I decided to avoid the first option and look for another ways which would fulfill all my requirements.

I could, of course, give every customer read access to my private repository but, obviously, this is not a very scalable and flexible solution. So after doing some research I have finally chosen the third option, i.e. distributing the private pods alongside the main source code folder and using the relative references.

I this blog post I want to describe the precise steps of private pods configuration. Also I would like to show you how to distribute the source code containing private pods which would require zero-configuration and how to associate your private pod with a private repository.

Private Repos

There are a lot of articles in the internet describing the benefits of using a version control system (VCS) for your project changes management. In short words, with the help of VCS it is possible to undo all the messed-up things you've made in a few clicks. I would like to quote Troy Hunt here to encourage you to use VCS (in case if you don't):
The only measure of progress is working code in source control. If it's not in source control, it doesn't exist.
Besides the flexibility that VCS gives you for free it also can act as a backup tool because:
  1. Every collaborator has a full-fledged project version saved on his disk (including the project's complete history)
  2. The full project package is also stored in the center repository (which should be created in the dedicated sever) 
Depending on your project type, you can choose, basically,  between two repository configurations:
  1. Private repository
  2. Public repository
Public repositories are viewed by anyone who has a computer with access to the internet. They are usually used for open-source projects, when you don't need to control access to the source code and just want to show the world your great work and, perhaps, to draw attention of the potential collaborators.

If your application is closed-source, the private repositories would be your preferred choice. By using the private repos you can protect you code from being stolen by some bad guys. Of course, it's possible to indicates in the license notes that the derivative works and commercial usage are not allowed. But it's up to you to find out whether your code is used legally or not. 

There are many services you can use to create either public or private repositories. Each service provider has different terms and conditions. 

For example, GitHub allows you to create unlimited number of public repos, but at the same time to make it possible to create a private repo you should have a paid account.

Personally, for all my closed-source apps I've chosen BitBucket service. You can read more about its pricing model here. What sets it above all other solutions is that it's completely free for small teams (up to 5 developers) and you can have unlimited number of public and private repositories.

Before continue to the next section you should have already configured the private repo containing your app's source code using the preferred service. For example, to configure a BitBucket private repo you should follow these instructions

Private Spec Repo

When you run the following command from the project main directory:

pod install

the CocoaPods installed on your system, first of all, scans this directory for the presence of podfile. If the podfile has been found it starts to analyze the instructions contained in this file which specify how to resolve all dependencies required by the current project.

As an example, the podfile could look like the next code snippet:

platform :ios, '8.0'
use_frameworks!

source 'https://github.com/CocoaPods/Specs.git'

def common_pods
    pod 'XCGLogger', '~> 3.3'
    pod 'SVProgressHUD', '~> 2.0'
end

target 'YOUR_PROJECT_TARGET' do
    common_pods
 
    target 'YOUR_PROJECT_TEST_TARGET' do
      inherit! :search_paths

    end
end

As you can see, in the podfile we only indicate the required libraries names and the preferred versions. This file is, basically, just a declarative way used to describe what pods do you need for your app. But CocoaPods system also needs to know the exact instructions of how to integrate these pods with the Xcode project. Podspec files include exactly these details.

By default, as depicted in the previous code snippet, the podspec files are located in the CocoaPods public Spec repository. It is specified by this line:

source 'https://github.com/CocoaPods/Specs.git'

So if your goal is to make all things to be private you should create your own private Spec repository. This can be an ordinary repository just like the one you have created in the previous section using BitBucket's tutorial (or instructions of your preferred service provider).

Don't push anything to the private Spec repository for now. You will use CocoaPods pod tool for this purpose in the next section.

What you should understand now is that all podspec files of your privately developed pods are hosted in your private Spec repository. And to correctly integrate private pods with your project the URL of this repo must be specified in the podfile like this:

source 'URL_TO_YOUR_PRIVATE_SPEC_REPO'

Note: You shouldn't be worried about the source lines order unless your pods are located in both repositories. In the last case CocoaPods will use the highest version of a Pod of the first source which includes the Pod.

Add Private Spec Repo to CocoaPods

To simplify the process of adding the podspec files to you private Spec repo you should firstly add this Spec repo to CocoaPods installed on your local machine. It can be done with the help of the following command:

pod repo add REPO_NAME SOURCE_URL

As a REPO_NAME you can use any name you want (for example, this can be "my-cool-pods-spec").

A SOURCE_URL must match the URL of the previusly configured private Spec repo.

It's worth noting that you can always check which Spec repos you have already added by using the next command:

cd ~/.cocoapods/repos/; ls

On my test machine this command gives the output that is depicted in the following image:

The output that shows all added Spec repos names

Push Podspec Files to Private Spec Repo

Now to push the podspec files of your privately developed pods to the private Spec repo all you need to do is to run the following command:

pod repo push REPO_NAME SPEC_NAME.podspec

REPO_NAME is a name of the previously added private Spec repo  to your local CocoaPods installation.

SPEC_NAME.podspec is a podspec file of some privately developed pod.

CocoaPods will automatically run all the required validation tests (like pod spec lint) and configure the remote private Spec repo without your intrusion.

Note: I assume that you already know how to create and configure the pod libraries. I can create another blog post regarding this subject. Please, let me know in the comments whether you need it.

This is all configuration that must be done for the development stage. But if also you want to distribute the source code to your customers and to make it possible for them to run your app just by double-clicking the PROJECT_NAME.xcworkspace file (without preliminary setup), please, read the next section.

Distribute Source Code to Customers

To simplify the process of source code configuration for your customers you can follow the next advices:
  • Before distributing the source code copy all the required private pods into the main project directory. Let's call it 'Private Pods'.
  • In your podfile add the relative references to each private pod used in the project. It can be done like this:
pod 'MyCoolPrivatePod', :path => './Private Pods/MyCoolPrivatePod'

Now when the customers run pod install command all required pods will be installed automatically without the need of the private repos configuration. Hence, the line with the source URL should be removed (or commented out) from your podfile as it is no longer required:

#source 'URL_TO_YOUR_PRIVATE_SPEC_REPO'

Also don't forget about the following edge cases:
  • Do not add Pods folder to .gitignore, this way your project could be compiled out of the box without the need to run pod install
  • It's fine to add Private Pods folder to .gitignore because every private pod should be already under the source control system
That's it. Thanks for your time. Hope this blog post will be helpful for you.

Used Software Versions

CocoaPods: 1.0.1
Xcode: 7.3.1