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)