Android中的IPC方式

使用Bundle

我们知道,四大组件中的三大组件(Activity,Service,Broadcast)都是支持在Intent中传输Bundle数据的,由于Bundle实现了Parcelable接口,所以他可以很方便的在进程间传输,通过Bundle我们不就可以传输基本类型,也可以传输实现了Parcelable接口或者Serializable接口的对象

使用文件共享

  • 在Windows上,一个文件如果被加了排斥锁将会导致其他线程无法对其进行访问,包括读和写
  • 而Android由于是基于Linux系统,使得这个并发读写操作可以无限制的进行,尽管这可能出问题
  • 但是这种方式却是解决了不同进程数据传输的问题

接下来简单的举个栗子示范一下在本进程中写数据:

Data data = new Data(10,"这是主进程写的数据");
            try {
                ObjectOutputStream out = new ObjectOutputStream(
                        new FileOutputStream("/storage/emulated/0/1/sina/sdadas.txt"));
                out.writeObject(data);
                out.close();
            } catch (IOException e) {
                e.printStackTrace();
                Log.d(TAG, "ssss: 序列化的时候失败" + e.toString());
            }

在另一进程中读数据

Data data = null;
            try {
                ObjectInputStream in = new ObjectInputStream(
                        new FileInputStream("/storage/emulated/0/1/sina/sdadas.txt"));
                data = (Data) in.readObject();
            }catch (IOException e){
                e.printStackTrace();
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }

            if(data != null){
                Log.d(TAG, "onClick: 读取成功 , data.id = "  + data.id + "data.name = " + data.name);
            }else {
                Log.d(TAG, "onClick: 错误");
            }

这是正常并且操作成功的,说明这种方式是可行的,不过我们这里虽然说的是传输数据,只不过我们保证的是内容一样,他们实际上还是两个不同的对象,通过这种方式,我们应尽量控制读写时的时序问题

使用Messenger

  • Messenger可以翻译为信使,通过它可以在不同进程中传递Messenger对象,在Messenger中可以放入我们需要传递的对象,就可以轻松跨进程传输

  • Messenger是一种轻量级的IPC方案,他的底层实现是AIDL,为什么这么说呢,我们去看一下他的构造方法

    public Messenger(Handler target) {
      mTarget = target.getIMessenger();
    }
    public Messenger(IBinder target) {
      mTarget = IMessenger.Stub.asInterface(target);
    }
    

从上构造方法我们可以很容易想到底层是AIDl的
Messenger的用法非常简单,他对AIDL做了封装,使得我们可以更简单的跨进程通信
同时,由于他一次处理一个请求,因此在服务端我们不同考虑线程同步的问题,这是因为服务端不承诺在并发执行的问题,下面我们来看看怎么实现一个Messenger

我们首先来看看服务端进程中怎么实现
只需要在服务端创建一个Service来处理客户端的连接请求,并通过它来创建一个Messenger对象,然后在Service的onBind中返回这个Messenger对象底层的Binder即可

public class MessengerService extends Service {

private static final String TAG = "MessengerService";

private static class Messengerhandler extends Handler {
    @Override
    public void handleMessage(Message msg) {
        switch (msg.what){
            case 1:
                Log.d(TAG, "handleMessage: receive msg from Client " + msg.getData().getString("msg"));
                break;
        }
        super.handleMessage(msg);
    }
}
private final Messenger mMessenger = new Messenger(new Messengerhandler());

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

可以看到,因为我们在上面的Messenger构造器中发现Messenger的构造器需要传入一个handler 类型的参数,很明显这个Handler就是用来处理客户端发来的Message消息的
接下来看看客户端的代码
客户端首先要绑定Service,然后发送Message类型的消息

void bindMyMessengerService(){
    ServiceConnection mConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            mService = new Messenger(service);
            sendData();
        }
        @Override
        public void onServiceDisconnected(ComponentName name) {
            mService = null;
        }
    };
    Intent intent = new Intent(this,MessengerService.class);
    bindService(intent,mConnection, Context.BIND_AUTO_CREATE);
}

