When I went to interview with Tencent, I never expected that the interviewer would use Android multi-process to question me and save my child...

Preface

Today I saw a Tencent interview question about Android multi-process, so let’s talk about it today.

How to create multiple processes in Android

1) The first type, as everyone knows, is to AndroidManifestassign android:processattributes to the four major components .

<activity android:name="com.example.uithread.UIActivity" 
      android:process=":test"/>

   <activity android:name="com.example.uithread.UIActivity2"
      android:process="com.example.test"/>

As you can see, android:processthere are two ways of expression:

  • :test. ":" means to add the current package name in front of the current process name, if the current package name is com.example.jimu. Then the process name should be com.example.jimu:test. This kind of process starting with a colon belongs to the private process of the current application, and other application components cannot run in the same process with him.
  • com.example.test. The second way of expression is a complete naming method, which is the process name of the new process, which belongs to the global process, and other applications can run into the same process by way of shareUID.

To put it simply shareUID: Normally, each app in Android is a separate process, which corresponds to a unique linux user ID, so the files or components of the application can be kept visible only to the application. But there is also a way to share files with different apks, which is through shareUID, which allows different apks to use the same user ID. Post usage:

//app1
<manifest package="com.test.app1"
android:sharedUserId="com.test.jimu"
>

//app2
<manifest package="com.test.app2"
android:sharedUserId="com.test.jimu"
>

//app1中获取app2的上下文:
Context mContext=this.createPackageContext("com.test.app2", Context.CONTEXT_IGNORE_SECURITY);

2) The second method of creating a process is nativeto fork a process in the layer through JNI .

This is more complicated. I searched for some information on the Internet and found a fork of an ordinary process:

//主要代码
long add(long x,long y) {
   //fpid表示fork函数返回的值 
    pid_t fpid; 
    int count=0; 
    fpid=fork();  
}

//结果:
USER       PID   PPID   VSZ     RSS  STAT  NAME                 
root       152  1              S    zygote
u0_a66   17247  152   297120  44096  S  com.example.jni
u0_a66   17520  17247  0    0    Z  com.example.jni

The final result is that a process can be created, but it is not running, the memory occupied is 0, and it is in the state of a zombie program.

But it forkcomes out through ordinary processes . We know that all processes in Android are fork out directly through the zygote process (fork can be understood as a copy of the current process hatched). So I don’t know if it can be successful to operate the zygote process directly. Anyone who knows it can tell you about it in the WeChat discussion group.

By the way, some friends may ask, why all processes must use zygoteprocess fork?

  • This is because forkthe behavior is to copy the entire user's spatial data and all system objects, and only copy the current thread to the new process. In other words, the parent process 其他线程disappears in the child process. In order to prevent various problems (such as deadlocks and inconsistent states), only the zygoteprocess, this single-threaded process, will fork the new process.
  • And zygotesome initialization work will be done in the process, such as starting the virtual machine and loading system resources. In this way, the child process can be directly shared when fork, and efficiency is improved. This is also the advantage of this mechanism.

Is there any problem with using multiple processes for an application?

As mentioned above, the method of creating a process is very simple. Just write an android:process attribute. Isn’t it that simple to use? Obviously not, multiple processes in an application will cause various problems, mainly as follows:

  • The static member and singleton patterns are completely invalid . Because each process is assigned to an independent virtual machine, and different virtual machines have different address spaces in memory allocation, so accessing objects of the same class in different processes, that is, different virtual machines, will generate more Copies.
  • The thread synchronization mechanism is completely invalid . As above, different memory cannot guarantee thread synchronization, because the objects locked by threads are different.
  • SharedPreferences is no longer reliable . A previous article about SharedPreferences said this, SharedPreferences does not support multi-process.
  • Application will be created multiple times . Multi-process actually corresponds to multiple applications, so the process of creating a new process actually starts a new application, and naturally a new Application is created. Application and virtual machine correspond to the components in a process one-to-one.

IPC method in Android

