Monday, August 6, 2012

Android - Bind Service and expose APIs using AIDL

Android - Bind Service and expose APIs using AIDL.

In this , I am trying to demonstrate a sample for the AIDL. For those, who needs to know more theoratical part of it, http://developer.android.com/guide/components/aidl.html.

For me, AIDL is a way to expose a set of APIs, which we can use it from other application by binding this service, in which we implemented the set of APIs.

Will start the sample code now.

First we create the .aidl file.

IAIDLFunctions.aidl

package com.test.aidlfunctions;
  
interface IAIDLFunctions {
 int add(in int num1,in int num2);
 double divide (in int num1,in int num2);
 int mul(in int num1,in int num2);
 int sub(in int num1,in int num2); 
}


And In Service, we implement the functions in onBind(),
package com.test.aidlfunctions;

import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteException;

public class AIDLFunctionService extends Service {

 @Override
 public void onCreate() {
  super.onCreate();
 }

 @Override
 public IBinder onBind(Intent intent) {
  return new IAIDLFunctions.Stub() {

   @Override
   public int sub(int num1, int num2) throws RemoteException {
    return (num1 - num2);
   }

   @Override
   public int mul(int num1, int num2) throws RemoteException {
    return (num1 * num2);
   }

   @Override
   public double divide(int num1, int num2) throws RemoteException {
    return (num1 / num2);
   }

   @Override
   public int add(int num1, int num2) throws RemoteException {
    return (num1 + num2);
   }
  };
 }

 @Override
 public void onDestroy() {
  super.onDestroy();
 }
}


And In Activity, we bind the service and and initialize the IAIDLFunctions object and call the api.
package com.test.aidlfunctions;

import android.app.Activity;
import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;

public class AIDLFunctionsActivity extends Activity implements OnClickListener {
 /** Called when the activity is first created. */
 private EditText edNum1;
 private EditText edNum2;
 private Button btnAdd;
 private Button btnSub;
 private Button btnMul;
 private Button btnDiv;
 private IAIDLFunctions iFunctions;
 private AIDLFunctionServiceConnection serviceConnection;

 @Override
 public void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.main);
  initAndBindService();
  edNum1 = (EditText) findViewById(R.id.num1);
  edNum2 = (EditText) findViewById(R.id.num2);
  btnAdd = (Button) findViewById(R.id.add);
  btnSub = (Button) findViewById(R.id.sub);
  btnMul = (Button) findViewById(R.id.mul);
  btnDiv = (Button) findViewById(R.id.div);
  btnAdd.setOnClickListener(this);
  btnSub.setOnClickListener(this);
  btnMul.setOnClickListener(this);
  btnDiv.setOnClickListener(this);
 }

 private void initAndBindService() {
  serviceConnection = new AIDLFunctionServiceConnection();
  Intent intent = new Intent();
//  intent.setClass(this, AIDLFunctionService.class);
  intent.setClassName("com.test.aidlfunctions", "com.test.aidlfunctions.AIDLFunctionService");
//  intent.setClassName(AIDLFunctionService.class.getPackage().getName(),
//    AIDLFunctionService.class.getSimpleName());
  try {
   boolean ret = bindService(intent, serviceConnection,
     BIND_AUTO_CREATE);
   Toast.makeText(this, "Connected : " + ret, Toast.LENGTH_LONG).show();
  } catch (Exception e) {
   // TODO: handle exception
  }
  
 }

 @Override
 public void onClick(View v) {
  int numOne = Integer.parseInt(edNum1.getText().toString().trim());
  int numTwo = Integer.parseInt(edNum2.getText().toString().trim());
  switch (v.getId()) {
  case R.id.add:
   try {
    Toast.makeText(this,
      "Result : " + iFunctions.add(numOne, numTwo),
      Toast.LENGTH_SHORT).show();
   } catch (RemoteException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
   }
   break;
  case R.id.sub:
   try {
    Toast.makeText(this,
      "Result : " + iFunctions.sub(numOne, numTwo),
      Toast.LENGTH_SHORT).show();
   } catch (RemoteException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
   }
   break;
  case R.id.mul:
   try {
    Toast.makeText(this,
      "Result : " + iFunctions.mul(numOne, numTwo),
      Toast.LENGTH_SHORT).show();
   } catch (RemoteException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
   }
   break;
  case R.id.div:
   try {
    Toast.makeText(this,
      "Result : " + (double)iFunctions.divide(numOne, numTwo),
      Toast.LENGTH_SHORT).show();
   } catch (RemoteException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
   }
   break;
  }
 }

 @Override
 protected void onDestroy() {
  unbindService(serviceConnection);
  serviceConnection = null;
  super.onDestroy();
 }

 class AIDLFunctionServiceConnection implements ServiceConnection {

  @Override
  public void onServiceConnected(ComponentName name, IBinder service) {
   iFunctions = IAIDLFunctions.Stub.asInterface(service);
  }

  @Override
  public void onServiceDisconnected(ComponentName name) {
   iFunctions = null;
  }

 }
}

