Android进程间通信方式

Posted 小图包

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android进程间通信方式相关的知识,希望对你有一定的参考价值。

目录

一 Intent

二 Content Provider

三 使用文件共享

四 使用 Messenger

五 AIDL

六 Socket的方式

IPC 适用的场景及优缺点


定义多进程

android应用中使用多进程只有一个办法(用NDK的fork来做除外),就是在AndroidManifest.xml中声明组件时,用android:process属性来指定。

不知定process属性,则默认运行在主进程中,主进程名字为包名。

android:process = package:remote,将运行在package:remote进程中,属于全局进程,其他具有相同shareUID与签名的APP可以跑在这个进程中。

android:process = :remote ,将运行在默认包名:remote进程中,而且是APP的私有进程,不允许其他APP的组件来访问。

多进程引发的问题

静态成员和单例失效:每个进程保持各自的静态成员和单例,相互独立。

线程同步机制失效:每个进程有自己的线程锁。

SharedPreferences可靠性下降:不支持并发写,会出现脏数据。

Application多次创建:不同进程跑在不同虚拟机,每个虚拟机启动会创建自己的Application,自定义Application时生命周期会混乱。

综上,不同进程拥有各自独立的虚拟机,Application,内存空间,由此引发一系列问题。

一 Intent

Activity,Service,Receiver 都支持在 Intent 中传递 Bundle 数据,而 Bundle 实现了 Parcelable 接口,可以在不同的进程间进行传输。

  Bundle bundle = new Bundle();
    bundle.putString("test", "来自A");
    Intent intent = new Intent(Intent.ACTION_MAIN);
    intent.addCategory(Intent.CATEGORY_LAUNCHER);
    ComponentName cn = new ComponentName("com.test",
            "com.test.MainActivity");//要启动的目标包名和包名+类名
    intent.setComponent(cn);
    intent.putExtras(bundle);
    startActivity(intent);

 

Intent callIntent = new  Intent(Intent.ACTION_CALL, Uri.parse("tel:12345678" );  
startActivity(callIntent);

二 Content Provider

Content Provider可以跨进程访问其他应用程序中的数据(以Cursor对象形式返回),当然,也可以对其他应用程序的数据进行增、删、改操 作;

Content Provider返回的数据是二维表的形式

三 使用文件共享

  1. Windows 上,一个文件如果被加了排斥锁会导致其他线程无法对其进行访问,包括读和写;而 Android 系统基于 Linux ,使得其并发读取文件没有限制地进行,甚至允许两个线程同时对一个文件进行读写操作,尽管这样可能会出问题。
  2. 可以在一个进程中序列化一个对象到文件系统中,在另一个进程中反序列化恢复这个对象(注意:并不是同一个对象,只是内容相同。)。
  3. SharedPreferences 是个特例,系统对它的读 / 写有一定的缓存策略,即内存中会有一份 ShardPreferences 文件的缓存,系统对他的读 / 写就变得不可靠,当面对高并发的读写访问,SharedPreferences 有很多大的几率丢失数据。因此,IPC 不建议采用 SharedPreferences。

四 使用 Messenger

Messenger 是一种轻量级的 IPC 方案,它的底层实现是 AIDL ,可以在不同进程中传递 Message 对象

  1 创建一个服务端的进程

public class TestMessengerService extends Service {


    private static final String TAG = TestMessengerService.class.getSimpleName();


    private class MessengerHandler extends Handler {

        /**
         * @param msg
         */
        @Override
        public void handleMessage(Message msg) {

            switch (msg.what) {
                case 100000  :

                    Toast.makeText(TestMessengerService.this, "receive msg from client: msg = [" + msg.getData().getString("msg_key_client") + "]", Toast.LENGTH_SHORT).show();
                    Messenger client = msg.replyTo;
                    Message replyMsg = Message.obtain(null,  100001);
                    Bundle bundle = new Bundle();
                    bundle.putString("msg_key_service", "我已经收到你的消息,稍后回复你!");
                    replyMsg.setData(bundle);
                    try {
                        client.send(replyMsg);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                    break;
                default:
                    super.handleMessage(msg);
            }
        }
    }

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


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

2 创建客户端进程 

 客户端进程:首先绑定服务端 Service ,绑定成功之后用服务端的 IBinder 对象创建一个 Messenger ,通过这个 Messenger 就可以向服务端发送消息了

public class MainActivity extends BaseActivity {

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

 
        Intent mIntent = new Intent(this, TestMessengerService .class);
        bindService(mIntent, mServiceConnection, Context.BIND_AUTO_CREATE);


        Message msg = Message.obtain(null,100000);
        Bundle data = new Bundle();
        data.putString("msg_key_client", "Hello! This is client.");
        msg.setData(data);
        msg.replyTo = mGetReplyMessenger;
        try {
            mService.send(msg);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }


    private Messenger mGetReplyMessenger = new Messenger(new MessageHandler());
    private Messenger mService;

    private class MessageHandler extends Handler {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case 100001:

                    Toast.makeText(MainActivity.this, "received msg form service: msg = [" + msg.getData().getString("msg_key_service") + "]", Toast.LENGTH_SHORT).show();
                    break;
                default:
                    super.handleMessage(msg);
            }
        }
    }


    private ServiceConnection mServiceConnection = new ServiceConnection() {
        /**
         * @param name
         * @param service
         */
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            mService = new Messenger(service);
            Message msg = Message.obtain(null,100000);
            Bundle data = new Bundle();
            data.putString("msg_key_client", "Hello! This is client.");
            msg.setData(data);
            //
            msg.replyTo = mGetReplyMessenger;
            try {
                mService.send(msg);
            } catch (Exception e) {
                e.printStackTrace();
            }

        }

        /**
         * @param name
         */
        @Override
        public void onServiceDisconnected(ComponentName name) {


        }
    };


}

五 AIDL

在Java层,想利用Binder进行夸进程的通信,那就得通过AIDL(Android 接口定义语言)了,AIDL是客户端与服务使用进程间通信 (IPC) 进行相互通信时都认可的编程接口,只有允许不同应用的客户端用 IPC 方式访问服务,并且想要在服务中处理多线程时,才有必要使用 AIDL.

1 创建aidl文件 并定义一个getString的方法



interface MyAidlService {
    /**
     * Demonstrates some basic types that you can use as parameters
     * and return values in AIDL.
     */
   String getString ();
}

2 创建需要一个Service配合,所以我们在服务端还要声明一个Service

public class MyService extends Service {


    class Mybind extends MyAidlService.Stub {
        @Override
        public String getString() throws RemoteException {
            String string = "我是从服务器返回的";
            return string;
        }
    }

    Mybind mybind = new Mybind();

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

    @Override
    public void onCreate() {
        super.onCreate();

    }
}
     <service
          android:name=".MyService"
          android:enabled="true"
          android:exported="true">

     </service>

3 创建客户端



    private MyAidlService myAidlService;

    private ServiceConnection serviceConnection = new ServiceConnection() {//创建服务连接对象
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {//当成功绑定服务时回调该方法
            myAidlService = MyAidlService.Stub.asInterface(service);//向下转型得到服务器端Mybind类的实例
            try {
                String str = myAidlService.getString();//调用实例的getString方法

                Log.d("TestServiceConnection", str);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {//解除服务绑定时回调该方法

        }
    };


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.test_activity);
   
        bindService();

    }


    private void bindService() {
        Intent intent = new Intent();
        intent.setClassName("服务端的包名", "服务端AIDL的Service的Class");
        bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE);
    }");
        bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE);
    }

六 Socket的方式

Socket也是实现进程间通信的一种方式,Socket也称为“套接字”(网络通信中概念),通过Socket也可以实现跨进程通信,Socaket主要还是应用在网络通信中。

1 服务端  我们创建一个Service做为服务端 

 <service
            android:name="com.lvhj.ipc.SocketService"
            android:enabled="true"
            android:exported="true"
            android:process=":remote" />

 

public class SocketService extends Service {


    private static final String TAG = "SocketService";

    private boolean isDestroyed = false;
    private String[] messages = new String[]{
            "测试1",
            "测试2",
            "测试3",
            "测试4",
            "测试5"
    };

    public SocketService() {
    }

    @Override
    public void onCreate() {
        super.onCreate();
        new Thread(new TCPServer()).start();
    }


    @Override
    public void onDestroy() {
        isDestroyed = true;
        super.onDestroy();
    }

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

    private class TCPServer implements Runnable {
        @Override
        public void run() {
            ServerSocket serverSocket = null;
            try {
                serverSocket = new ServerSocket(8688);
            } catch (IOException e) {
                e.printStackTrace();
            }
            while (!isDestroyed) {
                try {
                    final Socket client = serverSocket.accept();
                    Log.d(TAG, "accept");
                    new Thread() {
                        @Override
                        public void run() {
                            responseClient(client);
                        }
                    }.start();

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

        private void responseClient(Socket client) {
            try {
                // 接收客户端消息
                BufferedReader in = new BufferedReader(new InputStreamReader(client.getInputStream()));
                // 响应客户端消息
                PrintWriter out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(client.getOutputStream())), true);
                Log.d(TAG, "欢迎来到聊天室!");
                out.println("欢迎来到聊天室!");
                while (!isDestroyed) {
                    String line = in.readLine();
                    Log.d(TAG, "message from Client: " + line);
                    if (line == null) break;
                    int i = new Random().nextInt(messages.length);
                    String message = messages[i];
                    out.println(message);
                    Log.d(TAG, "response to Client: " + message);
                }
                out.close();
                in.close();

                client.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

客户端建立连接后,新建 Socket 客户端,接收消息并作出响应。

2、客户端

客户端部分首先启动 Socket 服务,并且在连接失败后会不断重新尝试连接。

public class MainActivity extends AppCompatActivity {

    private static final String TAG = "TestSocket";
    private PrintWriter mPrintWriter;
    private Socket mClientSocket;
    private Button button;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        initView();

        Intent service = new Intent(this, SocketService.class);
        startService(service);
        new Thread() {
            @Override
            public void run() {
                connectSocketServer();
            }
        }.start();


    }

    private void initView() {
        button = findViewById(R.id.btn_send_msg);


        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        mPrintWriter.println("客户端发送的消息");
                    }
                }).start();
            }
        });
    }

    private void connectSocketServer() {
        Socket socket = null;
        while (socket == null) {
            try {
                //选择和服务器相同的端口8688
                socket = new Socket("localhost", 8688);
                mClientSocket = socket;
                mPrintWriter = new PrintWriter(new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())), true);
            } catch (IOException e) {
                SystemClock.sleep(1000);
            }
        }
        try {
            // 接收服务器端的消息
            BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
            while (!isFinishing()) {
                final String msg = br.readLine();
                if (msg != null) {
                    runOnUiThread(
                            new Runnable() {
                                @Override
                                public void run() {

                                    Log.d("TestSocket", "n" + "服务端:" + msg);

                                }
                            }
                    );
                }
            }
            mPrintWriter.close();
            br.close();
            socket.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @Override
    protected void onDestroy() {
        if (mClientSocket != null) {
            try {
                mClientSocket.shutdownInput();
                mClientSocket.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        super.onDestroy();
    }
}

打印日志如下

IPC 适用的场景及优缺点

在这里插入图片描述

Demo地址

 

 

以上是关于Android进程间通信方式的主要内容,如果未能解决你的问题,请参考以下文章

Android进程间的通信之Messenger

Android进程间的通信之Messenger

Android进程间通信方式

Android初级教程进程间的通信AIDL

Android:安卓学习笔记之进程间通信方式(IPC)的简单理解和使用

Android:安卓学习笔记之进程间通信方式(IPC)的简单理解和使用