Android service

2015/10/26 posted in  Android

onCreate()方法只会在Service第一次被创建的时候调用,如果当前Service已经被创建过了,不管怎样调用startService()方法,onCreate()方法都不会再执行。

Service和Activity通信

public class MyService extends Service {  
  
    public static final String TAG = "MyService";  
  
    private MyBinder mBinder = new MyBinder();  
  
    @Override  
    public void onCreate() {  
        super.onCreate();  
        Log.d(TAG, "onCreate() executed");  
    }  
  
    @Override  
    public int onStartCommand(Intent intent, int flags, int startId) {  
        Log.d(TAG, "onStartCommand() executed");  
        return super.onStartCommand(intent, flags, startId);  
    }  
  
    @Override  
    public void onDestroy() {  
        super.onDestroy();  
        Log.d(TAG, "onDestroy() executed");  
    }  
  
    @Override  
    public IBinder onBind(Intent intent) {  
        return mBinder;  
    }  
  
    class MyBinder extends Binder {  
  
        public void startDownload() {  
            Log.d("TAG", "startDownload() executed");  
            // 执行具体的下载任务  
        }  
          
        public void stopDownload() {  
            Log.d("TAG", "stopDownload() executed");  
            // 执行具体的下载任务  
        }  
    }  
  
}  

新增了一个MyBinder类继承自Binder类,然后在MyBinder中添加了一个startDownload()方法用于在后台执行下载任务,当然这里并不是真正地去下载某个东西,只是做个测试,所以startDownload()方法只是打印了一行日志。

activity 中

private ServiceConnection connection = new ServiceConnection() {  
  
        @Override  
        public void onServiceDisconnected(ComponentName name) {  
        }  
  
        @Override  
        public void onServiceConnected(ComponentName name, IBinder service) {  
            myBinder = (MyService.MyBinder) service;  
            myBinder.startDownload(); 
            myBinder.stopDownload();   
        }  
    };  
  

Intent bindIntent = new Intent(this, MyService.class);  
bindService(bindIntent, connection, BIND_AUTO_CREATE); 

区别

  1. Started Service中使用StartService()方法来进行方法的调用,调用者和服务之间没有联系,即使调用者退出了,服务依然在进行【onCreate()- >onStartCommand()->startService()->onDestroy()】,注意其中没有onStart(),主要是被onStartCommand()方法给取代了,onStart方法不推荐使用了。

  2. BindService中使用bindService()方法来绑定服务,调用者和绑定者绑在一起,调用者一旦退出服务也就终止了【onCreate()->onBind()->onUnbind()->onDestroy()】

  1. 生命周期上的区别
    执行startService时,Service会经历onCreate->onStartCommand。当执行stopService时,直接调用onDestroy方法。调用者如果没有stopService,Service会一直在后台运行,下次调用者再起来仍然可以stopService。

执行bindService时,Service会经历onCreate->onBind。这个时候调用者和Service绑定在一起。调用者调用unbindService方法或者调用者Context不存在了(如Activity被finish了),Service就会调用onUnbind->onDestroy。这里所谓的绑定在一起就是说两者共存亡了。

多次调用startService,该Service只能被创建一次,即该Service的onCreate方法只会被调用一次。但是每次调用startService,onStartCommand方法都会被调用。Service的onStart方法在API 5时被废弃,替代它的是onStartCommand方法。

第一次执行bindService时,onCreate和onBind方法会被调用,但是多次执行bindService时,onCreate和onBind方法并不会被多次调用,即并不会多次创建服务和绑定服务。

2、调用者如何获取绑定后的Service的方法

onBind回调方法将返回给客户端一个IBinder接口实例,IBinder允许客户端回调服务的方法,比如得到Service运行的状态或其他操作。我们需要IBinder对象返回具体的Service对象才能操作,所以说具体的Service对象必须首先实现Binder对象。

3、既使用startService又使用bindService的情况

如果一个Service又被启动又被绑定,则该Service会一直在后台运行。首先不管如何调用,onCreate始终只会调用一次。对应startService调用多少次,Service的onStart方法便会调用多少次。Service的终止,需要unbindService和stopService同时调用才行。不管startService与bindService的调用顺序,如果先调用unbindService,此时服务不会自动终止,再调用stopService之后,服务才会终止;如果先调用stopService,此时服务也不会终止,而再调用unbindService或者之前调用bindService的Context不存在了(如Activity被finish的时候)之后,服务才会自动停止。

