What the hell! How is it possible that a singleton fails, setting a value in one place and not fetching it in another? It doesn’t make sense! Investigation along while, found that the two are not in a process, just suddenly realized…

What is a process

According to the operating system description: process generally refers to a unit of execution, on PC and mobile devices refers to a program or an application.

Why use multiple processes

As we all know, the memory allocated by the system for each process of APP is limited. If you want to get more memory allocation, you can use multiple processes to run some invisible services and relatively independent functions that occupy considerable memory in another process.

Directory Structure Preview

Release the directory structure after the final practice first, have a general impression, after one introduction.

How do I use multiple processes

Androidmanifest.xml register Activity, Service and other four components in the manifest file, specify the Android :process attribute to start multiple processes, such as:

<activity
    android:name=".Process1Activity"
    android:process=":process1" />
<activity
    android:name=".Process2Activity"
    android:process="com.wuxiaolong.androidprocesssample.process2" />
Copy the code

instructions

1, com. Wuxiaolong androidprocesssample, main process, the default application package name;

Android :process=”:process1″ + :process1; android:process=”:process1″ + :process1;

3, android: process = “com. Wuxiaolong. Androidprocesssample. Process2”, begin with a lowercase letter, belongs to the global process, other applications can be data sharing through ShareUID;

4. Process naming is the same as package naming.

Process defects

Create Application multiple times

D (” WXL “, “AndroidApplication onCreate”); And then start Process1Activity:

com.wuxiaolong.androidprocesssample D/wxl: AndroidApplication onCreate
com.wuxiaolong.androidprocesssample:process1 D/wxl: AndroidApplication onCreate
Copy the code

See that it was actually created twice, for the following reason: Android: Process pit, do you understand? In most cases, we will customize an Application class in the project to do some global initialization, because we need to distinguish it and let it be initialized in the main process.

@Override
public void onCreate(a) {
    super.onCreate();
    String processName = AndroidUtil.getProcessName();
    if (getPackageName().equals(processName)) {
        // Initialize the operation
        Log.d("wxl"."AndroidApplication onCreate="+ processName); }}Copy the code

AndroidUtil:

public static String getProcessName(a) {
    try {
        File file = new File("/proc/" + android.os.Process.myPid() + "/" + "cmdline");
        BufferedReader mBufferedReader = new BufferedReader(new FileReader(file));
        String processName = mBufferedReader.readLine().trim();
        mBufferedReader.close();
        return processName;
    } catch (Exception e) {
        e.printStackTrace();
        return null; }}Copy the code

Static member and singleton patterns fail

Create a class SingletonUtil:

public class SingletonUtil {
    private static SingletonUtil singletonUtil;
    private String userId = "0";

    public static SingletonUtil getInstance(a) {
        if (singletonUtil == null) {
            singletonUtil = new SingletonUtil();
        }
        return singletonUtil;
    }

    public String getUserId(a) {
        return userId;
    }

    public void setUserId(String userId) {
        this.userId = userId; }}Copy the code

Set in MainActivity:

SingletonUtil.getInstance().setUserId("007");
Copy the code

Process1Activity Value, print:

Log.d("wxl"."userId=" + SingletonUtil.getInstance().getUserId());
Copy the code

When userId=0 is printed, the singleton mode is invalid because the two processes do not share the same memory.

Interprocess communication

File sharing

Since the memory cannot be shared, is it possible to find a common place? Yes, you can save the data to be shared on the SD card. SingletonUtil implements Serializable serialization, stores the object to SD card, and then deserializes the object from SD card. The complete code is as follows:

SingletonUtil

public class SingletonUtil implements Serializable{
    public static String ROOT_FILE_DIR = Environment.getExternalStorageDirectory() + File.separator + "User" + File.separator;
    public static String USER_STATE_FILE_NAME_DIR = "UserState";
    private static SingletonUtil singletonUtil;
    private String userId = "0";

    public static SingletonUtil getInstance(a) {
        if (singletonUtil == null) {
            singletonUtil = new SingletonUtil();
        }
        return singletonUtil;
    }

