AOP (Aspect Oriented Programming) is slice-oriented programming. The so-called slice-oriented programming is that the program can be divided into numerous time nodes according to time. Using the idea of AOP, other codes can be inserted into any time node to achieve their business needs. In other words, for those non-user requirements, such as performance monitoring, logging, buried-point statistics, dynamic permissions, security control, exception handling, etc., can be perfectly implemented with AOP.
Take the execution of a method as an example:
The execution order of method is A - > B - > C. Through AOP, other code can be inserted before and after the execution of method B to solve some business requirements, such as the detection of execution time of method.
Dynamic proxy can also be said to embody the idea of AOP, but it can only do some operations when the interface method is executed, while AOP can do some operations before, when and after any method is executed.
1. The basic terms involved in AOP
crosscutting concern
AOP divides software into two parts: core concerns and cross-cutting concerns. Core concerns generally refer to some mainline services involving user services, while cross-cutting concerns refer to non-user services (horizontal requirements), but also need to interpolate the code executed in the mainline services, such as logging.
Connection point
Where there may be cross-cutting concerns at the core concerns, such as method entry, where click events occur, and so on, these are called join points.
notice
The actions performed at the connection point, or AOP-woven code, are called notifications, which are commonly used in three ways:
Before: Actions before the target method is executed.
around: The action of replacing the target code.
After: Actions after the execution of the target method.
breakthrough point
A collection of join points that determine when a notification will be triggered. That is to say, the target class to which the code is cut, and the target method is called the entry point.
A pointcut is usually represented by a regular expression or wildcard syntax, which can specify how to execute a method or how to execute multiple methods, such as specifying all methods that execute a tag for an annotation.
section
Pointcuts and notifications can be combined into a slice, which is simply a code snippet that cuts into a specified class-specified method.
Weave into
The process of injecting notifications into join points.
In fact, the main concern of developers is the entry point and cut-in. For example, when the program is written, a function of collecting error logs suddenly comes up, that is to say, it is necessary to save Msg and class information to local or server where Log.e(TAG, Msg) is invoked. At this time, it is impossible to change the code one by one. Using the idea of AOP, we can directly make a slice of "code segment collecting error information" and insert it into the e method of Log class, just like this. Lancet Implement AOP:
@Proxy("e")
@TargetClass("android.util.Log")
public static int rocordLogInfo(String tag, String msg){
RecordSDK.recordLog(This.get().getClass().getSimpleName(),
msg);
return (int) Origin.call();
}
This requirement can be solved by adding very little code. One word, cool!
2. Timing of AOP code weaving
There are generally three opportunities for code weaving:
Compile-time weaving: For example, inserting code to fulfill specific business requirements when compiling Java class files requires specific compiler plug-ins.
Class loading weaving: Enhance the bytecode of the class (insert bytecode) before loading the target shell into the virtual machine by using the custom class loader ClassLoader.
Runtime weaving: Cut insertion at some point in the run, such as dynamic proxy.
3. Application of AOP: Application of Android Dynamic Permission Based on Annotation and APT
As we all know, after Android 6.0, the system has more stringent control over privileges. Some dangerous privileges involving user privacy need to be applied dynamically when users use them. This is a good thing for users, but for developers, it is a non-user requirement (horizontal business). It will undoubtedly increase to integrate this kind of horizontal business into user requirement code. Because of the complexity of adding code, it is very appropriate for developers to implement this requirement with the idea of AOP, because it does not complicate the code.
Of course, based on different domestic ROM s, there may be many differences in the application rights and the processing of the rights. Here only provides a solution and a library based on the process of official processing rights: PermissionManager It is a summary of this part of learning.
3.1 Official Dynamic Privilege Application Procedure
Three methods in v4 packages are commonly used:
ContextCompat.checkSelfPermission()
ActivityCompat.requestPermissions()
ActivityCompat.shouldShowRequestPermissionRationale()
1. Check whether there is a permission:
if (ContextCompat.checkSelfPermission(context, Manifest.permission. SEND_SMS)
!= PackageManager.PERMISSION_GRANTED) {
// No authority, need to apply.
}else{
// Have jurisdiction
}
2. If you do not have permission to apply:
ActivityCompat.requestPermissions(activity, new String[]{Manifest.permission.SEND_SMS},10001 );
3. Processing callbacks of permission results.
@Override
public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) {
switch (requestCode) {
case 10001: {
if (grantResults.length > 0
&& grantResults[0] == PackageManager.PERMISSION_GRANTED) {
// User agrees to authorize
} else {
// User Denies Authorization
}
return;
}
}
}
4. Should Show Request Permission Rationale () return flase if the user clicks and does not ask again. This method returns true by clicking on the denial of authorization, so you can also pop up some instructions based on the return value of this method to indicate why the user needs this permission.
3.2 Annotation + APT + AOP Optimize the Logic of Applying for Permission
1. The Role of Annotation + APT
According to the annotations, find the class that initiated the permission request and generate a specific callback Listener at compile time to call back the result of the permission request to Activity or Fragment, such as:
It is MainActivity that initiates the permission request.
@NeedPermission
public class MainActivity extends AppCompatActivity {
private static final String TAG = "MainActivity";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
PermissionManager.requestPermissions(this,new String[]{Manifest.permission.CAMERA,Manifest.permission.WRITE_EXTERNAL_STORAGE});
}
@OnGranted
void granted(){
Log.d(TAG, "YES!User agrees to authorize!");
}
@OnDenied
void denied(){
Log.d(TAG, "oh NO!Users refused authorization!");
}
@OnShowRationale
void showRationale(){
Log.d(TAG, "Users choose not to ask");
}
}
Then the following code is generated at compile time:
public class MainActivity_PermissionListener implements PermissionListener<MainActivity> {
@Override
public void onGranted(MainActivity target) {
target.granted();;
}
@Override
public void onDenied(MainActivity target) {
target.denied();;
}
@Override
public void onShowRationale(MainActivity target) {
target.showRationale();;
}
}
2. Opportunity of AOP
Using AOP, you can weave the code related to the processing of permission results into the onRequestPermissionsResult() method so that you don't have to rewrite the method any more. All the logic for processing permission results is handled in a way that uses annotations (@OnGranted,@OnDenied,@OnShowRationale) tags.
Like this:
public class PremissionResultAspect {
@TargetClass(value = "android.support.v7.app.AppCompatActivity", scope = Scope.LEAF)
@Insert(value = "onRequestPermissionsResult", mayCreateSuper = true)
public void onRequestPermissionsResultAspect(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
Log.d("xsz", "onRequestPermissionsResultAspect -> The number of permissions is " + permissions.length);
Origin.callVoid();
PermissionListener listener = PermissionManager.getPermissionListener(This.get());
if (grantResults.length > 0 && listener != null) {
for (int i = 0; i < grantResults.length; i++) {
if (PermissionHelper.hasGrantedPermission(grantResults[i])) {
//User privileges have been granted
listener.onGranted(this);
} else {
// User Denies Authorization
if (ActivityCompat.shouldShowRequestPermissionRationale((Activity) This.get(), permissions[i])) {
//Users refused authorization, but did not click to ask again
//Hint the user why APP needs this permission
listener.onDenied(this);
} else {
//Return to false - > Users Click to stop asking
listener.onShowRationale(this);
}
}
}
}
}
}
This completes the simple encapsulation of the process of requesting dynamic permissions. Its main purpose is to learn the AOP idea, and then gradually improve the code details.