Android跨进程bindService与callback

Service的存在主要是为了让Application来bindService,然后通过binder来调用Service中的服务函数,然而在某些情况下,Service也需要回调Application,让其完成某些事情。
举一个简单的例子,我们有一个下载服务(Service),有一个客户端(Client),通常情况下的流程是这样的:

Created with Raphaël 2.1.0 Client Client Service Service download onDownloadCompleted

在服务端完成下载之后,是需要通过onDownloadComplete这个回调函数,让客户端在下载完成,做一些其他的事情,例如播放下载文件之类的。

因为是跨进程的通信,所以我们要实现两个application,一个Application实现Service,另一个Application实现Client。重点关注跨进程的Service与Client之间的交互。

我们的交互是这样的:
Client实现一个回调函数集合:IAidlCallback,里面有1个函数:
1. onDownloadCompleted();
Service实现一个服务函数集合:IAidlService, 里面有3个函数:
1. registerCallback()
2. download();
3. unregisterCallback()

Created with Raphaël 2.1.0 Client Client Service Service registerCallback download onDownloadCompleted unregisterCallback
我们建立一个Application,名字叫MyDownloadService,其中只有一个Service(作为服务提供端):
 <service    android:permission="com.android.permission.MY_SERVICE"     android:name=".MyService"    android:exported="true" >                   <intent-filter>    <action android:name="com.android.myservice.MyService" />    intent-filter>service>

然后我们建立一个Application,名字叫MyDownloadClient,其中只有一个Activity(作为服务调用端)

<uses-permission android:name="com.android.permission.MY_SERVICE" /><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>

注意在MyDownloadClient的manifest中记得使用uses-permisson声明Service所需要的permission。

需要在MyDownloadService和MyDownloadClient这两个应用中都创建同样的接口文件:
1. MyDownloadService中:
./MyDownloadService/src/com/android/myservice/aidl/IAidlService.aidl
./MyDownloadService/src/com/android/myservice/aidl/IAidlCallback.aidl
2. MyDownloadClient中:
./MyDownloadClient/src/com/android/myservice/aidl/IAidlService.aidl
./MyDownloadClient/src/com/android/myservice/aidl/IAidlCallback.aidl
接口文件必须是一样的,在这种情况下,跨进程的调用才能正确寻址。
IAidlService.aidl:

package com.android.myservice.aidl;import com.android.myservice.aidl.IAidlCallback;interface IAidlService {    void registerCallback(IAidlCallback cb);    void download();    void unregisterCallback();}

IAidlCallback.aidl:

package com.android.myservice.aidl;interface IAidlCallback {    void onDownloadCompleted();}

因为AIDL文件必须是相同的,跨进程函数调用才能正确地寻址
–> 所以AIDL文件的包名都是一致的
“package com.android.myservice.aidl”;
–> 所以AIDL文件的src相对路径也必须是一致的:
/src/com/android/myservice/aidl/

在Android.mk中,我们的LOCAL_SRC_FILES除了加上java文件之外,需要特别地加上aidl文件。这样,mm编译过程中,编译器会通过aidl文件生成java文件:
IAidlService.aidl -> IAidlService.java
IAidlCallback.aidl -> IAidlCallback.java
以MyService为例,看Android.mk的写法:

LOCAL_PATH := $(call my-dir)include $(CLEAR_VARS)LOCAL_PROGUARD_ENABLED := disabledLOCAL_MODULE_TAGS := optional# Only compile source java files in this apk.LOCAL_SRC_FILES := $(call all-java-files-under, src)LOCAL_SRC_FILES += \        src/com/android/myservice/aidl/IAidlService.aidl \        src/com/android/myservice/aidl/IAidlCallback.aidlLOCAL_PACKAGE_NAME := MyServiceLOCAL_CERTIFICATE := platforminclude $(BUILD_PACKAGE)

编译后就会生成MyService.apk.

Service

MyDownloadService中的MyService.java:

