android Service 总结

概述

Service是一种没有UI界面,在后台执行长期任务的系统控件。

Service可以用于IPC。

android:exported  false,Service只供自己应用使用;true 可以被其他应用使用

android:process 指定服务所在的进程,默认当前应用所在进程


Foreground Service

前台Service意味着用户知道Service的存在,在系统需要资源时不会kill它,前台Service必须在状态栏上提供Notification,该Notification不会消失,除非Service被停止或者其不再是前台Service。

开启  Service.startForeground(int id, Notification notification)

Notification notification = new Notification(R.drawable.icon, getText(R.string.ticker_text),
        System.currentTimeMillis());
Intent notificationIntent = new Intent(this, ExampleActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0);
notification.setLatestEventInfo(this, getText(R.string.notification_title),
        getText(R.string.notification_message), pendingIntent);
startForeground(ONGOING_NOTIFICATION_ID, notification);

停止  Service.stopForeground(boolean removeNotification)


生命周期管理

  1. A Started Service 开始于startService(),结束于Service自己调用stopSelf(),或者其他组件调用stopService()
  2. A bound Service  开始于 bindService(),结束于所有client unbindService()

一个Service可能既被Start,又被bind,这样的情况,需要所有client unbindService,且stopSelf或者stopService。



A Started Service

其他组件调用startService()可以开启Started Service,如果该Service还未创建,执行onCreate(),否则onCreate不执行(也即只执行一遍),一个Service可以被多次start,每次都会执行onStartCommand()。

Service被start后,其与start它的组件就没有关系了,即使start它的组件被销毁了,Service也可能会存在。要停止Service,需要调用stopService()或者stopSelf(),如果Service被多个组件start,停止服务时要用stopSelf(int startId),因为如果startId不是最新的,那Service就不会被停掉,保证所有start Service的请求都能得到响应。

IntentService封装了worker-thread,可避免ANR,能满足同时只处理一个请求,继承它只需要实现onHandleIntent()即可。

onStartCommand()返回值用于描述当该方法返回时,如果服务被系统kill掉了,系统该如何对待该服务。

  1. START_NOT_STICKY 系统不会重新创建Service,除非有延迟的Intent(pending Intent)需要传递,对避免不必要地重新创建Service和应用重新开始未完成的任务(when your application can simply restart any unfinished jobs.),这是最安全的选择。
  2. START_STICKY 系统会重新创建Service,调用onStartCommand(),但不会传递最后的Intent,实际上,传递的是一个NULL的Intent,除非有pending Intent start Service(Service被kill后,有Intent startService),该选项适用与media player。
  3. START_REDELIVER_INTENT 重新创建Service,调用onStartCommand(),且传递最后的传递给Service的Intent,对pending Intent则按顺序传递,该选项适用于那些需要马上恢复的任务,比如文件下载。

Bound Service


client 调用 bindService(Intent service, ServiceConnection conn,int flags),该方法调用后马上返回,系统创建了client和Service之间的连接后,会调用ServiceConnection#onServiceConnected(ComponentName name, IBinder service),返回Service#onBind(Intent intent)的IBinder。

多个client可以bindService,但系统只在第一个clientbindService时调用onBind(),其他情况下返回第一次产生的IBinder。
当最后一个client unbindService,系统销毁Service。

Activity,ContentProvider,Service可以bindService(),不能在Broadcast receiver里bindService()。

不要在onResume()和onPause()中执行bind和unbind,因为这样的操作会很频繁,系统开销会很大。

如果重写onUnbind(),让onUnbind()返回true,当之前的client都unbind后,有新的client bind时,系统会调用onRebind(),否则就调用onBind().

创建IBinder实例有3种方式
  1. Extending the Binder class Binder实现了接口IBinder。这种方式适合Service只供自己应用使用,且运行在和client相同的进程中,通过创建Binder实例,在onBind()中返回给client,client使用返回的Binder实例可以操作Binder中的public方法和其他Binder实现类中的方法。如果Service只是供自身应用使用的后台工作线程,这是最好的技术方式。像这样创建的接口的唯一不使用的理由是Service将会被别的应用使用或者跨进程通信。
    client和Service需要在同一个进程的原因是client能将返回的object类型转换,然后调用它的API。可以在返回的Binder对象中包含client可以访问的public方法,可以返回Service对象。

  2. Using a Messenger 这种是实现IPC的最简单的方式,因为Messenger把所有的请求放在一个队列中,由一个thread处理,不需要你处理多线程和线程安全的问题。

  3. Using AIDL  AIDL完成所有将对象decompose到能被操作系统理解的primitives,然后在不同进程间使用(marshall)它们,其实Messenger也是基于AIDL的,只不过它同一时刻只处理一个请求,如果你需要同时处理多个请求,那就使用AIDL吧,这就需要你自己处理多线程和线程安全的问题。Messeger 和 AIDL 与 IntentService和自己实现Service的子类之间的关系类似。  使用AIDL的步骤是,创建定义了程序接口的.aidl文件,使用SDK提供的工具生成实现了接口和能够处理IPC的抽象类,在Service中继承抽象类。注意,大部分应用中,都不需要使用AIDL,因为它比较复杂并且用其他方式,比如Messenger,可以满足大部分应用的需要。

Messenger使用步骤

  1. 在Service中,创建Handler,用于接收client的请求,进行回调
  2. 使用Handler创建Messenger实例
  3. 在onBind()返回Messenger.getBinder()
  4. client用接收到的IBinder创建Messenger,用Messenger.send(Message msg)给Service发信息
  5. Service接收到Message后,在Handler的handleMessage()中处理
