I have been in touch with Android development for some time. Recently, I began to find time to sort out some knowledge points, so as to reduce the time cost of repeated learning and improve my efficiency by sorting out notes.

At present, it summarizes some knowledge points of Android first, which is the main content of this article to share. I want to specially state that this summary is more from my own programming basis and focus, so there will be selective neglect and focus in the content, there are many references to the blog and pictures, there is no way to list one by one, if there are improper quotes will be deleted immediately, I hope you forgive me. The list of knowledge points can be viewed in the right sidebar.

In addition, the knowledge points that will be sorted out later will also include Java, Android source code, some other computer basics and common interview questions. In the next month, I will add and update them one after another. I have created a project on Github, and I want to pay attention to welcome Star.

  • Android Review materials
  • Android Review materials — Java Knowledge summary (a)

Processes and threads

When an application component is started and no other components are running, the Android system uses a single thread of execution to start a new Linux process for the application. By default, all components of the same application run in the same process and thread (called the “main” thread).

The manifest file entries for various component elements

,

, < Receiver >, and – all support the Android: Process property, which specifies which process the component should run in.

Process life cycle

1. Foreground processes

  • Hosting the Activity that the user is interacting with (that has called the ActivityonResume()Methods)
  • Hosts a Service that is bound to the Activity the user is interacting with
  • Hosting a Service that is running in the foreground (the Service has been invoked)startForeground())
  • Hosting a Service that is performing a lifecycle callback (onCreate(),onStart()onDestroy())
  • The trustee is executing itonReceive()Methods the BroadcastReceiver

2, visible process

  • Host an Activity that is not in the foreground but is still visible to the user (invoked)onPause()Methods). For example, this might happen if the RE foreground Activity starts a dialog box that allows the previous Activity to be displayed after it.
  • Hosts a Service bound to a visible (or foreground) Activity

3. Service process

  • A process that is running a service started with the startService() method and does not belong to either of the higher categories of processes described above.

4. Background processes

  • The process that contains the Activity that is currently invisible to the user (that has called the Activity)onStop()Methods). There are usually many background processes running, so they are saved in the LRU (Least Recently used) list to ensure that the process containing the Activity the user recently viewed is the last to terminate.

5. Empty processes

  • A process that does not contain any active application components. The sole purpose of keeping such processes is to be used as a cache to reduce the startup time needed to run components in it the next time. To balance overall system resources between the process cache and the underlying kernel cache, systems often kill these processes. \

Multiple processes

If any of the four registered components uses multiple processes, a new Application object is created when the component is run. In the case of multiple processes creating repeated applications, you only need to determine the current process in the class.

public class MyApplication extends Application {

    @Override
    public void onCreate(a) {
        Log.d("MyApplication", getProcessName(android.os.Process.myPid()));
        super.onCreate();
    }

