android remote service

    android的service分为本地service和远程service,二者的区别在于:

    1. 一个activity调用startService()或bindService(),创建出服务,若该服务与创建服务的activity运行在同一进程中,则把这种服务称为本地服务。并且,本地服务只能在创建该服务的应用程序内部使用,当应用程序终止时,本地服务也一同终止;

    2. 与本地服务不同,远程服务并不与创建者运行在同一进程中,它运行在单独的进程中,所以当主应用程序终止时,远程服务还会继续运行。

    在本地服务中,服务与使用服务的客户端程序运行在同一进程中,本地服务的绑定实际是指客户端程序获取了待绑定服务的一个引用。当绑定完成后,客户端即获取了服务的一个引用,通过该引用,客户端即可调用该服务提供的各种方法。

    就远程服务而言,activity与远程服务运行在不同的进程中,activity若想控制远程服务,必须使用IPC机制。


   在我们的项目中,我们需要创建一个service,要求该service能脱离主应用程序而独立存在,有较高的稳定性,该service能独立启动,应用程序崩溃不会影响该service,该service崩溃也不会影响主应用程序,因此选择采用remote service。

    区分一个service是本地服务还是远程服务,首先是在AndroidManifest.xml中声明该service,有点不同:

            <service
            android:name="com.dtri.clientagent.BackendService"
            android:enabled="true"
            android:exported="true"
            android:process="com.dtri.clientagent.BackendService" >

   android:process指定该service作为一个独立的进程运行,本地服务不用指定android:process,默认跟主应用程序运行在同一个进程里面。运行后,通过adb shell,ps后会发现有一个com.dtri.clientagent.BackendService进程,这个进程即为我们的远程服务进程。


    启动远程服务跟启动本地服务没有区别,都是通过startService()或bindService(),如果需要同service进行交互控制,则用bindService(),不用交互控制只需要启动起来,那么就用startService()。我们在这里采用bindService()。

    不同于本地服务,远程服务涉及到进程间的通信,就需要引入IPC机制,即通过AIDL来描述通信接口。

     我们创建ISecondary.aidl

     package com.dtri.clientagent;


import com.dtri.clientagent.RegisterArg;


interface ISecondary {


    int register(in RegisterArg arg);
    
    int getRegisterStatus();
    
    void notify_service();
    
    int updateCommand(String cmdtype, String potocoltype, String tarPath, String uid, String pwd);
    
    void writeLog(int logtype, String log);
    
    int getCAName();
    
}

该接口描述了远程服务可供外部activity调用的接口。


同时我们创建IBackendService.aidl

package com.dtri.clientagent;

import com.dtri.clientagent.IBackendServiceCallbacK;


interface IBackendService {
    void registerCallback(IBackendServiceCallback cb);
    
    void unregisterCallback(IBackendServiceCallback cb);
}
该接口提供了一个注册回调和反注册回调接口,service提供这两个接口用于回调创建者的接口,用来实现双向通信。


IBackendServiceCallback.aidl即描述了回调接口
package com.dtri.clientagent;
oneway interface IBackendServiceCallback {
    void updateRegisterStatus(String status, String host, String port, String isAuto);
    void  reportStatus(int status);
    void CANameChanged(String name);
}
 

光这些aidl文件还是不够的,但android系统会根据这些aidl文件自动生成其它java文件用于完成IPC通信,节省了我们开发时间和难度,因此我们只需要关注aidl接口和实现上面,至于之间怎么通信就不是我们要重点考虑的方面了。

基本流程如下:

1. 在activity中通过bindService()来启动远程service

           this.getApplicationContext().bindService(new Intent(IBackendService.class.getName()),
                mConnection, Context.BIND_AUTO_CREATE);
        this.getApplicationContext().bindService(new Intent(ISecondary.class.getName()),
                mSecondaryConnection, Context.BIND_AUTO_CREATE);

因为service提供了两套aidl接口,因此都要bind。


2. 服务启动后,会调用onBind()方法。在onBind()里面,生成用于处理Binder IPC的Binder对象。