Since there are many problems with multiple processes, there is naturally a solution. Although memory cannot be shared, data interaction can be carried out, that is, multi-process communication can be carried out, referred to as IPC.

The following will specifically talk about the eight IPC methods in Android:

1. The four major components of Bundle Android support the use of intents Bundleto transfer data , so the direct inter-process communication of the four major components can use Bundle. But the Bundle has a size limit. Note that bundlethe data transfer limit is 1M. If your data exceeds this size, you need to use other communication methods.

2. File sharing is the way that multiple processes 读写一个文件exchange data and complete inter-process communication. But this method has a big drawback that multi-threaded reading and writing are prone to problems, that is 并发问题, if concurrent reading or concurrent writing occurs, they are prone to problems, so this method is suitable for direct communication between processes that do not require high data synchronization.

Someone may be surprised here, is SharedPreferencen't it just reading and writing xml files? Why doesn't it support inter-process communication?

  • This is because the system has SharedPreferencea read-write cache strategy, that is, there is a cache of SharedPreference files in memory, which involves memory, which is definitely not so reliable in multi-process.

3. Messenger Messengeris used to transfer the Message object, and the data we want to transfer can be placed in the Message . It is a lightweight IPC solution, and the underlying implementation is AIDL.

Look at the usage, client and server communication:

//客户端
public class MyActivity extends Activity {

    private Messenger mService;
    private Messenger mGetReplyMessager=new Messenger(new MessengerHandler());

    private static class MessengerHandler extends Handler{
        @Override
        public void handleMessage(@NonNull Message msg) {
            switch (msg.what){
                case 1:
                    //收到消息
                    break;
            }
            super.handleMessage(msg);
        }
    }

    private ServiceConnection mConnection=new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            mService = new Messenger(service);
            Message msg = Message.obtain(null,0);
            Bundle bundle = new Bundle();
            bundle.putString("test", "message1");
            msg.setData(bundle);
            msg.replyTo = mGetReplyMessager; //设置接受消息者
            try {
                mService.send(msg);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {

        }
    };

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

        Intent i=new Intent(this,ServerService.class);
        bindService(i,mConnection, Context.BIND_AUTO_CREATE);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        unbindService(mConnection);
    }
}

//服务端    
public class ServerService extends Service {

    Messenger messenger = new Messenger(new MessageHandler());

    public ServerService() {
    }

    public IBinder onBind(Intent intent) {
        return messenger.getBinder();
    }

    private class MessageHandler extends Handler {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what){
                case 0:
                    //收到消息并发送消息
                    Messenger replyTo = msg.replyTo;
                    Message replyMsg = Message.obtain(null,1);
                    Bundle bundle = new Bundle();
                    bundle.putString("test","111");
                    replyMsg.setData(bundle);
                    try {
                        replyTo.send(replyMsg);
                    } catch (RemoteException e) {
                        e.printStackTrace();
                    }
                    break;
            }
            super.handleMessage(msg);
        }
    }

}

4. AIDL

Although Messenger can send and receive messages, it cannot process a large number of messages at the same time, and it cannot cross-process methods. But AIDL can do it, here is a brief description of AIDLthe use process:

The server first establishes a Service to listen to the client's connection request, then creates an AIDL file, declares the interface exposed to the client in this AIDL file, and finally implements this AIDLinterface in the Service .

The client needs to bind the service of this server, and then convert the Binderobject returned by the server into the attributes of the AIDL interface, and then it can call the methods in the AIDL.

5. ContentProvider

Everyone should be familiar with this, one of the four major components, specifically for data sharing between different applications. Its underlying implementation is achieved through Binder. There are two main steps:

1. Declare Provider

<provider 
    android:authorities="com.test.lz"  //provider的唯一标识 
    android:name=".BookProdiver"
    android:permission="com.test.permission"
    android:process=":provider"/>
  • android:authorities, a unique identifier, generally use the package name. When accessing data, the outside world is accessed through uri, which consists of four parts