那么,什么情况下既使用startService,又使用bindService呢?

如果你只是想要启动一个后台服务长期进行某项任务同时还想要与正在运行的Service取得联系,那么有两种方法:一种是使用broadcast,另一种是使用bindService。前者的缺点是如果交流较为频繁,容易造成性能上的问题,而后者则没有这些问题。因此,这种情况就需要startService和bindService一起使用了。

另外,如果你的服务只是公开一个远程接口,供连接上的客户端(Android的Service是C/S架构)远程调用执行方法,这个时候你可以不让服务一开始就运行,而只是bindService,这样在第一次bindService的时候才会创建服务的实例运行它,这会节约很多系统资源,特别是如果你的服务是远程服务,那么效果会越明显(当然在Servcie创建的是偶会花去一定时间,这点需要注意)

销毁 service

  • Bind Service -> Unbind Service
  • Start Service -> Stop Service
  • Bind Service + Start Service -> Unbind Service + Stop Service

注意点

onServiceDisconnected 会在程序意外退出时才会调用

IntentService

  1. 独立的线程
  2. 自动创造一个队列处理事务
  3. 当所有的请求处理完之后,自动销毁,不用调用 stopSelf()
  4. 自动 onBind 返回 null
  5. 只要将需要处理的事务放在onHandleIntent()即可
  6. 在service构造函数要返回super("ServiceName");

remote service

注册Service的时候将它的android:process属性指定成:remote就可以了
进程id不同了,就连应用程序包名也不一样

AIDL

由于 remote 的 service 在别的进程中,与 activity 的同学需要用 AIDL(Android Interface Definition Language ,Android接口定义语言的意思),它可以用于让某个Service与多个应用程序组件之间进行跨进程通信,从而可以实现多个应用程序共享同一个Service的功能

AIDL使用场景

Note: Using AIDL is necessary only if you allow clients from different applications to access your service for IPC and want to handle multithreading in your service. If you do not need to perform concurrent IPC across different applications, you should create your interface by implementing a Binder or, if you want to perform IPC, but do not need to handle multithreading, implement your interface using a Messenger. Regardless, be sure that you understand Bound Services before implementing an AIDL.

多个应用和 service 的进程通信,而且在 service 中是多线程处理的。如果只是不同程序间的进程通信,可以使用 Messenger

android studio 构建 aidl

先在main目录下新建一个文件夹,命名为aidl,再在该目录下新建一个包,包名跟AndroidManifest中的package同名,然后在该包下创建aidl文件,创建完之后在build/generated/source/aidl/debug下就可以见到自动生成的java文件。

service 声明成远程 service

<service  
    android:name="com.example.servicetest.MyService"  
    android:process=":remote" >  
</service>  

修改 service 中的代码,实现 adidl 中定义的接口

@Override  
public IBinder onBind(Intent intent) {  
    return mBinder;  
}  
  
MyAIDLService.Stub mBinder = new Stub() {  
  
    @Override  
    public String toUpperCase(String str) throws RemoteException {  
        if (str != null) {  
            return str.toUpperCase();  
        }  
        return null;  
    }  
  
    @Override  
    public int plus(int a, int b) throws RemoteException {  
        return a + b;  
    }  
}; 

编译之后 android sdk 会根据这些 aidl 文件生成 .java 文件

  • AIDL中方法的参数,有 场内 out inout 三个种类,默认是 in, 表输入 输出 xxx,
  • mBinder 是 MyAIDLService.Stub 的实例,其中定义的接口,在 IBinder中返回,以便在调用的地方可以使用

调用 service 中的接口


private ServiceConnection connection = new ServiceConnection() {  
  
    @Override  
    public void onServiceDisconnected(ComponentName name) {  
    }  
  
    @Override  
    public void onServiceConnected(ComponentName name, IBinder service) {  
        myAIDLService = MyAIDLService.Stub.asInterface(service);  
        try {  
            int result = myAIDLService.plus(3, 5);  
            String upperStr = myAIDLService.toUpperCase("hello world");  
            Log.d("TAG", "result is " + result);  
            Log.d("TAG", "upperStr is " + upperStr);  
        } catch (RemoteException e) {  
            e.printStackTrace();  
        }  
    }  
}; 
    
    
    Intent bindIntent = new Intent(this, MyService.class);  
