Introduction to Android development: basic knowledge, programming models, development processes, components and system features, etc.

Author: Zen and the Art of Computer Programming

1 Introduction

Android is an open source mobile device operating system, led by Google. It is a Linux-based mobile operating system, originally called Harmony OS. The main versions currently on the market include Nexus, Pixel, Samsung Galaxy series, HTC One series, MIUI, etc. It has been widely used in various terminal devices such as smartphones, tablets, and routers. In 2017, Google launched the AOSP (Android Open Source Project) project based on Android source code. The Android system source code became an open community for developers to modify and customize.
  This article will take the Android Nougat version as the research object and conduct an in-depth analysis of the basic knowledge, programming model, development process, components and system characteristics of the Android system.

2. Explanation of basic concepts and terms

This section will briefly introduce some keywords or nouns to facilitate readers' understanding.

  1. SDK (Software Development Kit): Software Development Kit (Software Development Kit), a development environment and tools provided by hardware manufacturers, software developers and licensees to developers for developing, testing, and publishing application software. The main functions provided are: compilation, debugging, running, analysis, document generation, etc.
      2. API (Application Programming Interface): Application Programming Interface (API) is a way to call an application program to perform specific functions through computer programming. APIs are generally divided into three categories: system-level APIs, framework-level APIs, and library-level APIs. Usually system-level APIs provide system kernel-level functions, such as operating systems, databases, networks, etc.; framework-level APIs provide developers with application-level development interfaces, such as Android Support Package or Google Play Services Library, which encapsulate each product respectively. Development interface; library-level API provides developers with the implementation of basic functions, such as graphics rendering, image processing, encoding and decoding, network connection, data caching, etc., which can all be implemented through the API.
      3. ART (Android Run-time for Transitions): Android Runtime (ART) is the Android Runtime Virtual Machine (VMO). It is Google’s optimized version of Dalvik VM (the virtual machine before ART), with the purpose of speeding up system startup. Speed ​​and reduced memory footprint. While ART improves performance, it also reduces compatibility, because ART cannot support all Dalvik instruction sets and can only recognize and translate some of the known Dalvik instruction sets.
      4. Dalvik VM: Dalvik virtual machine was originally a replacement for the Android virtual machine in Android 1.5. It contains a Java bytecode interpreter and executes bytecode in memory. Its advantage is fast startup, but its disadvantage is that it takes up memory. Too high. Dalvik VM has been gradually replaced by ART.
      5. JVM (Java Virtual Machine): Java virtual machine is a virtual machine that can run any Java program on a certain machine. The JVM compiles all Java code into native code (machine language) and then runs it.
      6. Native C/C++: Essentially, it calls the underlying functions provided by the operating system. Since the operating system itself is written in C, it can run on a variety of different platforms.
     
     # 3. Explanation of core algorithm principles, specific operating steps and mathematical formulas
      This section will explain the important functions and implementation methods of the Android system at each level from three levels: system level, framework level and library level.
      System level: System-level API covers interfaces of system kernel, file system, database, network communication, multimedia, camera, GPS and other modules. Each mobile phone model corresponds to an SDK version. The interfaces between different mobile phone models may be different, but they are generally the same. As mentioned before, the system-level API provides developers with a system kernel-level development interface. Therefore, developers need to choose the appropriate system-level API according to the specific situation to achieve their own needs.
     
      Framework level: Framework-level APIs, such as Android Support Package and Google Play Services Library, provide application-level development interfaces so that developers do not have to reinvent the wheel. Among them, Android Support Package provides compatible implementations of some system-level APIs, such as SharedPreferences, Content Provider, etc. Google Play Services encapsulates services of different platforms such as advertising, maps, etc., providing developers with a unified development interface.
     
      Library level: Library-level API, including basic function implementation, such as graphics rendering, image processing, encoding and decoding, network connection, data caching, etc., which can all be implemented through the API. In addition, there are some third-party libraries that provide more functions, such as the image loading framework Picasso, the layout manager RecyclerView, etc.
      The specific operation steps and mathematical formulas are introduced below.
      1. Bitmap: Bitmap (Bitmap) is the basic knowledge related to images. Bitmap is a two-dimensional array, each element represents a pixel value. Each picture is actually composed of several "pixel blocks" arranged in a certain order. These pixel blocks are arranged in a two-dimensional array in order. Each row represents a pair of pixel data, each column represents a column of pixel data, and a picture It consists of these two-dimensional arrays. When the program draws an image, it will first read the bitmap information of the image, and then use the bitmap information to draw the image. When using hardware acceleration to display an image, the bitmap of the image will still be read into the memory first, but this time it is not the CPU used for display, but the GPU for processing. The GPU will compress the image and reduce the display. The size of the area is then sent to the screen for display. Here's how to get Bitmap:
    // 获取Activity的Window
    Window window = activity.getWindow();
    // 从window中获取DecorView
    View decorView = window.getDecorView();
    // 获取DecorView中的findViewById
    View view = decorView.findViewById(R.id.your_imageview);
    // 将ImageView转换为Bitmap
    Bitmap bitmap = ((BitmapDrawable)(view.getDrawable())).getBitmap();
    
    2. OpenGL ES: OpenGL ES is a GLES extension of OpenGL and a portable, cross-platform system-level graphics interface based on the OpenGL specification. The goal of the OpenGL ES standard is to allow developers to use hardware acceleration directly without using drivers. That is to say, when a hardware device (such as a graphics card) supports OpenGL ES, this specification can be implemented and directly used in graphics applications. The ability of the hardware device to be used in the application. In the Android system, it is very simple to use OpenGL ES to draw 2D and 3D graphics. Just convert the graphics to be displayed into OpenGL ES drawable data structures, such as vertex coordinates, colors, texture coordinates, etc., and pass them to OpenGL ES to complete the drawing. Below shows a simple example of drawing a circle:
   private static final int POSITION_COMPONENT_COUNT = 2;
   private static final int COLOR_COMPONENT_COUNT = 3;
   
   private float[] vertexData = {
    
    
       0.0f,  0.5f,    1.0f, 0.0f, 0.0f,
      -0.5f, -0.5f,    0.0f, 1.0f, 0.0f,
       0.5f, -0.5f,    0.0f, 0.0f, 1.0f
   };
   
   @Override
   protected void onDraw(Canvas canvas) {
    
    
       super.onDraw(canvas);
       ByteBuffer byteBuffer = ByteBuffer.allocateDirect(vertexData.length * 4);
       byteBuffer.order(ByteOrder.nativeOrder());
       FloatBuffer vertexBuffer = byteBuffer.asFloatBuffer();
       vertexBuffer.put(vertexData);
       vertexBuffer.position(0);
   
       GL10 gl = getGL();
       gl.glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
       gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
   
       gl.glVertexPointer(POSITION_COMPONENT_COUNT, GL10.GL_FLOAT, 0, vertexBuffer);
       gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
   
       gl.glColorPointer(COLOR_COMPONENT_COUNT, GL10.GL_FLOAT, 0, vertexBuffer);
       gl.glEnableClientState(GL10.GL_COLOR_ARRAY);
   
       gl.glDrawArrays(GL10.GL_TRIANGLES, 0, vertexData.length / POSITION_COMPONENT_COUNT);
        gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);
       gl.glDisableClientState(GL10.GL_COLOR_ARRAY);
   }
    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
    
    
       super.surfaceChanged(holder, format, width, height);
       mWidth = width;
       mHeight = height;
       Log.d("TAG", "width=" + width + ",height=" + height);
   }
  1. Handler: message mechanism (Handler). In the Android system, Handler is a message queue used to process messages, events and tasks. The role of Handler is largely to decouple complex business logic from the UI thread. Messages are stored through MessageQueue (message queue), and Handler consumes messages. When a message occurs, a message is first stored in the message queue through the Handler's sendMessage() method, and then the message is processed through the Handler's handleMessage() method. After the message is stored in the queue, it will be executed by the message queue dispatch (dispatchMessage() method). Here's a simple usage example:
 // 创建Handler
 Handler handler = new Handler(){
    
    
      @Override
     public void handleMessage(Message msg) {
    
    
         switch (msg.what){
    
    
             case MSG_WHAT:
                 break;
              default:
                 super.handleMessage(msg);
                 break;
         }
     }
 };
 
 // 发送消息
 Message message = new Message();
 message.what = MSG_WHAT;
 handler.sendMessage(message);
  1. Multi-process: Multi-process mechanism (MultiProcess). The multi-process mechanism provided by the Android system is mainly used to solve the problem of resource competition when multiple applications share the same data. The Android system places different applications in different processes, avoiding resource competition problems caused by data sharing between multiple applications and improving system stability. Each process has its own virtual machine and independent stack space, without interfering with each other, so that it can be isolated from each other and avoid the problem of resources affecting each other. In addition, the Android system provides a more flexible multi-process communication method through the Binder IPC (inter-process communication) mechanism. The following introduces the process of creating multiple processes and some precautions:
