參考文章
http://blog.csdn.net/johnny901114/article/details/52662376
http://blog.csdn.net/johnny901114/article/details/52664112
http://blog.csdn.net/johnny901114/article/details/52672188
http://www.cnblogs.com/peida/archive/2013/04/24/3036689.html
這個(gè)demo沒有在項(xiàng)目中涉及废亭,只是用來理解java的注解及使用,并不是一個(gè)完整的框架。通過這個(gè)demo,能掌握注解的相關(guān)知識(shí)趁桃,并且提高了自己的逼格雁歌,O__O "…主要是提高了逼格呻引。
一颜及、
在項(xiàng)目中用到了mvp,封裝了一下
public class EditorActivity extends BindingActivity<ActivityEditorBinding, EditorPresenter> implements EditorContract.View{
@Override
protected EditorPresenter createPresenter() {
return new EditorPresenter(this);
}
@Override
protected int createLayoutId() {
return R.layout.activity_editor;
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
@Override
public void showMessage(String message) {
GlobalToast.show(message);
}
}
上面創(chuàng)建了一個(gè)簡單的activity仆葡, 再createPresenter方法中創(chuàng)建了Presenter在createLayoutId方法中傳入了布局的id赏参。每個(gè)activity都要有這兩個(gè)方法,寫起來還挺繁瑣的沿盅。有沒有更好的方法呢把篓?
要是像butterknife和dragger一樣通過注解注入該多好
二、
一個(gè)叫刀一個(gè)劍的腰涧,這個(gè)demo的名字就叫fork把韧掩,和butterknife一樣,都和吃有關(guān)系
先看一下完成之后的activity
@ForkLayoutId(R.layout.activity_main)
@ForkPresenter(MainPresenter.class)
public class MainActivity extends ForkActivity<ActivityMainBinding, MainPresenter> implements MainContract.View {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Fork.bind(this);
binding.rvText.setText("haha");
mvpPresenter.run();
}
@Override
public void showMessage(String message) {
Toast.makeText(this, message, Toast.LENGTH_SHORT).show();
}
}
費(fèi)了大半天的勁窖铡,少了倆方法疗锐,呵呵
三坊谁、
說了一堆廢話,記錄一下實(shí)現(xiàn)吧滑臊!
1口芍、首先再android studio 中創(chuàng)建一個(gè)java library(一定要是java 項(xiàng)目,不然android項(xiàng)目可找不到項(xiàng)目需要的包)
module 名字就叫 fork-annotations 吧雇卷,這里準(zhǔn)備主要放用到的注解
首先鬓椭,創(chuàng)建今天的第一個(gè)注解 名字叫ForkLayoutId
@Retention(RetentionPolicy.SOURCE)
@Target(ElementType.TYPE)
public @interface ForkLayoutId {
int value();
}
注解跟普通的java接口的定義很像,但接口是給程序員看的关划,而注解是給計(jì)算機(jī)看的小染,所以這里的interface前面加上了一個(gè)@
@Retention
是用來標(biāo)記這個(gè)注解的生命周期,有以下幾種
- RetentionPolicy.SOURCE : 注解只保留在源文件中
- RetentionPolicy.CLASS : 注解保留在class文件中祭玉,在加載到JVM虛擬機(jī)時(shí)丟棄
- RetentionPolicy.RUNTIME : 注解保留在程序運(yùn)行期間氧映,此時(shí)可以通過反射獲得定義在某個(gè)類上的所有注解春畔。
@Target
是用來標(biāo)記注解所修飾的屬性
- ElementType.TYPE:說明該注解只能被聲明在一個(gè)類前脱货。
- ElementType.FIELD:說明該注解只能被聲明在一個(gè)類的字段前。
- ElementType.METHOD:說明該注解只能被聲明在一個(gè)類的方法前律姨。
- ElementType.PARAMETER:說明該注解只能被聲明在一個(gè)方法參數(shù)前振峻。
- ElementType.CONSTRUCTOR:說明該注解只能聲明在一個(gè)類的構(gòu)造方法前。
- ElementType.LOCAL_VARIABLE:說明該注解只能聲明在一個(gè)局部變量前择份。
- ElementType.ANNOTATION_TYPE:說明該注解只能聲明在一個(gè)注解類型前扣孟。
- ElementType.PACKAGE:說明該注解只能聲明在一個(gè)包名前。
int value();
這個(gè)就是接口的參數(shù)了
再定義另一個(gè)接口荣赶,不凤价, 注解!
@Retention(RetentionPolicy.SOURCE)
@Target(ElementType.TYPE)
public @interface ForkPresenter {
Class value();
}
2拔创、
兩個(gè)注解定義完之后利诺,接下來就是最重要的類 AbstractProcessor
在程序編譯時(shí),就通過 public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv)
這個(gè)方法剩燥,處理我們的注解
處理的過程看似復(fù)雜慢逾,其實(shí)很簡單。獲得我們需要的值灭红,動(dòng)態(tài)生成java代碼侣滩,生成代碼也是一個(gè)庫javapoet,就直接貼代碼吧变擒,更直觀些君珠。(javapoet的用法這里就不說了)
@SupportedAnnotationTypes({"org.fork.annotation.ForkLayoutId", "org.fork.annotation.ForkPresenter"})
@SupportedSourceVersion(SourceVersion.RELEASE_7)
@SuppressWarnings("All")
public class ForkProcessor extends AbstractProcessor {
private String packageName;
private String activityName;
private TypeMirror activityClass;
private int layoutId;
private String presenterName;
@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
if (annotations.size() > 0) {
parseBindViews(annotations, roundEnv);
javaPoet();
}
return true;
}
private void javaPoet() {
MethodSpec getPresenter = MethodSpec.methodBuilder("getPresenter")
.addModifiers(Modifier.PUBLIC, Modifier.FINAL)
.returns(Object.class)
.addParameter(TypeName.OBJECT, "activity")
.addStatement("return new " + presenterName + "((" + activityName + ")activity)")
.build();
MethodSpec getLayoutId = MethodSpec.methodBuilder("getLayoutId")
.addModifiers(Modifier.PUBLIC, Modifier.FINAL)
.returns(int.class)
.addStatement("return " + layoutId)
.build();
TypeSpec clazz = TypeSpec.classBuilder(activityName + "$$Provider")
.addModifiers(Modifier.PUBLIC, Modifier.FINAL)
.addSuperinterface(Provider.class)
.addMethod(getPresenter)
.addMethod(getLayoutId)
.build();
JavaFile.Builder builder = JavaFile
.builder(packageName, clazz);
JavaFile javaFile = builder.build();
try {
javaFile.writeTo(processingEnv.getFiler());
} catch (IOException e) {
e.printStackTrace();
}
}
private void parseBindViews(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
for (Element element : roundEnv.getElementsAnnotatedWith(ForkLayoutId.class)) {
if (element.getKind() == ElementKind.CLASS) {
layoutId = element.getAnnotation(ForkLayoutId.class).value();
activityName = element.getSimpleName().toString();
activityClass = element.asType();
packageName = element.toString().replace("." + activityName, "");
}
}
for (Element element : roundEnv.getElementsAnnotatedWith(ForkPresenter.class)) {
if (element.getKind() == ElementKind.CLASS) {
try {
presenterName = element.getAnnotation(ForkPresenter.class).value().getSimpleName().toString();
} catch (MirroredTypeException mte) {
presenterName = mte.getTypeMirror().toString().replace(packageName + ".", "");
}
}
}
}
}
- @SupportedAnnotationTypes 用來指定這里會(huì)處理的注解
- @SupportedSourceVersion(SourceVersion.RELEASE_7) 指定java版本,網(wǎng)上貼子說娇斑,這個(gè)寫成注解兼容性更好策添,當(dāng)然澈段,class開頭的兩句,也可以使用java代碼來聲明
- 注意舰攒,再這個(gè)java庫的build.gradle中败富,我們還要配置一遍java環(huán)境
sourceCompatibility = "1.7"
targetCompatibility = "1.7"
3、這些都寫完了摩窃,還要加上一個(gè)配置文件
再與java 同級(jí)兽叮,添加 resources / META_INF / services / javax.annotation.processing.Processor 這樣一個(gè)文件
填寫里面的內(nèi)容
org.fork.annotation.ForkProcessor
這是完整的目錄結(jié)構(gòu)
ForkProcessor這個(gè)文件會(huì)報(bào)錯(cuò),但是沒什么影響猾愿○写希可能是android studio支持不夠好吧,再IntelliJ中不會(huì)報(bào)錯(cuò)
通過javapoet蒂秘,編譯之后會(huì)生成如下代碼泽本,當(dāng)然生成什么都是自己控制的。下面就詳細(xì)的說一下生成的這個(gè)類
package org.demo.tiny;
import java.lang.Object;
import org.fork.annotation.Provider;
public final class MainActivity$$Provider implements Provider {
public final Object getPresenter(Object activity) {
return new MainPersenter((MainActivity)activity);
}
public final int getLayoutId() {
return 2130968603;
}
}
我們?cè)賏ctivity使用注解姻僧,將pressenter的字節(jié)碼文件和layout的id傳入规丽。
我們通過注解能拿到這個(gè)activity的名字,也就是上面的MainActivity
加上final 防止復(fù)寫方法撇贺。getLayoutId沒什么說的赌莺,getPresenter的強(qiáng)轉(zhuǎn)有些蛋疼,一會(huì)兒再說松嘶。
provider 接口提供了兩個(gè)方法
public interface Provider {
Object getPresenter(Object obj);
int getLayoutId();
}
費(fèi)了九牛二虎之力艘狭,通過注解,拿到了layout的id并創(chuàng)建了presenter翠订。
下面巢音,我們就要使用他們了。 fork類上場(chǎng)尽超。為了將這個(gè)demo封裝起來官撼,作為一個(gè)三方框架,我新建了一個(gè)android library
public final class Fork {
public static void bind(ForkActivity activity) {
Provider provider = null;
try {
try {
provider = (Provider) Class.forName(activity.getClass().getName() + "$$Provider").newInstance();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
if (provider != null) {
activity.binding = DataBindingUtil.setContentView(activity, provider.getLayoutId());
activity.mvpPresenter = provider.getPresenter(activity);
}
}
}
通過加載器橙弱,創(chuàng)建了Provider的一個(gè)實(shí)例歧寺。這樣我們就可以得到layout id 和presenter了
為了實(shí)現(xiàn)封裝,我創(chuàng)建了一個(gè)ForkActivity
public class ForkActivity<B extends ViewDataBinding, P> extends Activity {
protected B binding;
protected P mvpPresenter;
}
這里就是為什么要進(jìn)行強(qiáng)轉(zhuǎn)了棘脐,因?yàn)橄氚裝inding和mvpPresenter這兩個(gè)屬性封裝起來斜筐,放進(jìn)父類。但我們自動(dòng)生成的代碼的報(bào)名卻和ForkActivity 不在同一個(gè)包下蛀缝∏炅矗總不能把兩個(gè)屬性全公有吧。
此外屈梁,還有一個(gè)坑嗤练,Provider 并不是我們自己生成的榛了,所以不可能知道Activity的名字,這里也就只有寫Object 了煞抬。會(huì)涉及幾處的強(qiáng)轉(zhuǎn)霜大。
再Fork.java中傳遞的是MainActivity,再注解創(chuàng)建presenter是革答,我們知道這是MainActivity战坤,所以將其強(qiáng)轉(zhuǎn)創(chuàng)建一個(gè)presenter,但是mvpPresenter又被抽取再ForkActivity中残拐,我們并不知道實(shí)際的activity是Main途茫,所以又強(qiáng)轉(zhuǎn)成ForkActivity,并賦值mvpPresenter溪食。我們?cè)費(fèi)ainActivity中使用mvpPresenter囊卜,通過泛型,聲明了他的類型是MainActivity