Android Basic Training Course: Basic Services Patterns

    In the previous post, I presented the Service theory, through that you can see the components that build up the Service and it's clients, now we are going to see some examples using the Service and the way how the these scenario executed.

IntentServices for one time tasks: Download File Service

    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 terminates itself automatically. An example for its usage would be downloading certain resources from the internet.
    The IntentService class offers onHandleIntent() method which will be asynchronously called by the Android system.
    DEMO VIDEO:

    Firstly in this "download file by Service" project, creating an activity layout file simple like this:
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:padding="@dimen/activity_horizontal_margin">
 
    <Button
        android:id="@+id/btn_download"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentLeft="true"
        android:layout_alignParentRight="true"
        android:layout_alignParentTop="true"
        android:layout_marginTop="30dp"
        android:text="Download" />
 
    <TextView
        android:id="@+id/text_view"
        android:textColor="@android:color/holo_green_dark"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentLeft="true"
        android:layout_centerVertical="true"
        android:text="Status"
        android:textAppearance="?android:attr/textAppearanceMedium" />
 
    <TextView
        android:id="@+id/download_status"
        android:layout_width="wrap_content"
        android:textColor="@android:color/holo_blue_dark"
        android:layout_height="wrap_content"
        android:layout_alignBaseline="@+id/text_view"
        android:layout_alignRight="@+id/btn_download"
        android:textAppearance="?android:attr/textAppearanceMedium" />
 
</RelativeLayout>
    Now, go to programmatically code, as note above, we must create a subclass of IntentService, mean that this Service run one time and auto destroy after finish downloading file process.
    By using this Service type, the most important work is overriding the onHandleIntent(), in this example, we create an InputStream to get data from an URL (a PNG image):
DownloadService.java
package devexchanges.info.downloadservice;
 
import android.app.Activity;
import android.app.IntentService;
import android.content.Intent;
import android.os.Environment;
 
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.URL;
 
public class DownloadService extends IntentService {
 
    public static final String URL = "url";
    public static final String FILENAME = "name";
    public static final String FILEPATH = "path";
    public static final String RESULT = "result";
    public static final String NOTIFICATION = "notification";
 
    public DownloadService() {
        super("DownloadService");
    }
 
    @Override
    protected void onHandleIntent(Intent intent) {
        String urlPath = intent.getStringExtra(URL);
        String fileName = intent.getStringExtra(FILENAME);
        int result = Activity.RESULT_CANCELED;
 
        try {
            URL url = new URL(urlPath);
            InputStream input = url.openStream();
            //The sdcard directory e.g. '/sdcard' can be used directly, or
            //more safely abstracted with getExternalStorageDirectory()
            File storagePath = new File(Environment.getExternalStorageDirectory() + "/Pictures");
            OutputStream output = new FileOutputStream(new File(storagePath, fileName));
            try {
                byte[] buffer = new byte[1024];
                int bytesRead = 0;
                while ((bytesRead = input.read(buffer, 0, buffer.length)) >= 0) {
                    output.write(buffer, 0, bytesRead);
                }
                result = Activity.RESULT_OK;
            } finally {
                output.close();
                input.close();
            }
        } catch (Exception ex) {
            ex.printStackTrace();
        }
 
        publishResults(result);
    }
 
    private void publishResults(int result) {
        Intent intent = new Intent(NOTIFICATION);
        intent.putExtra(RESULT, result);
        sendBroadcast(intent);
    }
}
    Note: As you see in code, we publish download result (Error or Success) to UI thread by BroadcastReceiver. In Service class, this action will invoke by call sendBroadcast(Intent).
    In this activity, we will talk more clearly about the BroadcastReceiver. Declaring an instance to receive the Service result and update data to views, overriding onReceive() is required, receiving Bundle put from Service here:
private BroadcastReceiver receiver = new BroadcastReceiver() {
        @SuppressLint("SetTextI18n")
        @Override
        public void onReceive(Context context, Intent intent) {
            Bundle bundle = intent.getExtras();
            if (bundle != null) {
                int resultCode = bundle.getInt(DownloadService.RESULT);
                if (resultCode == RESULT_OK) {
                    Toast.makeText(MainActivity.this, "File downloaded!", Toast.LENGTH_LONG).show();
                    downloadStatus.setText("Download completed!");
                } else {
                    Toast.makeText(MainActivity.this, "Error Downloading process!", Toast.LENGTH_LONG).show();
                    downloadStatus.setText("Download failed!");
                }
            }
        }
    };
    With BroadcastReceiver, we must register it in onResume() and unregistering it in onPause():
@Override
protected void onResume() {
    super.onResume();
    registerReceiver(receiver, new IntentFilter(DownloadService.NOTIFICATION));
}
 
@Override
protected void onPause() {
    super.onPause();
    unregisterReceiver(receiver);
}

    In this project, we start download process after click at a Button, save file (image) to SD card, this is full code for this activity:
MainActivity.java
package devexchanges.info.downloadservice;
 
import android.annotation.SuppressLint;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;
 
