Android中的多进程模式

  1. 开始多线程模式:

    1. 在android,大多数情况下的多进程模式指的是同一个应用中的多个进程的情况;

    2. 使用多进程的方式:就是给四大组件指定android:process;(常规模式);

    3. 注册活动的属性:

      android:label = ""就是值在任务执行列表时显示给用户的名字;如果在切换到这个活动有新指定的这个属性,那么就会发生覆盖;

      android:exported="true" 其他应用是否允许启动这个活动;

      andorid:configChanges = ""当发生变化:屏幕旋转等等,默认的操作就是销毁这个活动重启,如果设置了代表我自己处理这个变化;

    4. android:process属性值

      默认的主活动已经有自己的进程了,默认为包名;

      属性值为":名字"的时候,默认为包名+:+名字;

      属性值"包名.名字",此时才是完整的命名方式;

      1. 以:命名,是当前应用的私有进程;其他应用的组件不能跟当前应用的组件跑在同一个进程中;
      2. 以包名.名字命名,是全局进程;可以在同一个进程,必须shardUID相同;

      每个应用都有一个UID,相同的UID可以共享数据;

      如果是相同的ShardUID,并且签名(APK数字身份证,签名相同,sharduid才能使用,为了覆盖原软件,说明发行人)也相同:

      1. 两个应用才能在同一个进程中:除了可以共享data信息,组件信息,还可以共享内存;
      2. 不在;

多进程模式的运行机制

  1. 基本知识:Android中,每个进程都有自己独立的虚拟机,不同的虚拟机在内存分配上有不同的地址空间;不同的虚拟机中访问一个类会产生多个副本;

  2. 所以在不同进程下的组件是无法同享内存来共享信息的;

  3. 问题:

    1. 静态成员和单例模式完全失效;
    2. 线程同步机制失效;锁的对象都不是同一块内存;
    3. sp可靠性下降;计数器问题(不同进程同时对一个变量进行增值,导致增值后的结果相同;ps是有缓存机制的,导致获取到的是同一个数值),sp的全量写入(因为每次改变一个值就会新增加一条,如果不同进程改变的是不同的值,那么就会发生覆盖,因为读到是同一个缓存);
    4. application会多次创建:创建一个进程,就会有新的application;

IPC机制的基础知识

序列化的两种方式

Serializable
Percelable
  1. 怎么写?

    protected Book(Parcel in) {
        usrid = in.readInt();
        name = in.readString();
        ismale = in.readInt() == 1;
        in.readParcelable(Thread.currentThread().getContextClassLoader());
    }
      public void writeToParcel(@NonNull Parcel parcel, int i) {
            //i == 1代表当前对象需要作为返回值返回,不能立刻释放资源
            parcel.writeInt(usrid);
            parcel.writeString(name);
            parcel.writeInt(ismale ? 1 : 0);
            parcel.writeParcelable("可序列化的对象",0);
    
        }
    

    实现接口,写成员变量以及构造方法,写序列化和反序列化的方式;

    1. Parcel : Parcel内部包装了可序列化的数据;读写都是通过Parcel对象来进行读写;
    2. 如果我们序列化的对象本身就是一个序列化对象,那么当反序列化时,需要传递当前线程的上下文加载器,否则可能找不到这个类;
  2. 使用注意事项?

    1. describeContents() :返回当前对象的内容描述,有两个值,通常为0,返回1时(CONTENTS_FILE_DESCRIPTOR))代表含有文件描述符;
    2. writeToParcel第二个参数为1 代表当前对象需要作为返回值返回,不能立刻释放资源;

  3. 完整结构

    public class Book implements Parcelable {
        public int usrid;
        public String name;
        public boolean ismale;
    
        public Book(boolean ismale, String name, int usrid) {
            this.ismale = ismale;
            this.name = name;
            this.usrid = usrid;
        }
    
        protected Book(Parcel in) {
            usrid = in.readInt();
            name = in.readString();
            ismale = in.readInt() == 1;
            in.readParcelable(Thread.currentThread().getContextClassLoader());
        }
    
        public static final Creator<Book> CREATOR = new Creator<Book>() {
            @Override
            public Book createFromParcel(Parcel in) {
            //从序列化后的对象中创建原始对象
                return new Book(in);
            }
    
            @Override
            public Book[] newArray(int size) {
                return new Book[size];
            }
        };
        //返回当前对象的内容描述,如果有文件描述符,返回1(CONTENTS_FILE_DESCRIPTOR);
        @Override
        public int describeContents() {
            return 0;
        }
    
        @Override
        public void writeToParcel(@NonNull Parcel parcel, int i) {
            //i == 1代表当前对象需要作为返回值返回,不能立刻释放资源
            parcel.writeInt(usrid);
            parcel.writeString(name);
            parcel.writeInt(ismale ? 1 : 0);
            parcel.writeParcelable("可序列化的对象",0);
    
        }
    }
    
  4. 与Serializable的选取

    1. Serializable 是Java中的序列化接口,简单但是开销很大,一般在对象序列化存储设备中或者对象序列化之后通过网络传输;
      2.Parcelable 是安卓中的序列化接口,复杂但是效率很高,安卓开发中一般使用这个,一般在内存的序列化上;

