Android跨进程bindService与callback
Android跨进程bindService与callback
Service的存在主要是为了让Application来bindService,然后通过binder来调用Service中的服务函数,然而在某些情况下,Service也需要回调Application,让其完成某些事情。
举一个简单的例子,我们有一个下载服务(Service),有一个客户端(Client),通常情况下的流程是这样的:
在服务端完成下载之后,是需要通过onDownloadComplete这个回调函数,让客户端在下载完成,做一些其他的事情,例如播放下载文件之类的。
因为是跨进程的通信,所以我们要实现两个application,一个Application实现Service,另一个Application实现Client。重点关注跨进程的Service与Client之间的交互。
我们的交互是这样的:
Client实现一个回调函数集合:IAidlCallback,里面有1个函数:
1. onDownloadCompleted();
Service实现一个服务函数集合:IAidlService, 里面有3个函数:
1. registerCallback()
2. download();
3. 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函数有异曲同工之妙;
更多相关文章
- Android项目文件结构
- Android(安卓)命令行手动编译打包详解
- Android读写XML(下)——创建XML文档
- Android(安卓)APK开发 Drawable文件夹下的自定义Drawable文件
- Android(安卓)中的Parcelable序列化对象
- Android系统权限和root权限
- 箭头函数的基础使用
- NPM 和webpack 的基础使用
- Python技巧匿名函数、回调函数和高阶函数