void sendData(){
    Message msg = Message.obtain(null,1);
    Bundle data = new Bundle();
    data.putString("msg","这是客户端的消息");
    msg.setData(data);
    try{
        mService.send(msg);
    }catch (RemoteException e) {
        e.printStackTrace();
    }
}

这段代码相信没什么难度,不过这里问题又来了?诶?我们只是发送了消息给服务端,可是通信,通信,不是应该能互相发的吗?客户端是怎么接收服务端的呢?
接下来我们看看服务端怎么给客户端发送消息,先来看看服务端怎么修改

private static class Messengerhandler extends Handler {
    @Override
    public void handleMessage(Message msg) {
        switch (msg.what){
            case 1:
                Log.d(TAG, "handleMessage: receive msg from Client " + msg.getData().getString("msg"));
                Messenger client = msg.replyTo;
                Message clientData = Message.obtain(null,1);
                Bundle bundle = new Bundle();
                bundle.putString("reply","嗯,我收到了你的消息,我是服务端");
                clientData.setData(bundle);
                try {
                    client.send(clientData);
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
                break;
        }
        super.handleMessage(msg);
    }
}
  • 这里我将服务端接收消息的handler的Hand可Message方法改变了一下,让他能在接收到消息之后直接再给客户端发送一个消息

  • 这里可以看到,我们发送消息的Messenger是从客户端发送过来msg中得到的,能猜到吧,客户端将自己进程所在的Messenger通过Messenger发送给服务端,然后服务端就可以拿到客户端进程的Messenger句柄给客户端发送消息了

  • 看一下客户端的代码

    private Messenger mCilentMessenger = new Messenger(new ClientMessenger());
    private static class ClientMessenger extends Handler{
    @Override
    public void handleMessage(Message msg) {
    switch (msg.what){
    case 1:
    Log.d(TAG, "handleMessage: 接收到服务端的消息 = " +msg.getData().getString("reply"));
    break;
    }

          super.handleMessage(msg);
      }
    

    }

void sendData(){
    Message msg = Message.obtain(null,1);
    msg.replyTo = mCilentMessenger;
    Bundle data = new Bundle();
    data.putString("msg","这是客户端的消息");
    msg.setData(data);
    try{
        mService.send(msg);
    }catch (RemoteException e) {
        e.printStackTrace();
    }
}
  • 这里我添加了一个Messenger对象负责作为发送消息时候的replyTo参数,然后将这个参数在发送消息时添加到replyTo参数上即可
  • 具体代码其实也没啥难度,不太清楚的小伙伴认真分析一下即可
Messenger总结
  • 我们发现Messenger的核心就是使用本进程的Messenger在另外一个线程发送(Message)消息就可

使用AIDL

  • Binder之间通信还是分为客户端和服务端

  • 先来看服务端,还是创建一个Service来监听客户端的连接请求,然后创建一个AIDL文件,将暴露给客户端的接口在这个AIDL文件中声明,最后在Service中实现这个AIDL接口即可

  • 先看看服务端AIDL接口的创建吧

    // IBookManager.aidl
    package com.example.learnretrofit.LearnMoreProcess;
    import com.example.learnretrofit.LearnMoreProcess.Book;
    interface IBookManager {
    List<Book> getBookList();
    void addBook(in Book book);
    }
    
  • 创建AIDL文件的方法上篇文章讲过,在文章最开始有链接

  • 在AIDL文件中,并不是所有类型都可使用,他所能使用的数据类型如下

基本数据类型

