android service的使用与理解

来源:互联网 发布:表示喜欢的网络用语 编辑:程序博客网 时间:2024/06/07 03:23

Service是android四大组件之一,它默默在后台工作处理一些如,播放音乐、文件下载等耗时操作。Service有两种工作方式:startService() 和bindService()两种。

startService方式:启动后一直运行,直到被销毁(stopService()或者stopSelf()),期间不会与启动该服务的组件有任何的通信;

 bindService方式:启动后Service会和启动它的组件绑定在一起,随其消亡,使用unbindService() 进行销毁,期间可以通过进程间通信既(IPC)与启动该服务的组件进行通信。比如:发送数据、返回结果等。多个组件可以绑定同一个服务,当所有组件都调用unbindService()方法后,则服务销毁。

不管那种方式的Service,它都是运行在主线程里面,不会在单独的线程或者单独的进程中运行(除非你指定),所以在进行耗时操作时,自己需要在服务中单独创建线程,以防止(ANR)。

两种不同的Service方式:

startService方式:通过调用startService方法来启动Service,并通过Intent进行数据的传递。startService方式的特点:1、启动后有独立的生命周期,不随启动他的组件的生命周期。2、运行在主线程中,不在单独的进程中。3、可以通过Intent传递数据,Serivce的onStartCommand可以获得Intent对象。4、想要获得Service返回的结果数据可以通过在客户端创建PendingIntent .getBroadcast()方式并包裹在Intent中传递到Service中去,在客户端接收广播。5、onStartCommand()方法的返回值可以取1、START_NOT_STICKY 2、START_STICKY、START_REDELIVR_INTENT三种不同的值:

START_NOT_STICKY:系统把Serivce杀死不会再重新启动它;

START_STICKY:系统把Service杀死会重新启动它,但是调用的onStartCommand传递的Intent为null;

START_REDELIVR_INTENT:系统把Service杀死会重新启动它,并且调用onStartCommand传递的Intent为最后传递给Service的Intent。

stopService的方式: 其他组件可以调用stopService方法,Service自身可以调用stopSelf方法。当同时在onStartCommand中传递啦多个任务时,最好用stopSelf方式进行停止。

 

与Serivce交互传递数据的启动方式bindService方式:

 

Serivce允许多个客户端进行绑定,但只在第一次被绑定的时候才会调用onBind方法,返回IBinder对象,其他客户端绑定的时候就不会再调用onBind方法,直接返回相同的IBinder对象

Service可以通过三种方式进行绑定:ExtendingtheBinder class、Using aMessenger、Using AIDL:

Extendingthe Binder class:适用于Client和Service在同一个应用程序中:

