Android APT 컨트롤 주입 프레임 워 크 SqInject 구현 예제

저자.
안녕하세요,저 는 작은 흠 이 라 고 합 니 다.크레용 작은 흠 이 라 고 불 러 도 됩 니 다.😊;
저 는 17 년 동안 중 산 대학 교 를 졸 업 했 고 2018 년 7 월 에 37 모 바 일 게임 안 드 로 이 드 팀 에 가 입 했 으 며 구 방 디지털 에서 안 드 로 이 드 개발 엔지니어 로 일 했 습 니 다.
현재 37 모 바 일 게임 안 드 로 이 드 팀 의 해외 책임자 로 관련 업무 개발 을 맡 고 있다.동시에 일부 기초 건설 관련 업 무 를 동시에 고려 하 다.
배경
게임 발행 에 있어 서 가방 을 자 르 는 경우 가 많 습 니 다.R.id.xxx 를 직접 사용 하면 컴 파일 할 때 resources.arsc 가 다시 컴 파일 되 기 때문에 R 류 의 id 값 과 resources.arsc 의 대응 관계 가 이상 하여 프로그램 이 이상 합 니 다.우 리 는 두 가지 해결 방법 이 있다.
하 나 는 패 킷 을 자 르 는 과정 에서 R 류 의 값 을 바로 잡 는 것 으로 실현 방안 에 대한 구체 적 인 소 개 는 볼 수 있다.
게임 에서 패키지 자원 인덱스 충돌 해결 방안 을 발행 합 니 다.링크 는 다음 과 같 습 니 다.
//www.jb51.net/article/207579.htm
또 다른 솔 루 션 은 getIdentifier 를 사용 하여 자원 ID 를 가 져 오고 R 류 를 사용 하 는 것 을 포기 하 는 것 입 니 다.이런 방식 으로 인 코딩 이 번 거 롭 고 getIdentifier 에 문자열 을 써 야 하기 때문에 잘못 쓰기 쉽 고 컴 파일 과정 에서 발견 할 수 없습니다.이러한 상황 을 바탕 으로 getIdentifier 기반 컨트롤 주입 프레임 워 크 를 개 발 했 습 니 다.
1.APT 기술 소개
1.APT 정의
APT(Annotation Processing Tool)는 주 해 를 처리 하 는 도구 입 니 다.정확히 말 하면 자바 c 의 도구 입 니 다.컴 파일 할 때 주 해 를 스 캔 하고 처리 하 는 데 사 용 됩 니 다.주해 처리 기 는 자바 코드 를 입력 하여 자바 파일 을 출력 으로 생 성 합 니 다.
2.주해 정의
1.주 해 는 자바 코드 에 추 가 될 수 있 는 메타 데이터 로 클래스,방법,변수,파라미터 와 가방 은 모두 주해 로 수식 할 수 있 습 니 다.
2.주 해 는 수 정 된 코드 에 직접적인 영향 을 주지 않 습 니 다.
3.APT 원리 소개

Annotation processing 은 컴 파일 단계 에서 실 행 됩 니 다.그 원 리 는 자바 소스 코드 를 읽 고 주 해 를 해석 한 다음 에 새로운 자바 코드 를 생 성 하 는 것 입 니 다.새로 생 성 된 자바 코드 는 마지막 으로 자바 바이트 코드 로 컴 파일 되 었 으 며,주석 해석 기(Annotation Processor)는 읽 은 자바 클래스 를 변경 할 수 없습니다.예 를 들 어 자바 방법 을 추가 하거나 삭제 할 수 없습니다.
2.APT 실전 사용
1.SqInject 프레임 워 크 원본
모 바 일 게임 발행 에 있어 서 가방 을 자 르 고 게임 을 SDK 1 의 가방 을 받 으 며 역 컴 파일 을 통 해 smali 파일 과 다른 자원 파일 을 교체 하 는 방식 으로 채널 SDK 2 의 채널 가방 으로 교체 해 야 합 니 다.이 역 컴 파일 과정 에서 자원 색인 ID(즉 R 류 와 resources.arsc 의 ID 맵 관계)가 충돌 하여 프로그램 에 이상 이 생 길 수 있 습 니 다.즉,특수 처 리 를 하지 않 으 면 채널 SDK 및 SDK 발행 은 R 류 를 직접 사용 할 수 없습니다.getIdentifier 를 사용 하여 자원 ID 를 가 져 와 야 합 니 다.
프로그램 에서 getIdentifier 를 사용 하도록 요구 하 는 것 은 개발 과정 에서 비교적 번 거 로 운 일이 다.이러한 조건 하에 서 우 리 는 butterknife 와 같은 프레임 워 크 를 사용 할 수 없다.따라서 우 리 는 butterknife 를 모방 하여 getIdentifier 기반 컨트롤 주입 프레임 워 크 SqInject 를 개발 했다.다음은 SqInject 의 실현 을 소개 합 니 다.먼저 간단하게 사용 해 보 겠 습 니 다.

