Showing posts with label Android Coding Problems. Show all posts
Showing posts with label Android Coding Problems. Show all posts

How to Fix Android App Bugs Without Breaking Everything Else

When fixing bugs in an Android app project, it's crucial to not just patch the issue, but to ensure stability, performance, and maintainability of the app. Here's a structured list of key areas to address during bug fixing:


 1. Reproduce and Isolate the Bug

  • Reproduce the issue consistently in a local or QA environment.

  • Logcat analysis: Use Log.d, Log.e, Timber, etc., to check stack traces and contextual logs.

  • Crash reports: Utilize tools like Firebase Crashlytics, Sentry, or Bugsnag to identify the root cause.

  • Debug tools: Use Android Studio debugger, breakpoints, or Flipper for runtime inspection.


 2. Identify Root Cause (Not Just Symptoms)

  • Avoid surface-level fixes that mask the issue.

  • Trace through stack traces, thread state, and lifecycle behavior.

  • Use techniques like binary search debugging, rollback comparison, or git bisect.


 3. Check Affected Scope and Dependencies

  • Understand the scope: What modules/features are impacted?

  • Review dependencies (third-party libraries, APIs, database, sensors, etc.).

  • Check for side effects or hidden regressions in related components.


4. Fix Using Best Practices

  • Apply Kotlin null safety, immutability, lifecycle awareness, and coroutine exception handling.

  • Respect architecture (e.g., Clean Architecture, MVVM).

  • Avoid memory leaks (e.g., by cleaning up observers or references in onCleared() or onDestroy()).


5. Test the Fix Thoroughly

  • Unit test the logic.

  • UI/Instrumentation tests (e.g., using Espresso).

  • Edge cases: test on different devices, orientations, languages, and API levels.

  • Use mock data and real-world scenarios.

  • Automate via CI/CD if possible.


6. Code Review & PR Standards

  • Push changes to a feature or hotfix branch.

  • Follow team PR templates/checklists.

  • Include before/after behavior screenshots or videos (especially for UI bugs).

  • Peer review for readability, performance, and maintainability.


7. Update Logs, Metrics, and Documentation

  • Update CHANGELOG.md or release notes.

  • Add JIRA/Trello references and resolution notes.

  • If it's a backend-related issue, sync with backend devs.

  • If API contract changes, update API schema or versioning.


8. Deploy Carefully

  • Use staged rollout (e.g., Play Console’s percentage rollout).

  • Monitor logs and crash analytics post-deploy.

  • Add feature flags if needed for emergency disable.

9. Retrospective or Root Cause Analysis (RCA)

  • Log the root cause and learnings in a shared document.

  • Improve test coverage or monitoring based on the gap.

  • Share insights with the team in sprint retro or knowledge base.


Tools That Help:

Category Tool
Logging Timber, Logcat, Crashlytics
Debugging Android Studio, Flipper, LeakCanary
Monitoring Firebase Performance, Sentry
Testing JUnit, Espresso, MockK, Robolectric
CI/CD GitHub Actions, Bitrise, Jenkins
Issue Tracking Jira, Trello, Linear

Here’s a realistic example of a bug fix in an Android app, including the bug description, root cause, fix implementation, and testing strategy.


Example : App Crashes on Orientation Change While Loading Data


Bug Report

  • Title: App crashes when rotating screen during data loading on ProfileFragment.

  • Severity: High (crashes app)

  • Environment: Android 12, Pixel 5, API 31

  • Steps to Reproduce:

    1. Open the app and navigate to Profile tab.

    2. While the data is loading (loading spinner showing), rotate the screen.

    3. App crashes with IllegalStateException.


Crash Log

java.lang.IllegalStateException: ViewModel not attached to lifecycle yet
    at ProfileViewModel.getProfileData(ProfileViewModel.kt:45)

Root Cause

  • The ProfileFragment was trying to observe LiveData before the view was fully recreated after orientation change.

  • The data load was tied to the fragment’s onCreateView rather than viewLifecycleOwner.


Fix

Refactor LiveData observer to bind with viewLifecycleOwner instead of the fragment's lifecycle.

// Before (incorrect)
viewModel.profileLiveData.observe(this, Observer {
    // update UI
})

