您的当前位置:首页Android Service详解(二)

Android Service详解(二)

2024-12-12 来源:哗拓教育

上一篇我们了解了Service的一些概念以及使用方式,这篇着重讲解使用Service实现IPC通信的2中方式。

借助AIDL实现IPC通信

一、代码实操---与远端进程的Service绑定

上面的代码都是在当前进程内跟Service通信,现在我们来实现一下,不同进程内Service如何绑定。

AIDL:Android Interface Definition Language,即Android接口定义语言。

Service跨进程传递数据需要借助aidl,主要步骤是这样的:

  1. 编写aidl文件,AS自动生成的java类实现IPC通信的代理
  2. 继承自己的aidl类,实现里面的方法
  3. 在onBind()中返回我们的实现类,暴露给外界
  4. 需要跟Service通信的对象通过bindService与Service绑定,并在ServiceConnection接收数据。

我们通过代码来实现一下:

  1. 首先我们需要新建一个Service

    public class MyRemoteService extends Service {
        @Nullable
        @Override
        public IBinder onBind(Intent intent) {
            Log.e("MyRemoteService", "MyRemoteService thread id = " + Thread.currentThread().getId());
        return null;
        }
    }
    
  2. 在manifest文件中声明我们的Service同时指定运行的进程名,这里并是不只能写remote进程名,你想要进程名都可以

    <service
            android:name=".service.MyRemoteService"
            android:process=":remote" />
    
  3. 新建一个aidl文件用户进程间传递数据。

    AIDL支持的类型:八大基本数据类型、String类型、CharSequence、List、Map、自定义类型。List、Map、自定义类型放到下文讲解。

    image.png

里面会有一个默认的实现方法,删除即可,这里我们新建的文件如下:

```
package xxxx;//aidl所在的包名
//interface之前不能有修饰符
interface IProcessInfo {
    //你想要的通信用的方法都可以在这里添加
    int getProcessId();
}

4.  实现我们的aidl类

 ```
 public class IProcessInfoImpl extends IProcessInfo.Stub {
     @Override
     public int getProcessId() throws RemoteException {
         return android.os.Process.myPid();
     }
 }
 ```

5.  在Service的onBind()中返回

 ```
 public class MyRemoteService extends Service {
     IProcessInfoImpl mProcessInfo = new IProcessInfoImpl();
     @Nullable
     @Override
     public IBinder onBind(Intent intent) {
         Log.e("MyRemoteService", "MyRemoteService thread id = " + Thread.currentThread().getId());
     return mProcessInfo;
     }
 }
 ```

6.  绑定Service

 ```
  mTvRemoteBind.setOnClickListener(new View.OnClickListener() {
         @Override
         public void onClick(View v) {
             Intent intent = new Intent(MainActivity.this, MyRemoteService.class);
             bindService(intent, mRemoteServiceConnection, BIND_AUTO_CREATE);
         }
     });

 mRemoteServiceConnection = new ServiceConnection() {
         @Override
         public void onServiceConnected(ComponentName name, IBinder service) {

             Log.e("MainActivity", "MyRemoteService onServiceConnected");
             // 通过aidl取出数据
             IProcessInfo processInfo = IProcessInfo.Stub.asInterface(service);
             try {
                 Log.e("MainActivity", "MyRemoteService process id = " + processInfo.getProcessId());
             } catch (RemoteException e) {
                 e.printStackTrace();
             }
         }

         @Override
         public void onServiceDisconnected(ComponentName name) {
             Log.e("MainActivity", "MyRemoteService onServiceDisconnected");
         }
     };
 ```

只要绑定成功就能在有log打印成MyRemoteService所在进程的进程id。这样我们就完成了跟不同进程的Service通信的过程。

#### 二、代码实操---调用其他app的Service

跟调同app下不同进程下的Service相比,调用其他的app定义的Service有一些细微的差别

1.  由于需要其他app访问,所以之前的bindService()使用的隐式调用不在合适,需要在Service定义时定义action

 **我们在定义的线程的App A 中定义如下Service:**

 ```
 <service android:name=".service.ServerService">
     <intent-filter>
         //这里的action自定义
         <action android:name="com.jxx.server.service.bind" />
       <category android:name="android.intent.category.DEFAULT" />
     </intent-filter>
 </service>
 ```

**2.  我们在需要bindService的App B 中需要做这些处理**

 *   首先要将A中定义的aidl文件复制到B中,比如我们在上面定义的IProcessInfo.aidl这个文件,包括路径在内需要原封不动的复制过来。

 *   在B中调用Service通过显式调用

     ```
     mTvServerBind.setOnClickListener(new View.OnClickListener() {
         @Override
         public void onClick(View v) {
             Intent intent = new Intent();
             intent.setAction("com.jxx.server.service.bind");//Service的action
             intent.setPackage("com.jxx.server");//App A的包名
             bindService(intent, mServerServiceConnection, BIND_AUTO_CREATE);
         }
     });