    public String getUserId(a) {
        return userId;
    }

    public void setUserId(String userId) {
        this.userId = userId; }}Copy the code

Serialization and deserialization

public class AndroidUtil {
    public static boolean createOrExistsDir(final File file) {
        // True if a directory exists, false if a file exists, and successful creation if no file exists
        returnfile ! =null && (file.exists() ? file.isDirectory() : file.mkdirs());
    }

    /** * delete directory **@paramDirectory dir *@return {@codeTrue}: delete successfully <br>{@codeFalse}: Delete failed */
    public static boolean deleteDir(final File dir) {
        if (dir == null) return false;
        // The directory does not exist returns true
        if(! dir.exists())return true;
        // Not a directory returns false
        if(! dir.isDirectory())return false;
        // The file now exists and is a folder
        File[] files = dir.listFiles();
        if(files ! =null&& files.length ! =0) {
            for (File file : files) {
                if (file.isFile()) {
                    if(! file.delete())return false;
                } else if (file.isDirectory()) {
                    if(! deleteDir(file))return false; }}}return dir.delete();
    }

    /** * serialize, store the object to SD card **@paramObj stores object *@paramDestFileDir Target path of the SD card *@paramDestFileName Indicates the file name of the SD card */
    public static void writeObjectToSDCard(Object obj, String destFileDir, String destFileName) {

        createOrExistsDir(new File(destFileDir));
        deleteDir(new File(destFileDir + destFileName));
        FileOutputStream fileOutputStream = null;
        ObjectOutputStream objectOutputStream = null;
        try {
            fileOutputStream = new FileOutputStream(new File(destFileDir, destFileName));
            objectOutputStream = new ObjectOutputStream(fileOutputStream);
            objectOutputStream.writeObject(obj);

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                if(objectOutputStream ! =null) {
                    objectOutputStream.close();
                    objectOutputStream = null;
                }
                if(fileOutputStream ! =null) {
                    fileOutputStream.close();
                    fileOutputStream = null; }}catch(IOException e) { e.printStackTrace(); }}}/** * deserialize, fetch object ** from SD card@paramDestFileDir Target path of the SD card *@paramDestFileName Indicates the file name of the SD card */
    public static Object readObjectFromSDCard(String destFileDir, String destFileName) {
        FileInputStream fileInputStream = null;

        Object object = null;
        ObjectInputStream objectInputStream = null;

        try {
            fileInputStream = new FileInputStream(new File(destFileDir, destFileName));
            objectInputStream = new ObjectInputStream(fileInputStream);
            object = objectInputStream.readObject();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                if(objectInputStream ! =null) {
                    objectInputStream.close();
                    objectInputStream = null;
                }
                if(fileInputStream ! =null) {
                    fileInputStream.close();
                    fileInputStream = null; }}catch(IOException e) { e.printStackTrace(); }}returnobject; }}Copy the code

Required permissions:

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
Copy the code

MainActivity sequence writes

SingletonUtil singletonUtil = SingletonUtil.getInstance();
singletonUtil.setUserId("007");
AndroidUtil.writeObjectToSDCard(singletonUtil, SingletonUtil.ROOT_FILE_DIR, SingletonUtil.USER_STATE_FILE_NAME_DIR);
Copy the code

Process1Activity Deserializes the value

Object object = AndroidUtil.readObjectFromSDCard(SingletonUtil.ROOT_FILE_DIR, SingletonUtil.USER_STATE_FILE_NAME_DIR);
if(object ! =null) {
    SingletonUtil singletonUtil = (SingletonUtil) object;
    Log.d("wxl"."userId=" + singletonUtil.getUserId());// Print: userId=007
}
Copy the code

AIDL

AIDL, the Android interface definition language, defines the communication between the client and the server process. If the server has multiple threads, it is necessary to use AIDL. Otherwise, you can use Messenger.

Single application, multiple processes

The service side

Int, long, Boolean, float, double; String, CharSequence, List, Map; Parcelable; Specify in (client data object flows to server) and OUT (data object flows from server to client).