bindService(bindIntent, connection, BIND_AUTO_CREATE);  
  • 调用是远程异步的,会花费时间,不要在 UI 线程调用
  • 如果 service 和调用者处理两个 app 中,

如果是在别的 app 中,需要用隐性 intent,客户端程序必须保存一份 。adil,以便生成接口

在 service 中增加 action

<service  
    android:name="com.example.servicetest.MyService"  
    android:process=":remote" >  
    <intent-filter>  
        <action android:name="com.example.servicetest.MyAIDLService"/>  
    </intent-filter>  
</service>  

发起 intent

Intent intent = new Intent("com.example.servicetest.MyAIDLService");  
                bindService(intent, connection, BIND_AUTO_CREATE);  

AIDL 中传入自定义类

aidl中支持的参数类型为:基本类型(int,long,char,boolean等),String,CharSequence,List,Map,其他类型必须使用import导入,即使它们可能在同一个包里。

接口中的参数除了aidl支持的类型,其他类型必须标识其方向:到底是输入还是输出抑或两者兼之,用in,out或者inout来表示,上面的代码我们用in标记,因为它是输入型参数

在 aidl 中加入 getbook方法接口

package io.github.xuyushi.servicetest2;
import io.github.xuyushi.servicetest2.Book;

interface MyAIDLService {
    Book getBook();
    int plus(int a, int b);
}

注意 这里一定要 import book的包

写一个与Book类对应的aidl,命名为Book.aidl

package io.github.xuyushi.servicetest2;

parcelable Book;

实现 book 类

package io.github.xuyushi.servicetest2;

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

/**
 * Created by xuyushi on 15/10/31.
 */
public class Book implements Parcelable {

    private String bookName;
    private int bookPrice;

    public Book(){

    }

    public Book(Parcel parcel){
        bookName = parcel.readString();
        bookPrice = parcel.readInt();
    }

    public String getBookName() {
        return bookName;
    }
    public void setBookName(String bookName) {
        this.bookName = bookName;
    }
    public int getBookPrice() {
        return bookPrice;
    }
    public void setBookPrice(int bookPrice) {
        this.bookPrice = bookPrice;
    }

    public int describeContents() {
        return 0;
    }
    public void writeToParcel(Parcel parcel, int flags) {
        parcel.writeString(bookName);
        parcel.writeInt(bookPrice);
    }

    public static final Parcelable.Creator<Book> CREATOR = new Creator<Book>() {
        public Book createFromParcel(Parcel source) {

            return new Book(source);
        }
        public Book[] newArray(int size) {
            return new Book[size];
        }
    };
}

后续

Parcelable 接口

Parcelable 可以是 android 系统将 objectes 分解成可以被进程处理的原始种类

  1. 必须提供一个名为CREATOR的static final属性 该属性需要实现android.os.Parcelable.Creator接口
  2. writeToParcel 注意写入变量和读取变量的顺序应该一致 不然得不到正确的结果
  3. readFromParcel 注意读取变量和写入变量的顺序应该一致 不然得不到正确的结果

调用方法和之前一样

Messenger

如果 service 处理的不是多线程的推荐用 Messenger,对于大部分不用多线程处理的 app 来说,使用 Messenger 更轻量级一点,但是service一次只能处理一个线程

  1. servive 实现一个继承 Handler 的类,其中的回调函数用来处理客户端的请求
  2. 再用 Handler 来构造一个 Messenger
  3. 用 Messenger 构造 IBinder,并在 onBind 中返回
  4. 客户端使用 IBinder 来初始化 Messenger,客户端可以法 Messafe 给 service
  5. service 可以在 handleMessage() 接受到客户端发来的 message
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 ActivityMessenger extends Activity {
    /** Messenger for communicating with the service. */
    Messenger mService = null;

    /** Flag indicating whether we have called bind on the service. */
    boolean mBound;

    /**
     * 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;
        }
    };

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

    @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;
        }
    }
}