public class LocalService extends Service {    // Binder givento clients    private finalIBinder mBinder=new LocalBinder();    // Random numbergenerator    private finalRandom mGenerator= new Random();    /**     * Class used for the client Binder.  Because we knowthis service always     * runs in the same process as its clients, we don't need todeal with IPC.     */    public classLocalBinderextendsBinder{        LocalServicegetService(){            // Return this instance of LocalService soclients can call public methods            return LocalService.this;        }    }    @Override    public IBinder onBind(Intent intent){        return mBinder;    }    /** method forclients */    public int getRandomNumber(){        return mGenerator.nextInt(100);    }}


public class BindingActivity extends Activity {    LocalService mService;    boolean mBound = false;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.main);    }    @Override    protected void onStart() {        super.onStart();        // Bind to LocalService        Intent intent = new Intent(this, LocalService.class);        bindService(intent, mConnection, Context.BIND_AUTO_CREATE);    }    @Override    protected void onStop() {        super.onStop();        // Unbind from the service        if (mBound) {            unbindService(mConnection);            mBound = false;        }    }    /** Called when a button is clicked (the button in the layout file attaches to     * this method with the android:onClick attribute) */    public void onButtonClick(View v) {        if (mBound) {            // Call a method from the LocalService.            // However, if this call were something that might hang, then this request should            // occur in a separate thread to avoid slowing down the activity performance.            int num = mService.getRandomNumber();            Toast.makeText(this, "number: " + num, Toast.LENGTH_SHORT).show();        }    }    /** Defines callbacks for service binding, passed to bindService() */    private ServiceConnection mConnection = new ServiceConnection() {        @Override        public void onServiceConnected(ComponentName className,                                       IBinder service) {            // We've bound to LocalService, cast the IBinder and get LocalService instance            LocalBinder binder = (LocalBinder) service;            mService = binder.getService();            mBound = true;        }        @Override        public void onServiceDisconnected(ComponentName arg0) {            mBound = false;        }    };}


Messager方式:



public class MessengerService extends Service {    /** Command to the service to display a message */    static final int MSG_SAY_HELLO = 1;    /**     * Handler of incoming messages from clients.     */    class IncomingHandler extends Handler {        @Override        public void handleMessage(Message msg) {            switch (msg.what) {                case MSG_SAY_HELLO:                    Toast.makeText(getApplicationContext(), "hello!", Toast.LENGTH_SHORT).show();                    break;                default:                    super.handleMessage(msg);            }        }    }    /**     * Target we publish for clients to send messages to IncomingHandler.     */    final Messenger mMessenger = new Messenger(new IncomingHandler());    /**     * When binding to the service, we return an interface to our messenger     * for sending messages to the service.     */    @Override    public IBinder onBind(Intent intent) {        Toast.makeText(getApplicationContext(), "binding", Toast.LENGTH_SHORT).show();        return mMessenger.getBinder();    }}



public class MainActivity extends AppCompatActivity {    Messenger mService = null;    boolean mBound;    private ServiceConnection mConnection = new ServiceConnection() {        public void onServiceConnected(ComponentName className, IBinder service) {            mService = new Messenger(service);            mBound = true;        }        public void onServiceDisconnected(ComponentName className) {            mService = null;            mBound = false;        }    };    private Handler mActivityHandler = new Handler() {        @Override        public void handleMessage(Message msg) {            super.handleMessage(msg);        }    };    private Messenger mActivityMessenger = new Messenger(mActivityHandler);    public void sayHello(View v) {        if (!mBound) return;        Message msg = Message.obtain(null, MessengerService.MSG_SAY_HELLO, 0, 0);        msg.replyTo = mActivityMessenger;        try {            mService.send(msg);        } catch (RemoteException e) {            e.printStackTrace();        }    }    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.main);    }    @Override    protected void onStart() {        super.onStart();        // Bind to the service        bindService(new Intent(this, MessengerService.class), mConnection,                Context.BIND_AUTO_CREATE);    }    @Override    protected void onStop() {        super.onStop();        // Unbind from the service        if (mBound) {            unbindService(mConnection);            mBound = false;        }    }}

注意:仅Actvity、Service、ContentProvider可以绑定Service,broadcast receiver不可以绑定Service。onServiceDisconnected()只有在与Service的绑定意外丢失的时候才会调用:比如Service崩溃或者被杀的时候。unbindService不会调用它。

 

bindService的AIDL方式

 

aidl是一种IPC(跨进程通信)方式,在Serivce使用它的原因是你的服务需要多个不同的客户端调用,你的服务执行在多线程中。

下面介绍执行aidl的调用的步骤:

1、aidl文件的编写

(src/ directory/)目录下创建.Aidl文件,该文件中只包括接口和方法的定义。它会在(gen/ directory/)目录下生成对应的.java文件。

// IRemoteService.aidlpackage com.example.android;// Declare any non-default types here with import statements/** Example service interface */interface IRemoteService {    /** Request the process ID of this service, to do evil things with it. */    int getPid();    /** Demonstrates some basic types that you can use as parameters     * and return values in AIDL.     */    void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,                    double aDouble, String aString);}


1.AIDL支持Java原始数据类型。

2.AIDL支持StringCharSequence

3.AIDL支持传递其他AIDL接口,但你引用的每个AIDL接口都需要一个import语句,即使位于同一个包中。

4.AIDL支持传递实现了Android.os.Parcelable接口的复杂类型,同样在引用这些类型时也需要import语句。

5.AIDL支持java.util.Listjava.util.Map,但是有一些限制。集合中项的允许数据类型包括Java原始类型、StringCharSequence或是android.os.Parcelable。无需为ListMap提供import语句,但需要为Parcelable提供import语句。

6.非原始类型中,除了StringCharSequence以外,其余均需要一个方向指示符。方向指示符包括inout、和inoutAIDL中的定向 tag 表示了在跨进程通信中数据的流向,其中 in表示数据只能由客户端流向服务端, out表示数据只能由服务端流向客户端,而 inout则表示数据可在服务端与客户端之间双向流通。其中,数据流向是针对在客户端中的那个传入方法的对象而言的。in为定向 tag的话表现为服务端将会接收到一个那个对象的完整数据,但是客户端的那个对象不会因为服务端对传参的修改而发生变动;out的话表现为服务端将会接收到那个对象的参数为空的对象,但是在服务端对接收到的空对象有任何修改之后客户端将会同步变动;inout为定向 tag 的情况下,服务端将会接收到客户端传来对象的完整信息,并且客户端将会同步服务端对该对象的任何变动。

实现了Android.os.Parcelable接口的复杂类型一个继承Parcelable的类,可以通过aidl进行数据传递。继承Parcelable接口的步骤如下:

1.继承Parcelable接口。重写writeToParcel和Parcelable.Creator方法,这是一个序列化和反序列化的过程。

2.创建一个aidl文件。具体见代码如下:

import android.os.Parcel;        import android.os.Parcelable;public final class Rect implements Parcelable {    public int left;    public int top;    public int right;    public int bottom;    public static final Parcelable.Creator<Rect> CREATOR = new            Parcelable.Creator<Rect>() {                public Rect createFromParcel(Parcel in) {                    return new Rect(in);                }                public Rect[] newArray(int size) {                    return new Rect[size];                }            };    public Rect() {    }    private Rect(Parcel in) {        readFromParcel(in);    }    public void writeToParcel(Parcel out) {        out.writeInt(left);        out.writeInt(top);        out.writeInt(right);        out.writeInt(bottom);    }    public void readFromParcel(Parcel in) {        left = in.readInt();        top = in.readInt();        right = in.readInt();        bottom = in.readInt();    }}



package android.graphics;        // Declare Rect so AIDL can find it and knows that it implements        // the parcelable protocol.        parcelable Rect;

2、实现该接口, 在不同进程中的客户端,需要把aidl的完整目录拷到自己的工程目录下面,aidl文件的目录要和Serivce中aidl文件的目录完全一致。

3、向客户端公开接口