// After (correct)
viewModel.profileLiveData.observe(viewLifecycleOwner, Observer {
    // update UI
})

Additionally, use repeatOnLifecycle for Kotlin Flow in Compose or ViewModel coroutine collection:

lifecycleScope.launch {
    viewLifecycleOwner.lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) {
        viewModel.profileFlow.collect {
            // Update UI
        }
    }
}

Testing Done

  • Rotated device multiple times during and after data load.

  • Verified no crashes on API 30, 31, 34.

  • Verified ViewModel data not lost.

  • Espresso UI test added for rotation.


PR Notes

πŸ“Œ Fixed crash on orientation change in ProfileFragment by using viewLifecycleOwner for LiveData observer. Added test for configuration change handling.
JIRA Ticket: APP-2931
Impact: Profile screen lifecycle handling.


Improvement

Added lifecycle-aware logging to catch future orientation-related issues:

Log.d("Lifecycle", "ProfileFragment state: ${lifecycle.currentState}")

πŸ“’ Feedback: Did you find this article helpful? Let me know your thoughts or suggestions for improvements! 😊 please leave a comment below. I’d love to hear from you! πŸ‘‡

Happy coding! πŸ’»✨

Bit Manipulation - Finding the missing number in a sequence in Kotlin


Problem Statement:

You are given an array containing n distinct numbers from 0 to n. Exactly one number in this range is missing from the array. You must find this missing number using bit manipulation techniques.

Example:

Input: [3, 0, 1]
Output: 2

Input: [9,6,4,2,3,5,7,0,1]
Output: 8

Explanation (using XOR):

A very efficient way to solve this using bit manipulation is to leverage XOR (^), which has these properties:

  • a ^ a = 0 (XOR of a number with itself is zero)
  • a ^ 0 = a (XOR of a number with zero is itself)
  • XOR is commutative and associative

Therefore, if we XOR all the indices and all the numbers, every number present will cancel out, leaving the missing number.


Implementation in Kotlin:

fun missingNumber(nums: IntArray): Int {
    var xor = nums.size // start with n, since array is from 0 to n
    for (i in nums.indices) {
        xor = xor xor i xor nums[i]
    }
    return xor
}

fun main() {
    println(missingNumber(intArrayOf(3, 0, 1))) // Output: 2
    println(missingNumber(intArrayOf(9,6,4,2,3,5,7,0,1))) // Output: 8
    println(missingNumber(intArrayOf(0,1))) // Output: 2
}

Complexity:

  • Time Complexity: O(n) (Iterates through the array once)
  • Space Complexity: O(1) (No extra space used)


Thanks for reading! πŸŽ‰ I'd love to know what you think about the article. Did it resonate with you? πŸ’­ Any suggestions for improvement? I’m always open to hearing your feedback to improve my posts! πŸ‘‡πŸš€. Happy coding! πŸ’»✨

Code Challenge: Number of Islands in Kotlin

The Number of Islands problem is a common interview question that involves counting the number of islands in a 2D grid. Each island is made up of connected pieces of land (denoted as '1') surrounded by water (denoted as '0'). The challenge is to count how many separate islands exist in the grid, where an island is formed by horizontally or vertically adjacent lands.



We will discuss multiple ways to solve this problem, explaining their pros and cons. Let's dive into solving this problem using Kotlin.


Problem Definition

Given a 2D binary grid grid, return the number of islands. An island is surrounded by water and is formed by connecting adjacent lands horizontally or vertically. The grid is surrounded by water on all sides.

Example 1:

Input:

[
  ["1", "1", "1", "1", "0"],
  ["1", "1", "0", "1", "0"],
  ["1", "1", "0", "0", "0"],
  ["0", "0", "0", "0", "0"]
]

Output:

1

Example 2:

Input:

[
  ["1", "1", "0", "0", "0"],
  ["1", "1", "0", "0", "0"],
  ["0", "0", "1", "0", "0"],
  ["0", "0", "0", "1", "1"]
]

Output:

3

Approach 1: Depth-First Search (DFS)

The most intuitive approach is to use Depth-First Search (DFS). We start from each land cell ('1'), mark it as visited (or change it to water '0'), and recursively check its adjacent cells (up, down, left, right). Every time we find an unvisited land cell, we count it as a new island.

