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
Process life cycle
1. Foreground processes
- Hosting the Activity that the user is interacting with (that has called the Activity
onResume()
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 it
onReceive()
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/
<?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.