Binder

主播在之前的文章中介绍过Binder,大家移步:【Android】Binder原理初探:理解Android通信机制

死亡代理

  1. 因为在Binder运行在服务端进程,如果服务端进程由于某些原因异常停止,那么这个时候服务器的Binder断裂,就是Binder死亡;

方法一:

  1. 但是客户段的功能会有影响,所以需要给客户端连接到服务器之后,通过binder.linkToDeath(m,0)
  2. 第一个参数是IBinder.DeathRecipient的对象,它的参数的匿名实现只有一个回调方法,在方法里实现断裂之后的代码通常会重新绑定远程服务;第二个参数是标志位;

方法二:

  1. 在onServieceDisconnected中重连线程;

区别就是:

onServieceDisconnected在UI线程,BinderDied是在客户端的Binder线程池中运行;

Android中IPC的六种方式:

使用Bundle

  1. 四大组件中的三大组件(Activity,Service,Receive)都支持在Intent中传递Bundle数据的;
  2. 因为Bundle实现了parcelable对象,而且做了处理,所以可以方便的在不同进程中进行ipc数据传输;
    3. bundle数据传输的对象:基本数据类型,实现parcelable对象,实现了serilizable对象,特殊对象;
  3. 使用场景:加入传递的对象需要计算,计算后的对象不是可传递的对象,那么可以放到新的进程中计算再在进程中调用;

使用文件共享:

  1. 安卓是基于Linux开发的,所以允许多个线程同时对一个文件进行读和写;两个进程读写同一个文件来进行交互;

    而且对于内容没有具体要求,可以是文本文件也可以是xml文件;但是会存在一些并发问题;

  2. 但是windows,一个文件加了排斥锁的话,其他线程就无法访问;

  3. SP:

    1. sp是一种轻量级存储方式,通过键值对存储,底层实现上通过xml来存储键值对,位置在当前包的data目录下就可以看到;
    2. 由于缓存策略,内存有一份sp的缓存,多进程模式下。读写不可靠,高并发的镀锡而,可能会丢失数据;线程a的内存缓存更新了,b的缓存没更新,a改变就会有问题;