package com.android.myservice;import com.android.myservice.aidl.IAidlCallback;import com.android.myservice.aidl.IAidlService;public class MyService extends Service {    private static final String TAG = "MyService";    private IAidlCallback mCallback = null;    private IAidlService.Stub mBinder = new      IAidlService.Stub() {        @Override        public void registerCallback(IAidlCallback cb) throws RemoteException {            mCallback = cb;        }        @Override        public void download() throws RemoteException {            doDownload();        }        @Override        public void unregisterCallback() throws RemoteException {            mCallback = null;        }    };    @Override    public IBinder onBind(Intent intent) {        Log.i(TAG, "onBind");        return mBinder;    }    @Override    public boolean onUnbind(Intent intent) {        Log.i(TAG, "onUnbind");        return super.onUnbind(intent);    }    @Override    public void onCreate() {        super.onCreate();        Log.i(TAG, "onCreate");    }    @Override    public void onDestroy() {        Log.i(TAG, "onDestroy");        super.onDestroy();    }    private void doDownload() {        Log.d(TAG, "doDownload");        Log.d(TAG, "Download Start");        Log.d(TAG, "Downloading");        Lod.d(TAG, "Download Complete")        mCallback.onDownloadCompleted();    }

在这里,Service实现了IAidlService.Stub中的每一个接口函数,包括将Client提供的回调函数注册到Service当中,从Service当中注销,以及比较特殊的download接口函数,这个函数会使用回调函数onDownloadCompleted让Client完成下载完成后要做的事情

Client

MyDownloadClient中的MainActivity.java:

package com.android.myClient;import com.android.myservice.aidl.IAidlCallback;import com.android.myservice.aidl.IAidlService;public class MainActivity extends Activity {        private static final String TAG = "MyClient";       private int mDice = 0;    private IAidlService mService = null;    private IAidlCallback mCallback = new IAidlCallback.Stub() {        @Override        public void onDownloadComplete() throws RemoteException {            // TODO Auto-generated method stub            Log.d(TAG, "onDownloadComplete");            doPrecedureAfterDownload();        }    };    private ServiceConnection mServiceConnection = new ServiceConnection() {        @Override        public void onServiceConnected(ComponentName name, IBinder service) {            // TODO Auto-generated method stub            mService = IAidlService.Stub.asInterface(service);            try {                mService.registerCallback(mCallback);            } catch (RemoteException e) {                // TODO Auto-generated catch block                e.printStackTrace();            }        }        @Override        public void onServiceDisconnected(ComponentName name) {            // TODO Auto-generated method stub            Log.d(TAG, "onServiceDisconnected");        }    };    private void bindService() {        try {            Intent intent = new Intent();            intent.setComponent(new ComponentName("com.android.myservice","com.android.myservice.MyService"));            bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE);        } catch (Exception e) {            e.printStackTrace();        }    }    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        Log.d(TAG, "onCreate");        this.setContentView(R.layout.activity_main);        bindService();        Button downloadButton = (Button) this.findViewById(R.id.download);        downloadButton.setOnClickListener(new View.OnClickListener() {            @Override            public void onClick(View v) {                // TODO Auto-generated method stub                try {                    mService.download();                } catch (RemoteException e) {                    // TODO Auto-generated catch block                    e.printStackTrace();                }            }        });    }    protected void onDestroy() {        unbindService(mServiceConnection);    }    private void doPrecedureAfterDownload() {        Log.i(TAG, "doPrecedureAfterDownload");    }

从Client的MainActivity文件中,需要做的事情主要是实现IAidlCallback.Stub中的接口函数(回调函数集),在ServiceConnection bind成功之后,获得Service对象mService,以后可以使用该mService来调用Service的接口函数,然后注册回调函数到Service中,最后注册button的click listener,在onClick的时候,通过mService调用download接口,然后等download完成之后,Service端变会通过mCallback回调onDownloadCompleted函数,让Client端执行doPrecedureAfterDownload()。

总结起来,需要注意的是以下6点:
1. AIDL接口必须是一样的,这就意味着AIDL的包名一定是一样的,本例中的”com.android.myservice.aidl”, 也就意味着,在各个使用该AIDL接口的project里面的src路径都必须是一样的/src/com/android/myservice/aidl/;
2. AIDL接口文件在eclipse下面设置好之后,通过build project,会在gen下面生成各自的IAidlService.java 和 IAidlCallback.java文件;
3. Service实现的是Service的接口, 本例的IAidlService.aidl,在MyService中,实现IAidlService.Stub mBinder, = new IAidlService.Stub() { ** } 然后在onBind的时候,将这个mBinder返回;
4. Client实现的是Callback的接口,本里的IAidlCallback.aidl, 在MainActivity中,实现IAidlCallback mCallback = new IAidlCallback.Stub() { ** }, 在将该callback注册到Service之后,Service便可以通过该callback引用来调用Client这边提供的IAidlCallback的回调函数;
5. Client实现ServiceConnection, 这个ServiceConnection是用来bindService用的,需要实现onServiceConnected, onServiceDisconnected,这两个钩子函数是让Client可以在bindService/unbindService之后,系统成功连接/断开Service的时候,调用这些钩子函数,并且在钩子函数中做一些初始化(init)或者去初始化(deinit)的操作,最重要的是要通过:mService = IAidlService.Stub.asInterface(service); 语句获得IAidlService的引用,使得以后可以直接使用mService调用Service接口,本例中在获得mService自后,马上使用了mService.registerCallback(mCallback); 注册回调函数,这些都属于初始化(init)的操作。这种模板方法模式的应用,与click listener的onClick函数有异曲同工之妙;

更多相关文章

  1. Android项目文件结构
  2. Android(安卓)命令行手动编译打包详解
  3. Android读写XML(下)——创建XML文档
  4. Android(安卓)APK开发 Drawable文件夹下的自定义Drawable文件
  5. Android(安卓)中的Parcelable序列化对象
  6. Android系统权限和root权限
  7. 箭头函数的基础使用
  8. NPM 和webpack 的基础使用
  9. Python技巧匿名函数、回调函数和高阶函数

随机推荐

  1. 广告控件中xml文件的写法
  2. 在 XSL/XSLT 中实现随机排序
  3. 读取XML为行记录
  4. WML初级教程之从实际应用中了解WML
  5. 从XML中读取数据到内存的实例
  6. 一个简单的基于XML的模块集成框架
  7. 从无到有实现一个xml数据库登录验证
  8. Xml_javascript分页
  9. 使用xml作为数据源,配合asp:Menu类自由扩
  10. 创建带有关联的 XML 架构的 XML 文件 &&