Download Android Services

Survey
yes no Was this document useful for you?
   Thank you for your participation!

* Your assessment is very important for improving the workof artificial intelligence, which forms the content of this project

Document related concepts
no text concepts found
Transcript
Android Services Tutorial
Credits:
• http://developer.android.com/reference/android/app/Service.html
• http://www.vogella.com/articles/AndroidServices/article.html
• Meier, Reto, Professional Android 4 Application Development.
Submission requirements:
Complete the first part of the tutorial. The advanced section is optional. Take a
screenshot of your completed download service app and submit it as well as
feedback on this tutorial to Blackboard.
Extras:
•
•
Retrieve the download page using adb pull and display in a browser.
Complete the advanced section of the tutorial.
Android Service
•
An Android Service is a component, which runs in the background without
direct user interaction with the user.
•
A Service is not bound to the lifecycle of an activity.
•
Useful for repetitive and potentially long running operations. E.g., Internet
downloads, and data processing.
•
Services run with a higher priority than inactive or invisible activities, so they
are less likely to be terminated.
•
If a Service gets terminated by the Android system, it can be configured to
be restarted automatically once sufficient system resources become
available again.
•
It’s possible to assign services the same priority as foreground activities. In
this case it’s required to have a visible notification active for the related
service. For example, services which play videos or music.
Jay Urbain, PhD 1 Services and background processing
•
A Service runs by default in the same process and in the main thread of the
application.
•
You need to use asynchronous processing in the service to perform
resource intensive tasks in the background.
•
A common pattern for a Service implementation is to create and run a
new Thread in the service to perform the processing in the background, then
terminate the service once it has been finished the processing.
Platform service and custom services
•
Android provides and runs predefined system services and every Android
application can use them, given the right permissions.
•
Your Android application can define and use new services.
•
Defining custom services allows you to design responsive applications.
Starting and defining custom services
•
Custom services are started from other Android components, i.e. Activities,
BroadcastReceivers, and other Services.
Jay Urbain, PhD 2 Defining custom services
A service needs to be declared in the AndroidManifest.xml file (within the
<application> tag). This should happen automatically if you created your
Service within AndroidStudio.
<service android:name="MyService" android:icon="@drawable/icon"
android:label="@string/service_name">
</service>
The Service class or one of its subclasses must be extended:
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;
}
}
Jay Urbain, PhD 3 Start a service
An Android component (Service, Receiver, Activity) can trigger the execution
of a service via the startService(intent) method.
// start and trigger a service
Intent i= new Intent(context, MyService.class);
// optionally add data to the intent
i.putExtra("KEY1", "Value to be used by the service");
context.startService(i);
Note: you can also start a service via the bindService() method call. This
allows you to communicate directly with the Service.
Service start process and execution
•
If the startService(intent) method is called, and the Service is not yet
running, the Service object is created and the onCreate() method of the
service is called.
•
Once the service is started, the startService(intent) method in
the Service is called. It passes in the Intent object from the
startService(intent) call.
•
If startService(intent) is called while the service is running, its
onStartCommand() is called. Therefore your service needs to be prepared
that onStartCommand() can be called several times.
•
A service is only started once, no matter how often you call the
startService() method.
Stopping a service
•
You stop a service via the stopService() method. No matter how
frequently you called the startService(intent)method, one call to
the stopService() method stops the service.
•
A service can terminate itself by calling the stopSelf() method. This is
typically done if the service finishes his work.
Jay Urbain, PhD 4 Binding to services from activities
•
Service clients can also use Context.bindService() to obtain a persistent
connection to a Service.
•
Context.bindService() creates the Service if it is not already running
(calling onCreate() while doing so), but does not call onStartCommand().
•
The client will receive the IBinder object that the service returns from
its onBind(Intent) method, allowing the client to then make calls back to the
Service.
•
The Service will remain running as long as the connection is established
(whether or not the client retains a reference on the service's IBinder).
•
Usually the IBinder returned is for a complex interface that has been written
in Android Interface Definition Language (AIDL)
•
http://developer.android.com/guide/components/aidl.html.
Local services bindings
If the service runs in the same process as the activity, it is possible to return
the Service to the Activity. This allows the Activity to call methods of the
service directly.
Running a service in its own process
You can also specify that your Service runs in a separate process via the
android:process=":process_description" attribute.
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.
•
Applications rarely need to run a service in its own process. Running a
Service in its own process makes the communication of the other Android
components and the service harder to implement.
Jay Urbain, PhD 5 •
If you want to make a service to other Android application available, they
must run in their own process.
Using Intent data to communicate with Services
Useful when no direct communication is required. The Service receives the
Intent data from the starting Android component and performs its work. No
notification is necessary.
Using a receiver to communicate with a Service
You can also use broadcast events, and registered broadcast receivers for
communication. For example, your activity can dynamically register a
BroadcastReceiver for an event and the service sends outs corresponding
events.
Jay Urbain, PhD 6 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, and recommended for Activities which need to have a fast
communication layer with the Service.
•
This approach works for local Services.
Jay Urbain, PhD 7 Example: Using services and service communication
•
The following example demonstrates how to use a service to download a file
from the Internet based on a Button click from an Activity. Once done, the
Service notifies the activity via a BroadcastReceiver that the download is
complete.
•
You will use the IntentService class to provide automatic background
processing.
•
Create a new project called DownloadService with an Activity
called MainActivity in the package com.example.downloadservice.
•
Create the following class for the Service:
package com.jayurbain.downloadservice;
// Jay Urbain, 10/11/2016
//
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.os.IBinder;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
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;
/**
* @author jayurbain
*
* Demonstrates how to use a service to download a file from the Internet based on a
* button click from an Activity. Once done, the service notifies the activity via a
* broadcast receiver that the download is complete.
*/
public class DownloadService extends IntentService {
private int result = Activity.RESULT_CANCELED;
public static final String URL = "urlpath";
public static final String FILENAME = "filename";
public static final String FILEPATH = "filepath";
public static final String RESULT = "result";
Jay Urbain, PhD 8 public static final String NOTIFICATION = "com.example.downloadservice";
public DownloadService() {
super("DownloadService");
}
// Will be called asynchronously be Android
@Override
protected void onHandleIntent(Intent intent) {
String urlPath = intent.getStringExtra(URL);
String fileName = intent.getStringExtra(FILENAME);
urlPath += "/"+fileName;
File output = null;
if( canWriteOnExternalStorage() ) {
output = new File(Environment.getExternalStorageDirectory(), fileName);
}
else {
output = new File(getApplicationContext().getFilesDir(), 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);
int next = -1;
while ((next = reader.read()) != -1) {
fos.write(next);
}
// Successful 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();
}
}
}
publishResults(output.getAbsolutePath(), result);
}
/**
* Method to check if user has permissions to write on external storage or not
*/
Jay Urbain, PhD 9 public static boolean canWriteOnExternalStorage() {
// get the state of your external storage
String state = Environment.getExternalStorageState();
if (Environment.MEDIA_MOUNTED.equals(state)) {
// if storage is mounted return true
Log.v("sTag", "Yes, can write to external storage.");
return true;
}
return false;
}
private void publishResults(String outputPath, int result) {
Intent intent = new Intent(NOTIFICATION);
intent.putExtra(FILEPATH, outputPath);
intent.putExtra(RESULT, result);
sendBroadcast(intent);
}
}
Verify you have a main activity class and your download service class in
your AndroidManifest.xml file. Also add the permission to write to
external storage and to access the Internet. The
resulting AndroidManifest.xml file should look similar to the following
listing.
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.jayurbain.downloadservice">
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<service
android:name=".DownloadService"
android:exported="false"></service>
</application>
</manifest>
Jay Urbain, PhD 10 Change the layout file of your activity 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="Download" />
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content" >
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Status: " />
<TextView
android:id="@+id/status"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Not started" />
</LinearLayout>
</LinearLayout>
Jay Urbain, PhD 11 MainActivity to the following:
package com.jayurbain.downloadservice;
import android.os.Bundle;
import android.app.Activity;
import android.view.Menu;
import android.app.Activity;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
import android.view.View;
import android.widget.TextView;
import android.widget.Toast;
/**
* @author jayurbain
*
* MainActivity
* Demonstrates how to use a service to download a file from the Internet based on a
* button click from an Activity. Once done, the service notifies the activity via a
* broadcast receiver that the download is complete.
*/
public class MainActivity extends Activity {
private TextView textView;
private BroadcastReceiver receiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
Bundle bundle = intent.getExtras();
if (bundle != null) {
String string = bundle.getString(DownloadService.FILEPATH);
int resultCode = bundle.getInt(DownloadService.RESULT);
if (resultCode == RESULT_OK) {
Toast.makeText(com.jayurbain.downloadservice.MainActivity.this,
"Download complete. Download URI: " + string,
Toast.LENGTH_LONG).show();
textView.setText("Download done");
} else {
Toast.makeText(com.jayurbain.downloadservice.MainActivity.this, "Download failed",
Toast.LENGTH_LONG).show();
textView.setText("Download failed");
}
}
}
};
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
textView = (TextView) findViewById(R.id.status); }
@Override
protected void onResume() {
super.onResume();
registerReceiver(receiver, new IntentFilter(DownloadService.NOTIFICATION));
Jay Urbain, PhD 12 }
@Override
protected void onPause() {
super.onPause();
unregisterReceiver(receiver);
}
public void onClick(View view) {
Intent intent = new Intent(this, DownloadService.class);
// add info for the service which file to download and where to store
intent.putExtra(DownloadService.FILENAME, "index.html");
intent.putExtra(DownloadService.URL,
"http://jayurbain.com/msoe/se4910");
startService(intent);
textView.setText("Service started");
}
}
•
If you run your example and press the button, the download should be
performed by the service. Once done, the user interface is updated and a
Toast with the file name is shown.
•
Change the setting so that the service runs in its own process. Ensure that
the application still works, as broadcast receivers are received across
process boundaries.
Jay Urbain, PhD 13 Service Lifecycle (Advanced)
•
There are two ways a service is run by the system.
•
If someone calls Context.startService() then the system will retrieve the
service (creating it and call its onCreate() method if needed) and then call
its onStartCommand(Intent, int, int) method with the arguments supplied by
the client.
•
The service will continue running until Context.stopService() or stopSelf() is
called.
Note: Multiple calls to Context.startService() do not nest (though they do
result in multiple corresponding calls to onStartCommand()), so no matter
how many times it is started a service will be stopped once
Context.stopService() or stopSelf() is called.
•
Services can use their stopSelf(int) method to ensure the service is not
stopped until started intents have been processed.
•
For started services, there are two additional major modes of operation they
can decide to run in, depending on the value they return from
onStartCommand(): START_STICKY is used for services that are explicitly
started and stopped as needed, while START_NOT_STICKY or
START_REDELIVER_INTENT are used for services that should only remain
running while processing any commands sent to them.
•
Clients can also use Context.bindService() to obtain a persistent connection
to a service. This creates the service if it is not already running
(calling onCreate() while doing so), but does not call onStartCommand().
•
The client will receive the IBinder object that the service returns from
its onBind(Intent) method, allowing the client to then make calls back to the
service. The service will remain running as long as the connection is
established (whether or not the client retains a reference on the service's
IBinder). Usually the IBinder returned is for a complex interface that has
been written in aidl.
Jay Urbain, PhD 14 •
A service can be both started and have connections bound to it. In such a
case, the system will keep the service running as long as either it is
started or there are one or more connections to it with the Context.BIND_AUTO_CREATE flag.
•
Once neither of these situations hold, the service's onDestroy()method is
called and the service is effectively terminated. All cleanup (stopping
threads, unregistering receivers) should be complete upon returning from
onDestroy().
Jay Urbain, PhD 15