1, the Userbean. Java

public class UserBean implements Parcelable {
    private int userId;
    private String userName;

    public int getUserId(a) {
        return userId;
    }

    public void setUserId(int userId) {
        this.userId = userId;
    }

    public String getUserName(a) {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public UserBean(a) {}private UserBean(Parcel in) {
        userId = in.readInt();
        userName = in.readString();
    }

    / * * *@return0 or 1, 1 contains the file descriptor */
    @Override
    public int describeContents(a) {
        return 0;
    }

    /** * serialization **@paramDest Current object *@paramFlags 0 or 1,1 indicates that the current object needs to be returned and the resource */ cannot be released immediately
    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeInt(userId);
        dest.writeString(userName);
    }

    /** * deserialize */
    public static final Creator<UserBean> CREATOR = new Creator<UserBean>() {
        @Override
        public UserBean createFromParcel(Parcel in) {
            return new UserBean(in);
        }

        @Override
        public UserBean[] newArray(int size) {
            return newUserBean[size]; }}; }Copy the code

2, the UserBean. Aidl

Userbean. Java creates the corresponding userbean. aidl file under the same package, and calls and interacts with the AIDL.

// UserBean.aidl
package com.wuxiaolong.androidprocesssample;

parcelable UserBean;
Copy the code

3, IUserManager aidl

// IUserManager.aidl package com.wuxiaolong.androidprocesssample; / / Declare any non - default types here with the import statements / / manually import import com. Wuxiaolong. Androidprocesssample. The UserBean;  Interface IUserManager {// Basic data type: int, long, Boolean,float, double, String void hello(String aString); // Non-basic data type, pass object void getUser(inUserBean userBean); //inClient -> Server}Copy the code

4. Service classes

Create a new AIDLService that inherits the Service and implement the onBind() method to return a Stub class generated by your implementation and expose it to the client. Stub defines several auxiliary methods, most notably asInterface(), which receives an IBinder and returns an instance of the Stub interface.

public class AIDLService extends Service {

    private Binder binder = new IUserManager.Stub() {

        @Override
        public void getUser(UserBean userBean) throws RemoteException {
            Log.d("wxl", userBean.getUserId() + "," + userBean.getUserName() + " from AIDL Service");
        }

        @Override
        public void hello(String aString) throws RemoteException {
            Log.d("wxl", aString + " from AIDL Service"); }};@Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return binder;
    }

    @Override
    public void onCreate(a) {
        super.onCreate(); }}Copy the code

AndroidManifest registration:

<service
    android:name=".AIDLService"
    android:process=":aidlRemote" />
Copy the code

After the above creation, build Clean will automatically generate the Java class corresponding to AIDL for the client to call.

The client

1, the app/build. Gradle

The aiDL path needs to be specified:

android {
    / /...
    sourceSets {
        main {
            java.srcDirs = ['src/main/java'.'src/main/aidl']}}}Copy the code

2. Start the service and establish connections

public class MainActivity extends AppCompatActivity {

    private ServiceConnection aidlServiceConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            IUserManager remoteService = IUserManager.Stub.asInterface(service);
            UserBean userBean = new UserBean();
            userBean.setUserId(1);
            userBean.setUserName("WuXiaolong");
            try {
                remoteService.getUser(userBean);
                remoteService.hello("Hello");
            } catch(RemoteException e) { e.printStackTrace(); }}@Override
        public void onServiceDisconnected(ComponentName name) {}};@Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        Intent intent = new Intent(this, AIDLService.class);
        bindService(intent, aidlServiceConnection, Context.BIND_AUTO_CREATE);
    }

    @Override
    protected void onDestroy(a) {
        unbindService(aidlServiceConnection);
        super.onDestroy(); }}Copy the code

Print:

com.wuxiaolong.androidprocesssample:aidlRemote D/wxl: 1,WuXiaolong from AIDL Service
com.wuxiaolong.androidprocesssample:aidlRemote D/wxl: Hello from AIDL Service
Copy the code

Multiple applications, multiple processes

The server and the client can communicate with each other. Note:

1. Copy the aiDL file created by the server to the client project with the package;

2. The client starts the service implicitly. In Android 5.0, there are restrictions on the implicit start of service, which must be set by action and package.

AndroidManifest registration:

<service android:name=".AIDLService">
    <intent-filter>
        <action android:name="android.intent.action.AIDLService" />
    </intent-filter>
Copy the code

Start the service:

Intent intent = new Intent();
intent.setAction("android.intent.action.AIDLService");
intent.setPackage("com.wuxiaolong.aidlservice");
bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE);
Copy the code

Use the Messenger

Messenger can pass Message objects between different processes, and we can put the data we need in the Message object to enable communication between processes. The underlying implementation of Messenger is AIDL, which is encapsulated and does not need to deal with multi-threading. The implementation steps are also divided into server side and client side. The code is as follows:

The service side

MessengerService:

public class MessengerService extends Service {

    private final Messenger messenger = new Messenger(new MessengerHandler());


    private static class MessengerHandler extends Handler {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case MainActivity.MSG_FROM_CLIENT:
                    //2
                    Log.d("wxl"."msg=" + msg.getData().getString("msg"));

                    //4. The server replies the message to the client
                    Messenger serviceMessenger = msg.replyTo;
                    Message replyMessage = Message.obtain(null, MSG_FROM_SERVICE);
                    Bundle bundle = new Bundle();
                    bundle.putString("msg"."Hello from service.");
                    replyMessage.setData(bundle);
                    try {
                        serviceMessenger.send(replyMessage);
                    } catch (RemoteException e) {
                        e.printStackTrace();
                    }
                    break;
            }
            super.handleMessage(msg); }}@Nullable
    @Override
    public IBinder onBind(Intent intent) {
        returnmessenger.getBinder(); }}Copy the code

AndroidManafest. XML is registered:

<service
    android:name=".MessengerService"
    android:process=":messengerRemote" />
Copy the code

The client

MainActivity

public class MainActivity extends AppCompatActivity {
    public static final int MSG_FROM_CLIENT = 1000;
    public static final int MSG_FROM_SERVICE = 1001;
    private Messenger clientMessenger;
    private ServiceConnection messengerServiceConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            //1. Send messages to the server
            clientMessenger = new Messenger(service);
            Message message = Message.obtain(null, MSG_FROM_CLIENT);
            Bundle bundle = new Bundle();
            bundle.putString("msg"."Hello from client.");
            message.setData(bundle);
            //3, this sentence is used by the server to reply to the client
            message.replyTo = getReplyMessenger;
            try {
                clientMessenger.send(message);
            } catch(RemoteException e) { e.printStackTrace(); }}@Override
        public void onServiceDisconnected(ComponentName name) {}};private final Messenger getReplyMessenger = new Messenger(new MessengerHandler());

    private static class MessengerHandler extends Handler {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case MainActivity.MSG_FROM_SERVICE:
                    //5. The server replies the message to the client, and the client picks up the message
                    Log.d("wxl"."msg=" + msg.getData().getString("msg"));
                    break;
            }
            super.handleMessage(msg); }}@Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
 
        // Messenger to communicate
        Intent intent = new Intent(this, MessengerService.class);
        bindService(intent, messengerServiceConnection, Context.BIND_AUTO_CREATE);

    }

    @Override
    protected void onDestroy(a) {
        unbindService(messengerServiceConnection);
        super.onDestroy(); }}Copy the code

Print information:

com.wuxiaolong.androidprocesssample:remote D/wxl: msg=Hello from client.
com.wuxiaolong.androidprocesssample D/wxl: msg=Hello from service.
Copy the code

The last

“Android development art Exploration” book about the Android process communication this piece, as well as ContentProvider, Socket way, due to the space limit, here is not introduced, interested can view. If you need the source code of Sample this time, you can reply “AndroidProcessSample” on my public account “Wu Xiaolong”.

reference

Exploring the Art of Android Development

Multiple processes in Android, something you should know

Android uses AIDL for Inter-process Communication (IPC)