aidl中自定义对象的传递

主要步骤如下:

  1. 定义自定对象,需要实现Parcelable接口
  2. 新建自定义对象的aidl文件
  3. 在传递数据的aidl文件中引用自定义对象
  4. 将自定义对象以及aidl文件拷贝到需要bindService的app中,主要路径也要原封不动

我们来看一下具体的代码:

  1. 定义自定义对象,并实现Parcelable接口

    public class ServerInfo implements Parcelable {
    
    public ServerInfo() {
    
    }
    
    String mPackageName;
    
    public String getPackageName() {
        return mPackageName;
    }
    
    public void setPackageName(String packageName) {
        mPackageName = packageName;
    }
    
    protected ServerInfo(Parcel in) {
        mPackageName = in.readString();
    }
    
    public static final Creator<ServerInfo> CREATOR = new Creator<ServerInfo>() {
        @Override
        public ServerInfo createFromParcel(Parcel in) {
            return new ServerInfo(in);
        }
    
        @Override
        public ServerInfo[] newArray(int size) {
            return new ServerInfo[size];
        }
    };
    
    @Override
    public int describeContents() {
        return 0;
    }
    
    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeString(mPackageName);
    }
    
    //使用out或者inout修饰时需要自己添加这个方法
    public void readFromParcel(Parcel dest) {
        mPackageName = dest.readString();
    }
    }
    

2. 新建自定义对象的aidl文件

 package com.jxx.server.aidl;
 //注意parcelable 是小写的
 parcelable ServerInfo;

3. 引用自定义对象

 package com.jxx.server.aidl;
 //就算在同一包下,这里也要导包
 import com.jxx.server.aidl.ServerInfo;
 interface IServerServiceInfo {
     ServerInfo getServerInfo();
     void setServerInfo(inout ServerInfo serverinfo);
 }

注意这里的set方法,这里用了inout,一共有3种修饰符

 - in:客户端写入,服务端的修改不会通知到客户端
 - out:服务端修改同步到客户端,但是服务端获取到的对象可能为空
 - inout:修改都收同步的

当使用out和inout时,除了要实现Parcelable外还要手动添加readFromParcel(Parcel dest)

4. 拷贝自定义对象以及aidl文件到在要引用的App中即可。

5. 引用

 mServerServiceConnection = new ServiceConnection() {
         @Override
         public void onServiceConnected(ComponentName name, IBinder service) {
             IServerServiceInfo serverServiceInfo = IServerServiceInfo.Stub.asInterface(service);
             try {
                 ServerInfo serviceInfo = serverServiceInfo.getServerInfo();
                 Log.e("MainActivity", "ServerService packageName = " + serviceInfo.getPackageName());
             } catch (RemoteException e) {
                 e.printStackTrace();
             }
         }

         @Override
         public void onServiceDisconnected(ComponentName name) {
             Log.e("MainActivity", "ServerService onServiceDisconnected");
         }
     };

List、Map中引用的对象也应该是符合上面要求的自定义对象,或者其他的几种数据类型。

使用Messenger实现IPC通信

步骤是这样的:

  1. 在Server端新建一个Messenger对象,用于响应Client端的注册操作,并在onBind()中传递出去
  2. 在Client端的ServiceConnection中,将Server端传递过来的Messenger对象进行保存
  3. 同时Client端也新建一个Messenger对象,通过Server传递过来的Messenger注册到Server端,保持通信用。
  4. 不管是否进行unbindService()操作,只要Client保有Server端的Messenger对象,仍然能和Server端进行通信。

一、Server端代码