public class MainActivity extends AppCompatActivity {
	
 //  ID
 @BindView(SqR.id.tv)
 TextView hello;

 @Override
 protected void onCreate(Bundle savedInstanceState) {
 super.onCreate(savedInstanceState);
 setContentView(R.layout.activity_main);
 SqInject.bind(this);
 Log.e("SqInject", hello.getText().toString());
 }

 //      
 @OnClick(SqR.id.tv)
 public void click(View view) {
 Intent intent = new Intent(MainActivity.this, TestActivity.class);
 startActivity(intent);
 }
}
2.SqInject 의 실현 원리
2.1 주해 프로세서 모듈 실현
위 에서 말 한 APT 는 코드 생 성 에 자주 사 용 됩 니 다.SqInject 에서 APT 주해 처리 부분 에서 절 차 는 다음 그림 과 같 습 니 다.

컴 파일 과정 에서 주 해 를 스 캔 하여 자바 코드 를 생 성 한 후 다시 컴 파일 합 니 다.
SqInject 코드 에서 다음 과 같이 실 현 됩 니 다.

@AutoService(Processor.class)
@SupportedSourceVersion(SourceVersion.RELEASE_7)
public class SqInjectProcessor extends AbstractProcessor {

 ...
	
 //    
 @Override
 public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {
 //       ,ResChecker    id   ,     "  +$ViewBinder ,      
 BindViewBuilder bindViewBuilder = new BindViewBuilder(roundEnvironment, mResChecker, mElementUtils, mTypeUtils, mFiler, mMessager);
 bindViewBuilder.build();
 //id     ,ResChecker    id   ,     "  +$IdBinder ",      
 BindIdsBuilder bindIdsBuilder = new BindIdsBuilder(roundEnvironment, mResChecker, mElementUtils, mTypeUtils, mFiler, mMessager);
 bindIdsBuilder.build();
 return false;
 }
 
}
컨트롤 이 관련 코드 를 주입 하기 전에 프레임 워 크 에서 자원 id 의 합 법성 을 먼저 검 측 합 니 다.이 프레임 워 크 에서 주 해 를 사용 할 때 문자열 이 들 어 옵 니 다.자원 이름 은 대응 하 는 자원 이 존재 하지 않 을 수 있 으 며 프레임 워 크 는 해당 하 는 검 측 을 할 수 있 습 니 다.
2.2 자원 검 측
Android 가 자원 을 컴 파일 하 는 과정 에서 R 류 가 생 성 됩 니 다.즉,R 류 에 존재 하 는 경우 에 만 getIdentifier 로 얻 을 수 있 습 니 다.그러면 우 리 는 R 류 로 들 어 오 는 매개 변수 가 합 리 적 인지 확인 할 수 있 습 니 다.코드 는 다음 과 같 습 니 다.

/**
 *     id R       
 * @param name
 * @param type
 * @return
 */
 public boolean isIdValid(String name, String type) {
 String RClassName = mPackageNeme + ".R." + type;
 TypeElement RClassType = mElementUtils.getTypeElement(RClassName);
 if (RClassType == null) {
  mMessager.printMessage(Diagnostic.Kind.ERROR, RClassName + "   ,         ,      ");
 } else {
  //    
  for (Element element : RClassType.getEnclosedElements()) {
  String fieldName = element.getSimpleName().toString();
  if (name.equals(fieldName)) {
   return true;
  }
  }
 }
 return false;
 }
