Tuesday, 4 June 2013

Broadcast receiver

Using BroadcastReceivers in Android
This tutorial describes how to create and consume Android services. It is based on Eclipse 4.2, Java 1.6 and Android 4.2.

1. Broadcast receiver

1.1. Definition

A broadcast receiver (short receiver) is an Android component which allows you to register for system or application events. All registered receivers for an event will be notified by the Android runtime once this event happens.
For example applications can register for the ACTION_BOOT_COMPLETED system event which is fired once the Android system has completed the boot process.

1.2. Implementation

A receiver can be registered via the AndroidManifest.xml file.
Alternatively to this static registration, you can also register a broadcast receiver dynamically via the Context.registerReceiver() method.
The implementing class for a receiver extends the BroadcastReceiver class.
If the event for which the broadcast receiver has registered happens the onReceive() method of the receiver is called by the Android system.

1.3. Long running operations

After the onReceive() of the BroadcastReceiver has finished, the Android system can recycle the BroadcastReceiver.
Therefore you cannot perform any asynchronous operation in the onReceive() method. If you have potentially long running operations you should trigger a service for that.

1.4. Restrictions for defining broadcast receiver

As of Android 3.1 the Android system will by default exclude all BroadcastReceiver from receiving intents if the corresponding application has never been started by the user or if the user explicitly stopped the application via the Android menu (in Manage Application).
This is an additional security features as the user can be sure that only the applications he started will receive broadcast intents.

2. System broadcasts

Several system events are defined as final static fields in the Intent class. Other Android system classes also define events, e.g. the TelephonyManager defines events for the change of the phone state.
The following table lists a few important system events.
Table 1. System Events
Event Description
Intent.ACTION_BOOT_COMPLETED Boot completed. Requires the android.permission.RECEIVE_BOOT_COMPLETED permission.
Intent.ACTION_POWER_CONNECTED Power got connected to the device.
Intent.ACTION_POWER_DISCONNECTED Power got disconnected to the device.
Intent.ACTION_BATTERY_LOW Battery gets low, typically used to reduce activities in your app which consume power.
Intent.ACTION_BATTERY_OKAY Battery status good again.


3. Automatically starting Services from a Receivers

To start Services automatically after the Android system starts you can register a BroadcastReceiver to the Android android.intent.action.BOOT_COMPLETED system event. This requires the android.permission.RECEIVE_BOOT_COMPLETED permission.
The following AndroidManifest.xml registers a receiver for the BOOT_COMPLETED event.

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="de.vogella.android.ownservice.local"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk android:minSdkVersion="10" />

    <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />

    <application
        android:icon="@drawable/icon"
        android:label="@string/app_name" >
        <activity
            android:name=".ServiceConsumerActivity"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

        <receiver android:name="MyScheduleReceiver" >
            <intent-filter>
                <action android:name="android.intent.action.BOOT_COMPLETED" />
            </intent-filter>
        </receiver>
        <receiver android:name="MyStartServiceReceiver" >
        </receiver>
    </application>

</manifest> 

In the onReceive() method the corresponding BroadcastReceiver would then start the service.

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;

public class MyReceiver extends BroadcastReceiver {

  @Override
  public void onReceive(Context context, Intent intent) {
    Intent service = new Intent(context, WordService.class);
    context.startService(service);
  }
} 

If you application is installed on the SD card, then it is not available after the android.intent.action.BOOT_COMPLETED event. Register yourself in this case for the android.intent.action.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE event.
Also note that as of Android 3.0 the user needs to have started the application at least once before your application can receive android.intent.action.BOOT_COMPLETED events.

4. Pending Intent

A PendingIntent is a token that you give to another application (e.g. Notification Manager, Alarm Manager or other 3rd party applications), which allows this other application to use the permissions of your application to execute a predefined piece of code.
To perform a broadcast via a pending intent so get a PendingIntent via the getBroadcast() method of the PendingIntent class. To perform an activity via an pending intent you receive the activity via PendingIntent.getActivity().

5. Tutorial: Broadcast Receiver

We will define a broadcast receiver which listens to telephone state changes. If the phone receives a phone call then our receiver will be notified and log a message.
Create a new project de.vogella.android.receiver.phone. Create a dummy activity as this is required so that the BroadcastReceiver also gets activated. Create the following AndroidManifest.xml file.

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="de.vogella.android.receiver.phone"
    android:versionCode="1"
    android:versionName="1.0" >

     <uses-sdk android:minSdkVersion="15" />
     
    <uses-permission android:name="android.permission.READ_PHONE_STATE" >
    </uses-permission>

    <application
        android:icon="@drawable/icon"
        android:label="@string/app_name" >
           <activity
            android:name=".MainActivity"
            android:label="@string/title_activity_main" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        
        <receiver android:name="MyPhoneReceiver" >
            <intent-filter>
                <action android:name="android.intent.action.PHONE_STATE" >
                </action>
            </intent-filter>
        </receiver>
    </application>

   

</manifest> 