   // IRemoteService.aidl        package com.example.android;// Declare any non-default types here with import statements/** Example service interface */interface IRemoteService {    /** Request the process ID of this service, to do evil things with it. */    int getPid();    /** Demonstrates some basic types that you can use as parameters     * and return values in AIDL.     */    void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,                    double aDouble, String aString);}    private final IRemoteService.Stub mBinder = new IRemoteService.Stub() {        public int getPid(){            return Process.myPid();        }        public void basicTypes(int anInt, long aLong, boolean aBoolean,                               float aFloat, double aDouble, String aString) {            // Does nothing        }    };



客户端通过下面的例子方法进行aidl调用


public static class Binding extends Activity {    /** The primary interface we will be calling on the service. */    IRemoteService mService = null;    /** Another interface we use on the service. */    ISecondary mSecondaryService = null;    Button mKillButton;    TextView mCallbackText;    private boolean mIsBound;    /**     * Standard initialization of this activity.  Set up the UI, then wait     * for the user to poke it before doing anything.     */    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.remote_service_binding);        // Watch for button clicks.        Button button = (Button)findViewById(R.id.bind);        button.setOnClickListener(mBindListener);        button = (Button)findViewById(R.id.unbind);        button.setOnClickListener(mUnbindListener);        mKillButton = (Button)findViewById(R.id.kill);        mKillButton.setOnClickListener(mKillListener);        mKillButton.setEnabled(false);        mCallbackText = (TextView)findViewById(R.id.callback);        mCallbackText.setText("Not attached.");    }    /**     * Class for interacting with the main interface of the service.     */    private ServiceConnection mConnection = new ServiceConnection() {        public void onServiceConnected(ComponentName className,                IBinder service) {            // This is called when the connection with the service has been            // established, giving us the service object we can use to            // interact with the service.  We are communicating with our            // service through an IDL interface, so get a client-side            // representation of that from the raw service object.            mService = IRemoteService.Stub.asInterface(service);            mKillButton.setEnabled(true);            mCallbackText.setText("Attached.");            // We want to monitor the service for as long as we are            // connected to it.            try {                mService.registerCallback(mCallback);            } catch (RemoteException e) {                // In this case the service has crashed before we could even                // do anything with it; we can count on soon being                // disconnected (and then reconnected if it can be restarted)                // so there is no need to do anything here.            }            // As part of the sample, tell the user what happened.            Toast.makeText(Binding.this, R.string.remote_service_connected,                    Toast.LENGTH_SHORT).show();        }        public void onServiceDisconnected(ComponentName className) {            // This is called when the connection with the service has been            // unexpectedly disconnected -- that is, its process crashed.            mService = null;            mKillButton.setEnabled(false);            mCallbackText.setText("Disconnected.");            // As part of the sample, tell the user what happened.            Toast.makeText(Binding.this, R.string.remote_service_disconnected,                    Toast.LENGTH_SHORT).show();        }    };    /**     * Class for interacting with the secondary interface of the service.     */    private ServiceConnection mSecondaryConnection = new ServiceConnection() {        public void onServiceConnected(ComponentName className,                IBinder service) {            // Connecting to a secondary interface is the same as any            // other interface.            mSecondaryService = ISecondary.Stub.asInterface(service);            mKillButton.setEnabled(true);        }        public void onServiceDisconnected(ComponentName className) {            mSecondaryService = null;            mKillButton.setEnabled(false);        }    };    private OnClickListener mBindListener = new OnClickListener() {        public void onClick(View v) {            // Establish a couple connections with the service, binding            // by interface names.  This allows other applications to be            // installed that replace the remote service by implementing            // the same interface.            bindService(new Intent(IRemoteService.class.getName()),                    mConnection, Context.BIND_AUTO_CREATE);            bindService(new Intent(ISecondary.class.getName()),                    mSecondaryConnection, Context.BIND_AUTO_CREATE);            mIsBound = true;            mCallbackText.setText("Binding.");        }    };    private OnClickListener mUnbindListener = new OnClickListener() {        public void onClick(View v) {            if (mIsBound) {                // If we have received the service, and hence registered with                // it, then now is the time to unregister.                if (mService != null) {                    try {                        mService.unregisterCallback(mCallback);                    } catch (RemoteException e) {                        // There is nothing special we need to do if the service                        // has crashed.                    }                }                // Detach our existing connection.                unbindService(mConnection);                unbindService(mSecondaryConnection);                mKillButton.setEnabled(false);                mIsBound = false;                mCallbackText.setText("Unbinding.");            }        }    };    private OnClickListener mKillListener = new OnClickListener() {        public void onClick(View v) {            // To kill the process hosting our service, we need to know its            // PID.  Conveniently our service has a call that will return            // to us that information.            if (mSecondaryService != null) {                try {                    int pid = mSecondaryService.getPid();                    // Note that, though this API allows us to request to                    // kill any process based on its PID, the kernel will                    // still impose standard restrictions on which PIDs you                    // are actually able to kill.  Typically this means only                    // the process running your application and any additional                    // processes created by that app as shown here; packages                    // sharing a common UID will also be able to kill each                    // other's processes.                    Process.killProcess(pid);                    mCallbackText.setText("Killed service process.");                } catch (RemoteException ex) {                    // Recover gracefully from the process hosting the                    // server dying.                    // Just for purposes of the sample, put up a notification.                    Toast.makeText(Binding.this,                            R.string.remote_call_failed,                            Toast.LENGTH_SHORT).show();                }            }        }    };    // ----------------------------------------------------------------------    // Code showing how to deal with callbacks.    // ----------------------------------------------------------------------    /**     * This implementation is used to receive callbacks from the remote     * service.     */    private IRemoteServiceCallback mCallback = new IRemoteServiceCallback.Stub() {        /**         * This is called by the remote service regularly to tell us about         * new values.  Note that IPC calls are dispatched through a thread         * pool running in each process, so the code executing here will         * NOT be running in our main thread like most other things -- so,         * to update the UI, we need to use a Handler to hop over there.         */        public void valueChanged(int value) {            mHandler.sendMessage(mHandler.obtainMessage(BUMP_MSG, value, 0));        }    };    private static final int BUMP_MSG = 1;    private Handler mHandler = new Handler() {        @Override public void handleMessage(Message msg) {            switch (msg.what) {                case BUMP_MSG:                    mCallbackText.setText("Received from service: " + msg.arg1);                    break;                default:                    super.handleMessage(msg);            }        }    };}

注意:如果客户端的的aidl调用和Service在同一个进程中,那就不用担心调用多线程的线程安全问题。调用在主线程执行,被aidl调用方法就在主线程中执行,调用在其他线程中执行,被aidl调用方法也在此线程中执行。如果客户端的aidl的调用和Serivce不在同一个进程中并且有多个客户端可以执行aidl调用,那被aidl调用的方法就不在同一个线程中,有可能出现同时调用的情况,此时被调用的方法就要注意线程安全,因为他们运行在不同的线程中。


在Service中自动创建单独线程,并在任务执行完之后自我销毁的特殊服务——IntentService

 

其实,IntentService服务就在服务中创建好线程独立运行,并在运行完指定任务后自我销毁。方便用户使用Service。(IntentService是一种特殊的Service)。

public class MyIntentService extends IntentService {    public MyIntentService() {        super("MyIntentService");    }    @Override    protected void onHandleIntent(Intentintent) {    }}

1、  提供了单独的线程对任务进行操作,需要在构造函数中调用父类的构造函数,并重写onHandleIntent方法。

2、  执行完任务后自动销毁。

3、  多个任务排队执行

4、  通过startService中intent进行传递。

5、  重写其他周期方法如:onCeate、onStartCommand、onDestory()方法的时候要调用父类的相关实现super以维护IntentService的正常执行流程。onBind方法不需要调用父类的方法。

 

提高服务存活率的方式-前台服务

 

Service是一种在后台默默运行的服务当内存资源不足的时候有可能被杀死,但是当一个Service与获得焦点的Activtiy绑定的话,它将不那么容易被回收,同时,一个前台Service一般很难被回收。