使用Messenger

  1. 介绍:

    1. Messenger是一种ipc的方式,由于一次只能处理一个请求,所以不会出现并发的问题;
    2. 底层实现是AIDL;

  2. 使用:

    在服务端中:

    1. 过程:在onbind方法里面返回一个Messenger的Binder对象 ->new Messenger 的参数是一个hanlder -> 创建我们自定类继承handler实现Handlemessage方法即可;

    2. 自定类继承handler类中,因为客户端send才会调用,所以根据Message的第二个参数得到我们想得到的,然后根据 Messenger messenger1 = msg.replyTo;send,同时客户端才能得到服务端的响应;

    3. 一些东东:

      1. 什么时候触发handleMessage?

      当Messager返回的Binder对象生成的Messager的send方法时,会回到这个进程的对象。

      1. Messager返回的Binder对象生成的Messager是一个吗?

      不是哦,但是是使用同一个Binder对象;

      public class MyService extends Service {
          public static final  String TAG = "ipcMessenger";
          public MyService() {
      
          }
          private static class messageHandelr extends Handler{
              @Override
              public void handleMessage(@NonNull Message msg) {
                if(msg.what == 0){
                    Log.d(TAG,"你好呀,我收到了客户端发来的消息:"+msg.getData().getString("null"));
                    Message message = Message.obtain(null,1);
                    Bundle bundle = new Bundle();
                    bundle.putString("null","我是swy");
                    message.setData(bundle);
                    Messenger messenger1 = msg.replyTo;
                    try {
                        messenger1.send(message);
                    } catch (RemoteException e) {
                        throw new RuntimeException(e);
                    }
      
                    super.handleMessage(msg);
                }
              }
          }
          Messenger messenger = new Messenger(new messageHandelr());
          @Override
          public IBinder onBind(Intent intent) {
              return messenger.getBinder();
          }
      }
      

    客户端:

    1. 过程:链接成功后,得到了Binder对想,通过这个对象得到messenger对象,然后messenger是通过message来传递数据的,同时创建一个新的Messenger对象,来接受服务端返回的信息;

      public class MainActivity extends AppCompatActivity {
      
          public static final  String TAG = "ipcMessenger";
      
      
      
         private  static  class cmh extends Handler{
             @Override
             public void handleMessage(@NonNull Message msg) {
                 Log.d(TAG,"我得到了服务端的数据耶"+msg.getData().getString("null"));
                 if(msg.what==1){
                     Log.d(TAG,"我得到了服务端的数据耶"+msg.getData().getString("null"));
                 }
                 super.handleMessage(msg);
             }
         }
      
         private Messenger messengers = new Messenger(new cmh());
      
      
          private ServiceConnection serviceConnection= new ServiceConnection() {
              @Override
              public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
                  Messenger messenger = new Messenger(iBinder);
                  Message message = Message.obtain(null, 0);
                  Bundle bundle = new Bundle();
                  bundle.putString("null","msg");
                  message.replyTo = messengers;
                  message.setData(bundle);
                  try {
                      messenger.send(message);
                  } catch (RemoteException e) {
                      throw new RuntimeException(e);
                  }
      
      
              }
      
              @Override
              public void onServiceDisconnected(ComponentName componentName) {
      
              }
          };
          @Override
          protected void onCreate(Bundle savedInstanceState) {
              super.onCreate(savedInstanceState);
              EdgeToEdge.enable(this);
              setContentView(R.layout.activity_main);
              ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main), (v, insets) -> {
                  Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars());
                  v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom);
                  return insets;
              });
              Intent intent = new Intent(this,MyService.class);
              intent.setPackage("com.example.binder");
              bindService(intent,serviceConnection, Context.BIND_AUTO_CREATE);
          }
      }
      

      Messenger是通过message来传递数据的,二者都实现了parcelable接口;

      message的载体:replyto(Messenger),arg1,arg2,what,bundle;

使用AIDL