  • String和CharSequence
  • List:只支持ArrayList,里面每个元素都必须能被AIDL支持
  • Map:只支持HashMap,里面每个元素都必须能被AIDL支持
  • Parcelable:所有实现了Parcelable接口的对象,使用时需要手动import
  • AIDL:所有的AIDL接口本身也可以在AIDL文件中使用,使用时需要手动import

这里由于在AIDL接口中使用了Book类,所以必须在前面import,另外如果AIDL文件用到了自定义的Parcelable对象,必须新建一个和他同名的AIDL文件,并在其中声明他为Parcelable类型,因为在上面的接口定义中,我们使用了Book类,所以我们必须定义一个aidl文件去声明他

// Book.aidl
package com.example.learnretrofit.LearnMoreProcess;

parcelable Book;
  • 不知道大家注意到接口定义的方法的参数声明前有个in,这个参数是什么意思呢?AIDL中除了基本数据类型之外,其他类型的参数都必须标上方向,in , out ,或者inout,in表示输入性参数,out表示输出型参数,inout表示输入输出型参数,因为这个参数代表着底层的操作,所以在标注的时候不能随便标

  • 最后要注意的是AIDL接口中只支持方法,不支持静态变量,还有,服务端和客户端的包结构必须一致,因为客户端需要反序列话服务器中和AIDL接口相关的所有类,如果包路径不同就会无法反序列化成功

  • 那么上面的接口定义好了,我们就来看看我们的服务端怎么实现
    注意,在编写好aidl文件之后记得build-make project

    public class BookManagerService extends Service {

    private static final String TAG = "BookManagerService";

    private CopyOnWriteArrayList<Apple> mAppleList = new CopyOnWriteArrayList<>();

    private Binder mBinder = new IAppleManager.Stub() {
    @Override
    public List<Apple> getAppleList() throws RemoteException {
    return mAppleList;
    }

      @Override
      public void addApple(Apple apple) throws RemoteException {
          mAppleList.add(apple);
      }
    };
    
    @Override
    public void onCreate() {
      super.onCreate();
      mAppleList.add(new Apple(10,"红富士"));
      mAppleList.add(new Apple(10,"早熟苹果"));
    }
    
    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
      return mBinder;
    }
    }
    
  • 这里的IAppleManager.Stub是系统给我们生成的东西,他实际上是一个Binder类,然后我们先在onCreate方法里面添加两个数据,注意到我们这里使用了CopyOnWriteArrayList,因为他在原理上是支持并发读写的,* * 而AIDL支持的是抽象的List这个接口,虽然服务端返回的是CopyOnWriteArrayList,但是在Binder中会按照List的规范去访问数据并最终形成一个ArrayList传递给客户端,所以我们在这里使用CopyOnWriteArrayList是完全合理的,类似的还有ConcurrentHashMap,然后在注册文件中注册这个Service
    接着来看看客户端的实现

  • 客户端的话我们就需要绑定服务,然后将服务端返回的Binder对象转换成AIDL接口,然后就可以通过这个接口去调服务端的远程方法了,看看客户端的代码

      void bindMyAppleService(){
      ServiceConnection mConnection = new ServiceConnection() {
          @Override
          public void onServiceConnected(ComponentName name, IBinder service) {
              IAppleManager appleManager = IAppleManager.Stub.asInterface(service);
              try {
                  List<Apple> list = appleManager.getAppleList();
                  Log.d(TAG, "onServiceConnected: query apple list ,list type is " + list.getClass().getCanonicalName());
    
                  for(Apple apple : list){
                      Log.d(TAG, "onServiceConnected: apple.color = " + apple.color + ",apple.size = " + apple.size);
                  }
              } catch (RemoteException e) {
                  e.printStackTrace();
              }
          }
          @Override
          public void onServiceDisconnected(ComponentName name) {}
      };
      bindService(new Intent(this,AppleManagerService.class),mConnection,Context.BIND_AUTO_CREATE);
    }
    
  • 在绑定成功之后直接跨进程获取数据,注意这里是为了方便才这么写,一般还是写在新线程中比较好,因为这个服务端响应时间是不定的,万一响应时间过长就会出现ANR

  • 如果你对AIDL还不太清楚,可能看这些有点模糊,烦请移驾androidIPC机制探索先把这个看看

  • 在启动程序后打印出来的log信息为

    onServiceConnected: query apple list ,list type is java.util.ArrayList
    onServiceConnected: apple.color = 红富士,apple.size = 10
    onServiceConnected: apple.color = 早熟苹果,apple.size = 10
    
  • 可见客户端收到的确实是Arraylist类型,证实了我们上面的说法,同时下面的信息也证明了我们的跨进程操作成功

  • 我们再试一下他的addApple方法,修改onServiceConnection方法

    public void onServiceConnected(ComponentName name, IBinder service) {
              IAppleManager appleManager = IAppleManager.Stub.asInterface(service);
              try {
                  List<Apple> list = appleManager.getAppleList();
                  Log.d(TAG, "onServiceConnected: query apple list ,list type is " + list.getClass().getCanonicalName());
                  for(Apple apple : list){
                      Log.d(TAG, "onServiceConnected: apple.color = " + apple.color + ",apple.size = " + apple.size);
                  }
                  Apple apple = new Apple(10,"绿色的苹果");
    
                  appleManager.addApple(apple);
                  list = appleManager.getAppleList();
                  Log.d(TAG, "onServiceConnected: 重新接收");
                  for(Apple apple1 : list){
                      Log.d(TAG, "onServiceConnected: apple.color = " + apple1.color + ",apple.size = " + apple1.size);
                  }
    
              } catch (RemoteException e) {
                  e.printStackTrace();
              }
          }
    