IPC间,Messenger是基于消息的通信,它不能像在同一个进程间继承Binder那样,直接调用Service的方法,Messenger通过传递消息在进程间通信。

连接建立时,创建Messenger实例
    /**
     * 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 object we can use to
            // interact with the service.  We are communicating with the
            // service using a Messenger, so here we get a client-side
            // representation of that from the raw IBinder object.
            mService = new Messenger(service);
            mBound = true;
        }

        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;
            mBound = false;
        }
    };

client向Service发送Message
    public void sayHello(View v) {
        if (!mBound) return;
        // Create and send a message to the service, using a supported 'what' value
        Message msg = Message.obtain(null, MessengerService.MSG_SAY_HELLO, 0, 0);
        try {
            mService.send(msg);
        } catch (RemoteException e) {
            e.printStackTrace();
        }
    }

Service
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();
    }
}


上面例子中,只能client向Service发送消息,如果需要Service向client发送消息,就在client里创建一个Messenger,当然需要Handler实例,用来响应Service发来的消息,将client的Messenger赋值给Message.replayTo,发送给Service,Service拿到client的Messenger就可以给client发送消息了。

client端
try {
                Message msg = Message.obtain(null,
                        MessengerService.MSG_REGISTER_CLIENT);
                msg.replyTo = mMessenger;
                mService.send(msg);

                // Give it some value as an example.
                msg = Message.obtain(null,
                        MessengerService.MSG_SET_VALUE, this.hashCode(), 0);
                mService.send(msg);
            } 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.
            }


AIDL

像其他IDL一样,AIDL用于定义跨进程client和server相互通信的接口。
在android上,一个进程不能访问另一个进程的内存,跨进程通信,需要把object decompose成操作系统能理解的primitive,这样的代码很繁琐,android使用AIDL来帮助开发者完成这样的工作。
注意,AIDL适用于IPC,且多线程;如果不是IPC,那继承Binder;如果IPC,但不是多线程,使用Messenger就可以。

定义AIDL接口

创建.aidl文件,定义在src/目录下,build代码后,在gen/下会生成对应的java文件,比如IRemoteService.aidl,生成IRemoteService.java,其内部抽象类Stub继承了Binder,实现了IRemoteService.aidl中定义的方法。
// 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);
}

public interface IRemoteService extends android.os.IInterface
{
/** Local-side IPC implementation stub class. */
public static abstract class Stub extends android.os.Binder implements com.example.demoproject.IRemoteService
{
...

实现接口并曝露给client,在Service中继承Stub,实现抽象方法,在onBind()返回Stub的对象。

public class RemoteService extends Service {
    @Override
    public void onCreate() {
        super.onCreate();
    }

    @Override
    public IBinder onBind(Intent intent) {
        // Return the interface
        return mBinder;
    }

    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
        }
    };
}
        @Override
        public boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
            String packageName = null;
            String[] packages = AIDLService.this.getPackageManager().getPackagesForUid(getCallingUid());
            if(packages != null && packages.length > 0){
                packageName = packages[0];
                Log.d("AIDLService","onTransact packageName "+packageName);
                if(!"com.package.test".equals(packageName)){
                    return false;
                }
            }
            return super.onTransact(code, data, reply, flags);
        }

Stub中的方法onTransact()可以用于权限管理,返回false表示权限认证失败,上面例子中如果请求访问的应用包名不是“com.package.test”,则不允许服务。

client得到IRemoteService,调用IRemoteService.aidl中声明的方法
           public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
                mService = IRemoteService.Stub.asInterface(iBinder);
...
    private int getPid(){
        try {
            return mService.getPid();
        } catch (RemoteException e) {
            e.printStackTrace();
        }
    }

IPC间可以传递对象,要求对象类型实现Parcelable接口,且用aidl文件中声明parcelable类,并且声明它的aidl文件要和parcelable类在同一包下
package com.example.demoproject.aidl;

import android.os.Parcel;
import android.os.Parcelable;

/**
 * Created by abner on 8/13/15.
 */
public class Person implements Parcelable {
    public int age;
    public String name;

    public Person(){

    }

    private Person(Parcel in){
        readFromParcel(in);
    }

    public static Parcelable.Creator<Person> CREATOR = new Parcelable.Creator<Person>(){

        @Override
        public Person createFromParcel(Parcel parcel) {
            return new Person(parcel);
        }

        @Override
        public Person[] newArray(int size) {
            return new Person[size];
        }
    };

    @Override
    public int describeContents() {
        return 0;
    }

    @Override
    public void writeToParcel(Parcel out, int i) {
        out.writeInt(age);
        out.writeString(name);
    }

    public void readFromParcel(Parcel in){
        age = in.readInt();
        name = in.readString();
    }
}

parcelable这里首字母小写,是个类型,与接口Parcelable要区分开
package com.example.demoproject.aidl;

parcelable Person;

将类型Person添加到aidl接口中
// IRemoteService.aidl
package com.example.demoproject.aidl;

import com.example.demoproject.aidl.Person;

// 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);

    List<Person> getPerson();

    void addPerson(in Person person);
}

aidl中支持的参数类型为:基本类型(int,long,char,boolean等),String,CharSequence,List,Map,其他类型必须使用import导入,即使它们可能在同一个包里,比如上面的Person,尽管它和IRemoteService在同一个包中,但是还是需要显示的import进来。另外,除了基本数据类型,其他类型都需要用in,out,或者inout来表明参数是输入还是输出,抑或两者都是。基本数据类型默认in,且不能是其他,因为(You should limit the direction to what is truly needed, because marshalling parameters is expensive.

参考文章:







  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值