./gradlew clean build

  1. Messenger和AIDL的区别:

    前者更聚焦于一条一条的处理,如果处理量过大,那么是不合适的;

    如果我们要调用服务端中的方法,AIDL能实现,Messenger不合适;

  2. 实现:
    生成AIDL类之后:

    尽量把aidl写在同一个包且包结构一致下;如果类的路径不一致,就会反序列化失败;

    1. 接口创建:

      1. 接口的支持类型:
        基本数据类型,String,charSequence(字符串序列,可以支持string,StringBuffer,StringBuilder等等),List(只支持arraylist,元素必须支持),map(只支持hashmap,元素必须支持),AIDL,Parcelable;

      2. 除了基本数据类型,其他数据类型必须标上方向:

        in:客户端发给服务端,服务端的修改客户端不会知道,速度最快,序列化最少;

        out:客户端发送一个空壳对象给服务端,服务端填写数据后返回;客户端传的初始值完全无效;

        inout:客户端传,服务端修改后返回;序列化次数最多。

        不可一味的out,inout开销很大

      3. AIDL仅支持接口,不支持常量;

    2. 客户端:

      利用服务端返回的Binder对象,装换为aidl接口对象,然后调用里面的方法即可;

    3. 服务端:

      实现AIDL接口;

CopyOnWriteArrayList支持并发读写,虽然服务端返回的是它,但是Binder会按照List的规范生成一个arraylist传给客户端;

有一个图书管理:每当有新书的时候,服务端需要通知给客户端;

我们来想想:服务端需要主动给客户端通知,那么肯定需要一个aidl接口,然后让客户端实现这个接口,接着服务端调用接口放置数据就好了,,,但是服务端怎么通知?所以我们需要保存客户端Binder的引用,所以我们需要再用户注册的时候保存引用,销毁的时候去掉引用;然后有新书时候,通知给每一个用户;

把书通知给客户端的接口

// IonNewBookArrivedListener.aidl
package com.example.aidl;
import com.example.aidl.Book;
// Declare any non-default types here with import statements

interface IonNewBookArrivedListener {
   void onNewBookArrived(in Book newBook);
}

客户端通知服务端的接口:IonNewBookArrivedListener 就是客户端实现sub类之后的Binder引用给服务端

// IBookManager.aidl
package com.example.aidl;
import com.example.aidl.Book;
// Declare any non-default types here with import statements
import com.example.aidl.IonNewBookArrivedListener;
interface IBookManager {
   void addBook(in Book book);
   List<Book> getBookList();
   void registerListener(IonNewBookArrivedListener lis);
   void unregisterListener(IonNewBookArrivedListener lis);
}

声明类文件

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

// Declare any non-default types here with import statements

parcelable Book;

客户端:

    public class MainActivity extends AppCompatActivity {
    IBookManager iBookManager;
    ServiceConnection serviceConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
           iBookManager = IBookManager.Stub.asInterface(iBinder);
            try {
                List<Book> myBookList = iBookManager.getBookList();
                Log.d("ljxljx","mybooklistsize: "+myBookList.size());
                Book book = new Book(3,"ljxaswy");
                iBookManager.addBook(book);
                Log.d("ljxljx","andBook" + book.getBookName());
                iBookManager.registerListener(ionNewBookArrivedListener);
            } catch (RemoteException e) {
                throw new RuntimeException(e);
            }
        }
    
        @Override
        public void onServiceDisconnected(ComponentName componentName) {
    
        }
    };
    private IonNewBookArrivedListener ionNewBookArrivedListener = new IonNewBookArrivedListener.Stub() {
        @Override
        public void onNewBookArrived(Book newBook) throws RemoteException {
            Log.d("ljxljx","newbookname"+newBook.getBookName());
        }
    };
    @Override
    protected void onCreate(Bundle savedInstanceState) {


        super.onCreate(savedInstanceState);
        EdgeToEdge.enable(this);
        setContentView(R.layout.activity_main);
        ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main), (v, insets) -> {
            Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars());
            v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom);
            return insets;
        });
        Intent intent = new Intent(this,MyService1.class);
        intent.setPackage("com.example.aidl");
        bindService(intent,serviceConnection, Context.BIND_AUTO_CREATE);
    }
    
    @Override
    protected void onDestroy() {
        if(iBookManager !=null && iBookManager.asBinder().isBinderAlive()){
            try {
                iBookManager.unregisterListener(ionNewBookArrivedListener);
            } catch (RemoteException e) {
                throw new RuntimeException(e);
            }
        }
        unbindService(serviceConnection);
        super.onDestroy();
    }
    }