log信息

  onServiceConnected: query apple list ,list type is java.util.ArrayL
 onServiceConnected: apple.color = 红富士,apple.size = 10
 onServiceConnected: apple.color = 早熟苹果,apple.size = 10
 onServiceConnected: 重新接收
 onServiceConnected: apple.color = 红富士,apple.size = 10
 onServiceConnected: apple.color = 早熟苹果,apple.size = 10
 onServiceConnected: apple.color = 绿色的苹果,apple.size = 10

没毛病,我们接下来看看AIDL更多的用法

AIDL深入探索

  • 我们看到,前面我们的跨进程通信都是一问一答式,就是客户端是主导,服务器是被主导,那么会不会有一种需求,我们不想去实时的查询了,因为这样太耗费时间,每次还不一定都有信息更新,所以,能不能有一种操作,客户端去问服务器:当有信息更新的时候你能不能直接给我说呢?

  • 以上需求是一种典型的观察者模式,服务器在有数据更新的时候通知每一位想知道这个消息的客户端,可是怎么用AIDL实现呢?

  • 首先,我们需要提供一个AIDL接口,每个用户都需要实现这个接口并对服务器申请数据更新的通知功能,当然用户也可以随时取消这种功能,这里我们创建一个

    // InewDataListener.aidl
    package com.example.learnretrofit.LearnMoreProcess;
    import com.example.learnretrofit.LearnMoreProcess.Apple;
    interface InewDataListener {
    void DataUpdata(in Apple apple);
    }
    

当然不止增加这个接口,我们还需要在原来接口做改动

// IBookManager.aidl
package com.example.learnretrofit.LearnMoreProcess;

import com.example.learnretrofit.LearnMoreProcess.Apple;
import com.example.learnretrofit.LearnMoreProcess.InewDataListener;