    /** * Get process name * based on process ID@paramPid Process ID *@returnProcess name * /
    public  String getProcessName(int pid){
        ActivityManager am = (ActivityManager)getSystemService(Context.ACTIVITY_SERVICE);
        List<ActivityManager.RunningAppProcessInfo> processInfoList = am.getRunningAppProcesses();
        if (processInfoList == null) {
            return null;
        }
        for (ActivityManager.RunningAppProcessInfo processInfo : processInfoList) {
            if (processInfo.pid == pid) {
                returnprocessInfo.processName; }}return null; }}Copy the code

Process of survival

OOM_ADJ

ADJ level The values explain
UNKNOWN_ADJ 16 It usually means that the process will be cached and cannot obtain a definite value
CACHED_APP_MAX_ADJ 15 Maximum adj for invisible processes
CACHED_APP_MIN_ADJ 9 Minimum adj for an invisible process
SERVICE_B_AD 8 Services in B List (older, less likely to be used)
PREVIOUS_APP_ADJ 7 Previous App process (usually by pressing the back key)
HOME_APP_ADJ 6 The Home process
SERVICE_ADJ 5 Service Process
HEAVY_WEIGHT_APP_ADJ 4 Background heavyweight process, set in the system/rootdir/init.rc file
BACKUP_APP_ADJ 3 The backup process
PERCEPTIBLE_APP_ADJ 2 Aware of processes, such as background music playback
VISIBLE_APP_ADJ 1 Visible Process
FOREGROUND_APP_ADJ 0 Foreground Process
PERSISTENT_SERVICE_ADJ – 11 Associated with systems or persistent processes
PERSISTENT_PROC_ADJ – 12 Persistent system processes, such as Telephony
SYSTEM_ADJ – 16 System processes
NATIVE_ADJ – 17 Native processes (not managed by the system)

The process was killed

Process survival scheme

  • Start an Activity with one pixel
  • Use the Front Desk service
  • Multiple processes wake each other up
  • JobSheduler awaken
  • Sticky services & bundled with system services

thread

When the application starts, the system creates a thread of execution (UI thread) named “main thread” for the application. This thread is important because it is responsible for dispatching events to the appropriate user interface widgets, including drawing events. In addition, it is the thread through which the application interacts with Android UI toolkit components (components from the Android.Widget and Android.View packages).

The system does not create a separate thread for each component instance. All components running in the same process are instantiated in the UI thread, and system calls to each component are dispatched by that thread. Therefore, methods that respond to system callbacks (for example, onKeyDown() or lifecycle callback methods that report user actions) always run in the UI thread of the process.

Android’s single-threaded mode must follow two rules:

  • Don’t block the UI thread
  • Do not access the Android UI toolkit outside of the UI thread

To solve this problem, Android provides several ways to access the UI thread from other threads:

  • Activity.runOnUiThread(Runnable)
  • View.post(Runnable)
  • View.postDelayed(Runnable, long)

IPC

IPC stands for inter-process Communication. Android is based on Linux, which, for security reasons, prevents different processes from manipulating each other’s data, a practice known as process isolation.

Virtual memory mechanism in Linux system, allocation of the linear continuous memory space for each process, the operating system will be this kind of virtual memory mapped to physical memory space, each process has its own virtual memory space, and cannot operate other processes memory space, only the operating system have permission to operate the physical memory space. Process isolation ensures memory safety for each process.

The IPC way

The name of the advantages disadvantages Applicable scenario
Bundle Simple and easy to use Only data types supported by the Bundle can be transferred Interprocess communication between the four components
File sharing Simple and easy to use It is not suitable for high concurrency scenarios and cannot achieve instant communication between processes In the case of no concurrent access, simple data exchange is not real-time
AIDL Powerful, support one – to – many concurrent communication, support real-time communication It’s a little more complicated to use and needs to handle thread synchronization One-to-many communication with RPC requirements
Messenger The function is general, support one to many serial communication, support real-time communication Does not handle high concurrency awareness very well, does not support RPC, and data is transmitted via Message, so only the data types supported by the Bundle can be transmitted Low concurrency one-to-many instant communication, no RPC requirements, or RPC requirements that do not return results
ContentProvider It is powerful for data source access, supports one-to-many concurrent data sharing, and extends other operations through the Call method You can think of it as a constrained AIDL that provides CRUD operations for the data source One-to-many interprocess data sharing
Socket Function please dial, can be transmitted through the network byte stream, support one-to-many concurrent real-time communication The implementation details are a bit cumbersome and do not support direct RPC Network data exchange

AIDL

Android Interface Definition Language

  • Create an AIDL interface file
// RemoteService.aidl
package com.example.mystudyapplication3;

interface IRemoteService {

    int getUserId(a);

}
Copy the code
  • Creating a Remote Service
public class RemoteService extends Service {

    private int mId = -1;

    private Binder binder = new IRemoteService.Stub() {

        @Override
        public int getUserId(a) throws RemoteException {
            returnmId; }};@Nullable
    @Override
    public IBinder onBind(Intent intent) {
        mId = 1256;
        returnbinder; }}Copy the code
  • Declare remote services
<service
    android:name=".RemoteService"
    android:process=":aidl" />
Copy the code
  • Bind remote Services
public class MainActivity extends AppCompatActivity {

    public static final String TAG = "wzq";

    IRemoteService iRemoteService;
    private ServiceConnection mConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            iRemoteService = IRemoteService.Stub.asInterface(service);
            try {
                Log.d(TAG, String.valueOf(iRemoteService.getUserId()));
            } catch(RemoteException e) { e.printStackTrace(); }}@Override
        public void onServiceDisconnected(ComponentName name) {
            iRemoteService = null; }};@Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        bindService(new Intent(MainActivity.this, RemoteService.class), mConnection, Context.BIND_AUTO_CREATE); }}Copy the code

Messenger

Messenger can pass Message objects between different processes. By putting the data we need to pass in the Message, we can easily pass data between processes. Messenger is a lightweight IPC solution with an underlying implementation of AIDL.

Context

Context itself is an abstract class that encapsulates a series of system service interfaces, including the management of internal resources, packages, class loading, I/O operations, permissions, main threads, IPC, and component startup operations. ContextImpl, Activity, Service, and Application are all direct or indirect subclasses of Context as follows:

ContextWrapper is an implementation of the proxy Context, simply delegating all of its calls to another Context (mBase).

Application, Activity, and Service call attachBaseContext() of ContextWrapper by attach(), setting mBase as ContextImpl. The core work of ontextWrapper is handed over to mBase(ContextImpl).

Activity

The life cycle

  • Activity A starts another Activity B with the following callback: OnPause () → onCreate() → onStart() → onResume() → onStop(); If B is A transparent topic or A DialogActivity, A’s onStop is not called;

Boot mode

LaunchMode instructions
standard The system creates a new instance of the activity in the task that starts it
singleTop If an instance of the activity already exists at the top of the current task, the system calls its onNewIntent()
singleTask The system creates a new task and instantiates the activity in the root directory of the task. But if an instance of the activity already exists in a separate task, its onNewIntent() method is called. Only one instance of an activity can exist at a time
singleInstance Like “singleTask”, an activity is always the only member of its task; Any activity that starts from this is opened in a separate task

 

Use Intent flags instructions
FLAG_ACTIVITY_NEW_TASK With singleTask
FLAG_ACTIVITY_SINGLE_TOP With singleTop
FLAG_ACTIVITY_CLEAR_TOP If the activity you are starting is already running in the current task, instead of starting a new instance of the activity, you destroy the activity on it and call its onNewIntent().

The boot process

ActivityThread.java

private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {... ActivityInfo aInfo = r.activityInfo;if (r.packageInfo == null) {
        // Step 1: Create LoadedApk objectr.packageInfo = getPackageInfo(aInfo.applicationInfo, r.compatInfo, Context.CONTEXT_INCLUDE_CODE); }...//component initialization process

    java.lang.ClassLoader cl = r.packageInfo.getClassLoader();
    Step 2: Create an Activity objectActivity activity = mInstrumentation.newActivity(cl, component.getClassName(), r.intent); .Step 3: Create the Application object
    Application app = r.packageInfo.makeApplication(false, mInstrumentation);

    if(activity ! =null) {
        // Step 4: Create ContextImpl object
        Context appContext = createBaseContextForActivity(r, activity);
        CharSequence title = r.activityInfo.loadLabel(appContext.getPackageManager());
        Configuration config = new Configuration(mCompatConfiguration);
        //step5: attach both Application/ContextImpl to the Activity object [see section 4.1]
        activity.attach(appContext, this, getInstrumentation(), r.token, r.ident, app, r.intent, r.activityInfo, title, r.parent, r.embeddedID, r.lastNonConfigurationInstances, config, r.referrer, r.voiceInteractor); .int theme = r.activityInfo.getThemeResource();
        if(theme ! =0) {
            activity.setTheme(theme);
        }

        activity.mCalled = false;
        if (r.isPersistable()) {
            Step 6: Execute the onCreate callback
            mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
        } else {
            mInstrumentation.callActivityOnCreate(activity, r.state);
        }

        r.activity = activity;
        r.stopped = true;
        if(! r.activity.mFinished) { activity.performStart();// Execute the onStart callback
            r.stopped = false;
        }
        if(! r.activity.mFinished) {// Execute the onRestoreInstanceState callback
            if (r.isPersistable()) {
                if(r.state ! =null|| r.persistentState ! =null) { mInstrumentation.callActivityOnRestoreInstanceState(activity, r.state, r.persistentState); }}else if(r.state ! =null) { mInstrumentation.callActivityOnRestoreInstanceState(activity, r.state); }}... r.paused =true;
        mActivities.put(r.token, r);
    }

    return activity;
}

Copy the code

Fragment

The characteristics of

  • Fragment solve the switching between activities is not smooth, light switching 】
  • The return result can be received from startActivityForResult, but the View cannot
  • Commit () can only be used to commit a transaction before the Activity saves its state (the user leaves the Activity). If you try to commit after that point, an exception is thrown. This is because the state after submission may be lost if the Activity needs to be resumed. For lost commits that don’t matter, use commitAllowingStateLoss().

The life cycle

Communicate with the Activity

A good way to do this is to define a callback interface within the fragment and ask the host Activity to implement it.

public static class FragmentA extends ListFragment {...// Container Activity must implement this interface
    public interface OnArticleSelectedListener {
        public void onArticleSelected(Uri articleUri); }... }public static class FragmentA extends ListFragment { OnArticleSelectedListener mListener; .@Override
    public void onAttach(Activity activity) {
        super.onAttach(activity);
        try {
            mListener = (OnArticleSelectedListener) activity;
        } catch (ClassCastException e) {
            throw newClassCastException(activity.toString()); }}... }Copy the code

Service

The life cycle

value instructions
START_NOT_STICKY If the system terminates the service after onStartCommand() returns, the system will not rebuild the service unless there is a pending Intent to deliver. This is the safest option to avoid running the service when it is not necessary and when the application can easily restart all outstanding jobs
START_STICKY If the system terminates the service after onStartCommand() returns, the service is rebuilt and onStartCommand() is called, but the last Intent is not redelivered. Instead, onStartCommand() is called with an empty Intent unless there is a pending Intent to start the service (in which case those intents will be delivered). This applies to media players (or similar services) that do not execute commands but run indefinitely and wait for jobs
START_REDELIVER_INTENT If the system terminates the service after onStartCommand() returns, the service is rebuilt and onStartCommand() is called with the last Intent passed to the service. Any pending intents are delivered in sequence. This applies to services that actively perform jobs that should be restored immediately, such as downloading files

Enabling the Foreground Service

<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
Copy the code
Notification notification = new Notification(icon, text, System.currentTimeMillis());
Intent notificationIntent = new Intent(this, ExampleActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(this.0, notificationIntent, 0);
notification.setLatestEventInfo(this, title, mmessage, pendingIntent);
startForeground(ONGOING_NOTIFICATION_ID, notification);
Copy the code

Data is stored

storage instructions
SharedPreferences Store private raw data in key-value pairs
Internal storage Stores private data in device memory
External storage Store common data in shared external storage
SQLite database Store structured data in a private database

SharedPreferences

SharedPreferences in the form of key-value pairs are mainly used for lightweight data storage, which is especially suitable for storing application configuration parameters. However, it is not recommended to use SharedPreferences to store large-scale data, which may degrade performance.

SharedPreferences XML file format is used to save data. The file is located in /data/data/ /shared_prefs, for example:

<?xml version='1.0' encoding='utf-8' standalone='yes' ? >
<map>
   <string name="blog">https://github.com/JasonWu1111/Android-Review</string>
</map>
Copy the code

Starting with Android N, the SP file mode created does not allow MODE_WORLD_READABLE and MODE_WORLD_WRITEABLE modules, otherwise SecurityException will be thrown directly. MODE_MULTI_PROCESS is not recommended by Google and will not be supported in the future.

When MODE_MULTI_PROCESS mode is set, the getSharedPreferences process checks the last modification time and file size of the SP file and reloads the file from disk once all changes are made.

access

getPreferences

Activity.getpreferences (mode): Uses the name of the Activity class as the SP file name. Namely xxxActivity. XML Activity. Java

public SharedPreferences getPreferences(int mode) {
    return getSharedPreferences(getLocalClassName(), mode);
}
Copy the code

getDefaultSharedPreferences

PreferenceManager. GetDefaultSharedPreferences (Context) : with the package name plus _preferences as file name, in MODE_PRIVATE mode create SP file. Namely packgeName_preferences. XML.

public static SharedPreferences getDefaultSharedPreferences(Context context) {
    return context.getSharedPreferences(getDefaultSharedPreferencesName(context),
           getDefaultSharedPreferencesMode());
}
Copy the code

getSharedPreferences

Direct call Context. GetSharedPreferences (name, mode), all methods are ultimately calls to the methods as follows:

class ContextImpl extends Context {
    private ArrayMap<String, File> mSharedPrefsPaths;

    public SharedPreferences getSharedPreferences(String name, int mode) {
        File file;
        synchronized (ContextImpl.class) {
            if (mSharedPrefsPaths == null) {
                mSharedPrefsPaths = new ArrayMap<>();
            }
            // First check with mSharedPrefsPaths to see if a file exists
            file = mSharedPrefsPaths.get(name);
            if (file == null) {
                // If the file does not exist, create a new filefile = getSharedPreferencesPath(name); mSharedPrefsPaths.put(name, file); }}returngetSharedPreferences(file, mode); }}Copy the code

architecture

SharedPreferences and Editor are just two interfaces. SharedPreferencesImpl and EditorImpl implement corresponding interfaces respectively. In addition, ContextImpl records important data for SharedPreferences.

The putxxx() operation writes data to editorImpl.mmodified;

Apply ()/commit() calls commitToMemory(‘, synchronizes data to SharedPreferencesImpl mMap and saves it to MemoryCommitResult mapToWriteToDisk, EnqueueDiskWrite () is called to write to the disk file. The original data is saved to a file with the suffix. Bak. The data can be recovered if any exception occurs during disk writing.

Getxxx () operation from SharedPreferencesImpl. MMap read data.

apply / commit

  • Apply has no return value, and commit has a return value to know if the change was committed successfully
  • Apply commits changes to memory and then asynchronously to disk files, while COMMIT commits to disk files synchronously
  • In the case of multiple concurrent COMMIT, the commit data being processed must be updated to disk files before the execution continues, which reduces the efficiency. However, apply is only atomic update to memory, and the subsequent call to apply directly overwrites the previous memory data, thus improving efficiency to a certain extent.

Pay attention to

  • It is strongly recommended not to store very large keys/values in sp to reduce the lag/ANR
  • Don’t use Apply too often and commit in batches whenever possible
  • Do not use MODE_MULTI_PROCESS
  • High frequency write keys and high frequency read keys can split files appropriately due to reduced synchronization lock contention
  • Instead of edit() multiple times in a row, you should get edit() once and then putxxx() multiple times to reduce memory fluctuations

View

The entire drawing process of a View can be divided into the following three stages:

  • Measure: Determines whether the View size needs to be recalculated or calculated if necessary
  • Layout: Determine whether the View position needs to be recalculated, if necessary
  • Draw: Determines whether the View needs to be redrawn or redrawn if necessary

MeasureSpec

MeasureSpec = MeasureSpec = MeasureSpec = MeasureSpec = MeasureSpec = MeasureSpec = MeasureSpec = MeasureSpec = MeasureSpec = MeasureSpec = MeasureSpec = MeasureSpec MeasureSpec is a static inner class of the View class that says how the View should be measured

Mode instructions
UNSPECIFIED The measurement mode is not specified, and the parent view does not limit the size of the child view. The child view can be any size desired. It is usually used inside the system and rarely used in application development.
EXACTLY Precise measurement mode, which takes effect when the width and height of the View is match_parent or a specific value, indicating that the parent View has determined the exact size of the child View. In this mode, the measured value of the View is the value of SpecSize
AT_MOST Max measurement mode, which takes effect when the width and height of the view is specified as wrAP_content, where the size of the child view can be any size that does not exceed the maximum size allowed by the parent view

For a DecorView, its MeasureSpec is determined by the window size and its own LayoutParams; For a normal View, its MeasureSpec is determined by the parent’s MeasureSpec and its own LayoutParams

Controls that inherit directly from the View need to override the onMeasure method and set the size of wrap_content itself, otherwise using wrap_content in the layout is equivalent to using match_parent. Solutions are as follows:

protected void onMeasure(int widthMeasureSpec, int height MeasureSpec) {
    super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);
    int widtuhSpecSize = MeasureSpec.getSize(widthMeasureSpec);
    int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec);
    // Specify the internal width/height (mWidth and mHeight) in the case of wrap_content
    int heightSpecSize = MeasureSpec.AT_MOST && heightSpecMode == MeasureSpec.AT_MOST) {
        setMeasuredDimension(mWidth, mHeight);
    } else if (widthSpecMode == MeasureSpec.AT_MOST) {
        setMeasureDimension(mWidth, heightSpecSize);
    } else if(heightSpecMode == MeasureSpec.AT_MOST) { setMeasureDimension(widthSpecSize, mHeight); }}Copy the code

Get the width and height of a View in the Activity

  • Activity/View#onWindowFocusChanged
// The Activity window is called once when it gets focus and when it loses focus. // If onResume and onPause are used frequently, Public void onWindowFocusChanged(Boolean hasFocus) { super.onWindowFocusChanged(hasFocus);if(hasFocus) { int width = view.getMeasureWidth(); int height = view.getMeasuredHeight(); }}Copy the code
  • view.post(runnable)
// Post a runnable to the end of the message queue, and then wait for Looper to call runnable once the View has already started // protected voidonStart() {
    super.onStart();
    view.post(new Runnable() {

        @Override
        public void run() { int width = view.getMeasuredWidth(); int height = view.getMeasuredHeight(); }}); }Copy the code
  • ViewTreeObserver
// The onGlobalLayout method is called when the state of the View tree changes or the visibility of the View inside the View tree changes
protected void onStart(a) {
    super.onStart();

    ViewTreeObserver observer = view.getViewTreeObserver();
    observer.addOnGlobalLayoutListener(new OnGlobalLayoutListener() {

        @SuppressWarnings("deprecation")
        @Override
        public void onGlobalLayout(a) {
            view.getViewTreeObserver().removeGlobalOnLayoutListener(this);
            int width = view.getMeasuredWidth();
            intheight = view.getMeasuredHeight(); }}); }Copy the code

The basic flow of Draw

// Drawing can be basically divided into six steps
public void draw(Canvas canvas) {...// Step 1: Draw the View backgrounddrawBackground(canvas); .// Step two: If necessary, keep the Canvas layer ready for FADINGsaveCount = canvas.getSaveCount(); . canvas.saveLayer(left, top, right, top + length,null, flags); .// Step 3: Draw the contents of the ViewonDraw(canvas); .// Step 4: Draw a child View of the ViewdispatchDraw(canvas); .// Step 5: If necessary, paint the View's fading edges and restore the layerscanvas.drawRect(left, top, right, top + length, p); . canvas.restoreToCount(saveCount); .// Step 6: Draw the View decorations (such as scroll bars, etc.)
    onDrawForeground(canvas)
}
Copy the code

Bitmap

Configure information and compression mode

There are two internal enumerated classes in Bitmap:

  • Config is used to set the color configuration information
  • CompressFormat is used to set the compression method
Config Number of bytes per pixel parsing
Bitmap.Config.ALPHA_8 1 The color information consists only of transparency, which is 8 bits
Bitmap.Config.ARGB_4444 2 Color information is made up of four RGBA parts, each with 4 bits, for a total of 16 bits
Bitmap.Config.ARGB_8888 4 The color information is made up of four RGBA parts, each with 8 bits, for a total of 32 bits. Is the default Bitmap color configuration and is the most space-intensive configuration
Bitmap.Config.RGB_565 2 Color information is composed of RGB three parts, R for 5, G for 6, B for 5, a total of 16 bits
RGBA_F16 8 Android 8.0 added (Richer color display HDR)
HARDWARE Special New in Android 8.0 (Bitmap stored directly in Graphic Memory)

When we usually optimize Bitmap, when we need to optimize performance or prevent OOM, we usually use bitmap.config. RGB_565 configuration, because bitmap.config. ALPHA_8 only has transparency, it is meaningless to display general images. ARGB_4444 display image is not clear, bitmap.config. ARGB_8888 occupies the most memory.

CompressFormat parsing
Bitmap.CompressFormat.JPEG JPEG compression algorithm for image compression, compression format can be “.jpg” or “.jpeg”, is a lossy compression
Bitmap.CompressFormat.PNG Color information is made up of four RGBA parts, each with 4 bits, for a total of 16 bits
Bitmap.Config.ARGB_8888 The color information is made up of four RGBA parts, each with 8 bits, for a total of 32 bits. Is the default Bitmap color configuration and is the most space-intensive configuration
Bitmap.Config.RGB_565 Color information is composed of RGB three parts, R for 5, G for 6, B for 5, a total of 16 bits

Common operations

Crop, scale, rotate, move

Matrix matrix = new Matrix();  
/ / zoom
matrix.postScale(0.8 f.0.9 f);  
// Left rotation, the argument is regular to the right
matrix.postRotate(-45);  
// Pan, modify set again based on the last modification each operation is the latest overwrite the last operation
matrix.postTranslate(100.80);
// Crop and perform the above operations
Bitmap bitmap = Bitmap.createBitmap(source, 0.0, source.getWidth(), source.getHeight(), matrix, true);
Copy the code

While Matrix can also call the postSkew method for skewing, it cannot be used when creating a Bitmap at this point.

Bitmap and Drawable conversion

// Drawable -> Bitmap
public static Bitmap drawableToBitmap(Drawable drawable) { Bitmap bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight(), drawable.getOpacity() ! = PixelFormat.OPAQUE ? Bitmap.Config.ARGB_8888 : Bitmap.Config.RGB_565); Canvas canvas =new Canvas(bitmap);
    drawable.setBounds(0.0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight();
    drawable.draw(canvas);
    return bitmap;
}

// Bitmap -> Drawable
public static Drawable bitmapToDrawable(Resources resources, Bitmap bm) {
    Drawable drawable = new BitmapDrawable(resources, bm);
    return drawable;
}
Copy the code

Save and release

Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.test);
File file = new File(getFilesDir(),"test.jpg");
if(file.exists()){
    file.delete();
}
try {
    FileOutputStream outputStream=new FileOutputStream(file);
    bitmap.compress(Bitmap.CompressFormat.JPEG,90,outputStream);
    outputStream.flush();
    outputStream.close();
} catch (FileNotFoundException e) {
    e.printStackTrace();
} catch (IOException e) {
    e.printStackTrace();
}
// Release bitmap resources. This is an irreversible operation
bitmap.recycle();
Copy the code

Image compression

public static Bitmap compressImage(Bitmap image) {
    if (image == null) {
        return null;
    }
    ByteArrayOutputStream baos = null;
    try {
        baos = new ByteArrayOutputStream();
        image.compress(Bitmap.CompressFormat.JPEG, 100, baos);
        byte[] bytes = baos.toByteArray();
        ByteArrayInputStream isBm = new ByteArrayInputStream(bytes);
        Bitmap bitmap = BitmapFactory.decodeStream(isBm);
        return bitmap;
    } catch (OutOfMemoryError e) {
        e.printStackTrace();
    } finally {
        try {
            if(baos ! =null) { baos.close(); }}catch(IOException e) { e.printStackTrace(); }}return null;
}
Copy the code

BitmapFactory

Bitmap creation process

The Option class

Commonly used method instructions
boolean inJustDecodeBounds If set to true, no image is retrieved, no memory is allocated, but the height and width of the image are returned
int inSampleSize Multiple of zoom in the image
int outWidth Gets the width value of the picture
int outHeight Gets the height value of the picture
int inDensity The pixel compression ratio used for bitmaps
int inTargetDensity Pixel compression ratio for target bitmap (bitmap to be generated)
byte[] inTempStorage Create a temporary file to store the image
boolean inScaled Image compression when set to true, from inDensity to inTargetDensity
boolean inDither If true, the decoder attempts dithering decoding
Bitmap.Config inPreferredConfig The default value is ARGB_8888. In this mode, a pixel takes up 4bytes of space. If transparency is not required, RGB_565 mode is used, and a pixel takes up 2bytes
String outMimeType Setting up decoded images
boolean inPurgeable Whether the memory used to store Pixel can be reclaimed when the system runs out of memory
boolean inInputShareable This parameter takes effect only when inPurgeable is true. Whether to share an InputStream
boolean inPreferQualityOverSpeed If it is true, the Bitmap quality is guaranteed first, followed by the decoding speed
boolean inMutable Configure whether the Bitmap can be changed, for example, by adding a line segment to the Bitmap several pixels apart
int inScreenDensity Pixel density of the current screen

The basic use

try {
    FileInputStream fis = new FileInputStream(filePath);
    BitmapFactory.Options options = new BitmapFactory.Options();
    options.inJustDecodeBounds = true;
    // After setting inJustDecodeBounds to true, use decodeFile() and other methods, which do not allocate real space (the decodeFile map is null, but can calculate the width and height of the original image). Namely the options. OutWidth and options. OutHeight
    BitmapFactory.decodeFileDescriptor(fis.getFD(), null, options);
    float srcWidth = options.outWidth;
    float srcHeight = options.outHeight;
    int inSampleSize = 1;

    if (srcHeight > height || srcWidth > width) {
        if (srcWidth > srcHeight) {
            inSampleSize = Math.round(srcHeight / height);
        } else {
            inSampleSize = Math.round(srcWidth / width);
        }
    }

    options.inJustDecodeBounds = false;
    options.inSampleSize = inSampleSize;

    return BitmapFactory.decodeFileDescriptor(fis.getFD(), null, options);
} catch (Exception e) {
    e.printStackTrace();
}
Copy the code

Memory recovery

if(bitmap ! =null && !bitmap.isRecycled()){ 
    // Collect and set it to null
    bitmap.recycle(); 
    bitmap = null; 
} 
Copy the code

Bitmap class constructors are private, so developers cannot directly create a Bitmap object. Instead, they can instantiate a Bitmap using static methods of the BitmapFactory class. A close look at the BitmapFactory source code shows that generating Bitmap objects is ultimately done through JNI calls. So, after loading the Bitmap into memory, there are two parts of memory. In short, part of it is Java and part of it is C. This Bitmap object is allocated by the Java part, when not used, the system will automatically reclaim, but the corresponding C available memory area, virtual machine is not directly reclaimed, this can only call the underlying function release. So you need to call the recycle() method to free up memory in part C. As you can see from the Bitmap source code, the RECYCLE () method does call JNI methods.

Handler

Handlers have two main uses :(1) scheduling messages and runnables to be executed at some point in the future; (2) Queue operations that will be performed on a thread other than your own. (Keep the UI safe while updating it concurrently in multiple threads.)

The Handler is created using the current thread’s Looper to construct the message loop. Note that threads do not have Looper by default. If you want to use Handler, you must create Looper for the thread because the default UI main thread, called ActivityThread, Looper is initialized when an ActivityThread is created, which is why you can use handlers in the main thread by default.

  • Message: The Message object received and processed by the Handler
  • MessageQueue: queue of messages, first in, first out, each thread can have a maximum of one
  • Looper: The message pump is the manager of the MessageQueue. It continuously retrieves messages from the MessageQueue and distributes the messages to the corresponding Handler. Each thread has only one Looper.

AsyncTask

  • Instances of asynchronous tasks must be created in the UI thread, that is, AsyncTask objects must be created in the UI thread.
  • execute(Params… The params method must be called in the UI thread.
  • Do not call onPreExecute(), doInBackground(), onProgressUpdate(), and onPostExecute() manually.
  • You cannot change UI component information in doInBackground().
  • A task instance can only be executed once, and a second execution will throw an exception.
import android.os.AsyncTask;

public class DownloadTask extends AsyncTask<String.Integer.Boolean> {

    @Override
    protected void onPreExecute(a) {
        super.onPreExecute();
    }

    @Override
    protected Boolean doInBackground(String... strings) {
        return null;
    }

    @Override
    protected void onProgressUpdate(Integer... values) {
        super.onProgressUpdate(values);
    }

    @Override
    protected void onPostExecute(Boolean aBoolean) {
        super.onPostExecute(aBoolean); }}Copy the code

Serializable/Parcelable

  • Serializable uses I/O read and write storage on the hard disk, while Parcelable is directly read and write in memory
  • Serializable will use reflection, serialization and deserialization process requires a lot of I/O operations, Parcelable own implementation of marshalling and unmarshalling (reflection) operation, data is also stored in Native memory, It’s much faster

Screen adaptation

unit

  • Dpi Number of pixels per inch

  • Dp density-independent pixel – an abstract unit based on the physical density of the screen. These units are relative to a 160 dpi screen, so a DP is a px on a 160 dpi screen. The ratio of DP to pixels will vary with screen density, but not necessarily directly. Provides consistency in the actual size of UI elements for different devices.

  • Sp scale independent pixels – this is similar to dp units, but it can also be scaled by the user’s font size preferences. It is recommended to use this unit when specifying font sizes so that they can be adjusted according to screen density and user preferences.

dpi = px / inch

density = dpi / 160

dp = px / density
Copy the code

Headline-matching scheme

private static void setCustomDensity(@NonNull Activity activity, @NonNull final Application application) {
    final DisplayMetrics appDisplayMetrics = application.getResources().getDisplayMetrics();
    if (sNoncompatDensity == 0) {
        sNoncompatDensity = appDisplayMetrics.density;
        sNoncompatScaledDensity = appDisplayMetrics.scaledDensity;
        // Listen for font switching
        application.registerComponentCallbacks(new ComponentCallbacks() {
            @Override
            public void onConfigurationChanged(Configuration newConfig) {
                if(newConfig ! =null && newConfig.fontScale > 0) { sNoncompatScaledDensity = application.getResources().getDisplayMetrics().scaledDensity; }}@Override
            public void onLowMemory(a) {}}); }// the dpi will be 360dpi after adaptation
    final float targetDensity = appDisplayMetrics.widthPixels / 360;
    final float targetScaledDensity = targetDensity * (sNoncompatScaledDensity / sNoncompatDensity);
    final int targetDensityDpi = (int) (160 * targetDensity);

    appDisplayMetrics.density = targetDensity;
    appDisplayMetrics.scaledDensity = targetScaledDensity;
    appDisplayMetrics.densityDpi = targetDensityDpi;

    final DisplayMetrics activityDisplayMetrics = activity.getResources().getDisplayMetrics();
    activityDisplayMetrics.density = targetDensity;
    activityDisplayMetrics.scaledDensity = targetScaledDensity;
    activityDisplayMetrics.densityDpi = targetDensityDpi
}
Copy the code

That’s all for this share. If you like it, you can like 👍 or follow it. Feel free to point out any mistakes in the comments.

This article is personally original, please indicate the source of reprint.