Android跨进程bindService与callback

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 := disabled
LOCAL_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.aidl
LOCAL_PACKAGE_NAME := MyService
LOCAL_CERTIFICATE := platform
include $(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,Service,callback,android,Service,callback)