Algorithm:

  1. Traverse the grid.
  2. If we find a '1', increment the island count and use DFS to mark the entire island as visited.
  3. For each DFS, recursively mark the neighboring land cells.

Kotlin Implementation:

fun numIslands(grid: Array<CharArray>): Int {
    if (grid.isEmpty()) return 0
    var count = 0

    // Define DFS function
    fun dfs(grid: Array<CharArray>, i: Int, j: Int) {
        // Return if out of bounds or at water
        if (i < 0 || i >= grid.size || j < 0 || j >= grid[0].size || grid[i][j] == '0') return
        // Mark the land as visited
        grid[i][j] = '0'
        // Visit all 4 adjacent cells
        dfs(grid, i + 1, j) // down
        dfs(grid, i - 1, j) // up
        dfs(grid, i, j + 1) // right
        dfs(grid, i, j - 1) // left
    }

    // Iterate over the grid
    for (i in grid.indices) {
        for (j in grid[i].indices) {
            if (grid[i][j] == '1') {
                // Found a new island
                count++
                dfs(grid, i, j)
            }
        }
    }
    return count
}

Time Complexity:

  • O(m * n), where m is the number of rows and n is the number of columns. Each cell is visited once.

Space Complexity:

  • O(m * n) in the worst case (if the entire grid is land), as we may need to store all cells in the call stack due to recursion.

Approach 2: Breadth-First Search (BFS)

We can also use Breadth-First Search (BFS). Instead of using recursion like in DFS, we use a queue to explore all adjacent cells iteratively. The process is similar, but the main difference lies in the order of exploration.

Algorithm:

  1. Start from an unvisited land cell ('1').
  2. Use a queue to explore all adjacent land cells and mark them as visited.
  3. Each BFS initiation represents a new island.

Kotlin Implementation:

fun numIslands(grid: Array<CharArray>): Int {
    if (grid.isEmpty()) return 0
    var count = 0
    val directions = arrayOf(intArrayOf(0, 1), intArrayOf(1, 0), intArrayOf(0, -1), intArrayOf(-1, 0))

    fun bfs(i: Int, j: Int) {
        val queue: LinkedList<Pair<Int, Int>>= LinkedList()
        queue.offer(Pair(i, j))
        grid[i][j] = '0' // Mark the starting cell as visited

        while (queue.isNotEmpty()) {
            val (x, y) = queue.poll()
            for (dir in directions) {
                val newX = x + dir[0]
                val newY = y + dir[1]
                if (newX in grid.indices && newY in grid[0].indices && grid[newX][newY] == '1') {
                    grid[newX][newY] = '0' // Mark as visited
                    queue.offer(Pair(newX, newY))
                }
            }
        }
    }

    for (i in grid.indices) {
        for (j in grid[i].indices) {
            if (grid[i][j] == '1') {
                count++
                bfs(i, j)
            }
        }
    }
    return count
}

Time Complexity:

  • O(m * n), where m is the number of rows and n is the number of columns. Each cell is visited once.

Space Complexity:

  • O(m * n), which is required for the queue in the worst case.

Approach 3: Union-Find (Disjoint Set)

The Union-Find (or Disjoint Set) approach is another efficient way to solve this problem. The idea is to treat each land cell as an individual component and then union adjacent land cells. Once all unions are complete, the number of islands is simply the number of disjoint sets.

Algorithm:

  1. Initialize each land cell as a separate island.
  2. For each neighboring land cell, perform a union operation.
  3. The number of islands will be the number of disjoint sets.

Kotlin Implementation:

class UnionFind(private val m: Int, private val n: Int) {
    private val parent = IntArray(m * n) { it }

    fun find(x: Int): Int {
        if (parent[x] != x) parent[x] = find(parent[x]) // Path compression
        return parent[x]
    }

    fun union(x: Int, y: Int) {
        val rootX = find(x)
        val rootY = find(y)
        if (rootX != rootY) parent[rootX] = rootY
    }

    fun getCount(): Int {
        return parent.count { it == it }
    }
}

