컴파일 시 메모를 사용하여 ButterKnife 구현

6001 단어
ButterKnife라는 프레임워크는 정말 사용하기 쉽고 대량의 코드를 간소화하여 효율적으로 손으로 쓴 코드에 비해 손실이 거의 없다.Butter Knife를 쓰지만.
그리고 ButterKnife의 원리도 알아야 한다.
그래서 ButterKnife 최초의 1.0 버전을 찾아서 그 원리를 배우고 모방해 봅시다.

최종 효과


Activity에서 메모 바인딩 컨트롤 사용
public class MainActivity extends AppCompatActivity {

    @InjectView(R.id.tv)
    public TextView mTextView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Views.inject(this);
        mTextView.setText("TextView");
    }
}

프레임은 주석에 따라 생성된 코드는 다음과 같다
public class MainActivity$$ViewInjector {
  public static void inject(MainActivity activity) {
    activity.mTextView = (android.widget.TextView) activity.findViewById(2131427413);
  }
}

프로젝트 구조

  • app
  • annotation
  • compiler
  • api

  • app


    바로 저희 안드로이드 프로젝트입니다.

    annotation


    정의 주해 InjectView 는 이 모듈에 놓습니다.

    compiler


    콜아웃 파서, 코드 생성

    api


    Butterkinfe의 API를 정의합니다. 예를 들어 우리가 자주 사용하는 Butterknife입니다.bind()

    annotation


    Android Studio에서 File - New Module - Java Library를 클릭하여 새 Java Module
    build.gradle 내용은 다음과 같다
    apply plugin: 'java'
    
    sourceCompatibility = JavaVersion.VERSION_1_7
    targetCompatibility = JavaVersion.VERSION_1_7
    

    그런 다음 메모를 정의합니다.
    import java.lang.annotation.ElementType;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
    
    @Retention(RetentionPolicy.CLASS)
    @Target(ElementType.FIELD)
    public @interface InjectView {
        int value();
    }
    

    compiler


    주석이 생기면, 이어서 주석을 해석하고 코드를 만들어야 한다.
    마찬가지로, Java Library,build.gradle의 정의는 다음과 같다
    apply plugin: 'java'
    
    sourceCompatibility = JavaVersion.VERSION_1_7
    targetCompatibility = JavaVersion.VERSION_1_7
    
    dependencies {
        compile 'com.google.auto.service:auto-service:1.0-rc2'
        compile project(':annotation')
    }
    

    Google의 auto-service 의존도가 여기에 도입되었습니다.META-INF 파일을 생성하는 데 유용합니다.
    이어서 주해 해석기를 만들고 실현하는 방법process
    @AutoService(Processor.class)
    public class AnnotationProcessor extends AbstractProcessor {
        @Override
        public boolean process(Set extends TypeElement> annotations, RoundEnvironment env) {
            return false;
        }
    }
    

    이 방법에서 주로 몇 가지 일을 했다
  • InjectView 메모를 사용하는 모든 클래스 찾기
  • InjectView 메모 해석 정보
  • 정보에 따라 코드 생성
  • api


    Android Library 만들기Views 클래스 만들기
    import android.app.Activity;
    
    import java.lang.reflect.Method;
    
    public class Views {
    
        private Views() {
            // No instances.
        }
    
        public static void inject(Activity activity) {
            try {
                Class> injector = Class.forName(activity.getClass().getName() + "$$ViewInjector");
                Method inject = injector.getMethod("inject", activity.getClass());
                inject.invoke(null, activity);
            } catch (RuntimeException e) {
                throw e;
            } catch (Exception e) {
                throw new RuntimeException("Unable to inject views for activity " + activity, e);
            }
        }
    }
    

    왜냐하면 우리가 생성한 코드의 클래스 이름은 Activity +$$ViewInjector이기 때문이다.예를 들어Activity명HelloActivity이라면 생성된 코드의 클래스명은HelloActivity$$ViewInjector이다.따라서 여기서 반사를 이용하여 클래스 이름의 규칙에 따라 우리가 생성한 클래스를 찾아 inject() 방법을 호출한다.
    물론 반사를 사용하지 않고 생성된 클래스HelloActivity$$ViewInjector.inject(Activity activity)를 직접 호출해도 된다.그러나 매번 주석이 끝날 때마다rebuild에서 프로젝트를 한 번만 하고 코드가 생성된 후에야 호출할 수 있다HelloActivity$$ViewInjector.inject(Activity activity).이렇게 하는 것도 귀찮으니 직접 주해를 쓰는 것이 좋겠다.

    app


    build.gradle의 정의는 다음과 같다
    apply plugin: 'com.android.application'
    apply plugin: 'com.neenbedankt.android-apt'
    
    android {
        compileSdkVersion 24
        buildToolsVersion "25.0.2"
        defaultConfig {
            applicationId "com.okada.viewinject"
            minSdkVersion 14
            targetSdkVersion 24
            versionCode 1
            versionName "1.0"
        }
    
        buildTypes {
            release {
                minifyEnabled false
                proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
            }
        }
    
    }
    
    dependencies {
        compile fileTree(include: ['*.jar'], dir: 'libs')
        compile 'com.android.support:appcompat-v7:24.2.1'
        compile project(':annotation')
        apt project(':compiler')
        compile project(':api')
    }
    

    프로젝트의build에 있습니다.gradle의 정의는 다음과 같다
    buildscript {
        repositories {
            jcenter()
        }
        dependencies {
            classpath 'com.android.tools.build:gradle:2.2.0'
            classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'  //      
        }
    }
    
    allprojects {
        repositories {
            jcenter()
        }
    }
    
    task clean(type: Delete) {
        delete rootProject.buildDir
    }
    

    이제 메모를 사용할 수 있습니다.
    public class MainActivity extends AppCompatActivity {
    
        @InjectView(R.id.tv)
        public TextView mTextView;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            Views.inject(this);
            mTextView.setText("TextView");
        }
    }
    

    그리고 Build - Rebuild Project를 클릭하면 app - build - generated - source - apt 폴더에서 생성된 코드를 볼 수 있습니다

    프로젝트 소스


    https://github.com/okadaNana/ViewInject

    참조 소스

  • 안드로이드 APT(컴파일 시 코드 생성) 모범 사례
  • 안드로이드는 컴파일할 때 주석을 기반으로 하는 항목을 어떻게 작성하는가
  • 안드로이드의 사용 apt 컴파일 시 주석
  • ButterKnife
  • 좋은 웹페이지 즐겨찾기