@Override
public IBinder onBind(Intent intent) {
// TODO: Return the communication channel to the service.
Log.i("siqun","Backendservice onBind");


if (IBackendService.class.getName().equals(intent.getAction())) {
return mBinder;
}
if (ISecondary.class.getName().equals(intent.getAction())) {
return mSecondaryBinder;
}
return null;
}

private final IBackendService.Stub mBinder = new IBackendService.Stub() {

@Override
public void unregisterCallback(IBackendServiceCallback cb)
throws RemoteException {
// TODO Auto-generated method stub
Log.i("siqun","backendservice unregistercallback");
if (cb != null) {
mCallbacks.unregister(cb);
}

}

@Override
public void registerCallback(IBackendServiceCallback cb)
throws RemoteException {
Log.i("siqun","backendservice registercallback");
// TODO Auto-generated method stub
if (cb != null) {
mCallbacks.register(cb);
}

}
};


binder对象同时实现了各自的aidl接口,比如IBackendService.aidl中定义的接口registerCallback和unregisterCallback就分别实现了,IBackendServiceCallback 是创建服务的activity提供的接口,注册以后,远程服务可以调用这些接口,从而实现跟activity的通信。


3. 在activity端,

private ServiceConnection mConnection = new ServiceConnection()  {
@Override
public void onServiceConnected(ComponentName className, IBinder service) {
mService = IBackendService.Stub.asInterface(service);


try {
mService.registerCallback(mCallback);
} catch (RemoteException e) {

}
mCallbackText.setText("Attached.");
}

@Override
public void onServiceDisconnected(ComponentName className) {
mService = null;
mCallbackText.setText("Disconnected.");


}
};

private ServiceConnection mSecondaryConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName className,IBinder service) {
mSecondaryService = ISecondary.Stub.asInterface(service);
}

@Override
public void onServiceDisconnected(ComponentName className) {
mSecondaryService = null;
}
};

    private IBackendServiceCallback mCallback = new IBackendServiceCallback.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.
         */
        
        @Override
public void updateRegisterStatus(String status, String host, String port, String isAuto) {
        Log.i("siqun","updateRegisterStatus "+"host="+host+"port="+port);
        Message msg = mHandler.obtainMessage();
        Bundle b = new Bundle();
//         b.putString("ca_name", ca_name);
        b.putString("status", status);
        b.putString("host", host);
        b.putString("port", port);
        b.putString("isAuto", isAuto);
        msg.what = REGISTER_RSP_MSG;
        msg.setData(b);
        mHandler.sendMessage(msg);
        }



一旦远程服务连接成功,会调用到onServiceConnected(),在这个函数里面得到远程服务的一个引用,通过该引用可以调用远程服务提供的接口,比如调用mService.registerCallback(mCallback);来将自己的aidl接口注册到远程服务中,供远程服务回调。



综上所述,activity如何同远程服务通信:

activity通过bindService()创建并启动远程服务;service端onBind()被调用,生成Binder对象并返回给activity,该Binder对象里面提供服务端aidl的实现,这些aidl是service端开放给activiy的接口(事先通过aidl文件来定义);一旦创建成功,activity端onServiceConnected()会被调用,在该函数里面可以获得远程服务端的引用,通过该引用就可以调用服务端开放的aidl接口,来实现activiy调用服务端的接口。


那么,远程服务端是如何实现反向调用activity的接口呢?

activity同时定义自己的aidl接口,比如上面的IBackendServiceCallback.aidl,同时service要提供registerCallback这个接口,该接口用于activity将自己的aidl注册到远程服务的回调接口列表中;在onServiceConnected中通过registerCallback注册到远程服务的回调列表中。

final RemoteCallbackList<IBackendServiceCallback> mCallbacks
    = new RemoteCallbackList<IBackendServiceCallback>();

注册实质上是加入到RemoteCallbackList中,调用mCallbacks.register来完成注册。


在远程服务中通过

final int N = mCallbacks.beginBroadcast();
for(int i=0; i<N; i++) {
                    try {
                        mCallbacks.getBroadcastItem(i).updateRegisterStatus(register_status, register_host, register_port,register_isAuto);
                    } catch (RemoteException e) {
                    }


}
mCallbacks.finishBroadcast();

来调用activity的接口

这样就实现了activity和service的双向通信


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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值