/**
* 创建新进程
*/
public class MyService extends Service {
    
    
    @Nullable
   @Override
   public IBinder onBind(Intent intent) {
    
    
       return null;
   }
    @Override
   public void onCreate() {
    
    
       startOtherProcess();
   }
    /**
    * 启动其他进程
    */
   private void startOtherProcess() {
    
    
       Intent intent = new Intent(this, OtherProcessService.class);
       String processName = "other_process";
       ProcessBuilder builder = new ProcessBuilder();
       builder.command("/system/bin/sh")
              .redirectErrorStream(true)
              .redirectInput(Redirect.PIPE)
              .redirectOutput(Redirect.PIPE)
              .directory(".")
              .argument("-c")
              .argument("exec app_process "
                       + "/data/app/" + getPackageName() + "-1/"
                       + "lib/" + processName + "/"
                       + " --nice-name=" + processName).redirectError();
        try {
    
    
           Log.i("MyService", "start other process");
           Process process = builder.start();
            OutputStream outputStream = process.getOutputStream();
           BufferedReader reader = new BufferedReader(new InputStreamReader(
                   process.getInputStream()));
           String line = "";
            while (!isInterrupted()) {
    
    
               if (line!= null &&!"".equals(line)) {
    
    
                   Log.i("MyService", line);
               }
                line = reader.readLine();
           }
        } catch (IOException e) {
    
    
           e.printStackTrace();
       } finally {
    
    
           Log.i("MyService", "end other process");
       }
   }
}
/**
* 服务端实现进程通信
*/
public class OtherProcessService extends Service {
    
    
   