 @Override    public int onStartCommand(Intent intent, int flags,int startId){        Log.d(TAG, "onStartCommand");        startForeground();        return super.onStartCommand(intent,flags, startId);    }    private void startForeground() {        Intent intentService = new Intent();        intentService.setAction("");        intentService.setPackage("");        PendingIntent pendingIntent =PendingIntent.getService(this, 0, intentService, 0);        Notification notification = new Notification.Builder(this)                .setTicker(" ")                .setContentText("")                .setContentTitle("")                .setContentIntent(pendingIntent)                .build();        startForeground(1,notification);    }


备注:可以通过stoptForeground(boolean)停止前台服务,参数代表是否清除通知栏的显示。停止前台服务不会停止服务。当服务被停止时,通知栏中的通知会被清除。

Service的生命周期:

Service生命周期中一般有这四个方法会被调用onCreate()、onBind()、onStartCommand()以及onDestory()方法:

onCreate()方法在Service在第一次创建的时候被调用,比如第一次startService或者第一次bindService;

onBind()方法在Service绑定的时候被调用。每一次绑定调用一次;

onStartCommand()方法在Service每次通过startService()创建的时候被调用,每创建一次调用一次,在onCreate()方法之后;

onDestory()方法在Service销毁之前被调用,比如:unBindService()、stopServcie()、stopSelf();   bindService 与unBindService相对应。



Service生命周期图


start&bindService生命周期图

startService方式:onCreate   onStartCommand    onDestory

bindService方式:onCreate    onBind    onDestory

startService&bindService方式:onCreate    onBind(bindService)  onStartCommand(startService)     onDestory(先unbindService 再stopService).

原创粉丝点击