Create the MyPhoneReceiver class.

package de.vogella.android.receiver.phone;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.telephony.TelephonyManager;
import android.util.Log;

public class MyPhoneReceiver extends BroadcastReceiver {

  @Override
  public void onReceive(Context context, Intent intent) {
    Bundle extras = intent.getExtras();
    if (extras != null) {
      String state = extras.getString(TelephonyManager.EXTRA_STATE);
      Log.w("MY_DEBUG_TAG", state);
      if (state.equals(TelephonyManager.EXTRA_STATE_RINGING)) {
        String phoneNumber = extras
            .getString(TelephonyManager.EXTRA_INCOMING_NUMBER);
        Log.w("MY_DEBUG_TAG", phoneNumber);
      }
    }
  }
} 

Install your application and simulate a phone call via the DDMS perspective in Eclipse. Your receiver is called and logs a message to the console.
 

6. Tutorial: System Services and BroadcastReceiver

In this chapter we will schedule a broadcast receiver via the AlertManager. Once called it will use the VibratorManager and a Toast to notify the user.
Create a new project called de.vogella.android.alarm with the activity called AlarmActivity.
Create the following layout.

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >

    <EditText
        android:id="@+id/time"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:hint="Number of seconds"
        android:inputType="numberDecimal" >
    </EditText>

    <Button
        android:id="@+id/ok"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:onClick="startAlert"
        android:text="Start Counter" >
    </Button>

</LinearLayout> 

Create the following broadcast receiver class. This class will get the Vibrator service.

package de.vogella.android.alarm;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.os.Vibrator;
import android.widget.Toast;

public class MyBroadcastReceiver extends BroadcastReceiver {
  @Override
  public void onReceive(Context context, Intent intent) {
    Toast.makeText(context, "Don't panik but your time is up!!!!.",
        Toast.LENGTH_LONG).show();
    // Vibrate the mobile phone
    Vibrator vibrator = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE);
    vibrator.vibrate(2000);
  }

} 

Maintain this class as broadcast receiver in AndroidManifest.xml and allow the vibrate authorization.

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="de.vogella.android.alarm"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk android:minSdkVersion="15" />

    <uses-permission android:name="android.permission.VIBRATE" >
    </uses-permission>

    <application
        android:icon="@drawable/icon"
        android:label="@string/app_name" >
        <activity
            android:name=".AlarmActivity"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

        <receiver android:name="MyBroadcastReceiver" >
        </receiver>
    </application>

</manifest> 

Change the code of your Activity "AlarmActivity" to the following. This activity will create an Intent for the Broadcast receiver and get the AlarmManager service.

package de.vogella.android.alarm;

import android.app.Activity;
import android.app.AlarmManager;
import android.app.PendingIntent;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.EditText;
import android.widget.Toast;

public class AlarmActivity extends Activity {
  
/** Called when the activity is first created. */
@Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); } public void startAlert(View view) { EditText text = (EditText) findViewById(R.id.time); int i = Integer.parseInt(text.getText().toString()); Intent intent = new Intent(this, MyBroadcastReceiver.class); PendingIntent pendingIntent = PendingIntent.getBroadcast(this.getApplicationContext(), 234324243, intent, 0); AlarmManager alarmManager = (AlarmManager) getSystemService(ALARM_SERVICE); alarmManager.set(AlarmManager.RTC_WAKEUP, System.currentTimeMillis() + (i * 1000), pendingIntent); Toast.makeText(this, "Alarm set in " + i + " seconds", Toast.LENGTH_LONG).show(); } }

Run your application on the device. Set your time and start the alarm. After the defined number of seconds a Toast should be displayed. Keep in mind that the vibrator alarm does not work on the Android emulator.

Alarm application running

7. Defining custom events and receivers

7.1. Registering broadcast receiver for custom events

You can register a receiver for your own customer actions.
The following AndroidManifest.xml file shows a broadcast receiver which is registered to a custom action.

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="de.vogella.android.receiver.own"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk android:minSdkVersion="15" />

    <application
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name" >
        <activity
            android:name=".MainActivity"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

        <receiver android:name="MyReceiver" >
            <intent-filter>
                <action android:name="de.vogella.android.mybroadcast" />
            </intent-filter>
        </receiver>
    </application>

</manifest> 

7.2. Sending broadcast intents

The sendBroadcast() method from the Context class allows you to send intents to your registered receivers. The following coding show an example.

Intent intent = new Intent();
intent.setAction("de.vogella.android.mybroadcast");
sendBroadcast(intent); 

You cannot trigger system broadcasts events, the Android system will prevent this.
 

7.3. Local broadcast events with LocalBroadcastManager

The LocalBroadcastManager class is used to register for and send broadcasts of Intents to local objects within your process. This is faster and more secure as your events don't leave your application.
The following example shows an activity which registers for a customer event called my-event.

@Override
public void onResume() {
  super.onResume();

  // Register mMessageReceiver to receive messages.
  LocalBroadcastManager.getInstance(this).registerReceiver(mMessageReceiver,
      new IntentFilter("my-event"));
}