服务端:


public class MyService1 extends Service {
    public MyService1() {
    }
    CopyOnWriteArrayList<Book> booklist = new CopyOnWriteArrayList<>();
    CopyOnWriteArrayList<IonNewBookArrivedListener> mlistenerlist = new CopyOnWriteArrayList<>();

    private Binder mybinder = new IBookManager.Stub() {
        @Override
        public void addBook(Book book) throws RemoteException {
            booklist.add(book);
        }

        @Override
        public List<Book> getBookList() throws RemoteException {
            return booklist;
        }

        @Override
        public void registerListener(IonNewBookArrivedListener lis) throws RemoteException {
            if(!mlistenerlist.contains(lis)){
                mlistenerlist.add(lis);
            }
        }

        @Override
        public void unregisterListener(IonNewBookArrivedListener lis) throws RemoteException {
            if(mlistenerlist.contains(lis)){
               mlistenerlist.remove(lis);
            }
        }
    };

    @Override
    public void onCreate() {
        booklist.add(new Book(1,"李佳雪"));
        booklist.add(new Book(2,"swy"));
        new Thread(new ServiceWorker()).start();
        super.onCreate();
    }
    @Override
    public IBinder onBind(Intent intent) {
        return mybinder;
    }
    private class ServiceWorker implements Runnable{

        @Override
        public void run() {
           while (true){
               try {
                   Thread.sleep(5000);
               } catch (InterruptedException e) {
                   throw new RuntimeException(e);
               }
               int bookid = booklist.size() +1;

               Book newbook = new Book(bookid,"newBook"+bookid);
               onNewBookArrived(newbook);
           }

        }
    }

    private void onNewBookArrived(Book newbook) {
        booklist.add(newbook);
        for (IonNewBookArrivedListener listener : mlistenerlist) {
            try {
                listener.onNewBookArrived(newbook);
            } catch (RemoteException e) {
                throw new RuntimeException(e);
            }
        }

    }
}

RemoteCallBackList

  1. 在跨进程传输中,客户端传给服务端会生成不同的对象,但是他们的底层对象Binder都是同一个;->远程调用方法可以,但是如果我们客户端取消注册时,发现找不到对应的客户端对象;

  2. RemoteCallBackList的介绍:

    1. 系统提供专门用户跨进程删除接口的,它是一个泛型,支持接管所有aidl接口;
    2. 内部是map,第一个参数是IBinder,第二个参数是callback类型;
    3. 当进程终止后,它能够自动移除客户端注册的接口回调;
    4. 内部实现了线程同步的功能;
    5. 必须这样遍历使用,它不是List;
  3. 应用:

    添加元素

        mlistenerlist.register(lis);
    

    删除

    mlistenerlist.unregister(lis);

    遍历

    int n =  mlistenerlist.beginBroadcast();
    for (int i = 0; i < n; i++) {
        IonNewBookArrivedListener broadcastItem = mlistenerlist.getBroadcastItem(i);
        try {
            broadcastItem.onNewBookArrived(newbook);
        } catch (RemoteException e) {
            throw new RuntimeException(e);
        }
    
    }
    mlistenerlist.finishBroadcast();
    

ANR

前提:客户端调用服务端的方法,那么这个方法会运行在服务端的Binder线程池中;

客户端:

  1. 客户端线程会被挂起,如果运行在UI线程,那么可能会导致ANR;
  2. 如果方法是耗时操作,建议避免在UI线程访问远程方法;

服务端:

  1. 服务端的方法运行在Binder线程池中,可以执行耗时操作,不要新开线程啦;

前提:服务端调用客户端的方法,那么这个方法会运行在客户端的Binder线程池中;

客户端:

  1. 此时方法运行在Binder线程池中,如果要使用UI元素,记得切换UI线程;

服务端:

  1. 同理,调用的方法耗时,别在UI线程;