   private static final String TAG = "OtherProcessService";
   
   @Nullable
   @Override
   public IBinder onBind(Intent intent) {
    
    
       return binder;
   }
    private IBinder.DeathRecipient deathRecipient = new IBinder.DeathRecipient() {
    
    
       @Override
       public void binderDied() {
    
    
           Log.e(TAG, "binder died");
       }
   };
    private Binder binder = new Binder();
    @Override
   public void onCreate() {
    
    
       registerBinder();
       startForegroundService();
   }
    private void registerBinder() {
    
    
       try {
    
    
           LocalBroadcastManager manager = LocalBroadcastManager.getInstance(this);
           IntentFilter filter = new IntentFilter(ACTION_BINDER_DIED);
           manager.registerReceiver(broadcastReceiver, filter);
       } catch (Exception e) {
    
    
           e.printStackTrace();
       }
   }
    BroadcastReceiver broadcastReceiver = new BroadcastReceiver() {
    
    
       @Override
       public void onReceive(Context context, Intent intent) {
    
    
           Log.i(TAG, "receive action:" + intent.getAction());
       }
   };
    private void startForegroundService() {
    
    
       Intent service = new Intent(this, ForegroundService.class);
       bindService(service, connection, Context.BIND_AUTO_CREATE);
   }
    Connection connection = new Connection() {
    
    
       @Override
       public void onServiceConnected(ComponentName name, IBinder service) {
    
    
           Log.i(TAG, "connected to foreground service");
           try {
    
    
               service.linkToDeath(deathRecipient, 0);
               IInterface iInterface = IInterface.Stub.asInterface(service);
               Method method = iInterface.getClass().getMethod("sayHelloFromOtherProcess");
               method.invoke(iInterface);
               unbindService(this);
           } catch (RemoteException | NoSuchMethodException | InvocationTargetException | IllegalAccessException e) {
    
    
               e.printStackTrace();
           }
       }
        @Override
       public void onServiceDisconnected(ComponentName name) {
    
    
           Log.i(TAG, "disconnected from foreground service");
       }
   };
}
/**
* 客户端接口定义
*/
interface IInterface extends IBinder {
    
    
   String DESCRIPTOR = "com.example.IInterface";
    String sayHelloFromOtherProcess();
}
/**
* 客户端调用服务端接口
*/
public class Client implements IServiceCallback{
    
    
    private static final String TAG = "Client";
    private IServiceProxy proxy;
    @Override
   public void onResult(String result) {
    
    
       Log.i(TAG, "result:" + result);
   }
    @Override
   public void onError(String errorMsg) {
    
    
       Log.i(TAG, "error:" + errorMsg);
   }
    @Override
   public void connect() {
    
    
       proxy = new IServiceProxy();
       proxy.setCallback(this);
       proxy.connect(getContext(), OtherProcessService.DESCRIPTOR);
   }
    @Override
   public void disconnect() {
    
    
       proxy.disconnect();
   }
    @Override
   public void callService() throws RemoteException {
    
    
       IInterface stub = IService.Stub.asInterface((IBinder) proxy.getService());
       String result = stub.sayHelloFromOtherProcess();
       Log.i(TAG, "call result:" + result);
   }
}