// handler for received Intents for the "my-event" event 
private BroadcastReceiver mMessageReceiver = new BroadcastReceiver() {
  @Override
  public void onReceive(Context context, Intent intent) {
    // Extract data included in the Intent
    String message = intent.getStringExtra("message");
    Log.d("receiver", "Got message: " + message);
  }
};

@Override
protected void onPause() {
  // Unregister since the activity is not visible
  LocalBroadcastManager.getInstance(this).unregisterReceiver(mMessageReceiver);
  super.onPause();
} 


// This method is assigned to button in the layout
// via the onClick property
public void onClick(View view) {
  sendMessage();
}

// Send an Intent with an action named "my-event". 
private void sendMessage() {
  Intent intent = new Intent("my-event");
  // Add data
  intent.putExtra("message", "data");
  LocalBroadcastManager.getInstance(this).sendBroadcast(intent);
} 

8. Dynamic broadcast receiver registration

8.1. Dynamically registered receiver

You can register a receiver dynamically via the Context.registerReceiver() method. You can also dynamically unregister receiver by using Context.unregisterReceiver() method.

Warning

Do not forget to unregister a dynamically registered receiver by using Context.unregisterReceiver() method. Otherwise the system will report a leaked broadcast receiver error. For instance if you registered a receive in onResume() methods of your activity, you should unregister it in the onPause() method.

8.2. Using the package manager to disable static receivers

You can use the PackageManager class to enable or disable receivers registered in your AndroidManifest.xml file.

ComponentName receiver = new ComponentName(context, myReceiver.class);
PackageManager pm = context.getPackageManager();

pm.setComponentEnabledSetting(receiver, 
  PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
  PackageManager.DONT_KILL_APP); 

8.3. Sticky broadcast intents

A normal broadcast intent is not available anymore after is was send and processed by the system. If you use the sendStickyBroadcast(Intent) method, the corresponding intent is sticky, meaning the intent you are sending stays around after the broadcast is complete.
You can can retrieve that data through the return value of registerReceiver(BroadcastReceiver, IntentFilter) . This works also for a null BroadcastReceiver.
In all other ways, this behaves the same as sendBroadcast(Intent).
The Android system uses sticky broadcast for certain system information. For example the battery status is send as sticky Intent and can get received at any time. The following example demonstrates that.

// Register for the battery changed event
IntentFilter filter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);

/ Intent is sticky so using null as receiver works fine
// return value contains the status
Intent batteryStatus = this.registerReceiver(null, filter);

// Are we charging / charged?
int status = batteryStatus.getIntExtra(BatteryManager.EXTRA_STATUS, -1);
boolean isCharging = status == BatteryManager.BATTERY_STATUS_CHARGING
  || status == BatteryManager.BATTERY_STATUS_FULL;

boolean isFull = status == BatteryManager.BATTERY_STATUS_FULL;

// How are we charging?
int chargePlug = batteryStatus.getIntExtra(BatteryManager.EXTRA_PLUGGED, -1);
boolean usbCharge = chargePlug == BatteryManager.BATTERY_PLUGGED_USB;
boolean acCharge = chargePlug == BatteryManager.BATTERY_PLUGGED_AC; 

Sticky broadcast intents typically require special permissions.

Android Service

Developing own services and using system services in Android
This tutorial describes how to create and consume Android services. It is based on Eclipse 4.2, Java 1.6 and Android 4.2.

1. Android Services

1.1. Service

A service is a component which runs in the background, without direct interaction with the user. The Android platform provides and runs predefined system services and every Android application can use them, given the right permissions.
An Android application can, in addition to consuming the existing Android platform services, define and use new services.

1.2. Android Platform Service

The Android platform provides pre-defined services, usually exposed via a specific Manager class. Access to them can be gained via the getSystemService() method.

1.3. Defining new services

Every Android application can define and start new services
If you use asynchronous processing in activities or fragments the corresponding threads are still connected to the life-cycle of the corresponding activity. The Android system may decide to terminate them at any point in time.
Services run with a higher priority than inactive or invisible activities and therefore it is less likely that the Android system terminates them.
Defining your own services allows you to design very responsive applications. You can fetch the application via a service and once the application is started by the user, it can present fresh data to the user.

2. Defining services

2.1. Declaring own services

A service needs to be declared in the AndroidManifest.xml and the implementing class must extend the Service class or one of its subclasses. The following code shows an example for a service declaration and its implementation.

<service
  android:name="MyService"
  android:icon="@drawable/icon"
  android:label="@string/service_name"
  >
</service> 


public class MyService extends Service {

  @Override
  public int onStartCommand(Intent intent, int flags, int startId) {
    //TODO do something useful
    return Service.START_NOT_STICKY;
  }

  @Override
  public IBinder onBind(Intent intent) {
  //TODO for communication return IBinder implementation
    return null;
  }
} 