fun numIslands(grid: Array<CharArray>): Int {
    if (grid.isEmpty()) return 0
    val m = grid.size
    val n = grid[0].size
    val uf = UnionFind(m, n)

    for (i in grid.indices) {
        for (j in grid[i].indices) {
            if (grid[i][j] == '1') {
                val index = i * n + j
                // Try to union with adjacent cells
                if (i + 1 &lt; m &amp;&amp; grid[i + 1][j] == '1') uf.union(index, (i + 1) * n + j)
                if (j + 1 &lt; n &amp;&amp; grid[i][j + 1] == '1') uf.union(index, i * n + (j + 1))
            }
        }
    }
    val islands = mutableSetOf&lt;Int&gt;()
    for (i in grid.indices) {
        for (j in grid[i].indices) {
            if (grid[i][j] == '1') {
                islands.add(uf.find(i * n + j))
            }
        }
    }
    return islands.size
}

Time Complexity:

  • O(m * n), as we perform a union operation for each adjacent land cell.

Space Complexity:

  • O(m * n) for the union-find parent array.

Calling in main():

fun main() {
    val grid1 = arrayOf(
        charArrayOf('1', '1', '1', '1', '0'),
        charArrayOf('1', '1', '0', '1', '0'),
        charArrayOf('1', '1', '0', '0', '0'),
        charArrayOf('0', '0', '0', '0', '0')
    )
    println("Number of Islands : ${numIslands(grid1)}")  // Output: 1
    
    val grid2 = arrayOf(
        charArrayOf('1', '1', '0', '0', '0'),
        charArrayOf('1', '1', '0', '0', '0'),
        charArrayOf('0', '0', '1', '0', '0'),
        charArrayOf('0', '0', '0', '1', '1')
    )
    println("Number of Islands : ${numIslands(grid2)}")  // Output: 3
}



Which Solution is Best?

  1. DFS/BFS (Approaches 1 & 2): These are the simplest and most intuitive solutions. Both have a time complexity of O(m * n), which is optimal for this problem. DFS uses recursion, which might run into issues with large grids due to stack overflow, but BFS avoids this problem by using an iterative approach. If you want simplicity and reliability, BFS is preferred.

  2. Union-Find (Approach 3): This approach is more advanced and has a similar time complexity of O(m * n). However, it can be more difficult to understand and implement. It also performs well with path compression and union by rank, but for this problem, the DFS/BFS approach is usually sufficient and easier to implement.

Conclusion

For this problem, BFS is the recommended solution due to its iterative nature, which avoids recursion issues with large grids, while still being efficient and easy to understand.


Full Problem description in LeetCode


Thank you for reading my latest article! I would greatly appreciate your feedback to improve my future posts. πŸ’¬ Was the information clear and valuable? Are there any areas you think could be improved? Please share your thoughts in the comments or reach out directly. Your insights are highly valued. πŸ‘‡πŸ˜Š.  Happy coding! πŸ’»✨

appcompat-v7:21.0.0': No resource found that matches the given name: attr 'android:actionModeShareDrawable' in Android

If you faced appcompat-v7:21.0.0': No resource found that matches the given name: attr 'android:actionModeShareDrawable' error on during developing android application.

Here is the solution in Android studio, Eclipse and  IntelliJ IDEA.

Prerequirements
Make sure that you've downloaded the latest extras as well as the Android 5.0 SDK via the SDK-Manager.
Picture of the SDK Manager


Android Studio:

Open the build.gradle file of your app-module and change your compileSdkVersion to 21. It's basically not necessary to change the targetSdkVersion SDK-Version to 21 but it's recommended since you should always target the latest android Build-Version.
In the end you gradle-file will look like this:
android {
    compileSdkVersion 21
    // ...

    defaultConfig {
        // ...
        targetSdkVersion 21
    }
}
Be sure to sync your project afterwards.
Android Studio Gradle Sync reminder


Eclipse:

The only thing you have to do is to open the project.properties file of the android-support-v7-appcompat and change the target from target=android-19 to target=android-21.
Afterwards just do a Project --> Clean... so that the changes take effect.


IntelliJ IDEA:

Right click on appcompat module --> Open Module Settings (F4) --> [Dependency Tab] Select Android API 21 Platform from the dropdown --> Apply
Select API 21 Platform
Then just rebuild the project (Build --> Rebuild Project) and you're good to go.

