AOPまたは分析用の自転車の書き方

画像

大規模なプロジェクトでは、追跡イベントのロジックを実装するときに、追跡メソッドを呼び出すことでコード汚染の問題に直面することがよくあります。



上記の理由により、私自身のソリューションを作成することになりました。もちろん、これは私のgitリポジトリとこの記事を超えるものではありません。



誰が反射と遅いコードを恐れていない-私は猫を求めます。



たぶん必要ない?



製品アプリケーションでは、このボタンをタップする回数またはリストをスクロールする回数は、製品としてのアプリケーションの開発、その外部インターフェースおよび機能に依存します。

このため、コードは、ビュー全体に散らばっているが特定のオブジェクトの状態を考慮した追跡イベントのメソッド呼び出しをカットし始めます。

新しいイベントを追加して、空中から遭遇たいくつかの問題を解決することで、痛みを軽減したいと思います。



私は次を達成したい:

1)新しいイベントのコードの最小量。

2)ビュー内のコードの最小量。

3)オブジェクトをイベントにバインドする便利なシステム。



この決定は、注釈、反映、および側面に基づいています。

アプリケーションのアスペクトを実装するには、AspectJを使用します。 これは、Java言語のアスペクト指向の拡張機能です。 現時点では、これはおそらく最も人気のあるAOPエンジンです。

ところで、このエンジンは、アスペクトのパラダイムを提案したまさにその人々によって開発されました。



仕組み

必要なメソッドの呼び出しをインターセプトするには、 @Aspectとしてマークされたクラスを作成します。

次に、メソッドを使用してジャンクションを作成し、ジャンクションで実行される@Aroundとマークされたメソッドを作成します。 AspectJは機能が豊富で、多数のスライスポイントオプションとヒントをサポートしていますが、それはポイントではありません。



@Aspect public class ViewEventsInjector { private static final String POINTCUT_METHOD = "execution(@com.makarov.ui.tracker.library.annotations.ViewEvent * *(..))"; @Pointcut(POINTCUT_METHOD) public void methodAnnotatedWithViewEvent() { } @Around("methodAnnotatedWithViewEvent()") public Object joinPoint(ProceedingJoinPoint joinPoint) throws Throwable { MethodSignature ms = (MethodSignature) joinPoint.getSignature(); Method method = ms.getMethod(); Object object = joinPoint.getThis(); Object[] arg = joinPoint.getArgs(); /*  ,                */ Object result = joinPoint.proceed(); return result; } }
      
      







実装



観察されたビューの注釈

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





注釈パラメーター-イベント/ログをより便利に読み取るためのビュー要素の名前。



その結果、初期化後、私たちが追跡した要素のidビューを含むMapができました。



イベントの傍受は、すべて側面の肩にかかっています。

イベントビューメソッドにラベルを付けた注釈に焦点を当てます。



ロジックは次のとおりです。

1)メソッド呼び出しをインターセプトします。

2)ハンドラーを見つけます。これは、可能なすべてのメソッドハンドラーとマップするために追加しました。

3)アノテーションのパラメーターによって、追跡する必要があるすべてのオブジェクトを見つけます。

4)受信したデータからイベントオブジェクトを作成します。

4)イベントを保存します。



イベントがハングするメソッドの注釈:



 @Retention(RetentionPolicy.CLASS) @Target({ ElementType.CONSTRUCTOR, ElementType.METHOD }) public @interface ViewEvent { String[] value(); }
      
      





イベントにバインドするモデルを統合するために、モデルが実装する必要があるインターフェイスを導入します。



 public interface LoggingModel { Map<String, String> getModelLogState(); }
      
      





インターフェイス実装の例:



 public class Artist implements LoggingModel { private final String mId; private final String mName; public Artist(String id, String name){ mId = id; mName = name; } /* ... */ @Override public Map<String, String> getModelLogState() { Map<String, String> logMap = new HashMap<>(); logMap.put("artistId", mId); logMap.put("artistName", mName); return logMap; } }
      
      







すべてをまとめる



最後に、これらすべてを収集し、いくつかの注釈で必要なイベントの追跡を開始します。



 public class MainActivity extends AppCompatActivity implements View.OnClickListener, TextWatcher{ public static final String TAG = MainActivity.class.getSimpleName(); @LoggerView("first button") public Button button; public Button button2; @LoggerView("test editText") public EditText editText; public Artist artist = new Artist("123", "qwe"); public Track track = new Track("ABS"); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); /*  view  */ ViewEventsInjector.init(); ViewEventsInjector.inject(this); } @Override @AttachState({"artist","track"}) @ViewEvent(ViewEventsTracker.CLICK) public void onClick(View v) { Log.d(TAG, "method onClick - " + v.getId()); } @Override public void beforeTextChanged(CharSequence s, int start, int count, int after) { } @Override public void onTextChanged(CharSequence s, int start, int before, int count) { } @Override @AttachState({"artist"}) @ViewEvent(ViewEventsTracker.AFTER_TEXT_CHANGED) public void afterTextChanged(Editable s) { Log.d(TAG, "afterTextChanged"); } }
      
      





プロジェクトを開始し、ボタンをタップして、テキストボックスに何かを入力します。



また、ビューにロジックが1行も表示されていない待望のログが表示されます。



 07-13 13:52:16.406 D/SimpleRepository﹕ Event{nameView='fist button', nameEvent='onClick', mModelList=[Artist@52a30ec8, Track@52a31040], methodParameters = null, mDate = Mon Jul 13 13:52:16 EDT 2015} 07-13 13:52:24.254 D/SimpleRepository﹕ Event{nameView='textView', nameEvent='afterTextChanged', mModelList=[Artist@52a30ec8], methodParameters= {text = hello}, mDate=Mon Jul 13 13:52:24 EDT 2015}
      
      





私の意見では、この単純なプロジェクトエンジニアでさえ、いくつかの問題を解決し、おそらく日常的なアクションの時間をある程度節約しました。

さらに時間をかける場合は、アスペクトのロジックを最適化することができます。たとえば、オブジェクトのストレージを少しやり直して、リフレクションによって毎回オブジェクトを受け取らないようにすることができます。



誰かが突然それを取り上げて、このことを思い出すことに決めた場合、 ここで歓迎されます



All Articles