A service runs by default in the same process as the application. in its own thread.
Therefore you need to use asynchronous processing in the service to to perform resource intensive tasks in the background.
Services which run in the process of the application are sometimes called local services.

2.2. Running a Services in its own process

You can also specify that your Service runs in a separate process via the android:process=":process_description" attribute.

<service
  android:name="WordService"
  android:process=":my_process" 
  android:icon="@drawable/icon"
  android:label="@string/service_name"
  >
</service> 

The colon prefix before the name tells Android that the Service is private to its declaring application. If the colon is not used the Service would be a global process and can be used by other Android applications.
Running a service in its own process will not block the application in case the service performs long running operations in its main thread. But as the services runs in its own process you need to use some interprocess communication (IPC) to communicate to your service from other parts.
Even if the service runs in its own process you need to use asynchronous processing to perform network access because Android does not allow network access in the main thread of a process.

2.3. When to run a service in a separate process?

Running a service in its own process gives it its own memory address space and a garbage collector of the virtual machine in this process does not affect the application process.
Application rarely need to run a service in its own process. Running a services in its own process make the communication of the other Android components and the service harder to implement.
If you want to make a service to other Android application available, they must run in their own process.

2.4. Intent Services

You can also extend the IntentService class for your service implementation.
The IntentService is used to perform a certain task in the background. Once done, the instance of IntentService terminate itself automatically. Examples for its usage would be to download a certain resources from the Internet.
The IntentService class offers the onHandleIntent() method which will be asynchronously called by the Android system.

3. Broadcast receiver

For an introduction into BroadcastReceiver please see Android Broadcast Receiver tutorial .

4. Starting services

4.1. Start a service

An Android component (service, receiver, activity) can start and trigger a service via the startService(intent) method. This method call starts the service if it is not running.

Intent service = new Intent(context, MyService.class);
context.startService(service); 

If the service started the onCreate() method is called.
Once the service is started the method call to start the service triggers startService(intent) method in the service. It passes in the Intent for the startService(intent) call.
If startService(intent) is called while the service is running, its onStartCommand() is also called. Therefore your service needs to be prepared that onStartCommand() can be called several times. This method is called in the main user interface thread therefore it cannot be called simultaneously from two different threads.
Alternatively to startService(intent) you can also start a service via the bindService() method call. This allows you to communicate directly with the service.

4.2. Stopping a service

You stop a service via the stopService() method. No matter how frequently you started the service with startService(intent) a call to stopService() stops it.
A service can stop itself by calling the stopSelf() method.

4.3. Binding between activities and local services

If the activity wants to interact with the service it can use the bindService() method to start the service.
This method requires as parameter a ServiceConnection object which allows to connect to the service. In the service the onBind() method is called. This method returns a IBinder object to the ServiceConnection.
This IBinder object can be used by the activity to communicate with the service.
Afterwards the binding was done the onStartCommand() method is called with the Intent data provided by the activity.
startService() also allows you to provide a flag which determines the restart behavior of the services. Service.START_STICKY is used for services which are explicit started or stopped. If these services are terminated by the Android system, they are restarted if sufficient resource are available again.
Services started with Service.START_NOT_STICKY are not automatically restarted if terminated by the Android system.

4.4. Starting services regularly via AlarmManager

As with activities the Android system may terminate the process of a service at any time to save resources. For this reason you cannot simple use a TimerTask in the service to ensure that it is executed on a regular basis.
For correct scheduling of the Service use the AlarmManager class. The following code demonstrates how to do this.

Calendar cal = Calendar.getInstance();

Intent intent = new Intent(this, MyService.class);
PendingIntent pintent = PendingIntent.getService(this, 0, intent, 0);

AlarmManager alarm = (AlarmManager)getSystemService(Context.ALARM_SERVICE);
// Start every 30 seconds
alarm.setRepeating(AlarmManager.RTC_WAKEUP, cal.getTimeInMillis(), 30*1000, pintent); 

5. Communicating with Services

There are several way for an activity to communicate with an service and vice versa. This section dicusses the different ways and gives recommendation which to use.

5.1. Activity binding to local service

If the Service is started in the same process as the activity, the activity can directly bind to the service. This is a relatively simple and efficient way to communication.

5.2. Using receiver

You can also use dynamically registered receivers for the communication. For example your activity can dynamically register a receiver and the service sends outs corresponding events.

5.3. AIDL for services in a different process

To bind to a service which runs in a different process you need to use Inter Process Communication (IPC) as the data needs to be send between different processes. For this you need to create a AIDL file which looks similar to an Java interface but ends with the .aidl file extension and is only allowed to extend other AIDL files.
This approach is required if your service should be provided to other applications, otherwise you should prefer a local service.

5.4. Sending Intent data and bundle to the services

The service receives data from the starting Android component and can use this data.

5.5. Handler and Messenger

If the service should be communicating back to the activity it can receive an object of type Messenger via the Intent data it receives from the activity. If the Messenger is bound to a Handler in the activity the service can send objects of type Message to the activity.
A Messenger is parcelable, which means it can be passed to another process and you can use this object to send Messages to the Handler in the activity.
Messenger provides also the method getBinder() which allows to pass a Messenger to the activity. The activity can therefore send Messages to the service.