public class MainActivity extends AppCompatActivity {
 
    private TextView downloadStatus;
 
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        downloadStatus = (TextView) findViewById(R.id.download_status);
        Button btnDownload = (Button) findViewById(R.id.btn_download);
        btnDownload.setOnClickListener(onDownloadListener());
    }
 
    private View.OnClickListener onDownloadListener() {
        return new View.OnClickListener() {
            @SuppressLint("SetTextI18n")
            @Override
            public void onClick(View v) {
                Intent intent = new Intent(MainActivity.this, DownloadService.class);
                intent.putExtra(DownloadService.FILENAME, "logo.png");
                intent.putExtra(DownloadService.URL, "http://i.imgur.com/cReBvDB.png");
                startService(intent);
                downloadStatus.setText("Downloading...");
            }
        };
    }
 
    @Override
    protected void onResume() {
        super.onResume();
        registerReceiver(receiver, new IntentFilter(DownloadService.NOTIFICATION));
    }
 
    @Override
    protected void onPause() {
        super.onPause();
        unregisterReceiver(receiver);
    }
 
    private BroadcastReceiver receiver = new BroadcastReceiver() {
        @SuppressLint("SetTextI18n")
        @Override
        public void onReceive(Context context, Intent intent) {
            Bundle bundle = intent.getExtras();
            if (bundle != null) {
                int resultCode = bundle.getInt(DownloadService.RESULT);
                if (resultCode == RESULT_OK) {
                    Toast.makeText(MainActivity.this, "File downloaded!", Toast.LENGTH_LONG).show();
                    downloadStatus.setText("Download completed!");
                } else {
                    Toast.makeText(MainActivity.this, "Error Downloading process!", Toast.LENGTH_LONG).show();
                    downloadStatus.setText("Download failed!");
                }
            }
        }
    };
}
    And this is output screens:
      Important note: never forget to adding INTERNET and WRITE_EXTERNAL_STORAGE permissions to your AndroidManifest.xml:
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    You can view full project code on @Github.

Unbounded Service: Audio Player

      Most audio player app in Android does not require the user to be in applications while listening to music. Conversely, the user can do other works with their devices, while music application will run background. This is very similar to the download process mentioned above. However, in this case, the user must stop playing music process.     Creating a simple activity layout includes 2 Button (to Start and Stop playing Music):
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context="devexchanges.info.mediaplayerservice.MainActivity">
 
    <Button
        android:id="@+id/btn_start"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:onClick="startPlayer"
        android:text="Start the Player" />
 
    <Button
        android:id="@+id/btn_stop"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_below="@+id/btn_start"
        android:text="Stop the Player" />
 
</RelativeLayout>
    Layout preview:
    We will create a Service's subclass and overriding some necessary methods. In onBind() we will return an IBinder instance (may be null), because onStartCommand() will be overridden, starting MediaPlayer here:
MediaPlayerService.java
package devexchanges.info.mediaplayerservice;
 
import android.app.Service;
import android.content.Intent;
import android.media.MediaPlayer;
import android.os.IBinder;
 
public class MediaPlayerService extends Service {
    private MediaPlayer player;
    public IBinder onBind(Intent arg0) {
 
        return null;
    }
 
    @Override
    public void onCreate() {
        super.onCreate();
        player = MediaPlayer.create(this, R.raw.canon_in_d);
        player.setLooping(true); // Set looping
        player.setVolume(100, 100);
 
    }
 
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        player.start();
        return 1;
    }
 
    @Override
    public void onDestroy() {
        player.stop();
        player.release();
    }
 
    @Override
    public void onLowMemory() {
 
    }
}
    Back to the activity code, we will start/stop Service by clicking 2 Buttons:
MainActivity.java
package devexchanges.info.mediaplayerservice;
 
import android.content.Intent;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
 
public class MainActivity extends AppCompatActivity {
 
    private View btnStart;
    private View btnStop;
 
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
 
        btnStart = findViewById(R.id.btn_start);
        btnStop = findViewById(R.id.btn_stop);
 
        btnStart.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                startPlayer(v);
            }
        });
 
        btnStop.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                stopPlayer(v);
            }
        });
    }
 
    public void startPlayer(View v) {
        Intent i = new Intent(this, MediaPlayerService.class);
        startService(i);
    }
 
    public void stopPlayer(View v) {
        stopService(new Intent(this, MediaPlayerService.class));
    }
}
    This app not require any special permission. From now on, when you click Start button, app will play your audio file, and as you see in the activity code, I used startService() (bindService()), so this Service will always run in the background even your activity destroyed. It's only stop when you hit stop button.     Note: Audio resource files must be put in res/raw folder. All Android Supported Media format you can find out here!

Conclusions

    By this post, I have present 2 sample project about Service in Android corresponding to 2 Service types: IntentService and normal (Unbounded) Service. I hope that you can help you to understand the philosophy of Service action. Further, you can go to these official docs to reading more:

Next Chapter

Share


Previous post
« Prev Post
Next post
Next Post »