The above example describes how to communicate between two different processes, where the first process is the server and the second process is the client. The client first creates a proxy class, and then calls the server's method through the proxy class. The server returns the result after receiving the client's request.
In practice, Messenger and AIDL mechanisms can be used to implement inter-process communication. Messenger is a lightweight IPC mechanism, suitable for frequent IPC scenarios, and supports multiple processes. AIDL (Android Interface Definition Language) is a language for defining interfaces in Android development. AIDL can be used to define the interface for communication between the server and the client. The following introduces two IPC mechanisms commonly used in practice:

  1. Messenger: Messenger is a lightweight IPC mechanism, suitable for scenarios with frequent IPC. Like Binder, Messenger is also implemented based on Binder. When using Messenger, the server and client need to implement the same interface and declare their interface names in the manifest file.
<receiver android:name=".MessengerService" >
<!-- Export service to remote clients -->
<intent-filter>
   <action android:name="android.intent.action.MESSENGER_SERVICE"/>
</intent-filter>
</receiver>
<service 
android:name=".MessengerService">
<intent-filter>
   <action android:name="android.intent.action.MAIN"/>
</intent-filter>
</service>
public class MessengerService extends Service {
    
    
private static final String TAG = "MessengerService";
private Messenger messenger = new Messenger(new IncomingHandler());
@Override
public void onCreate() {
    
    
   Log.d(TAG,"onCreate()");
}
@Override
public IBinder onBind(Intent intent) {
    
    
   Log.d(TAG,"onBind()");
   return messenger.getBinder();
}
class IncomingHandler extends Handler {
    
    
   @Override
   public void handleMessage(Message msg) {
    
    
       switch (msg.what) {
    
    
           case MSG_WHAT:
               Bundle bundle = msg.getData();
               int arg1 = bundle.getInt("arg1");
               int arg2 = bundle.getInt("arg2");
               Log.d(TAG,"arg1="+arg1+",arg2="+arg2);
               replyToClient(MSG_REPLY);
               break;
           case MSG_REPLY:
               Log.d(TAG,"reply received");
               break;
           default:
               super.handleMessage(msg);
               break;
       }
   }
}
}
public class MessengerClient {
    
    
private static final String TAG = "MessengerClient";
private Messenger messenger = new Messenger(new OutgoingHandler());
public void sendMessage() {
    
    
   Message msg = Message.obtain(null, MSG_WHAT);
   Bundle bundle = new Bundle();
   bundle.putInt("arg1",100);
   bundle.putInt("arg2",200);
   msg.setData(bundle);
   msg.replyTo = messenger;
   messenger.send(msg);
}
class OutgoingHandler extends Handler {
    
    
   @Override
   public void handleMessage(Message msg) {
    
    
       switch (msg.what) {
    
    
           case MSG_REPLY:
               Log.d(TAG,"receive reply");
               break;
           default:
               super.handleMessage(msg);
               break;
       }
   }
}
}

The above two classes constitute a simple application of Messenger. The client uses Messenger to send a message to the server through Binder, and the server replies after receiving the message.
2. AIDL: AIDL (Android Interface Definition Language) is a language for defining interfaces in Android development. AIDL can be used to define the interface for communication between the server and the client. AIDL syntax is similar to Java interfaces, and interface methods can be mapped to four types of IPC commands:
a. IN: input parameters, passed from the server to the client.
b. OUT: Output parameters, passed from the client to the server.
c. STREAM: Streaming parameters are passed to the client side by side.
d. REMOTE: remote call, the client calls the server's interface.
The method of using AIDL to define an interface is as follows:

  1. Define an interface for the server, such as IFooService.aidl:
package com.example;
interface IFooService {
    
    
int add(int a, int b) throws RemoteException;
int minus(int a, int b) throws RemoteException;
String echo(String str) throws RemoteException;
}
  1. Define an interface callback interface for the client, such as IFooCallback.aidl:
package com.example;
import com.example.IProgressListener;
interface IFooCallback extends IProgressListener.Stub {
    
    
void onSuccess(String result);
void onFailure(String reason);
}
  1. Define the server-side interface callback class for the client, such as FooCallback.java:
package com.example;
import android.os.Parcel;
import android.os.RemoteException;
public abstract class FooCallback extends IProgressListener.Stub {
    
    
@Override
public void progress(final Parcel data) throws RemoteException {
    
    
   final Parcelable parcelable = data.readParcelable(getClass().getClassLoader());
   getActivity().runOnUiThread(new Runnable() {
    
    
       @Override
       public void run() {
    
    
           updateProgress((Long) parcelable);
       }
   });
}
public abstract void updateProgress(long progress);
public abstract Activity getActivity();
}
  1. Implement client-side Activity or Fragment, such as MainActivity.java:
package com.example;
import android.app.Activity;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.os.IBinder;
import android.os.Parcel;
import android.os.RemoteException;
import android.support.v4.app.FragmentTransaction;
import android.util.Log;
import android.widget.TextView;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
public class MainActivity extends BaseActivity {
    
    
private TextView mTextView;
private IFooService fooService;
private IFooCallback fooCallback;
@Override
protected void onCreate(Bundle savedInstanceState) {
    
    
   super.onCreate(savedInstanceState);
   setContentView(R.layout.activity_main);
   mTextView = (TextView) findViewById(R.id.text_view);
    FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
   transaction.replace(R.id.container, MainFragment.newInstance()).commitAllowingStateLoss();
    initAidl();
   callService();
}
private void initAidl() {
    
    
   ComponentName componentName = new ComponentName(this, "com.example.IFooService");
   fooService = IFooService.Stub.asInterface(
           this.getSystemService(Context.BIND_PACKAGE_SERVICE));
   fooCallback = new FooCallback() {
    
    
       @Override
       public void updateProgress(long progress) {
    
    
           mTextView.setText("" + progress);
       }
        @Override
       public Activity getActivity() {
    
    
           return MainActivity.this;
       }
   };
}
private void callService() {
    
    
   try {
    
    
       Thread.sleep(3000);
        int ret = fooService.add(1, 2);
       Log.d("MainActicity", "" + ret);
        ret = fooService.minus(10, 5);
       Log.d("MainActicity", "" + ret);
        String str = fooService.echo("hello world!");
       Log.d("MainActicity", "" + str);
        Parcel data = Parcel.obtain();
       data.writeLong(100);
       fooService.progress(data);
        data.recycle();
   } catch (InterruptedException | RemoteException | IOException | ClassNotFoundException | NoSuchMethodException | InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
    
    
       e.printStackTrace();
   }
}
}
  1. Register the server-side Broadcast Receiver in the manifest file. For example, register the broadcast receiver in the manifest file:
<receiver android:name=".AidlDemoBroadcastReceiver">
<intent-filter>
   <action android:name="com.example.FOOBAR_BROADCAST"/>
</intent-filter>
</receiver>
  1. The server receives the client's broadcast, parses the message and feeds back the result. For example, the server defines a broadcast receiver in its own Manifest file:
<service android:name=".AidlDemoService">
<intent-filter>
   <action android:name="com.example.FOO_SERVICE"/>
</intent-filter>
</service>

When the client receives the notification, it calls the remote service interface to process the message. After the server completes processing, it will send a broadcast message.

5. Future development trends and challenges

This article introduces some basic concepts, programming models, development processes, components and system features of the Android system, and elaborates on the core principles and specific operation steps of the Android system based on specific cases. In the future, as the Android ecosystem progresses, there are still many places worth exploring and learning. Share some views below.

  1. Hardware acceleration: Google is planning to fully support OpenGL ES 3.0. This is to help developers take better advantage of hardware acceleration.
  2. Network security: The current network access permissions of the Android system are not refined enough, and security-related applications rely on the black-box Android system. Google is researching new mechanisms related to network security, hoping to improve the Android system's network security protection.
  3. Dynamic: Google is trying to provide Android applications with dynamic deployment capabilities through an open component system, which can realize online upgrades, hot updates, migration and other functions.
  4. VR/AR: In recent years, hardware scale and technological innovation in the VR/AR field have rapidly promoted the development of the entire industry, and Google is also actively exploring cooperation in this area.
  5. Operating system: There are already some open source embedded systems on the market that can run Android systems and can also serve as client desktop systems. Through deep integration, the open source OS can be deeply integrated with the Android system and give full play to their respective advantages.

Guess you like

Origin blog.csdn.net/universsky2015/article/details/132137976