Android JNI simple example

Android JNI simple example.

In this post, I am trying to demonstrate Android JNI with an example. For those , who need to know more about JNI in detail, https://developer.android.com/guide/practices/jni.html

I am trying to discuss the JNI with an example. I am building on Windows OS. So, I used Cygwin to use the make build.

Step 1 :  Download Android NDK.

Step 2 : Install Cygwin ( Windows ).

Step 3:  Edit the Environment path for these two, like

C:\cygwin\bin;
F:\Softwares\android-ndk-r8-windows\android-ndk-r8

Step 4:

Create an Android Project, and create a native interface in java (as below) and compile it, to get the .class file.

package com.jni;
public class Library {

            // here “shared” is the shared object (.so file) created by NDK
          // Load your library inside static block
              static{
                        System.loadLibrary("shared");
              }

// Native method declaration with native keyword
// here method name is getNumber()

              Public native int getNumber(int num);
}

Step 5:
Create the Activity, which use this method.

public class MainActivity extends Activity {

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

    Library lib = new Library(); //my Native interface in Java
    int a = lib.getNumber(100); //Call to native method

    Toast.makeText(getApplicationContext(), "res = "+a,Toast.LENGTH_SHORT).show();

    }
    
}

Step 6 :
         Create the corresponding header file for your native interface.(follow below steps).
a.    Go to your project directory.
b.    Run the following command.
                                          i.    Javah –jni –classpath bin <fully qualified class name of interface>
(here class name is com.jni.Library)
c.    It will generate the header file (com_jni_Library.h) in project directory.


Step 7 : 

 Create a project directory inside NDK_HOME and create a directory named “jni” inside that. (e.g. NDK_HOME/sample/jni)

Step 8 :
   
                     Copy generated header file (at step 5.c) to NDK_HOME/<project_dir>/jni/


          Step 9:


  Create a C file inside NDK_HOME/<project_dir>/jni/, in which you will implement the native method definition.

        In this example: (Test.c)



//Include the header file generated in step 3.c

#include "com_jni_Library.h"

//Copy the method declaration/signature from the same header file and pass the //arguments appropriately. (Refer JNI documentation for more info)

JNIEXPORT jint JNICALL Java_com_jni_Library_getNumber
  (JNIEnv * env, jobjectobj, jint a){
          return (a+100);
}


Step 10 :
  Create a file named “Android.mk” inside NDK_HOME/<project_dir>/jni/ and write lines given below.
LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

     # LOCAL_MODULE name should be the library name which you declared in
     # System.loadLibrary(“shared”) in step 2. Here lib name is “shared”

LOCAL_MODULE := shared

# LOCAL_SRC_FILES should be the C source file name created in step 6.
LOCAL_SRC_FILES := Test.c


include $(BUILD_SHARED_LIBRARY)

Step 11 : 

 Build your native code to generate shared object. (follow below steps)

a.    Start Cygwin and go to <NDK_HOME>/<project_dir>/

e.g. cd /cygdrive/d/android/android-ndk-r4b/sample