权限验证

在onBind中进行验证

服务端的权限:

<permission android:name="com.ryg.ms1"
    android:protectionLevel="normal">
@Override
public IBinder onBind(Intent intent) {
    int check = checkCallingOrSelfPermission("com.ryg.ms1");
    if(check == PackageManager.PERMISSION_DENIED){
        return null;
    }
    return mybinder;
}

客户端绑定权限:

<uses-permission android:name="com.ryg.ms1">
    
</uses-permission>

在onTransact方法中验证

包名必须要com.ryg开头

 int check = checkCallingOrSelfPermission("com.ryg.ms1");
    if(check == PackageManager.PERMISSION_DENIED){
        return false;
    }

使用ContentProvider

  1. 介绍:是安卓中专门用户不同应用之间传递数据的组件;

    底层实现是Binder,但是做了封装,比aidl简单;

  2. 使用:

    1. extends ContentProvider;

    2. 实现六种方法:

      1. Oncreate用于初始化操作,也是唯一一个由系统回调在主线程中的方法,其他都在外界回调运行在线程池中;
      2. getType: 用来返回一个URI请求所对应的MIME类型(多媒体类型);
      3. 剩下的四个方法是增删改查;
    3. 其他:

      1. 主要用表格的形式来组织数据;

      2. 支持文件数据(图片,视频),与表格类型不同,处理时可以在内容提供器中返回文件的句柄给外界,让外界访问contentprovider中的文件信息;

      3. 对底层的储存信息没有任何的要求,可以使用SQLite数据库,也可以使用普通的文件;

模拟实现:

没啥好说的;

public class BookProvider extends ContentProvider {
    public BookProvider() {
    }

    @Override
    public int delete(Uri uri, String selection, String[] selectionArgs) {
        Log.d("ljxswy","delete");
        // Implement this to handle requests to delete one or more rows.
        return 0;
    }

    @Override
    public String getType(Uri uri) {
        Log.d("ljxswy","getType");
        // TODO: Implement this to handle requests for the MIME type of the data
        // at the given URI.
        return null;
    }

    @Override
    public Uri insert(Uri uri, ContentValues values) {
        Log.d("ljxswy","insert");
        // TODO: Implement this to handle requests to insert a new row.
        return null;
    }

    @Override
    public boolean onCreate() {
        Log.d("ljxswy","oncreate");
        // TODO: Implement this to initialize your content provider on startup.
        return false;
    }

    @Override
    public Cursor query(Uri uri, String[] projection, String selection,
                        String[] selectionArgs, String sortOrder) {
        Log.d("ljxswy","threadname"+Thread.currentThread().getName());
        // TODO: Implement this to handle query requests from clients.
        return null;
    }

    @Override
    public int update(Uri uri, ContentValues values, String selection,
                      String[] selectionArgs) {
        Log.d("ljxswy","update");
        return 0;
    }
}
  1. 在注册的时候,加上:,其实还有readpermission,writepermission;其他应用有这个权限,才能访问这个内容提供器;
android:permission="com.ryg.PROVIDER"
  1. 这是provider 的唯一标识,便于外部应用访问
android:authorities="com.ryg.aidl.Book.Provider"

活动中:

 Uri uri = Uri.parse("content://com.ryg.aidl.Book.Provider");
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
    Log.d("ljxswy","newbookname");
    getContentResolver().query(uri,null,null,null,null);
    getContentResolver().query(uri,null,null,null,null);
    getContentResolver().query(uri,null,null,null,null);
}
  1. 使用content://com.ryg.aidl.Book.Provider唯一标识了内容提供器;
  2. 使用ContentResolver()的query方法查询;

以上是简单的实现,我们还可以加入数据库的交互,其实很简单;

  1. 创建自己的openhelper类;
  2. 在内容提供器的oncreate里,创建我们的数据库;
  3. 由于不知道访问的是哪个表,所以为他们定义单独的uri,uricode->将两者用UriMatcher相关联->当我们知道了uri得到uricode->访问表中数据;

