zoukankan      html  css  js  c++  java
  • Android Service实现双向通信(一)

    首先,大概来总结一下与Service的通信方式有很多种:

    1. 通过BroadCastReceiver:这种方式是最简单的,只能用来交换简单的数据;
    2. 通过Messager:这种方式是通过一个传递一个Messager给对方,通过这个它来发送Message对象。这种方式只能单向传递数据。可以是ServiceActivity,也可以是从Activity发送数据给Service。一个Messeger不能同时双向发送;
    3. 通过Binder来实现远程调用(IPC):这种方式是Android的最大特色之一,让你调用远程Service的接口,就像调用本地对象一样,实现非常灵活,写起来也相对复杂。

    本文最重点谈一下怎么使用AIDL实现Service端和Client端的双向通信(或者叫"调用")。

    首先定义一个AIDL接口如下:

    // IRemoteService.aidl
    package com.race604.servicelib;
    
    interface IRemoteService {  
        int someOperate(int a, int b);
    }
    

    这里只定义了一个简单的接口someOperate(),输入参数ab,返回一个int值。

    Service的实现如下:

    // RemoteService.java
    package com.race604.remoteservice;
    
    import ...
    
    public class RemoteService extends Service {  
        private static final String TAG = RemoteService.class.getSimpleName();
        private final IRemoteService.Stub mBinder = new IRemoteService.Stub() {
            @Override
            public int someOperate(int a, int b) throws RemoteException {
                Log.d(TAG, "called RemoteService someOperate()");
                return a + b;
            }
        };
    
        @Override
        public IBinder onBind(Intent intent) {
            return mBinder; // 注意这里返回binder
        }
    }
    

    这里,在RemoteService里面实现一个IRemoteService.Stub接口的Binder,并且在onBind()中返回此Binder对象。 在AndroidManifest.xmlRemoteService的申明如下:

    <service  
      android:name=".RemoteService"
      android:enabled="true"
      android:exported="true" >
    
      <intent-filter>
          <action android:name="com.race604.servicelib.IRemoteService" />
      </intent-filter>
    </service>  
    

    这里android:exported="true"表示可以让其他进程绑定,这里还有一个<action android:name="com.race604.servicelib.IRemoteService" />,这里是为了让后面的Client通过此Action来绑定。

    Client的调用方法如下:

    package com.race604.client;
    
    import ...
    
    public class MainActivity extends ActionBarActivity implements View.OnClickListener {
    
        private static final String TAG = MainActivity.class.getSimpleName();
        private IRemoteService mService;
    
        private ServiceConnection mServiceConnection = new ServiceConnection() {
            @Override
            public void onServiceConnected(ComponentName name, IBinder service) {
                Toast.makeText(MainActivity.this, "Service connected", Toast.LENGTH_SHORT).show();
                mService = IRemoteService.Stub.asInterface(service);
            }
    
            @Override
            public void onServiceDisconnected(ComponentName name) {
                Toast.makeText(MainActivity.this, "Service disconnected", Toast.LENGTH_SHORT).show();
                mService = null;
            }
        };
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
    
            findViewById(R.id.bind).setOnClickListener(this);
            findViewById(R.id.unbind).setOnClickListener(this);
            findViewById(R.id.call).setOnClickListener(this);
        }
    
        private void callRemote() {
    
            if (mService != null) {
                try {
                    int result = mService.someOperate(1, 2);
                    Toast.makeText(this, "Remote call return: " + result, Toast.LENGTH_SHORT).show();
                } catch (RemoteException e) {
                    e.printStackTrace();
                    Toast.makeText(this, "Remote call error!", Toast.LENGTH_SHORT).show();
                }
            } else {
                Toast.makeText(this, "Service is not available yet!", Toast.LENGTH_SHORT).show();
            }
        }
    
        @Override
        public void onClick(View v) {
            switch (v.getId()) {
                case R.id.bind:
                    Intent intent = new Intent(IRemoteService.class.getName());
                    bindService(intent, mServiceConnection, BIND_AUTO_CREATE);
                    break;
                case R.id.unbind:
                    unbindService(mServiceConnection);
                    break;
                case R.id.call:
                    callRemote();
                    break;
            }
        }
    }
    

    在客户端,使用Context.bindService()函数,绑定到远程的Service。注意到这里的Intent intent = new Intent(IRemoteService.class.getName());,和上面的Service申明的Action一致。BIND_AUTO_CREATE这个Flag从名字就能看出,表示如果Bind的时候,如果还没有Service的实例,就自动创建。
    这里有一个地方需要注意,在Android 5.0以后,就不允许使用非特定的Intent来绑定Service了,需要使用如下方法:

    Intent intent = new Intent(IRemoteService.class.getName());  
    intent.setClassName("com.race604.remoteservice", "com.race604.remoteservice.RemoteService");  
    // 或者setPackage()
    // intent.setPackage("com.race604.remoteservice");
    bindService(intent, mServiceConnection, BIND_AUTO_CREATE);  
    

    到这里就基本实现了一个完整的Client调用远程Service的实例了。

    源代码可以参考这个Commit

  • 相关阅读:
    课堂训练
    测试用例
    学生空间测试
    图书管理系统为例5w1h
    风险分析
    关于选择方案的练习作业
    图书管理系统需求报告书
    电梯演说模板
    对于敏捷开发的见解
    课堂练习2
  • 原文地址:https://www.cnblogs.com/android-blogs/p/5735608.html
Copyright © 2011-2022 走看看