b.    Run the build command inside project directory

                                          i.    ndk-build

c.    If everything goes fine, it will generate a shared object file as

sample/libs/armeabi/libshared.so

Step 12 :
Now,   Copy the “libs” directory generated in step 8.c into your Android Project directory.


Step 13 : 

Build and run your Android application.

Friday, August 3, 2012

Android Bind Service with Messenger

Android Bind Service with Messenger


Hi All, 

This example shows how we can bind a service and communicate via Handler.

Following is the Activity,
package com.aidl.messenger;
import android.app.Activity;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
import android.os.Messenger;
import android.os.RemoteException;
import android.widget.Toast;

public class AIDLMessengerActivity extends Activity {
 /** Called when the activity is first created. */
 private Messenger messenger;
 private MyServiceConnection conn;
 final Messenger mMessenger = new Messenger(new IncomingHandler());
 
 class IncomingHandler extends Handler{
  @Override
  public void handleMessage(Message msg) {
   super.handleMessage(msg);
   switch(msg.what){
   case 2:
    Bundle bundle = msg.getData();
    Toast.makeText(AIDLMessengerActivity.this, bundle.getString("reply"), Toast.LENGTH_SHORT).show();
    break;
   }
  }
 }
 
 @Override
 public void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.main);
  // intent.setClassName("com.aidl.messenger",
  // "com.aidl.messenger.MyService");
  conn = new MyServiceConnection();
  boolean mIsBound = bindService(new Intent(this, MyService.class), conn,
    Context.BIND_AUTO_CREATE);
 }

 @Override
 protected void onDestroy() {
  unbindService(conn);
  super.onDestroy();
 }

 class MyServiceConnection implements ServiceConnection {

  @Override
  public void onServiceConnected(ComponentName arg0, IBinder binder) {
   messenger = new Messenger(binder);
   Message msg = Message.obtain(null, MyService.MESSAGE_TEST, 0, 0);
   Bundle bundle = new Bundle();
   bundle.putString("name", "Jihad");
   msg.setData(bundle);
   msg.replyTo = mMessenger;
   try {
    messenger.send(msg);
   } catch (RemoteException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
   }
  }

  @Override
  public void onServiceDisconnected(ComponentName arg0) {
   messenger = null;
  }

 }
}


and the Service is, 
package com.aidl.messenger;

import android.app.Service;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.Messenger;
import android.os.RemoteException;
import android.widget.Toast;

public class MyService extends Service {

 public static final int MESSAGE_TEST = 1;
 public static final int MESSAGE_TEST_REPLY = 2;
 private Messenger mMessenger = null;
 private String mesg = "";
 class MyHandler extends Handler {
  @Override
  public void handleMessage(Message msg) {
   super.handleMessage(msg);
   switch (msg.what) {
   case MESSAGE_TEST:
    mesg = "Hi ";
    Toast.makeText(MyService.this, "received in service", Toast.LENGTH_SHORT).show();
    mMessenger = msg.replyTo;
    try {
     Bundle bundle = msg.getData();
     String name = bundle.getString("name");
     Message newMsg = new Message();
     newMsg.what = MESSAGE_TEST_REPLY;
     bundle.clear();
     bundle.putString("reply", mesg +name+", Welcome to Service");
     newMsg.setData(bundle);
     mMessenger.send(newMsg);
    } catch (RemoteException e) {
     // TODO Auto-generated catch block
     e.printStackTrace();
    }
    break;

   default:
    break;
   }
  }
 }
 
 public void testMethod(){
  Toast.makeText(MyService.this, "Test method", Toast.LENGTH_SHORT).show();
 }

 private Messenger messenger = new Messenger(new MyHandler());

 @Override
 public IBinder onBind(Intent intent) {
  return messenger.getBinder();
 }

}


and Manifest,


    

    
        
            
                

                
            
        
        
    




In Activity, We have one Messenger associated with a Handler. This is for handling the message from the service.

In Service, the Messenger will handle the request from the activity.

Thanks,