AndroidでのAOPの実現とAspectJの使用

1. OOPとAOPの簡単な紹介と違い

OOP(オブジェクト指向プログラミング):これは、私たちのAndroidにおけるオブジェクト指向の開発です。オブジェクト指向の3つの特性は、カプセル化、継承、ポリモーフィズムです。ここで繰り返すことはあまりありません。

AOP(アスペクト指向プログラミング):アスペクト指向プログラミング; AOPは、ビジネスロジック処理プロセスの側面、つまりプログラム処理の特定のステップまたはステージを抽出して、コード間の低カップリング、コード分離、およびコード改善を実現します再利用性


第二に、AndroidでのAOPの実現

2.1 Javaアノテーションの概要

andoridの開発ではアノテーション関数が使用されています。アノテーション付きのサードパーティライブラリはButterKnif、dagger2、EventBus、Retrofitです。実際、これらのライブラリのコア関数の一部もAOPに基づいて実装されています。ただし、APTなどの他のプラグインも使用します。APTはプログラムのコンパイル中にコードの注釈情報をスキャンし、手動で処理することなく関数を実装するためのJavaコードを生成します。

Javaアノテーションは、JDK5.0で導入されたアノテーションメカニズムです。私たちのコードでは。@Override:メソッドが親メソッドをオーバーライドすることがよくわかります。

java中的Annotation:

@Deprecated  --  所标注内容,不再被建议使用。
@Override    --  只能标注方法,表示该方法覆盖父类中的方法。
@Documented  --  所标注内容,可以出现在javadoc中。
@Inherited   --  只能被用来标注“Annotation类型”,它所标注的Annotation具有继承性。
@Retention   --  只能被用来标注“Annotation类型”,而且它被用来指定Annotation的RetentionPolicy属性。
@Target      --  只能被用来标注“Annotation类型”,而且它被用来指定Annotation的ElementType属性。
@SuppressWarnings --  所标注内容产生的警告,编译器会对这些警告保持静默。

2.2、findViewById関数を実現するためのカスタムアノテーションとリフレクション

カスタム注釈、独自の注釈を実現

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyInject {
    
    
    int value();
}

MyInjectのリフレクション処理ツール
public class MyInjectUtils {
    
    
    public static void injectViews(Activity activity) {
    
    
        Class<? extends Activity> object = activity.getClass(); // 获取activity的Class
        Field[] fields = object.getDeclaredFields(); // 通过Class获取activity的所有字段
        for (Field field : fields) {
    
     // 遍历所有字段
            // 获取字段的注解,如果没有ViewInject注解,则返回null
            MyInject viewInject = field.getAnnotation(MyInject.class);
            if (viewInject != null) {
    
    
                int viewId = viewInject.value(); // 获取字段注解的参数,这就是我们传进去控件Id
                if (viewId != -1) {
    
    
                    try {
    
    
                        // 获取类中的findViewById方法,参数为int
                        Method method = object.getMethod("findViewById", int.class);
                        // 执行该方法,返回一个Object类型的View实例
                        Object resView = method.invoke(activity, viewId);
                        field.setAccessible(true);
                        // 把字段的值设置为该View的实例
                        field.set(activity, resView);
                    } catch (NoSuchMethodException e) {
    
    
                        e.printStackTrace();
                    } catch (IllegalAccessException e) {
    
    
                        e.printStackTrace();
                    } catch (InvocationTargetException e) {
    
    
                        e.printStackTrace();
                    }
                }
            }
        }
    }
}

活動での使用
    @MyInject(R.id.button)
    Button button;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
    
    
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        MyInjectUtils.injectViews(this);
    }

このようにして、findViewByIdの機能を実現しました。この関数がButterKnifのfindViewByIdとよく似ていることを見つけるのは難しくありませんが、本質的な違いがあります。リフレクションを使用するため、Androidではパフォーマンスが非常に高くなります。そのため、これらのサードパーティライブラリは、Annotation + APTを使用して注釈をJavaコードに変換し、パフォーマンスの低下を回避します。しかし、これを知っている場合、インタビュアーはサードパーティライブラリの原則について引き続き質問するので、あなたは無言ではありません!