public class MessengerService extends Service {

    static final int MSG_REGISTER_CLIENT = 1;
    static final int MSG_UNREGISTER_CLIENT = 2;
    static final int MSG_SET_VALUE = 3;

    //这个是给client端接收参数用的
    static final int MSG_CLIENT_SET_VALUE = 4;

    static class ServiceHandler extends Handler {

        private final List<Messenger> mMessengerList = new ArrayList<>();

        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case MSG_REGISTER_CLIENT:
                    mMessengerList.add(msg.replyTo);
                    break;
                case MSG_UNREGISTER_CLIENT:
                    mMessengerList.remove(msg.replyTo);
                    break;
                case MSG_SET_VALUE:
                    int value = msg.arg1;
                    for (Messenger messenger : mMessengerList) {
                        try {
                            messenger.send(Message.obtain(null, MSG_CLIENT_SET_VALUE, value, 0));
                        } catch (RemoteException e) {
                            e.printStackTrace();
                        }
                    }
                    break;
                default:
                    super.handleMessage(msg);
            }
        }
    }

    private Messenger mMessenger = new Messenger(new ServiceHandler());

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return mMessenger.getBinder();
    }
}

二、Client端代码

public class MessengerClientActivity extends AppCompatActivity {
  //这些类型要和Server端想对应
    static final int MSG_REGISTER_CLIENT = 1;
    static final int MSG_UNREGISTER_CLIENT = 2;
    static final int MSG_SET_VALUE = 3;
    static final int MSG_CLIENT_SET_VALUE = 4;

    class ClientHandler extends Handler {

        @Override
        public void handleMessage(Message msg) {

            if (msg.what == MSG_CLIENT_SET_VALUE) {
                mTvValue.setText(msg.arg1 + "");
            } else {
                super.handleMessage(msg);
            }
        }
    }

    TextView mTvServerBind;
    TextView mTvServerUnbind;
    TextView mTvValue;
    TextView mTvSend;

    ServiceConnection mServerServiceConnection;
    Messenger mServerMessenger;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_messenger);

        mTvServerBind = findViewById(R.id.tv_server_bind);
        mTvServerUnbind = findViewById(R.id.tv_server_unbind);
        mTvValue = findViewById(R.id.tv_value);
        mTvSend = findViewById(R.id.tv_send);

        mTvServerBind.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent();
                
                
                bindService(intent, mServerServiceConnection, BIND_AUTO_CREATE);
            }
        });

        mTvServerUnbind.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                //就算这里我们unbindService,只要我们还保留有mServerMessenger对象,
                //我们就能继续与Server通信
                unbindService(mServerServiceConnection);
            }
        });

        mTvSend.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (mServerMessenger != null) {
                    try {
                        //测试一下能否设置数据
                        Message test = Message.obtain(null, MSG_SET_VALUE, new Random().nextInt(100), 0);
                        mServerMessenger.send(test);
                    } catch (RemoteException e) {
                        e.printStackTrace();
                    }
                }
            }
        });

        mServerServiceConnection = new ServiceConnection() {
            @Override
            public void onServiceConnected(ComponentName name, IBinder service) {
                //服务端的messenger
                mServerMessenger = new Messenger(service);

                //现在开始构client用来传递和接收消息的messenger
                Messenger clientMessenger = new Messenger(new ClientHandler());

                try {
                    //将client注册到server端
                    Message register = Message.obtain(null, MSG_REGISTER_CLIENT);
                    register.replyTo = clientMessenger;//这是注册的操作,我们可以在上面的Server代码看到这个对象被取出
                    mServerMessenger.send(register);

                    Toast.makeText(MessengerClientActivity.this, "绑定成功", Toast.LENGTH_SHORT).show();

                } catch (RemoteException e) {
                    e.printStackTrace();
                }
            }

            @Override
            public void onServiceDisconnected(ComponentName name) {

            }
        };
    }
【附】相关架构及资料
image

资料领取

点赞+加群免费获取

加群领取获取往期Android高级架构资料、源码、笔记、视频。高级UI、性能优化、架构师课程、NDK、混合式开发(ReactNative+Weex)微信小程序、Flutter全方面的Android进阶实践技术,群内还有技术大牛一起讨论交流解决问题。

显示全文