6. Tutorial: Using IntentService

The following example demonstrates how to use the IntentService class to download a file from the Internet. Once done the IntentService will use an instance of the Messenger class to inform the activity which started the service about the location of the downloaded file.
Create a new project called de.vogella.android.intentservice.download with a activity called MainActivity.
Create a service "DownloadService" by creating the following class and the entry in AndroidManifest.xml. Also add the permission to write to external storage and to access the Internet to the file.

package de.vogella.android.intentservice.download;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URL;

import android.app.Activity;
import android.app.IntentService;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.os.Environment;
import android.os.Message;
import android.os.Messenger;
import android.util.Log;

public class DownloadService extends IntentService {

  private int result = Activity.RESULT_CANCELED;

  public DownloadService() {
    super("DownloadService");
  }

  // Will be called asynchronously be Android
  @Override
  protected void onHandleIntent(Intent intent) {
    Uri data = intent.getData();
    String urlPath = intent.getStringExtra("urlpath");
    String fileName = data.getLastPathSegment();
    File output = new File(Environment.getExternalStorageDirectory(),
        fileName);
    if (output.exists()) {
      output.delete();
    }

    InputStream stream = null;
    FileOutputStream fos = null;
    try {

      URL url = new URL(urlPath);
      stream = url.openConnection().getInputStream();
      InputStreamReader reader = new InputStreamReader(stream);
      fos = new FileOutputStream(output.getPath());
      int next = -1;
      while ((next = reader.read()) != -1) {
        fos.write(next);
      }
      // Sucessful finished
      result = Activity.RESULT_OK;

    } catch (Exception e) {
      e.printStackTrace();
    } finally {
      if (stream != null) {
        try {
          stream.close();
        } catch (IOException e) {
          e.printStackTrace();
        }
      }
      if (fos != null) {
        try {
          fos.close();
        } catch (IOException e) {
          e.printStackTrace();
        }
      }
    }

    Bundle extras = intent.getExtras();
    if (extras != null) {
      Messenger messenger = (Messenger) extras.get("MESSENGER");
      Message msg = Message.obtain();
      msg.arg1 = result;
      msg.obj = output.getAbsolutePath();
      try {
        messenger.send(msg);
      } catch (android.os.RemoteException e1) {
        Log.w(getClass().getName(), "Exception sending message", e1);
      }

    }
  }
} 


<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="de.vogella.android.intentservice.download"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk android:minSdkVersion="15" />
    <uses-permission android:name="android.permission.INTERNET"/>
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

    <application
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name" >
        <activity
            android:name=".MainActivity"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

        <service android:name="DownloadService" >
        </service>
    </application>

</manifest> 

Change the main.xml layout to the following.

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >

    <Button
        android:id="@+id/button1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:onClick="onClick"
        android:text="Button" />

</LinearLayout> 

Change MainActivity to the following.

package de.vogella.android.intentservice.download;

import android.app.Activity;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.os.Messenger;
import android.view.View;
import android.widget.Toast;

public class MainActivity extends Activity {
  private Handler handler = new Handler() {
    public void handleMessage(Message message) {
      Object path = message.obj;
      if (message.arg1 == RESULT_OK && path != null) {
        Toast.makeText(MainActivity.this,
            "Downloaded" + path.toString(), Toast.LENGTH_LONG)
            .show();
      } else {
        Toast.makeText(MainActivity.this, "Download failed.",
            Toast.LENGTH_LONG).show();
      }

    };
  };

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

  }

  public void onClick(View view) {
    Intent intent = new Intent(this, DownloadService.class);
    // Create a new Messenger for the communication back
    Messenger messenger = new Messenger(handler);
    intent.putExtra("MESSENGER", messenger);
    intent.setData(Uri.parse("http://www.vogella.com/index.html"));
    intent.putExtra("urlpath", "http://www.vogella.com/index.html");
    startService(intent);
  }
} 

If you run your example and press the button, the download should be performed by the Service and once done the activity should show a Toast with the file name.

6. Tutorial: Using IntentService

The following example demonstrates how to use the IntentService class to download a file from the Internet. Once done the IntentService will use an instance of the Messenger class to inform the activity which started the service about the location of the downloaded file.
Create a new project called de.vogella.android.intentservice.download with a activity called MainActivity.
Create a service "DownloadService" by creating the following class and the entry in AndroidManifest.xml. Also add the permission to write to external storage and to access the Internet to the file.

package de.vogella.android.intentservice.download;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URL;

import android.app.Activity;
import android.app.IntentService;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.os.Environment;
import android.os.Message;
import android.os.Messenger;
import android.util.Log;

public class DownloadService extends IntentService {

  private int result = Activity.RESULT_CANCELED;

  public DownloadService() {
    super("DownloadService");
  }