3番目に、AspectJの使用と使用のシナリオ(強調)

AspectJ:コード生成ツールです。AspectJ文法は、コード生成ルールの文法を定義するために使用されます

3.1、プロジェクトでの参照

プロジェクトbuild.gradleで:

    dependencies {
    
    
        //...
        classpath 'com.hujiang.aspectjx:gradle-android-plugin-aspectjx:2.0.8'
    }

アプリのbuild.gradleの上部に追加

apply plugin: 'android-aspectjx'

3.2。使用シナリオ:データ埋め込みポイント

通常、データ埋め込みポイントは、アプリケーションのregisterActivityLifecycleCallbacksを通じて監視されます。ただし、ここではAspectJを使用します。コードは次のとおりです(ここのタグは@ Before、@ After、キーワードの実行、呼び出しについては後で詳しく説明します。ここでは最初に関数を実装します)。

//标注我们要通过Aspect语法生成代码的辅助类
@Aspect
public class AspectHelper {
    
    
    private final String TAG = this.getClass().getSimpleName();
    
    //com.lihang.aoptestpro.BaseActivity 是我项目里的BaseActivity
    //这句代码实现的功能是:会打印我们项目里所有Activity里所有的on开头的方法
    //joinPoint.getThis().getClass().getSimpleName() 当前Activity的类名
    @Before("execution(* com.lihang.aoptestpro.BaseActivity.on**(..))")
    public void onActivityStart(JoinPoint joinPoint) throws Throwable {
    
    
        String key = joinPoint.getSignature().toString();
        Log.i(TAG, key + "============" + joinPoint.getThis().getClass().getSimpleName());
    }

    //会打印我们项目里所有Activity里的onPause方法。
    @Before("execution(* com.lihang.aoptestpro.BaseActivity.onPause(..))")
    public void onActivityPause(JoinPoint joinPoint) throws Throwable {
    
    
        String key = joinPoint.getSignature().toString();
        Log.i(TAG, key + "============" + joinPoint.getThis().getClass().getSimpleName());
    }
}

この時点で、すべてのアクティビティのライフサイクルを取得した後、埋め込みポイント機能を実現できます。メモと要約:

  • 絶対に使わない
@Before("execution(* android.app.Activity.on**(..))")

ほとんどのインターネットはこの文を使用しており、練習では、私たちのアクティビティを歩くことに加えて、システムアクティビティとFragmentActivityも実行されます。少なくとも3回


  • BaseActivityを使用
@Before("execution(* com.lihang.aoptestpro.BaseActivity.on**(..))")

BaseActivityがシステムライフサイクルを実装していない場合、それが機能しないことがわかります。したがって、たとえば、onStartとonPauseのライフサイクルをキャッチする場合は、メソッドの本文が空であっても、BaseActivityで実装する必要があります。

実際、これは次の構文でも実現できます。ただし、すべてのアクティビティでクラス名の末尾に「アクティビティ」文字列を使用する必要がある場合

@Before("execution(* com.lihang.aoptestpro.*Activity.on**(..))")

3.3。使用シナリオ:ログイン検証

プロジェクトの開発中、一部の機能は使用する前にログインする必要があることがよくあります。ログインしていない場合は、ログインページにアクセスしてください。このように、if / elseの判断は避けられません。以下のように、以下のコードをクリックしてください

    public void follow() {
    
    
        if (MyApplication.getInstance().getLoginUser() != null) {
    
    
            User user = MyApplication.getInstance().getLoginUser();
            Log.i(TAG, "已登录,user不为空,用user信息去实现关注");
        } else {
    
    
            Log.i(TAG, "未登录,跳转登录页面");
        }
    }

では、AOPを非侵入的に使用する方法は?
最初にラベルを定義します

@Target(ElementType.METHOD)//这里是标注方法,之前那个Filed是标注属性
@Retention(RetentionPolicy.RUNTIME)
public @interface IsLogin {
    
    
}