好吧,还是代码为主吧;

private static final String AUTHORITY = "com.ryg.aidl.Book.Provider";
public static  final  Uri BOOK_URL = Uri.parse("content://"+AUTHORITY + "/Book");

public static  final  int BOOK_URL_CODE = 0;

private  static  final UriMatcher urimatcher = new UriMatcher(UriMatcher.NO_MATCH);
static {
    urimatcher.addURI(AUTHORITY,"Book",BOOK_URL_CODE);
}

然后写四个方法:

  1. 每次根据uri,然后根据urimacher.match(uri) ,来获得对应的表名,对对应表操作;
  2. 除了查询,其他方法都会provider的内容修改,此时都要调用mcontext.contentresolver().notifychange(uri,null);

SQLite内部对数据库做了同步的处理,但是如果通过多个对象来访问的话,还是有线程安全问题的;

使用Socket

  1. 介绍:socker也称为套接字,分为流式套接字和用户数据报套接字;

  2. 流式套接字:TCP协议;

    1. TCP:是面向连接的协议,连接的建立需要经过三次握手才能完成;
    2. 有超时重传机制,有很高的稳定性;
  3. 用户数据报套接字:UDP协议:

    1. 是无连接的,提供不稳定的单向通信功能,也可以双向;
    2. 在网络堵塞的情况下,数据不一定能正确传输;
    3. 优点是效率高;
  4. 不能在主线程中访问网络:

    1. 访问会报错;
    2. 网络是耗时的,不推荐;
  5. 通信过程:

    大致流程就是

    1. 服务端Service中,新开线程:通过servicesocker.accpect()得到客户端是socket对象,然后读数据,写数据;
    2. 客户端中,通过startservice();然后新开线程:生成对应的socket对象,读写数据;