content://com.test.lz/ table/ 100
|            |          |       |
固定字段   Authority   数据表名   数据ID
  • android:permission, permission attributes, and more readPermission,writePermission. If the provider declares permission-related attributes, other applications must also declare the corresponding permissions to perform read and write operations. such as:
//应用1
<permission android:name="me.test.permission" android:protectionLevel="normal"/>

 <provider 
    android:authorities="com.test.lz"
    android:name=".BookProdiver"
    android:permission="com.test.permission"
    android:process=":provider"/>  

//应用2
<uses-permission android:name="me.test.permission"/>

2. Then external applications can access the data through the add, delete, check and modify method of getContentResolver().

6. Socket

Sockets are used a lot in network communication, such as TCP and UDP. Regarding Socket communication, borrow a picture on the network to illustrate:

Then simply paste the key code:

//申请权限
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />

//服务端
ServerSocket serverSocket = new ServerSocket(8688);
while(isActive) { 
        try {
         //不断获取客户端连接
              final Socket client = serverSocket.accept();
              new Thread(){
                        @Override
                        public void run() {
                            try {
                               //处理消息
                            } catch (IOException e) {
                                e.printStackTrace();
                            }
                        }
                    }.start();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }

//客户端
Socket socket = null;
    while(socket==null){  
        //为空代表链接失败,重连
        try {
            socket = new Socket("localhost",8688);
            out = new PrintWriter(socket.getOutputStream(),true);
            handler.sendEmptyMessage(1);
            final Socket finalSocket = socket;
            new Thread(){
                @Override
                public void run() {
                    try {
                        reader = new BufferedReader(new InputStreamReader(finalSocket.getInputStream()));
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                    while(!MainActivity.this.isFinishing()){ 
                        //循环读取消息
                        try {
                            String msg = reader.readLine();
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }.start();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

7. Binder connection pool

About the introduction of Binder, the previous article has already said. Here mainly talk about a Binder's actual use of technology- Binder连接池. Since every AIDL request has to open a service to prevent too many services from being created, the Binder连接池technology is cited . The main function of the Binder connection pool is to Binderuniformly forward the requests of each business module to the remote Service for execution, thereby avoiding the Serviceprocess of repeated creation . Post the working principle of Binder connection pool:

  • Each business module creates its own AIDL interface and implements this interface, and then provides its own unique identifier and its corresponding Binder object to the server.
  • For the server side, only one Service is needed. The server side provides a queryBinder interface, which can return the corresponding Binder object to them according to the characteristics of the business module. After different business modules get the required Binder object You can make remote method calls.

How to use it? Or simply post the key source code

public class BinderPoolImpl extends IBinderPool.Stub {
    private static final String TAG = "Service BinderPoolImpl";

    public BinderPoolImpl() {
        super();
    }

    @Override
    public IBinder queryBinder(int binderCode) throws RemoteException {
        Log.e(TAG, "binderCode = " + binderCode);
        IBinder binder = null;
        switch (binderCode) {
            case 0:
                binder = new SecurityCenterImpl();
                break;
            case 1:
                binder = new ComputeImpl();
                break;
            default:
                break;
        }
        return binder;
    }
}

public class BinderPool {
    //...
    private IBinderPool mBinderPool;

    private synchronized void connectBinderPoolService() {
        Intent service = new Intent();
        service.setComponent(new ComponentName("com.test.lz", "com.test.lz.BinderPoolService"));
        mContext.bindService(service, mBinderPoolConnection, Context.BIND_AUTO_CREATE);
    }    

    public IBinder queryBinder(int binderCode) {
        IBinder binder = null;
        try {
            if (mBinderPool != null) {
                binder = mBinderPool.queryBinder(binderCode);
            }
        } catch (RemoteException e) {
            e.printStackTrace();
        }
        return binder;
    }

    private ServiceConnection mBinderPoolConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            mBinderPool = IBinderPool.Stub.asInterface(service);
            try {
                mBinderPool.asBinder().linkToDeath(mBinderPoolDeathRecipient, 0);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {

        }
    };

    private IBinder.DeathRecipient mBinderPoolDeathRecipient = new IBinder.DeathRecipient() {
        @Override
        public void binderDied() {
            Log.e(TAG, "binderDied");
            mBinderPool.asBinder().unlinkToDeath(mBinderPoolDeathRecipient, 0);
            mBinderPool = null;
            connectBinderPoolService();
        }
    };

}

8. BroadcastReceiver

Broadcasting, needless to say more~ For example, we can monitor system startup broadcasts, network change broadcasts, etc., which all reflect the role of inter-process communication.

to sum up

Prepare for the interview before the interview!

Next, I will share a review route for the interview. If you are also preparing for an interview but don't know how to review efficiently, you can refer to my review route. If you have any questions, please feel free to communicate with each other. Come on!

Here is a direction for everyone to learn systematically:

1. Watch the video for systematic learning

The experience of Crud in the past few years has made me realize that I am really a fighter in the rookie. It is also because of Crud that my technology is relatively fragmented, and it is not deep enough to be systematic, so it is necessary to study again. What I lack is the system knowledge, the poor structural framework and the thinking, so learning through videos is better and more comprehensive. Regarding video learning, individuals can recommend to study at station B. There are many learning videos on station B. The only drawback is that they are free and easily outdated.

In addition, I have collected several sets of videos myself, and I can share them with you if I need them.

2. To systematically sort out knowledge and improve reserves

There are so many knowledge points in client development, and there are still so little things in the interview. Therefore, there are no other tricks for the interview, just look at how well you prepare for these knowledge points. So, when you go out for an interview, it is good to see which stage you have reached in your review.

System learning direction:

  • Essential skills for architects to build foundations: in-depth Java generics + annotations in simple language + concurrent programming + data transmission and serialization + Java virtual machine principles + reflection and class loading + dynamic proxy + efficient IO

  • Android advanced UI and FrameWork source code: advanced UI promotion + Framework kernel analysis + Android component kernel + data persistence

  • 360° overall performance tuning: design ideas and code quality optimization + program performance optimization + development efficiency optimization

  • Interpretation of open source framework design ideas: hot repair design + plug-in framework interpretation + component framework design + image loading framework + network access framework design + RXJava responsive programming framework design + IOC architecture design + Android architecture component Jetpack

  • NDK module development: NDK basic knowledge system + underlying image processing + audio and video development

  • WeChat Mini Program: Mini Program Introduction + UI Development + API Operation + WeChat Docking

  • Hybrid development and Flutter: Html5 project combat + Flutter advanced

After the knowledge is sorted out, it is necessary to check and fill in the vacancies. Therefore, for these knowledge points, I have prepared a lot of e-books and notes on hand. These notes provide a perfect summary of each knowledge point.

3. Read the source code, look at the actual combat notes, and learn the ideas of God

"Programming language is the way the programmer expresses, and the architecture is the programmer's cognition of the world". Therefore, if programmers want to quickly understand and learn the architecture, reading the source code is essential. Reading the source code is to solve problems + understand things, and more importantly: to see the ideas behind the source code; programmers say: read ten thousand lines of source code, and practice ten thousand kinds of practices.

Mainly contains WeChat MMKV source code, AsyncTask source code, Volley source code, Retrofit source code, OkHttp source code, etc.

4. On the eve of the interview, sprint through questions

Within a week before the interview, you can start sprinting. Please keep in mind that when brushing the questions, the technology is the first priority, and the algorithm is basic, such as sorting, etc., and the intellectual questions are generally not asked unless they are school recruits.

Regarding the interview questions, I personally prepared a set of systematic interview questions to help you learn from one another:

There are also a series of Android learning resources that took more than a year to organize: Android源码解析、Android第三方库源码笔记、Android进阶架构师七大专题学习、历年BAT面试题解析包、Android大佬学习笔记wait.

The above content is free to share with everyone, friends who need the full version, click here to see all the content .

Guess you like

Origin blog.csdn.net/weixin_44339238/article/details/114952251