  // Will be called asynchronously be Android
  @Override
  protected void onHandleIntent(Intent intent) {
    Uri data = intent.getData();
    String urlPath = intent.getStringExtra("urlpath");
    String fileName = data.getLastPathSegment();
    File output = new File(Environment.getExternalStorageDirectory(),
        fileName);
    if (output.exists()) {
      output.delete();
    }

    InputStream stream = null;
    FileOutputStream fos = null;
    try {

      URL url = new URL(urlPath);
      stream = url.openConnection().getInputStream();
      InputStreamReader reader = new InputStreamReader(stream);
      fos = new FileOutputStream(output.getPath());
      int next = -1;
      while ((next = reader.read()) != -1) {
        fos.write(next);
      }
      // Sucessful finished
      result = Activity.RESULT_OK;

    } catch (Exception e) {
      e.printStackTrace();
    } finally {
      if (stream != null) {
        try {
          stream.close();
        } catch (IOException e) {
          e.printStackTrace();
        }
      }
      if (fos != null) {
        try {
          fos.close();
        } catch (IOException e) {
          e.printStackTrace();
        }
      }
    }

    Bundle extras = intent.getExtras();
    if (extras != null) {
      Messenger messenger = (Messenger) extras.get("MESSENGER");
      Message msg = Message.obtain();
      msg.arg1 = result;
      msg.obj = output.getAbsolutePath();
      try {
        messenger.send(msg);
      } catch (android.os.RemoteException e1) {
        Log.w(getClass().getName(), "Exception sending message", e1);
      }

    }
  }
} 


<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="de.vogella.android.intentservice.download"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk android:minSdkVersion="15" />
    <uses-permission android:name="android.permission.INTERNET"/>
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

    <application
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name" >
        <activity
            android:name=".MainActivity"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

        <service android:name="DownloadService" >
        </service>
    </application>

</manifest> 

Change the main.xml layout to the following.

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >

    <Button
        android:id="@+id/button1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:onClick="onClick"
        android:text="Button" />

</LinearLayout> 

Change MainActivity to the following.

package de.vogella.android.intentservice.download;

import android.app.Activity;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.os.Messenger;
import android.view.View;
import android.widget.Toast;

public class MainActivity extends Activity {
  private Handler handler = new Handler() {
    public void handleMessage(Message message) {
      Object path = message.obj;
      if (message.arg1 == RESULT_OK && path != null) {
        Toast.makeText(MainActivity.this,
            "Downloaded" + path.toString(), Toast.LENGTH_LONG)
            .show();
      } else {
        Toast.makeText(MainActivity.this, "Download failed.",
            Toast.LENGTH_LONG).show();
      }

    };
  };

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

  }

  public void onClick(View view) {
    Intent intent = new Intent(this, DownloadService.class);
    // Create a new Messenger for the communication back
    Messenger messenger = new Messenger(handler);
    intent.putExtra("MESSENGER", messenger);
    intent.setData(Uri.parse("http://www.vogella.com/index.html"));
    intent.putExtra("urlpath", "http://www.vogella.com/index.html");
    startService(intent);
  }
} 

If you run your example and press the button, the download should be performed by the Service and once done the activity should show a Toast with the file name.

http://www.vogella.com/code/index.html

Saturday, 1 June 2013

Consuming .NET Web Services via kSOAP Library

I have seen that many people having difficulties on consuming .NET web services via Android mobile devices.

I wrote an article on codeproject.com about this issue more than a year ago but many people do not know about it and still ask me.

Here is the original link for the article;

http://www.codeproject.com/KB/mobile/CallWebServiceFromkSOAP.aspx


Introduction

As many of you know web services are the great way to establish communication between distance and independant platforms.

It is straightforward to create .NET web services and easier to use them from any .NET Framework enabled system. Nevertheless, when the issue come to a non-.NET Frameworked system, some interoperability difficulties may appear. In order to enhance the interoperability of web services, the WS-I publishes profiles. The following article will be using RPC(Remote Procedure Call) messaging pattern of the widely used profile, SOAP(Simple Object Access Protocol) with .NET web services on the server side.

On the client side, Java enabled Android OS installed mobile device will be used. For Android OS we need a web service client library that is specially designed for constrained Java environments and kSOAP provides this facility for us in open source way!

Main purpose of the article is to demonstrate how to write a .NET web service that can communicate with an Android OS through kSOAP library.

Required Technologies

The versions of the softwares which are used during this article, are listed below;
  • SOAP v 1.1
  • kSOAP v 2.1.1 (patched with WSDL patch)
  • Microsoft .NET Framework 2.0 SDK
  • Sun Microsystems Java Development Kit 1.6.0
  • Android SDK m5-rc15 for Linux-x86
(At the time of the article is written, all of these softwares were freely downloadable from internet.)

Using the code

Web Service Definition(in .NET)

The source code file of the web service is given below. The important thing is all the method names should be unique, even if the method signatures are different. SOAPAction values must be unique across the namespace.