Binder连接池

  1. 解决场景:如果有很多不同线程通信很多aidl->有很多serviece->我们想实现只有一个service->需要一个Binder连接池类来进行管理;

  2. 思路:

    1. 建立不同的aidl文件->同时建立一个aidl文件来进行管理;

    2. 逻辑:

      客户端:通过一个方法连接到服务端->根据不同的参数得到自己的Binder->通信;

      Binder连接池:通过一个aidl与服务器通信->连接服务器->得到服务器返回的大Binder->实现这个类的内部方法(根据不同的请求返回不同的小Binder)->写小Binder的stub->客户端通过大Binder调用方法得到小Binder通信;

  3. 代码:

    Binder连接池类:

    public class BinderPool {
    
       private  Context mcontext;
        private static volatile BinderPool sInstance;
        private CountDownLatch countDownLatch;
        private IBinderPool mibinder;
        public BinderPool(Context mcontext) {
            this.mcontext = mcontext;
            conncectBinderPool();
        }
    
        private void conncectBinderPool() {
            Log.d("BinderPool", "bindService result: f");
            countDownLatch = new CountDownLatch(1);
            Log.d("BinderPool", "bindService result: f");
            Intent intent = new Intent(mcontext, binderpoolservice.class);
            intent.setPackage("com.example.aidl");
            Log.d("BinderPool", "bindService result: d");
            boolean b = mcontext.bindService(intent, serviceConnection, mcontext.BIND_AUTO_CREATE);
            Log.d("BinderPool", "bindService result: " +b);
            // 使用新的线程来等待服务连接,避免主线程阻塞
            try {
                countDownLatch.await();  // ✔ 在子线程阻塞
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        private ServiceConnection serviceConnection = new ServiceConnection() {
            @Override
            public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
                Log.d("ljxaswy","contecyed");
                mibinder = (IBinderPool) IBinderPool.Stub.asInterface(iBinder);
                try {
                    mibinder.asBinder().linkToDeath(deathRecipient,0);
                } catch (RemoteException e) {
                    throw new RuntimeException(e);
                }
                countDownLatch.countDown();
            }
            private IBinder.DeathRecipient deathRecipient = new IBinder.DeathRecipient() {
                @Override
                public void binderDied() {
                    mibinder.asBinder().unlinkToDeath(deathRecipient,0);
                    mibinder = null;
                    conncectBinderPool();
                }
            };
            @Override
            public void onServiceDisconnected(ComponentName componentName) {
                Log.d("ljxaswy","notcontecyed");
            }
        };
        public IBinder queryBinder(int code){
            IBinder  m= null;
            if(mibinder != null){
                try {
                    m = mibinder.querybinder(code);
                } catch (RemoteException e) {
                    throw new RuntimeException(e);
                }
            }
            if(m == null){
                Log.d("ljxaswy","nulll");
            }
            return m;
        }
        public static class binderpoolitem extends IBinderPool.Stub{
    
            public binderpoolitem() {
            }
    
            @Override
            public IBinder querybinder(int a) throws RemoteException {
                IBinder iBinder = null;
                Log.d("ljxaswy","leil");
                if(a == 0){
                    iBinder = new printljx();
                }
                else if(a == 1) {
                   iBinder = new printswy();
                }
                return iBinder;
            }
        }
        public static class printljx extends Iprintljx.Stub{
    
            @Override
            public void printLjx() throws RemoteException {
                Log.d("ljxaswy","雪雪砂仁女魔头");
            }
        }
        public static class printswy extends Iprintswy.Stub{
    
            @Override
            public void printSwy() throws RemoteException {
                Log.d("ljxaswy","万事都灵");
            }
        }
        public static BinderPool getInstance(Context context){
            Log.d("BinderPool", "bindService result: df");
            if(sInstance == null){
                Log.d("BinderPool", "bindService result: ddf");
                synchronized (BinderPool.class){
                    if(sInstance == null){
                        Log.d("BinderPool", "bindService result: dddf");
                        sInstance = new BinderPool(context);
                    }
                }
            }
            return sInstance;
        }
    
    
    
    
    }
    

服务端

package com.example.aidl;

import android.app.Service;
import android.content.Intent;
import android.os.Binder;
import android.os.IBinder;
import android.util.Log;

public class binderpoolservice extends Service {
    @Override
    public void onCreate() {
        Log.d("ljxaswy","oncreate");
        super.onCreate();
    }

    BinderPool.binderpoolitem binderpoolitem = new BinderPool.binderpoolitem();
    public binderpoolservice() {

    }

    @Override
    public IBinder onBind(Intent intent) {
         return binderpoolitem;
    }
}

客户端:

new Thread(new Runnable() {
    @Override
    public void run() {
        BinderPool binderPool = BinderPool.getInstance(getApplicationContext());
        Log.d("ljxaswy","ljxaswy1");
        IBinder iBinder = binderPool.queryBinder(0);
        Iprintljx iprintljx = Iprintljx.Stub.asInterface(iBinder);
        Log.d("ljxaswy","ljxaswy");
        try {
            iprintljx.printLjx();
        } catch (RemoteException e) {
            throw new RuntimeException(e);
        }
    }
}).start();

有几个注意的点:

1. private static volatile BinderPool sInstance;为什么要用volatile?

保持多个线程访问sinstance的一致性,禁止指令重排(创建的对象是完整的,有序的),避免创造单例出问题,内存的可见性;

有序性:jvm会进行优化,可能会分配内存,直接把引用赋值给sinstance,然后调用构造函数;

可见性:(保证一个线程修改了元素的值,能立马写到内存中和从内存中读出,这样其他线程进行读写的时候都能得到最新值);

  1. 客户端记得写在子线程中,否则客户端在主线成,导致 countDownLatch堵塞主线程,服务连接成功无法回调,会造成死锁;

好啦,本次分享到此结束,不喜勿喷哦嘻嘻;

Logo

北京人形旗下天工造物具身智能开源社区,聚焦具身天工与慧思开物两大平台

更多推荐