次に、アスペクトを見てください。

@Aspect
public class AspectHelper {
    
    
    private final String TAG = this.getClass().getSimpleName();
    
    @Around("execution(@com.lihang.aoptestpro.IsLogin * *(..))")
    public void isLoginOn(ProceedingJoinPoint joinPoint) throws Throwable {
    
    
        if (MyApplication.getInstance().getLoginUser() != null) {
    
    
            //joinPoint.proceed()可以看成就是我们用@IsLogin标注的那个方法,调用proceed意思就是继续执行方法
            //这里的意思就是所有用标注@IsLogin标注的,是登录状态才会继续执行方法,否则会执行我们下面的去登录,不会执行原方法
            joinPoint.proceed();
        } else {
    
    
            Log.i(TAG, "user为空,快去登录把!!");
        }
    }
}

次にfollowメソッドを見てください。@IsLoginでマークした後、ログインステータスを直接処理できます。本当に低いカップリング、高いコード再利用性

    @IsLogin
    public void follow() {
    
    
        User user = MyApplication.getInstance().getLoginUser();
        Log.i(TAG, "已登录,user不为空,用user信息去实现关注");
    }

4、AspectJの一般的なキーワードとその違い

4.1一般的な注釈の紹介

  • @Before:メソッドの前に実行する手段
    //意思是onActivityPause会在BaseActivity.onPause()方法前执行
    @Before("execution(* com.lihang.aoptestpro.BaseActivity.onPause(..))")
    public void onActivityPause(JoinPoint joinPoint) throws Throwable {
    
    

    }
  • @After:同じ理由は、メソッドの後に実行することです
  • @Around:@Beforおよび@Afterの機能を含み、制御できます
    //joinPoint.proceed()是控制方法是否继续往下执行
    //在joinPoint.proceed()前的逻辑代码,就是实现@Before的功能,在方法前执行
    //在joinPoint.proceed()后的逻辑代码,就是实现@After的功能,在方法后执行
    @Around("execution(@com.lihang.aoptestpro.IsLogin * *(..))")
    public void isLoginOn(ProceedingJoinPoint joinPoint) throws Throwable {
    
    
        if (MyApplication.getInstance().getLoginUser() != null) {
    
    
            joinPoint.proceed();
        } else {
    
    
            Log.i("MainActivity", "user为空,快去登录把!!");
        }
    }

注意点:

  1. ActionがBeforeまたはAfterの場合、メソッドの入力パラメーターはJoinPointです。
  2. ActionがAroundの場合、メソッドの入力パラメーターはProceedingPointです。
  3. AroundとBeforeおよびAfterの最大の違い:
    ProceedingPointは、ターゲットメソッドを実行するための続行メソッドを提供するという点で、JoinPointとは異なります。

4.2一般的なキーワードの紹介

多くの情報、同じコードを調べました。生成されたコードを見てください。

  • 呼び出し:関数本体の外側に挿入

簡単に言えば:

Call(Before)
Pointcut{
    
    
   Pointcut Method
}
Call(After)

  • 実行:関数本体に挿入

簡単に言えば:

Pointcut{
    
    
  execution(Before)
    Pointcut Method
  execution(After)
}

私はそれがどのように機能するかを知っていますが。ただし、作成者にも質問があります。つまり、呼び出しと実行で同じ機能を実現できます。しかし、どのシーンを使用する方が良いですか?知っている人が答えてくれるといいのですが

参照

AspectJのAndroidへの強力な挿入をご覧ください

。AOPとAndroidの愛と憎しみ

Androidオートメーションの埋め込みポイント:AspectJに基づくHujiang SDKの使用。

私の公式アカウント

最近は面接の準備もしています。ファインマンの学習方法は、自分で理解することから始まり、わかりやすい言葉で説明します。ブログも目的です。インタビュー資料を準備している同僚は新しい知識ポイントに遭遇し、それらを壊したい場合は、必要に応じて公式アカウントをフォローできます

おすすめ

転載: blog.csdn.net/leol_2/article/details/105416332