(imports are not shown for brevity.)
Collapse
[WebService(Namespace = "http://tempuri.org/")]  public class Service : System.Web.Services.WebService  {      public Service(){}      [SoapRpcMethod(), WebMethod]      public int GetGivenInt(int i)      {          return i;      }      [SoapRpcMethod(), WebMethod]      public Event GetGivenEvent(Event evnt)      {          return evnt;      }       [SoapRpcMethod(), WebMethod]      public int[] GetGivenIntArray(int[] array)      {          return array;      }       [SoapRpcMethod(), WebMethod]      public DateTime GetGivenDate(DateTime date)      {          return date;      }       [SoapRpcMethod, WebMethod]       public Event[] GetOnGoingEvents()      {          Event[] arrayToReturn = new Event[100];          Event e ;          for(int i = 0; i < 100; i++)          {          e = new Event();          e.Name = "Event"+i;          e.StartDate = new DateTime(2008, 6, 12);          e.EndDate = new DateTime(2008, 6, 20);          e.SubscriptionStartDate = new DateTime(2008, 3, 12);          e.SubscriptionEndDate = new DateTime(2008, 4, 12);          arrayToReturn[i] = e;          }           return arrayToReturn;      }       // Custom defined inner class to represent complex type.     public class Event      {           // Generate properties for           // String Name,           // int Key,           // DateTime SubscriptionStartDate, SubscriptionEndDate, StartDate, EndDate       }  

Client Side Complex Type Definitions(in Java)

(imports are not shown for brevity.)
Collapse
public abstract class BaseObject implements KvmSerializable {     public static final String NAMESPACE = "http://tempuri.org/encodedTypes";     public BaseObject() {         super();     } }  public class Event extends BaseObject {     public static Class EVENT_CLASS = new Event().getClass();         private String name;     private int key;     private Date subscriptionStartDate;     private Date subscriptionEndDate;     private Date startDate;     private Date endDate;      @Override     public Object getProperty(int index)     {         switch (index) {         case 0:             return name;         case 1:              return key;         case 2:             return subscriptionStartDate;         case 3:             return subscriptionEndDate;         case 4:             return startDate;         case 5:             return endDate;         default:             return null;         }     }      @Override     public int getPropertyCount() {         return 6;     }      @Override     public void getPropertyInfo(int index, Hashtable properties, PropertyInfo info) {         switch (index)         {         case 0:             info.type = PropertyInfo.STRING_CLASS;             info.name = "Name";             break;         case 1:             info.type = PropertyInfo.INTEGER_CLASS;             info.name = "Key";             break;         case 2:             info.type = MarshalDate.DATE_CLASS;             info.name = "SubscriptionStartDate";             break;         case 3:             info.type = MarshalDate.DATE_CLASS;             info.name = "SubscriptionEndDate";             break;         case 4:             info.type = MarshalDate.DATE_CLASS;             info.name = "StartDate";             break;         case 5:             info.type = MarshalDate.DATE_CLASS;             info.name = "EndDate";             break;         default:             break;         }     }      @Override     public void setProperty(int index, Object value) {         switch (index) {         case 0:             name = value.toString();              break;         case 1:              key = Integer.parseInt(value.toString());             break;         case 2:             subscriptionStartDate = (Date)value;             break;         case 3:             subscriptionEndDate = (Date)value;             break;         case 4:             startDate = (Date)value;             break;         case 5:             endDate = (Date)value;             break;         default:             break;         }     } // Getters and setters are omitted for brevity. } 

Defining Web Service Properties from Client Side

Defining parameters for calling the SOAP RPC web service methods.
Collapse
    private static final String SOAP_ACTION = "http://tempuri.org/MethodName";     private static final String METHOD_NAME = "MethodName";     private static final String NAMESPACE = "http://tempuri.org/";         private static final String URL = "http://192.168.2.200/Service.asmx"; 

All of the data above can be retrieved from the web service definition(WSDL).
METHOD_NAME is the name of the method that we define in the web service.
NAMESPACE is the namespace of the web service, default is “http://tempuri.org/”, can be specific to your own organization.
SOAP_ACTION is the direct concatenation of NAMESPACE followed by METHOD_NAME.
URL is the location where the web service can be accessed from. If the connection will be through SSL, you need to specify it here.(e.g. https)

Set the Arguments to Pass

Collapse
            SoapObject request = new SoapObject(NAMESPACE, METHOD_NAME);     

After defining our SoapObject, we can add the arguments that we are going to send to the web service method viaaddProperty() method.

If the web service method does not require any parameters, no need to add any property. If one or more parameters are required, the important thing while passing a parameter is the PropertyInfo's name and type should match with the original web service method's parameter names.

First example is GetGivenInt() web service method which gets a primitive int parameter and returns the same primitive integer value.
Collapse
            PropertyInfo pi = new PropertyInfo();              pi.setName("i");             pi.setValue(5);                         request.addProperty(pi);      
Second example web service method is GetGivenDate() which gets a DateTime parameter and returns the same value. We need to add marshalling for the simple types that are not standardized in kSOAP, I will talk about it in the next part.
Collapse
            PropertyInfo pi = new PropertyInfo();             pi.setName("date");             pi.setValue(new Date(System.currentTimeMillis()));                         request.addProperty(pi);      
Another example is a complex type, which is going to be sent as a parameter to the GetGivenEvent() web service method which returns the same Event object back. Since the type Event is complex, we set the type as the Eventclass.
Collapse
            PropertyInfo pi = new PropertyInfo();             pi.setName("evnt");             Event e = new Event();             e.setName("Antalya, Turkey");             e.setKey(1);             e.setEndDate(new Date(EndDate.timeMillis()));             e.setStartDate(new Date(StartDate.timeMillis()));             e.setSubscriptionEndDate(new Date(SubscriptionEndDate.timeMillis()));             e.setSubscriptionStartDate(new Date(SubscriptionStartDate.timeMillis()));             pi.setValue(e);             pi.setType(Event.EVENT_CLASS);             request.addProperty(pi); 

Set Up the Envelope

Collapse
            SoapSerializationEnvelope envelope =                  new SoapSerializationEnvelope(SoapEnvelope.VER11);             envelope.dotNet = true;             envelope.setOutputSoapObject(request); 
In this example SOAP version 1.1 is used but both version 1.1 and 1.2 are supported by the .NET Framework. The dotNet flag needs to be true for a .NET web service call from kSOAP2. In the end SoapObject instance “request” is assigned as the outbound message of the soap call to the envelope.

Add Necessary Marshals

Marshalling is even required for simple types if they are not defined by kSOAP library by default. The class that we are going to register for marshalling should implement the Marshal interface which has three important methods.

readInstance() method is required to parse the xml string to the simple type when a response is retreived. Here is the given example of MarsalDate class, stringToDate() method should be changed to your defined type parsing method.
Collapse
   public Object readInstance(XmlPullParser parser, String namespace, String name,                       PropertyInfo expected) throws IOException, XmlPullParserException {         return IsoDate.stringToDate(parser.nextText(), IsoDate.DATE_TIME);    }  
writeInstance() method is required to parse simple type to xml string while sending a request. The given example is from MarsalDate class, for other types the parsing method should be implemented by yourself.
Collapse
   public void writeInstance(XmlSerializer writer, Object obj) throws IOException {        writer.text(IsoDate.dateToString((Date) obj, IsoDate.DATE_TIME));    }  
register() method tells the envelope that all the xml elements suit this namespace and name will be marshalled by the given class.
Collapse
   public void register(SoapSerializationEnvelope cm) {        cm.addMapping(cm.xsd, "dateTime", MarshalDate.DATE_CLASS, this);    } 
Before calling the web service appropriate marshals should be registered to the envelope, otherwise either request or respond will give parsing errors.
Collapse
            Marshal dateMarshal = new findthefashion.serialization.MarshalDate();             dateMarshal.register(envelope);

Add Necessary Mapping

Mapping is required for complex type object parsing. The idea is similar with marshalling but the complex type object should implement the KvmSerializable and its required methods for parsing. The mapping should be added before the web service call.
Collapse
            envelope.addMapping(BaseObject.NAMESPACE, "Event", new Event().getClass()); 

Invoke the Web Service Method

After setting the parameters, marshals and mappings we are ready for a call to the web service. StandardHttpTranport class is used for the call, but for the Android OS we changed some parts of the call for tracing that's why it is called AnroidHttpTransport. However, the main idea is the same. Providing the SOAP_ACTION, URL and the envelope to the call will be enough.
Collapse
            AndroidHttpTransport androidHttpTransport = new AndroidHttpTransport(URL);             androidHttpTransport.call(SOAP_ACTION, envelope); 

Parse the Response

If the response in the envelope is not an array, after getting the response we can directly cast it to desired type.
Collapse
            int receivedInt = (Integer)envelope.getResponse();             Log.v(“FINDTHEFASHION”, receivedInt.toString());  
Same rules apply for the complex and not defined simple types.
Collapse
            Date receivedDate = (Date)envelope.getResponse();             Event receivedEvent = (Event)envelope.getResponse(); 
If the response in envelope is an array of any type(complex or primitive), the casting should be done into a Vector at first. By using the power of generics, we can define a Vector which contains the desired type.
Collapse
            Vector receivedEvents = (Vector)envelope.getResponse();             if(receivedEvents != null)             {                 for(Event curEvent : receivedEvents)                 {                     Log.v(“FINDTHEFASHION”, curEvent.toString());                 }             } 

Tracing Request & Response

The typical problem while creating a web service request call or getting a response is tracing the ongoing data. All the important data is moving between network interfaces and the exception that is thrown in the application may not be so helpful sometimes. Therefore, a packet sniffer application is required to trace all the steps and do not miss anything. Wireshark(formerly Ethereal) is one of the best network protocol analyzer which can help you on this issue. The Wireshark project is open source and binaries are freely available. I bet it will be your best friend while tracing.