Saturday 15 April 2017

HB Blog 133: Loader View Library For Android.

Now-a-days, people find animations and graphics pretty interesting. Mobile animations not only give an attractive user experience but, they also make the data loading process bearable for users. Users find progress bars, and loaders bit boring which make them loose interest in an application. There are many ways to handle these kinda problems.

In these post, I will show how to use a library for providing animations while loading data without user getting bored. These library is been developed to provide both TextView and ImageView the ability to show shimmer (animation loader) before any text or image is shown. Useful when waiting for data to be loaded from the network.

Refer the below link for complete sample code:-

Download Sample Code

Have a look on few code snippets,

//build.gradle
1
2
3
dependencies {
    compile 'com.elyeproj.libraries:loaderviewlibrary:1.3.0'
}

//activity_main.xml
 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
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical">

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_margin="5dp"
        android:layout_height="wrap_content">

        <com.elyeproj.loaderviewlibrary.LoaderImageView
            android:id="@+id/image_icon"
            android:layout_width="100dp"
            android:layout_height="100dp"
            android:layout_marginEnd="16dp"
            android:layout_marginRight="16dp"
            app:use_gradient="true" />

        <LinearLayout
            android:id="@+id/container_text"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_centerVertical="true"
            android:layout_toEndOf="@id/image_icon"
            android:layout_toRightOf="@id/image_icon"
            android:orientation="vertical">

            <com.elyeproj.loaderviewlibrary.LoaderTextView
                android:id="@+id/txt_name"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:textSize="@dimen/title_font_size"
                android:textStyle="bold"
                app:height_weight="0.8"
                app:use_gradient="true"
                app:width_weight="0.6" />

            <com.elyeproj.loaderviewlibrary.LoaderTextView
                android:id="@+id/txt_title"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_marginTop="8dp"
                android:textSize="@dimen/standard_font_size"
                app:height_weight="0.8"
                app:width_weight="1.0" />

            <com.elyeproj.loaderviewlibrary.LoaderTextView
                android:id="@+id/txt_phone"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_marginTop="4dp"
                android:textSize="@dimen/standard_font_size"
                app:height_weight="0.8"
                app:width_weight="0.4" />

            <com.elyeproj.loaderviewlibrary.LoaderTextView
                android:id="@+id/txt_email"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_marginTop="4dp"
                android:textSize="@dimen/standard_font_size"
                app:height_weight="0.8"
                app:width_weight="0.9" />

        </LinearLayout>

    </RelativeLayout>

    <Button
        android:id="@+id/btn_reset"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_margin="@dimen/activity_vertical_margin"
        android:onClick="resetLoader"
        android:text="Reset" />
</LinearLayout>

//MainActivity.java
 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
package com.example.harshalbenake.loaderviewanimation;

import android.os.AsyncTask;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;

import com.elyeproj.loaderviewlibrary.LoaderImageView;
import com.elyeproj.loaderviewlibrary.LoaderTextView;

public class MainActivity extends AppCompatActivity {

    private int WAIT_DURATION = 5000;
    private DummyWait dummyWait;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        loadData();
    }

    private void loadData() {
        if (dummyWait != null) {
            dummyWait.cancel(true);
        }
        dummyWait = new DummyWait();
        dummyWait.execute();
    }

    private void postLoadData() {
        ((TextView)findViewById(R.id.txt_name)).setText("Harshal Benake");
        ((TextView)findViewById(R.id.txt_title)).setText("Android Dev");
        ((TextView)findViewById(R.id.txt_phone)).setText("7588871488");
        ((TextView)findViewById(R.id.txt_email)).setText("harshalbenake@gmail.com");
        ((ImageView)findViewById(R.id.image_icon)).setImageResource(R.drawable.ic_launcher);
    }

    public void resetLoader(View view) {
        ((LoaderTextView)findViewById(R.id.txt_name)).resetLoader();
        ((LoaderTextView)findViewById(R.id.txt_title)).resetLoader();
        ((LoaderTextView)findViewById(R.id.txt_phone)).resetLoader();
        ((LoaderTextView)findViewById(R.id.txt_email)).resetLoader();
        ((LoaderImageView)findViewById(R.id.image_icon)).resetLoader();
        loadData();
    }

    class DummyWait extends AsyncTask<Void, Void, Void> {
        @Override
        protected void onPreExecute() {
            super.onPreExecute();
        }

        @Override
        protected Void doInBackground(Void... params) {
            try {
                Thread.sleep(WAIT_DURATION);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return null;
        }

        @Override
        protected void onPostExecute(Void result) {
            super.onPostExecute(result);
            postLoadData();
        }
    }

}

Saturday 1 April 2017

HB Blog 132: Does Your Phone Get Hang? Know Why...

Android phone sometimes gets hang. Does your phone also have same problem? Don't blame OEMs the original equipment manufacturers or Open Handset Alliance for these problems. At least, not completely because Android applications are more responsible for that.
Developers call it as memory leaks in technical terms. Now a days, lot of devices with unlimited memory are coming in the market. But, it is not the SD card memory that we are having scarcity. It is the heap size or the application size.

So what is the heap size?
Android is a full multitasking system so it’s possible to run multiple programs at the same time and obviously each one can’t use all of your device memory. For this reason there is a hard limit on your Android application’s heap size: if your application needs to allocate more memory and you have gone up to that heap size already, you are basically going to get a “out of memory error”.
Heap size limit is device dependent and it has changed a lot over the years, the first Android phone (the G1) has 16MB heap size limit.

Even if you do not plan on using all of this memory, you should use as little as possible to let other applications run without getting them killed. The more applications Android can keep in memory, the faster it will be for the user to switch between his apps. If these memory is allocated more by the application, your phone gets hang and we get a memory leaks issues.

There are many reasons why we face these kind of problems such as large bitmaps, resources, etc. But, most of time it is the context that are kept long-lived references. In Android, a Context is used for many operations but mostly to load and access resources. This is why all the widgets receive a Context parameter in their constructor. In a regular Android application, you usually have two kinds of Context, Activity and Application. It's usually the first one that the developer passes to classes and methods that need a Context. This means that views have a reference to the entire activity and therefore to anything your activity is holding onto, usually the entire View hierarchy and all its resources. Therefore, if you leak the Context ("leak" meaning you keep a reference to it thus preventing the GC from collecting it), you leak a lot of memory. Leaking an entire activity can be really easy if you're not careful.
When the screen orientation changes the system will, by default, destroy the current activity and create a new one while preserving its state. In doing so, Android will reload the application's UI from the resources.

Basically, we can avoid configuration changes by locking screen rotation. But, it is something that wont provide user experience. Such as watching movie in just portrait mode without landscape feature. So we can go for config change attribute in Android Manifest file as below snippet,


1
2
3
<activity android:name=".MyActivity"
    android:configChanges="orientation|keyboardHidden"
android:label="@string/app_name">


also, do not keep long-lived references to a context-activity (a reference to an activity should have the same life cycle as the activity itself), try using the context-application instead of a context-activity.
Avoid non-static inner classes in an activity if you don't control their life cycle, use a static inner class and make a weak reference to the activity inside. And, many more ways to explore time to time while developing Android applications...