interface IAppleManager {
List<Apple> getAppleList();
void addApple(in Apple apple);

void registerListener(InewDataListener listener);
void unRegisterListener(InewDataListener listener);    
}
  • 这两个方法很容易理解

  • 然后我们build -> make project

  • 然后服务端的Binder对象就会提示我们有两个接口方法没实现,手动实现,然后看看此时的服务端代码有哪些改动吧

    private AtomicBoolean mAtomicBoolean = new AtomicBoolean(false);
    private CopyOnWriteArrayList<InewDataListener> mListeners = new CopyOnWriteArrayList<>();
    
    private Binder mBinder = new IAppleManager.Stub() {
      @Override
      public List<Apple> getAppleList() throws RemoteException {
          return mAppleList;
      }
    
      @Override
      public void addApple(Apple apple) throws RemoteException {
          mAppleList.add(apple);
      }
    
      @Override
      public void registerListener(InewDataListener listener) throws RemoteException {
          if(!mListeners.contains(listener)){
              mListeners.add(listener);
          }
      }
    
      @Override
      public void unRegisterListener(InewDataListener listener) throws RemoteException {
          if(mListeners.contains(listener)){
              mListeners.remove(listener);
          }
      }
    };
    
    
    public void onCreate() {
      super.onCreate();
      mAppleList.add(new Apple(10,"红富士"));
      mAppleList.add(new Apple(10,"早熟苹果"));
      new Thread(new ServiceWorker()).start();
    }
    
    private class ServiceWorker implements Runnable{
    
      @Override
      public void run() {
          while (!mAtomicBoolean.get()){
              try{
                  Thread.sleep(5000);
                  int appleId = mAppleList.size() + 1;
                  Apple apple = new Apple(appleId,"苹果 " + appleId + " 号");
    
                  addNewApple(apple);
    
              } catch (InterruptedException e) {
                  e.printStackTrace();
              }
          }
      }
    }
    
    private void addNewApple(Apple apple) {
      mAppleList.add(apple);
      for(int i = 0;i < mListeners.size() ; i ++) {
          InewDataListener listener = mListeners.get(i);
          try {
              listener.DataUpdata(apple);
          } catch (RemoteException e) {
              e.printStackTrace();
          }
      }
    }
    
  • 分析起来不难理解,这里就不再说了,我们再看看客户端的改动

    private InewDataListener mInewDataListener = new InewDataListener.Stub() {
      @Override
      public void DataUpdata(Apple apple) throws RemoteException {
          mHandler.obtainMessage(10,apple).sendToTarget();
      }
    };
    
    void bindMyAppleService(){
      ServiceConnection mConnection = new ServiceConnection() {
          @Override
          public void onServiceConnected(ComponentName name, IBinder service) {
              IAppleManager appleManager = IAppleManager.Stub.asInterface(service);
              try {
                  List<Apple> list = appleManager.getAppleList();
                  Log.d(TAG, "onServiceConnected: query apple list ,list type is " + list.getClass().getCanonicalName());
                  for(Apple apple : list){
                      Log.d(TAG, "onServiceConnected: apple.color = " + apple.color + ",apple.size = " + apple.size);
                  }
                  Apple apple = new Apple(10,"绿色的苹果");
    
                  appleManager.addApple(apple);
                  list = appleManager.getAppleList();
                  Log.d(TAG, "onServiceConnected: 重新接收");
                  for(Apple apple1 : list){
                      Log.d(TAG, "onServiceConnected: apple.color = " + apple1.color + ",apple.size = " + apple1.size);
                  }
                  appleManager.registerListener(mInewDataListener);
    
              } catch (RemoteException e) {
                  e.printStackTrace();
              }
          }
          @Override
          public void onServiceDisconnected(ComponentName name) {}
      };
      bindService(new Intent(this,AppleManagerService.class),mConnection,Context.BIND_AUTO_CREATE);
    }
    
    
      @SuppressLint("HandlerLeak")
    private Handler mHandler = new Handler(){
      @Override
      public void handleMessage(Message msg) {
          switch (msg.what) {
              case 10:
                  Log.d(TAG, "handleMessage: receive The new Data " + msg.obj);
          }
          super.handleMessage(msg);
      }
    };
    
  • 定义了接口,声明了当被回调时候的动作,接着在onServiceConnection中注册接口

  • 这里要注意的是接口回调是在Binder线程池中执行的,这一点可以通过我的上篇博客看到,这里不再多说,因此我们要进行UI操作的话,还是尽量使用Handler将他弄到主线程使用

  • 这里运行程序看到log信息为

    onServiceConnected: query apple list ,list type is java.util.ArrayList
    onServiceConnected: apple.color = 红富士,apple.size = 10
    onServiceConnected: apple.color = 早熟苹果,apple.size = 10
    onServiceConnected: 重新接收
    onServiceConnected: apple.color = 红富士,apple.size = 10
    onServiceConnected: apple.color = 早熟苹果,apple.size = 10
    onServiceConnected: apple.color = 绿色的苹果,apple.size = 10
    handleMessage: receive The new Data 苹果 4 号
    handleMessage: receive The new Data 苹果 5 号
    handleMessage: receive The new Data 苹果 6 号
    

