`
wang_peng1
  • 浏览: 3903840 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

service的使用

阅读更多
http://www.androidcompetencycenter.com/2009/01/basics-of-android-part-iii-android-services/

 本文出自:

Many a times the application needs to do some tasks in the background for which user interventions is not required (or very less intervention is required). These background processes keeps working even if user is using some other application on the phone.

To define such background processes android has a concept of Services. Service in android is long lived application component. Service doesn’t implement any User Interface. Common example of service is Media Player application that keeps playing song in the background, file download application that can download the file in the background.

Let’s see how to create a service.

Creating a service

Android has defined a base class for all services as ‘Service’. All the services have to extend from this Service class. Service class defines service lifecycle methods like onCreate(), onStart(), onDestroy(). Here is the example a service class

package com.wissen.testApp.service;
public class MyService extends Service {
	@Override
	public IBinder onBind(Intent intent) {
		return null;
	}
	@Override
	public void onCreate() {
		super.onCreate();
		Toast.makeText(this, "Service created...", Toast.LENGTH_LONG).show();
	}

	@Override
	public void onDestroy() {
		super.onDestroy();
		Toast.makeText(this, "Service destroyed...", Toast.LENGTH_LONG).show();
	}
}
The above service is notifying the user when the service is created and service is destroyed.

Like every thing else in android, Service in android are also associated with the intents. This intent is required while using the service.

The service entry has to be done in the AndroidManifest.xml file along with the service intent as shown below:

<service class=".service.MyService">
<intent-filter>
<action android:value="com.wissen.testApp.service.MY_SERVICE" />
</intent-filter>
</service>
Now our service is created and can be used by the application code.

Using the service:

The application can start the service with the help of Context.startService method. The method will call the onCreate method of the service if service is not already created; else onStart method will be called. Here is the code to start the MyService

..

Intent serviceIntent = new Intent();
serviceIntent.setAction("com.wissen.testApp.service.MY_SERVICE");
startService(serviceIntent);
The service started with startService method will keep on running until stopService() is called or stopSelf() method is called.

Another way to use service is to bind to the service. The service contented this way will be considered required by the system only for as long as the calling context exists. To bind to the service a service connection object need to be created. The service connection object tell the application when the service is connected or disconnected. Here is how you can bind to the service.

…

ServiceConnection conn = new ServiceConnection() {
	@Override
	public void onServiceConnected(ComponentName name, IBinder service) {
	Log.i("INFO", "Service bound ");
	@Override
	public void onServiceDisconnected(ComponentName arg0) {
	Log.i("INFO", "Service Unbound ");
	}
	}

	bindService(new Intent("com.wissen.testApp.service.MY_SERVICE"), conn, Context.BIND_AUTO_CREATE);
}
…

The application can communicate with the service when application is connected with the service. Generally the service communicate is done with the help of Service Interface. Service interface defines methods for which service can provider implementation. For example here is some interface:

package com.wissen.testApp;
public interface IMyService {
public int getStatusCode();
}
Using this interface the application can ask the Service about its status. Lets see how the service can support this interface. Previously we saw a method called onBind which return IBinder object, the method gets called when some client of the service binds to the service. This is the same object that is passed to the onServiceConnected method. The application can communicate with the service using this IBinder object. Here is how this can be done:

 

public class MyService extends Service {
	private int statusCode;
	private MyServiceBinder myServiceBinder = new MyServiceBinder();
	@Override
	public IBinder onBind(Intent intent) {
		return myServiceBinder; // object of the class that implements Service
								// interface.
	}
	public class MyServiceBinder extends Binder implements IMyService {

		public int getStatusCode() {
			return statusCode;
		}
	}
	// .......
}
 

And here is how application can call getStatusCode method:

 

	ServiceConnection conn = new ServiceConnection() {
		@Override
		public void onServiceConnected(ComponentName name, IBinder service) {
			IMyService myService = (IMyService) service;
			statusCode = myService.getStatusCode();
			Log.i("INFO", "Service bound ");
		}
		// ........
	};

You can also define ServiceListener interface which the client of the service has to be implemented to gets update from the service. In that case the service interface will have to define methods to register and unregister the ServiceListener objects.

Communication with Remote Service:

The services that we defined until now run in the application processes, you can define service that can run in their own process. For two processes to communicate with each other they need to marshal the object to sent to other process.

Android provide an AIDL tool (Android Interface definition Language) to handle all marshalling and communication part.

 

The service has to provide the Service interface as an aidl file. The AIDL tool will create a java interface corresponding for the aidl Service Interface. The AIDL tool also defines a stub class in the generated service interface, which implements the Service Interface (as abstract methods) and also provides some other required functionality. The service interface implementation class has to extend this stub class and define the service interface methods. The service onBind method will return object of this implementation class so that the client application can use the service methods. Here is the how the communication can be done:

Create a file as IMyRemoteService.aidl as follows:

 

package com.wissen.testApp;

interface IMyRemoteService {
int getStatusCode();
}
The eclipse android plug-in will create a Java interface for the aidl file created above as the part of build process.

The interface generated above will have a Stub inner class. Define a class that extends this stub class. Here is the code for the RemoteService class:

package com.wissen.testApp;
class RemoteService implements Service {
	int statusCode;

	@Override
	public IBinder onBind(Intent arg0) {
		return myRemoteServiceStub;
	}
	private IMyRemoteService.Stub myRemoteServiceStub = new IMyRemoteService.Stub() {
		public int getStatusCode() throws RemoteException {
			return 0;
		}
	};
	// ........
}
When the client application connect to the service the onServiceConnected method will be called and client will get the IBinder object of the service. The Stub class also provides a method to obtain the Service Interface object from the IBinder object. Here is the client onServiceConnected code

 

…

	ServiceConnection conn = new ServiceConnection() {
		@Override
		public void onServiceConnected(ComponentName name, IBinder service) {
			IMyRemoteService myRemoteService = IMyRemoteService.Stub
					.asInterface(service);

			try {

				statusCode = myRemoteService.getStatusCode();
			} catch (RemoteException e) {

				// handle exception

			}

			Log.i("INFO", "Service bound ");
		}
		// .........
	};

Permissions:

Service may specify required user permissions in the AndroidManifest.xml in <service> tag like this,

<service class=".service.MyService" android:permission="com.wissen.permission.MY_SERVICE_PERMISSION">
<intent-filter>
<action android:value="com.wissen.testApp.service.MY_SERVICE" />
</intent-filter>
</service>
Then to use above service the application has to ask for permission with the help of <user-permission> tag as follows:

<uses-permission android:name="com.wissen.permission.MY_SERVICE_PERMISSION"></uses-permission>
So in today’s post we saw how to create service and use it. In the next post we will see how to use ContentProviders. 

 

2.

AndroidManifest.xml:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
      package="com.exampleservice"
      android:versionCode="1"
      android:versionName="1.0">
    <application android:icon="@drawable/icon" 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=".MyService"></service>
    </application>
    <uses-sdk android:minSdkVersion="8" />
</manifest> 
res\values\strings.xml:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string name="app_name">ExampleService</string>
    <string name="service_started">Example Service started</string> 
    <string name="service_label">Example Service Label</string> 
</resources>
res\layout\main.xml:

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

<RelativeLayout android:id="@+id/RelativeLayout01" android:layout_width="fill_parent" android:layout_height="wrap_content">
<Button android:id="@+id/btnStart" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Start Service"></Button>
<Button android:id="@+id/btnStop" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Stop Service" android:layout_alignParentRight="true"></Button>
</RelativeLayout>

<RelativeLayout android:id="@+id/RelativeLayout02" android:layout_width="fill_parent" android:layout_height="wrap_content">
<Button android:id="@+id/btnBind" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Bind to Service"></Button>
<Button android:id="@+id/btnUnbind" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Unbind from Service" android:layout_alignParentRight="true"></Button>
</RelativeLayout>

<TextView android:id="@+id/textStatus" android:textSize="24sp" android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="Status Goes Here" />
<TextView android:id="@+id/textIntValue" android:textSize="24sp" android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="Integer Value Goes Here" />
<TextView android:id="@+id/textStrValue" android:textSize="24sp" android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="String Value Goes Here" />

<RelativeLayout android:id="@+id/RelativeLayout03" android:layout_width="fill_parent" android:layout_height="wrap_content">
<Button android:id="@+id/btnUpby1" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Increment by 1"></Button>
<Button android:id="@+id/btnUpby10" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Increment by 10" android:layout_alignParentRight="true"></Button>
</RelativeLayout>

</LinearLayout>

 

src\com.exampleservice\MainActivity.java:

package com.exampleservice;

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.Message;
import android.os.Messenger;
import android.os.RemoteException;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;

public class MainActivity extends Activity {
    Button btnStart, btnStop, btnBind, btnUnbind, btnUpby1, btnUpby10;
    TextView textStatus, textIntValue, textStrValue;
    Messenger mService = null;
    boolean mIsBound;
    final Messenger mMessenger = new Messenger(new IncomingHandler());

    class IncomingHandler extends Handler {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
            case MyService.MSG_SET_INT_VALUE:
                textIntValue.setText("Int Message: " + msg.arg1);
                break;
            case MyService.MSG_SET_STRING_VALUE:
                String str1 = msg.getData().getString("str1");
                textStrValue.setText("Str Message: " + str1);
                break;
            default:
                super.handleMessage(msg);
            }
        }
    }
    private ServiceConnection mConnection = new ServiceConnection() {
        public void onServiceConnected(ComponentName className, IBinder service) {
            mService = new Messenger(service);
            textStatus.setText("Attached.");
            try {
                Message msg = Message.obtain(null, MyService.MSG_REGISTER_CLIENT);
                msg.replyTo = mMessenger;
                mService.send(msg);
            } catch (RemoteException e) {
                // In this case the service has crashed before we could even do anything with it
            }
        }

        public void onServiceDisconnected(ComponentName className) {
            // This is called when the connection with the service has been unexpectedly disconnected - process crashed.
            mService = null;
            textStatus.setText("Disconnected.");
        }
    };

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        btnStart = (Button)findViewById(R.id.btnStart);
        btnStop = (Button)findViewById(R.id.btnStop);
        btnBind = (Button)findViewById(R.id.btnBind);
        btnUnbind = (Button)findViewById(R.id.btnUnbind);
        textStatus = (TextView)findViewById(R.id.textStatus);
        textIntValue = (TextView)findViewById(R.id.textIntValue);
        textStrValue = (TextView)findViewById(R.id.textStrValue);
        btnUpby1 = (Button)findViewById(R.id.btnUpby1);
        btnUpby10 = (Button)findViewById(R.id.btnUpby10);

        btnStart.setOnClickListener(btnStartListener);
        btnStop.setOnClickListener(btnStopListener);
        btnBind.setOnClickListener(btnBindListener);
        btnUnbind.setOnClickListener(btnUnbindListener);
        btnUpby1.setOnClickListener(btnUpby1Listener);
        btnUpby10.setOnClickListener(btnUpby10Listener);

        restoreMe(savedInstanceState);

        CheckIfServiceIsRunning();
    }

    @Override
    protected void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        outState.putString("textStatus", textStatus.getText().toString());
        outState.putString("textIntValue", textIntValue.getText().toString());
        outState.putString("textStrValue", textStrValue.getText().toString());
    }
    private void restoreMe(Bundle state) {
        if (state!=null) {
            textStatus.setText(state.getString("textStatus"));
            textIntValue.setText(state.getString("textIntValue"));
            textStrValue.setText(state.getString("textStrValue"));
        }
    }
    private void CheckIfServiceIsRunning() {
        //If the service is running when the activity starts, we want to automatically bind to it.
        if (MyService.isRunning()) {
            doBindService();
        }
    }

    private OnClickListener btnStartListener = new OnClickListener() {
        public void onClick(View v){
            startService(new Intent(MainActivity.this, MyService.class));
        }
    };
    private OnClickListener btnStopListener = new OnClickListener() {
        public void onClick(View v){
            doUnbindService();
            stopService(new Intent(MainActivity.this, MyService.class));
        }
    };
    private OnClickListener btnBindListener = new OnClickListener() {
        public void onClick(View v){
            doBindService();
        }
    };
    private OnClickListener btnUnbindListener = new OnClickListener() {
        public void onClick(View v){
            doUnbindService();
        }
    };
    private OnClickListener btnUpby1Listener = new OnClickListener() {
        public void onClick(View v){
            sendMessageToService(1);
        }
    };
    private OnClickListener btnUpby10Listener = new OnClickListener() {
        public void onClick(View v){
            sendMessageToService(10);
        }
    };
    private void sendMessageToService(int intvaluetosend) {
        if (mIsBound) {
            if (mService != null) {
                try {
                    Message msg = Message.obtain(null, MyService.MSG_SET_INT_VALUE, intvaluetosend, 0);
                    msg.replyTo = mMessenger;
                    mService.send(msg);
                } catch (RemoteException e) {
                }
            }
        }
    }


    void doBindService() {
        bindService(new Intent(this, MyService.class), mConnection, Context.BIND_AUTO_CREATE);
        mIsBound = true;
        textStatus.setText("Binding.");
    }
    void doUnbindService() {
        if (mIsBound) {
            // If we have received the service, and hence registered with it, then now is the time to unregister.
            if (mService != null) {
                try {
                    Message msg = Message.obtain(null, MyService.MSG_UNREGISTER_CLIENT);
                    msg.replyTo = mMessenger;
                    mService.send(msg);
                } catch (RemoteException e) {
                    // There is nothing special we need to do if the service has crashed.
                }
            }
            // Detach our existing connection.
            unbindService(mConnection);
            mIsBound = false;
            textStatus.setText("Unbinding.");
        }
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        try {
            doUnbindService();
        } catch (Throwable t) {
            Log.e("MainActivity", "Failed to unbind from the service", t);
        }
    }
}

src\com.exampleservice\MyService.java:

package com.exampleservice;

import java.util.ArrayList;
import java.util.Timer;
import java.util.TimerTask;

import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
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.util.Log;

public class MyService extends Service {
    private NotificationManager nm;
    private Timer timer = new Timer();
    private int counter = 0, incrementby = 1;
    private static boolean isRunning = false;

    ArrayList<Messenger> mClients = new ArrayList<Messenger>(); // Keeps track of all current registered clients.
    int mValue = 0; // Holds last value set by a client.
    static final int MSG_REGISTER_CLIENT = 1;
    static final int MSG_UNREGISTER_CLIENT = 2;
    static final int MSG_SET_INT_VALUE = 3;
    static final int MSG_SET_STRING_VALUE = 4;
    final Messenger mMessenger = new Messenger(new IncomingHandler()); // Target we publish for clients to send messages to IncomingHandler.


    @Override
    public IBinder onBind(Intent intent) {
        return mMessenger.getBinder();
    }
    class IncomingHandler extends Handler { // Handler of incoming messages from clients.
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
            case MSG_REGISTER_CLIENT:
                mClients.add(msg.replyTo);
                break;
            case MSG_UNREGISTER_CLIENT:
                mClients.remove(msg.replyTo);
                break;
            case MSG_SET_INT_VALUE:
                incrementby = msg.arg1;
                break;
            default:
                super.handleMessage(msg);
            }
        }
    }
    private void sendMessageToUI(int intvaluetosend) {
        for (int i=mClients.size()-1; i>=0; i--) {
            try {
                // Send data as an Integer
                mClients.get(i).send(Message.obtain(null, MSG_SET_INT_VALUE, intvaluetosend, 0));

                //Send data as a String
                Bundle b = new Bundle();
                b.putString("str1", "ab" + intvaluetosend + "cd");
                Message msg = Message.obtain(null, MSG_SET_STRING_VALUE);
                msg.setData(b);
                mClients.get(i).send(msg);

            } catch (RemoteException e) {
                // The client is dead. Remove it from the list; we are going through the list from back to front so this is safe to do inside the loop.
                mClients.remove(i);
            }
        }
    }

    @Override
    public void onCreate() {
        super.onCreate();
        Log.i("MyService", "Service Started.");
        showNotification();
        timer.scheduleAtFixedRate(new TimerTask(){ public void run() {onTimerTick();}}, 0, 100L);
        isRunning = true;
    }
    private void showNotification() {
        nm = (NotificationManager)getSystemService(NOTIFICATION_SERVICE);
        // In this sample, we'll use the same text for the ticker and the expanded notification
        CharSequence text = getText(R.string.service_started);
        // Set the icon, scrolling text and timestamp
        Notification notification = new Notification(R.drawable.icon, text, System.currentTimeMillis());
        // The PendingIntent to launch our activity if the user selects this notification
        PendingIntent contentIntent = PendingIntent.getActivity(this, 0, new Intent(this, MainActivity.class), 0);
        // Set the info for the views that show in the notification panel.
        notification.setLatestEventInfo(this, getText(R.string.service_label), text, contentIntent);
        // Send the notification.
        // We use a layout id because it is a unique number.  We use it later to cancel.
        nm.notify(R.string.service_started, notification);
    }
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.i("MyService", "Received start id " + startId + ": " + intent);
        return START_STICKY; // run until explicitly stopped.
    }

    public static boolean isRunning()
    {
        return isRunning;
    }


    private void onTimerTick() {
        Log.i("TimerTick", "Timer doing work." + counter);
        try {
            counter += incrementby;
            sendMessageToUI(counter);

        } catch (Throwable t) { //you should always ultimately catch all exceptions in timer tasks.
            Log.e("TimerTick", "Timer Tick Failed.", t);            
        }
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        if (timer != null) {timer.cancel();}
        counter=0;
        nm.cancel(R.string.service_started); // Cancel the persistent notification.
        Log.i("MyService", "Service Stopped.");
        isRunning = false;
    }
}

  • 大小: 27.3 KB
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics