Several ways of Android process communication

foreword

When doing application development, multi-process issues in Android are actually rare, but they are often asked during interviews. Here are some basic summaries of commonly used communication methods.

Bundle

The four major components of Android can communicate through Intent. The Bundle object in the Intent is used to save various communication data. However, if the data of a custom type is passed, it needs to support serialization, which can be Serializable or Parcelable.

// sender进程
Intent intent = new Intent("com.example.bundle.receiver");
intent.putExtra("message", "Bundle Hello world");
startActivity(intent);

// receiver进程
public class BundleReceiverActivity extends AppCompatActivity {
    private TextView textView;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_bundle_receiver);
        textView = findViewById(R.id.text);
        textView.setText(getIntent().getStringExtra("message"));
    }
}

<activity android:name=".BundleReceiverActivity">
    <intent-filter>
        <action android:name="com.example.bundle.receiver" />
        <category android:name="android.intent.category.DEFAULT" />
    </intent-filter>
</activity>

Of course, the activity of the sending process and the activity of the receiving process communicate through implicit jumps. The communication between activities is the most used in development. In fact, this method is very common, and other communication methods of components will not be demonstrated.

File

As a function provided by the operating system, files can be accessed by multiple processes at the same time. Therefore, in order to ensure data consistency, it is necessary to perform lock synchronization when multiple processes access. The FileLock file lock object in Java can ensure the synchronization effect of multi-process access, but you need to use nio's FileChannel to obtain FileLock.

// sender进程
File file = new File(Environment.getExternalStorageDirectory(), "readMe.txt");
FileChannel fileChannel = null;
FileLock fileLock = null;
try {
    if (!file.exists()) {
        file.createNewFile();
    }
    RandomAccessFile randomAccessFile = new RandomAccessFile(file.getAbsolutePath(), "rw");
    fileChannel = randomAccessFile.getChannel();
    fileLock = fileChannel.lock();
    ByteBuffer byteBuffer = ByteBuffer.wrap(("File Hello World" + (count += 111)).getBytes());
    fileChannel.write(byteBuffer);
    Toast.makeText(this, "写入" + new String(byteBuffer.array()), Toast.LENGTH_SHORT).show();
} catch (IOException e) {
    e.printStackTrace();
} finally {
    IOUtils.close(fileChannel);
    if (fileLock != null) {
        try {
            fileLock.release();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

The sending data process monopolizes the current file and writes data through FileLock, while the receiving process needs to acquire the exclusive lock before it can read the file content.

File file = new File(Environment.getExternalStorageDirectory(), "readMe.txt");
FileChannel fileChannel = null;
FileLock fileLock = null;
if (file.exists()) {
    try {
        RandomAccessFile randomAccessFile = new RandomAccessFile(file.getAbsolutePath(), "rw");
        fileChannel = randomAccessFile.getChannel();
        fileLock = fileChannel.lock();
        ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
        fileChannel.read(byteBuffer);
        Toast.makeText(this, new String(byteBuffer.array()), Toast.LENGTH_SHORT).show();
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        IOUtils.close(fileChannel);
        if (fileLock != null) {
            try {
                fileLock.release();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

AIDL

AIDL is Android Interface Definition Language (Android Interface Definition Language), which is a process key communication mechanism provided by Android itself. You can use bindService to obtain the Binder object provided in another process, and realize inter-process communication through this remote Binder.
Add aidl file in Android Studio. The syntax of aidl is very similar to java but not exactly the same. The following code defines a sending interface.

// ICommunicate.aidl
package com.example.process;

interface ICommunicate {
    void send(String str);
}

Android Studio will automatically generate Java code for the aidl file and return a custom Stub object in the Service of the receiver process.

public class AIDLService extends Service {
    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return new Hello();
    }

    private class Hello extends ICommunicate.Stub {
        @Override
        public void send(final String str) throws RemoteException {
            new Handler(Looper.getMainLooper()).post(new Runnable() {
                @Override
                public void run() {
                    Toast.makeText(getApplicationContext(), str, Toast.LENGTH_SHORT).show();
                }
            });
        }
    }
}

In order to be able to send messages to the receiver process, the sender process calls the bindService method to obtain this custom Binder object.

Intent intent = new Intent();
intent.setAction("com.example.aidl.receiver");
intent.setPackage("com.example.receiver");
bindService(intent, new AIDLCallback(), BIND_AUTO_CREATE);

private class AIDLCallback implements ServiceConnection {

    @Override
    public void onServiceConnected(ComponentName name, IBinder service) {
        communicate = ICommunicate.Stub.asInterface(service);
        sendAIDLMessage();
    }

    @Override
    public void onServiceDisconnected(ComponentName name) {

    }
}

private void sendAIDLMessage() {
    try {
        communicate.send( "Hello world from AIDL" + (count += 111));
    } catch (RemoteException e) {
        e.printStackTrace();
    }
}

Messenger

Messenger is a simple encapsulation of AIDL functions provided by the Android system. You can also use bindService to obtain the Binder object of the remote process in Messenger, so that the two can communicate between processes through the Binder in the Messenger. Return the Binder inside the Messenger in the receiver's Service.

// 接收者所在的服务
public class ReceiverService extends Service {
    private class MessageHandler extends Handler {
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            switch (msg.what) {
                case 1:
                    Bundle bundle = msg.getData();
                    Toast.makeText(getApplicationContext(), bundle.getString("message"), Toast.LENGTH_SHORT).show();
                    break;
                default:
                    break;
            }
        }
    }

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        Messenger messenger = new Messenger(messageHandler);
        return messenger.getBinder();
    }

    private MessageHandler messageHandler = new MessageHandler();
}

The sender process obtains this Binder object through bindService.

Intent intent = new Intent();
intent.setAction("com.example.messenger.receiver");
intent.setPackage("com.example.receiver");
bindService(intent, new ServiceCallback(), BIND_AUTO_CREATE);

private class ServiceCallback implements ServiceConnection {

    @Override
    public void onServiceConnected(ComponentName name, IBinder service) {
        sender = new Messenger(service);
        sendMessage();
    }

    @Override
    public void onServiceDisconnected(ComponentName name) {
        sender = null;
    }
}

private void sendMessage() {
    Message message = Message.obtain();
    message.what = 1;
    String str = "Hello world from Messenger" + (count += 111);
    Bundle bundle = new Bundle();
    bundle.putString("message", str);
    message.setData(bundle);
    try {
        sender.send(message);
    } catch (RemoteException e) {
        e.printStackTrace();
    }
}

Socket

Socket is a network connection object provided by the operating system. It not only supports local inter-process communication, but also supports process communication of different network host keys. However, this relatively common communication method makes it slower than other communication methods. First, a ServerSocket object needs to be created in the receiver process.

new Thread(new Runnable() {
    @Override
    public void run() {
        ServerSocket serverSocket = null;
        Socket socket = null;
        try {
            serverSocket = new ServerSocket(8888);
            socket = serverSocket.accept();
            BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
            final StringBuilder stringBuilder = new StringBuilder();
            String str;
            while ((str = reader.readLine()) != null) {
                stringBuilder.append(str);
            }

            handler.post(new Runnable() {
                @Override
                public void run() {
                    Toast.makeText(context, stringBuilder.toString(), Toast.LENGTH_SHORT).show();
                }
            });
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (serverSocket != null) {
                try {
                    serverSocket.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }

            if (socket != null) {
                try {
                    socket.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}).start();

The connection of the sender process to the ServerSocket will cause the accept method to return and generate a server-side Socket, so that the client and server-side Sockets can communicate with each other.

new Thread(new Runnable() {
    @Override
    public void run() {
        Socket sock = null;
        try {
            sock = new Socket("127.0.0.1", 8888);
            PrintWriter printWriter = new PrintWriter(sock.getOutputStream());
            printWriter.write("Socket Hello World" + (count += 111));
            printWriter.flush();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (sock != null) {
                try {
                    sock.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

        socket.post(new Runnable() {
            @Override
            public void run() {
                socket.setEnabled(true);
            }
        });
    }
}).start();

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=324480936&siteId=291194637