Sometimes , If above solution doesnot work then try this
Haven't set compileSdkVersion to 21 in your build.gradle file and change targetSdkVersion to 21.
android {
    //...
    compileSdkVersion 21

    defaultConfig {
        targetSdkVersion 21
    }
    //...
}
This requires you to have downloaded the latest SDK updates to begin with.
Android Studio SDK Manager
Once  downloaded all the updates (don't forget to also update the Android Support Library/Repository, too!) and updated your compileSdkVersion, re-sync Gradle project.
Edit: For Eclipse or general IntelliJ users


Happy Coding!!!

Failed to install *.apk on device *: timeout in Android

I faced many times Failed to install *.apk on device *: timeout in Android. So, i have to decided, I tries to clean project many times but unable to solve ,but now how to solve this problem in android.


First you have do this  , change the ADB connection timeout. I think it defaults that to 5000ms and I changed mine to 10000ms to get rid of that problem.

If you are in Eclipse, you can do this by going through

Window -> Preferences -> Android -> DDMS -> ADB Connection Timeout (ms)

If the above solution is working in case your eclipse, do this

Restart the adb server by typing in the cmd:

adb kill-server

adb start-server

Or
simple go to DDMS Perspective and click on Reset adb.

and

There is many way to slove such kind of problem, sometimes it work when you do this

Reboot the phone and completely power down and power up.

Happy Coding !!!

MediaPlayer stutters at start of mp3 playback in android


     A Local HTTP Streaming Server (LocalHTTPServer) for Android. This version was specifically made to stream encrypted MP3 files using a CipherInputStream to MediaPlayer but should be easily modified to work on ordinary files. It has been tested on API 9+ and works fine on large files (tested on up to 20MB files).

 Here is the simple example,


import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketException;
import java.net.SocketTimeoutException;
import java.net.UnknownHostException;
import android.os.AsyncTask;
import android.os.Looper;
import android.util.Log;

public class StreamProxy implements Runnable {

    private static final int SERVER_PORT=8888;

    private Thread thread;
    private boolean isRunning;
    private ServerSocket socket;
    private int port;

    public StreamProxy() {

        // Create listening socket
        try {
          socket = new ServerSocket(SERVER_PORT, 0, InetAddress.getByAddress(new byte[] {127,0,0,1}));
          socket.setSoTimeout(5000);
          port = socket.getLocalPort();
        } catch (UnknownHostException e) { // impossible
        } catch (IOException e) {
          Log.e(TAG, "IOException initializing server", e);
        }

    }

    public void start() {
        thread = new Thread(this);
        thread.start();
    }

    public void stop() {
        isRunning = false;
        thread.interrupt();
        try {
            thread.join(5000);
        } catch (InterruptedException e) {
          e.printStackTrace();
        }
    }

    @Override
      public void run() {
        Looper.prepare();
        isRunning = true;
        while (isRunning) {
          try {
            Socket client = socket.accept();
            if (client == null) {
              continue;
            }
            Log.d(TAG, "client connected");

            StreamToMediaPlayerTask task = new StreamToMediaPlayerTask(client);
            if (task.processRequest()) {
                task.execute();
            }

          } catch (SocketTimeoutException e) {
            // Do nothing
          } catch (IOException e) {
            Log.e(TAG, "Error connecting to client", e);
          }
        }
        Log.d(TAG, "Proxy interrupted. Shutting down.");
      }




    private class StreamToMediaPlayerTask extends AsyncTask<String, Void, Integer> {

        String localPath;
        Socket client;
        int cbSkip;

        public StreamToMediaPlayerTask(Socket client) {
            this.client = client;
        }

        public boolean processRequest() {
            // Read HTTP headers
            String headers = "";
            try {
              headers = Utils.readTextStreamAvailable(client.getInputStream());
            } catch (IOException e) {
              Log.e(TAG, "Error reading HTTP request header from stream:", e);
              return false;
            }

            // Get the important bits from the headers
            String[] headerLines = headers.split("\n");
            String urlLine = headerLines[0];
            if (!urlLine.startsWith("GET ")) {
                Log.e(TAG, "Only GET is supported");
                return false;               
            }
            urlLine = urlLine.substring(4);
            int charPos = urlLine.indexOf(' ');
            if (charPos != -1) {
                urlLine = urlLine.substring(1, charPos);
            }
            localPath = urlLine;

            // See if there's a "Range:" header
            for (int i=0 ; i<headerLines.length ; i++) {
                String headerLine = headerLines[i];
                if (headerLine.startsWith("Range: bytes=")) {
                    headerLine = headerLine.substring(13);
                    charPos = headerLine.indexOf('-');
                    if (charPos>0) {
                        headerLine = headerLine.substring(0,charPos);
                    }
                    cbSkip = Integer.parseInt(headerLine);
                }
            }
            return true;
        }

        @Override
        protected Integer doInBackground(String... params) {

                        long fileSize = GET CONTENT LENGTH HERE;

            // Create HTTP header
            String headers = "HTTP/1.0 200 OK\r\n";
            headers += "Content-Type: " + MIME TYPE HERE + "\r\n";
            headers += "Content-Length: " + fileSize  + "\r\n";
            headers += "Connection: close\r\n";
            headers += "\r\n";

            // Begin with HTTP header
            int fc = 0;
            long cbToSend = fileSize - cbSkip;
            OutputStream output = null;
            byte[] buff = new byte[64 * 1024];
            try {
                output = new BufferedOutputStream(client.getOutputStream(), 32*1024);                           
                output.write(headers.getBytes());

                // Loop as long as there's stuff to send
                while (isRunning && cbToSend>0 && !client.isClosed()) {

                    // See if there's more to send
                    File file = new File(localPath);
                    fc++;
                    int cbSentThisBatch = 0;
                    if (file.exists()) {
                        FileInputStream input = new FileInputStream(file);
                        input.skip(cbSkip);
                        int cbToSendThisBatch = input.available();
                        while (cbToSendThisBatch > 0) {
                            int cbToRead = Math.min(cbToSendThisBatch, buff.length);
                            int cbRead = input.read(buff, 0, cbToRead);
                            if (cbRead == -1) {
                                break;
                            }
                            cbToSendThisBatch -= cbRead;
                            cbToSend -= cbRead;
                            output.write(buff, 0, cbRead);
                            output.flush();
                            cbSkip += cbRead;
                            cbSentThisBatch += cbRead;
                        }
                        input.close();
                    }

                    // If we did nothing this batch, block for a second
                    if (cbSentThisBatch == 0) {
                        Log.d(TAG, "Blocking until more data appears");
                        Thread.sleep(1000);
                    }
                }
            }
            catch (SocketException socketException) {
                Log.e(TAG, "SocketException() thrown, proxy client has probably closed. This can exit harmlessly");
            }
            catch (Exception e) {
                Log.e(TAG, "Exception thrown from streaming task:");
                Log.e(TAG, e.getClass().getName() + " : " + e.getLocalizedMessage());
                e.printStackTrace();                
            }

            // Cleanup
            try {
                if (output != null) {
                    output.close();
                }
                client.close();
            }
            catch (IOException e) {
                Log.e(TAG, "IOException while cleaning up streaming task:");                
                Log.e(TAG, e.getClass().getName() + " : " + e.getLocalizedMessage());
                e.printStackTrace();                
            }

            return 1;
        }

    }
}

Enjoy and make app appropriate for you desire.
Happy Coding!!!

Action Bar Demo in Android

The action bar is a window feature that identifies the user location, and provides user actions and navigation modes. Using the action bar offers your users a familiar interface across applications that the system gracefully adapts for different screen configurations.
Lessons

Setting Up the Action Bar
Learn how to add a basic action bar to your activity, whether your app supports only Android 3.0 and higher or also supports versions as low as Android 2.1 (by using the Android Support Library).
Adding Action Buttons
Learn how to add and respond to user actions in the action bar.
Styling the Action Bar
Learn how to customize the appearance of your action bar.
Overlaying the Action Bar
Learn how to overlay the action bar in front of your layout, allowing for seamless transitions when hiding the action bar.
Here is the example of Action Bar demo App

first define menu in   res/menu/main.xml:

<menu xmlns:android="http://schemas.android.com/apk/res/android" >

    <item
        android:id="@+id/refresh"
        android:alphabeticShortcut="r"
        android:icon="@drawable/ic_action_refresh"
        android:orderInCategory="100"
        android:showAsAction="always"/>
    <item
        android:id="@+id/share"
        android:actionProviderClass="android.widget.ShareActionProvider"
        android:icon="@drawable/ic_action_share"
        android:orderInCategory="1"
        android:showAsAction="collapseActionView"
        android:title="Share"/>
    <item
        android:id="@+id/update"
        android:icon="@drawable/db_update_24"
        android:orderInCategory="100"
        android:showAsAction="collapseActionView"
        android:title="Update"/>

</menu>

Now define for refresh icons with circling with progress in to: res/layout/actionbar_indeterminate_progress.xml 

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
   android:layout_height="wrap_content"
   android:layout_width="56dp"
   android:minWidth="56dp">
    <ProgressBar android:layout_width="32dp"
       android:layout_height="32dp"
       android:layout_gravity="center"/>
</FrameLayout>

another xml file defined in layout: res/layout/main.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:ads="http://schemas.android.com/apk/res-auto"
    android:id="@+id/relativeLayout"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:background="#00FFFF" >

</RelativeLayout>


Now finally add your project MainActivity java file with full code:

package com.example.searchdemo;

import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.os.StrictMode;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.Toast;
import android.annotation.TargetApi;
import android.os.Build;
import android.view.ViewConfiguration;
import android.widget.ShareActionProvider;

@TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
public class MainActivity extends Activity {
private int toast_time = 100;
private Menu optionsMenu;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
getOverflowMenu();
}

// check strictmode and ignore it
private void strictMode() {
if (android.os.Build.VERSION.SDK_INT > 9) {
StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder()
.permitAll().build();
StrictMode.setThreadPolicy(policy);
}
}


// inflate for action bar
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
this.optionsMenu = menu;
getMenuInflater().inflate(R.menu.main, menu);
return true;
}