AIDL解决跨进程取消注册问题

  • Android系统为我们提供了一个专门用于删除跨进程listener的接口–RemoteCallbackList,他是一个泛型,支持管理任意的AIDL接口,这点从他的声明就可以看出来,因为所有的AIDL接口都继承自IInterface接口,这点如果是因为我们写的aidl接口都会由系统为我们自行生成一个java文件,而标准格式就是都会继承自IInterface接口,下面是这个RemoteCallbackList接口的声明

    public class RemoteCallbackList<E extends IInterface>
    

它的工作原理很简单,在他的内部有一个Map结构专门用来保存所有的AIDL回调,这个Map的key是IBinder类型,value是Callback类型

ArrayMap<IBinder, Callback> mCallbacks = new ArrayMap<IBinder, Callback>();

其中Callback中封装了真正的远程Listener,当客户端注册listener的时候,他会把这个listener信息存入mCallback中,其中key和value分别通过下面的方式获得


1.png
  • 到这里的时候有没有点明悟了,还记得我们负责注册的过程是谁来完成的吗?是服务端给我们返回的一个Binder对象,而我们的跨进程通信都是基于这个对象的,而一般一个组件都是只有一个Binder的,所以?我们每个客户端与服务器之间的底层Binder是一一对应的,这点是不会变的,所以就有了上面的写法,我们存binder就行了,到时候你想取消注册,那就看看你的底层是哪个binder

  • RemoteCallbackList这个接口不光可以解决这个问题,他还有一个很有用的功能,那就是当客户端进程终止时,他会自动移除客户端所注册的listener

  • 另外,RemoteCallbackList内部自动实现了线程同步的功能,所以我们用它的时候不需要做额外的线程同步工作

  • 说了这么多,我们来看看他到底怎么用吧

  • 服务端的改变

  • 先用RemoteCallbackList创建新的集合代替原来的集合

    private RemoteCallbackList<InewDataListener> mCallbackList = new RemoteCallbackList<>();
    
    //    private CopyOnWriteArrayList<InewDataListener> mListeners = new CopyOnWriteArrayList<>();
    
  • 修改注册和取消注册两个接口的实现

    public void registerListener(InewDataListener listener) throws RemoteException {
    //            if(!mListeners.contains(listener)){
    //                mListeners.add(listener);
    //            }
    
          mCallbackList.register(listener);
      }
    
      @Override
      public void unRegisterListener(InewDataListener listener) throws RemoteException {
    //            if(mListeners.contains(listener)){
    //                mListeners.remove(listener);
    //            }
          mCallbackList.unregister(listener);
      }
    
  • 接下来修改通知的方法

    private void addNewApple(Apple apple) {
      mAppleList.add(apple);
    //        for(int i = 0;i < mListeners.size() ; i ++) {
    //            InewDataListener listener = mListeners.get(i);
    //            try {
    //                listener.DataUpdata(apple);
    //            } catch (RemoteException e) {
    //                e.printStackTrace();
    //            }
    //        }
      final int N = mCallbackList.beginBroadcast();
      for (int i = 0;i < N;i++){
          InewDataListener listener = mCallbackList.getBroadcastItem(i);
          if(listener != null){
              try{
                  listener.DataUpdata(apple);
              } catch (RemoteException e) {
                  e.printStackTrace();
              }
          }
      }
      mCallbackList.finishBroadcast();
    
    }
    
  • 是不是非常简单呢,我们来看一下具体效果吧,我在客户端进程的activity中添加一个取消注册的按钮

  • 运行之后的log

    unRegisterListener: 取消之前的数量是 1
    unRegisterListener: 取消之后的数量是 0
    
  • 注意:使用RemoteCallbackList的时候,我们无法像操作List一样去操作它,他并不是一个List,而且,遍历RemoteCallbackList的时候,必须要将代码写在CallbackList.beginBroadcast()和CallbackList.finishBroadcast()这两个方法之间,无论是遍历,还是哪怕只是获取个元素个数

    final int N = mCallbackList.beginBroadcast();
      for (int i = 0;i < N;i++){
          InewDataListener listener = mCallbackList.getBroadcastItem(i);
          if(listener != null){
              try{
                  listener.DataUpdata(apple);
              } catch (RemoteException e) {
                  e.printStackTrace();
              }
          }
      }
    mCallbackList.finishBroadcast();
    
  • 到这里关于AIDL的基本东西就说完了,这里说几点注意

  • 我们必须清楚什么方法执行在主线程,什么方法执行在Binder线程池,一些耗时的或者不可知的操作尽可能放在多线程中解决
  • 涉及到UI操作的,如果当前线程非主线程,要使用Handler切换到主线程再进行操作
    还有,binder是有可能意外死亡的,为了我们程序的健壮性,我们有必要给Binder设置死亡监听

