怎么在Android中利用Service实现IPC通信

本篇文章给大家分享的是有关怎么在Android中利用Service实现IPC通信,小编觉得挺实用的,因此分享给大家学习,希望大家阅读完这篇文章后可以有所收获,话不多说,跟着小编一起来看看吧。

目前创新互联建站已为上千多家的企业提供了网站建设、域名、虚拟主机、网站托管、服务器托管、企业网站设计、江门网站维护等服务,公司将坚持客户导向、应用为本的策略,正道将秉承"和谐、参与、激情"的文化,与客户和合作伙伴齐心协力一起成长,共同发展。

Android是什么

Android是一种基于Linux内核的自由及开放源代码的操作系统,主要使用于移动设备,如智能手机和平板电脑,由美国Google公司和开放手机联盟领导及开发。

借助AIDL实现IPC通信

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、自定义类型放到下文讲解。

怎么在Android中利用Service实现IPC通信

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

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();
        intent.setAction("jxx.com.server.service.messenger");
        intent.setPackage("jxx.com.server");
        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) {

      }
    };
  }

以上就是怎么在Android中利用Service实现IPC通信,小编相信有部分知识点可能是我们日常工作会见到或用到的。希望你能通过这篇文章学到更多知识。更多详情敬请关注创新互联行业资讯频道。

当前题目:怎么在Android中利用Service实现IPC通信
浏览地址:https://www.cdcxhl.com/article18/gcoggp.html

成都网站建设公司_创新互联,为您提供移动网站建设外贸建站软件开发搜索引擎优化小程序开发定制网站

广告

声明:本网站发布的内容(图片、视频和文字)以用户投稿、用户转载内容为主,如果涉及侵权请尽快告知,我们将会在第一时间删除。文章观点不代表本网站立场,如需处理请联系客服。电话:028-86922220;邮箱:631063699@qq.com。内容未经允许不得转载,或转载时需注明来源: 创新互联

外贸网站制作