// handle click events for action bar items
@SuppressLint("NewApi")
@Override
public boolean onOptionsItemSelected(MenuItem item) {

switch (item.getItemId()) {

case R.id.refresh:
// showToast(getResources().getString(R.string.refresh));
setRefreshActionButtonState(true);
Intent intent = getIntent();
finish();
startActivity(intent);
// setRefreshActionButtonState(false);
return true;

case R.id.share:
// showToast("Share was clicked.");
String shareBody="Sharing Message";
ShareActionProvider myShareActionProvider = (ShareActionProvider) item
.getActionProvider();

Intent myIntent = new Intent();
myIntent.setAction(Intent.ACTION_SEND);
myIntent.putExtra(Intent.EXTRA_TEXT, shareBody);
myIntent.setType("text/plain");

myShareActionProvider.setShareIntent(myIntent);

return true;
case R.id.update:
showToast("Update");
return true;
default:
return super.onOptionsItemSelected(item);
}
}


@TargetApi(Build.VERSION_CODES.HONEYCOMB)
@SuppressLint("NewApi")
public void setRefreshActionButtonState(final boolean refreshing) {
if (optionsMenu != null) {
final MenuItem refreshItem = optionsMenu.findItem(R.id.refresh);
if (refreshItem != null) {
if (refreshing) {
refreshItem
.setActionView(R.layout.actionbar_indeterminate_progress);
} else {
refreshItem.setActionView(null);
}
}
}
}



// put the other two menu on the three dots (overflow)
private void getOverflowMenu() {

try {

ViewConfiguration config = ViewConfiguration.get(this);
java.lang.reflect.Field menuKeyField = ViewConfiguration.class
.getDeclaredField("sHasPermanentMenuKey");
if (menuKeyField != null) {
menuKeyField.setAccessible(true);
menuKeyField.setBoolean(config, false);
}
} catch (Exception e) {
e.printStackTrace();
}

}

// so that we know something was triggered
public void showToast(String msg) {
Toast.makeText(this, msg, toast_time).show();
}

}

here is the output of above demo app snapshots:






Hope, this demo helpful for developing apps.

Do you want more about action bar go following links:
http://developer.android.com/guide/topics/ui/actionbar.html

https://developer.android.com/training/basics/actionbar/index.html

https://www.youtube.com/watch?v=4BOE9TeUY1w

http://developer.android.com/design/patterns/actionbar.html

http://www.androidhive.info/2013/11/android-working-with-action-bar/

http://jgilfelt.github.io

Happy Coding !!!