AIDL拓展

  • 如果我们的服务想要更健全的话,我们有必要给他加上权限验证功能,因为在默认情况下,我们的远程服务是任何人都能连接的,这是我们不愿意看到的,所以我们必须给服务加入权限验证功能
    怎么加这个功能呢?我们想一下,服务最早接触客户端的方法是哪个方法?
  • 当然是onBind方法了,我们在绑定服务的时候,会使用onBind方法返回的对象来拿到我们操作服务的句柄,那么我们的权限验证就可以在这里做
  • 不过在做之前我们首先应该定义自己的权限
  • 这里简单说一下自定义权限
自定义权限简单说明
  • 在AndroidManifest.xml中,像这个样子

    <permission android:description="string resource"
         android:icon="drawable resource"
         android:label="string resource"
         android:name="string"
         android:permissionGroup="string"
         android:protectionLevel=["normal" | "dangerous" |"signature" | "signatureOrSystem"] />
    
  • 这里对各个属性做个说明


    2.png
  • protectionLevel权限等级的说明


    3.png
  • 然后我们在这里在我们的AndroidManifest.xml声明权限

    <permission
      android:name="com.example.learnretrofit.LearnMoreProcess.permission.ACCESS_APPLE_SERVICE"
      android:protectionLevel="normal"/>
    
  • 然后在我们的onBind方法添加权限验证

      public IBinder onBind(Intent intent) {
      int check = checkCallingOrSelfPermission("com.example.learnretrofit.LearnMoreProcess.permission.ACCESS_APPLE_SERVICE");
      if(check == PackageManager.PERMISSION_DENIED){
          return null;
      }
      return mBinder;
    }
    
  • 如果我们自己的应用想要绑定我们的服务,只需要在他的AndroidManifest.xml中声明权限即可

    <uses-permission android:name="com.example.learnretrofit.LearnMoreProcess.permission.ACCESS_APPLE_SERVICE"/>
    
  • 当然,权限验证不仅仅局限于AIDL,他可以用在所有你想用的地方

  • 第二种权限验证的方法

  • 我们可以在服务端的Binder的onTransact方法中进行权限验证,如果验证失败,就直接返回false,这样服务端就不会去执行客户端想要的逻辑 ,也达到了权限验证的效果

  • 我们甚至还可以采用UID和PID来做验证,通过getCallingUID和getCallingPid来拿到客户端所属进程的UID和Pid,这样我们就可以验证包名,等具体的操作大家具体去尝试吧

  • 下面我们继续学习另外的IPC方式

使用ContentProvider

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 225,165评论 6 523
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 96,476评论 3 405
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 172,446评论 0 368
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 61,157评论 1 301
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 70,164评论 6 400
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 53,615评论 1 316
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 41,969评论 3 430
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 40,959评论 0 279
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 47,495评论 1 324
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 39,529评论 3 347
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 41,641评论 1 355
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 37,233评论 5 351
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 42,976评论 3 340
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 33,407评论 0 25
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 34,552评论 1 277
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 50,218评论 3 381
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 46,715评论 2 366

推荐阅读更多精彩内容