2.3 주석 해석,코드 생 성
BindView 를 예 로 들 면 코드 는 다음 과 같 습 니 다.

/**
 *   BindView  
 * @return
 */
 private Map<TypeElement, List<VariableElement>> parseBindView(){
 Set<Element> elements = (Set<Element>) mRoundEnvironment.getElementsAnnotatedWith(BindView.class);
 if (!Utils.isEmpty(elements)) {
  Map<TypeElement, List<VariableElement>> map = new HashMap<>();
  for (Element element : elements) {
  if (element instanceof VariableElement) {
   //        
   TypeElement targetElement = (TypeElement) element.getEnclosingElement();
   mTargetSet.add(targetElement);
   if (map.get(targetElement) == null) {
   List<VariableElement> targetStringLists = new ArrayList<>();
   targetStringLists.add((VariableElement) element);
   map.put(targetElement, targetStringLists);
   } else {
   map.get(targetElement).add((VariableElement) element);
   }
  }
  }
  return map;
 }
 return null;
 }
BindView 주 해 를 분석 한 후 자바 poet 을 사용 하여 코드 를 생 성 합 니 다.편폭 이 제한 되 어 있 습 니 다.다음은 매개 변수 와 생 성 코드 의 일부분 만 보 여 줍 니 다.

if (mBindViewIdTargetMap != null && mBindViewIdTargetMap.get(targetElement) != null) {
  List<VariableElement> viewElements = mBindViewIdTargetMap.get(targetElement);
  //   
  for (VariableElement viewElement : viewElements) {
   //     
   String fieldName = viewElement.getSimpleName().toString();
   //    
   TypeMirror typeMirror = viewElement.asType();
   TypeElement fieldClassElement = (TypeElement) mTypeUtils.asElement(typeMirror);
   mMessager.printMessage(Diagnostic.Kind.NOTE, "        : " + fieldClassElement.getQualifiedName().toString());
   TypeElement fieldType = mElementUtils.getTypeElement(fieldClassElement.getQualifiedName());
   ClassName fieldClassName = ClassName.get(fieldType);
   //  @BindView    ,   
   String name = viewElement.getAnnotation(BindView.class).value();
   //        
   if(!mResChecker.isIdValid(name, "id")){
   mMessager.printMessage(Diagnostic.Kind.ERROR, "R      id " + name + "  ");
   }
   methodBuilder.addStatement("target.$N = $T.findRequiredViewAsType(source, $S, $S, $T.class)", fieldName, JptConstants.UTILS, name, "field " + fieldName,fieldClassName);
  }
  }
요약 하면 주해 프로세서 에서 가장 중요 한 두 부분 중 하 나 는 주 해 를 해석 하고 파 라 메 터 를 얻 은 다음 자바 poet 프레임 워 크 를 이용 하여 코드 를 만 드 는 것 입 니 다.
생 성 된 코드 를 보 겠 습 니 다.

public class MainActivity$ViewBinder implements ViewBinder<MainActivity> {
 @Override
 public void bindView(final MainActivity target, final View source) {
 //            
 target.hello = ViewUtils.findRequiredViewAsType(source, "tv", "field hello", TextView.class);
 IdUtils.findViewByName("tv", source).setOnClickListener(new DebouncingOnClickListener() {
 public void doClick(View v) {
 target.click(v);
 }
 } );
 }
}
여기까지 APT 생 성 코드 를 소 개 했 습 니 다.본 논문 에서 언급 한 프레임 워 크 SqInject 는 우리 가 일상 업무 에서 사용 할 수 있 는 SqInject 프레임 워 크 입 니 다.이 프레임 워 크 는 오픈 소스 입 니 다.주 소 는github.com/37sy/SqInje…관심 있 는 환영 스타 입 니 다.
이상 은 안 드 로 이 드 APT 구현 컨트롤 주입 프레임 워 크 SqInject 의 예제 에 대한 상세 한 내용 입 니 다.안 드 로 이 드 APT 컨트롤 주입 프레임 워 크 SqInject 에 관 한 자 료 는 다른 관련 글 을 주목 하 십시오!

좋은 웹페이지 즐겨찾기