簡(jiǎn)介
fragmentargs可以用來(lái)處理Fragment屬性的保存(Fragment.setArguments(Bundle bundle))和自動(dòng)賦值(Fragment.getArguments())邏輯,以在編譯時(shí)自動(dòng)生成源代碼的方式來(lái)減少一些重復(fù)代碼的編寫(xiě),可以查看這篇博文了解一下
流程圖
簡(jiǎn)單的流程圖

編譯時(shí)注解
由于運(yùn)行時(shí)注解用到了反射技術(shù),在性能方面會(huì)有影響,而編譯時(shí)注解是在編譯期生成源代碼的方式,性能會(huì)好一些
如何Debug
在Android Studio中想在工程編譯時(shí)進(jìn)行Debug會(huì)比較麻煩一些,要做一些配置,具體的可以查看這篇博文,下面簡(jiǎn)要介紹下(小坑還挺多)
1、配置~/.gradle/gradle.properties
org.gradle.daemon=true
org.gradle.jvmargs=-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005
然后啟動(dòng)gradle daemon(mac平臺(tái))
./gradlew --daemon
2、在AS中配置remote debugger,其實(shí)是新建一個(gè)Run/Debug Configuration(remote類(lèi)型)。我們每次創(chuàng)建一個(gè)Android Project的時(shí)候AS都會(huì)默認(rèn)創(chuàng)建一個(gè)Run/Debug Configuration(Android Application類(lèi)型),名字叫做app,這個(gè)大家都挺眼熟的:)
Run/Debug Configuration信息基本上不用做任何更改,只要確保端口號(hào)跟上一個(gè)步驟的一致就行

3、在自定義的
annotation processor設(shè)置斷點(diǎn),啟動(dòng)remote debugger,在命令行中運(yùn)行./gradlew clean assembleDebug

fragmentargs annotation processor分析
fragmentargs庫(kù)中的annotation processor主要生成兩種類(lèi)型的java源代碼,首先會(huì)生成被注解過(guò)的Frament的FragmentBuilder.java文件(會(huì)有多個(gè)),然后會(huì)生成FragmentArgsInjector.java文件(僅一個(gè))
MyFragmentBuilder.java
package com.tubb.argknife;
import android.os.Bundle;
public final class MyFragmentBuilder {
private final Bundle mArguments = new Bundle();
public MyFragmentBuilder(int dis) {
mArguments.putInt("dis", dis);
}
public static MainActivity.MyFragment newMyFragment(int dis) {
return new MyFragmentBuilder(dis).build();
}
public static final void injectArguments(MainActivity.MyFragment fragment) {
Bundle args = fragment.getArguments();
if (args == null) {
throw new IllegalStateException("No arguments set");
}
if (!args.containsKey("dis")) {
throw new IllegalStateException("required argument dis is not set");
}
fragment.dis = args.getInt("dis");
}
public MainActivity.MyFragment build() {
com.tubb.argknife.MainActivity.MyFragment fragment = new com.tubb.argknife.MainActivity.MyFragment();
fragment.setArguments(mArguments);
return fragment;
}
public <F extends MainActivity.MyFragment> F build(F fragment) {
fragment.setArguments(mArguments);
return fragment;
}
}
AutoFragmentArgInjector.java
package com.hannesdorfmann.fragmentargs;
import android.os.Bundle;
public final class AutoFragmentArgInjector
implements com.tubb.argknife.annotation.FragmentArgsInjector {
public void inject(Object target) {
Class<?> targetClass = target.getClass();
String targetName = targetClass.getSimpleName();
if ( com.tubb.argknife.OuterFragment.class.getSimpleName().equals(targetName) ) {
com.tubb.argknife.OuterFragmentBuilder.injectArguments( ( com.tubb.argknife.OuterFragment ) target);
return;
}
if ( com.tubb.argknife.MainActivity.MyFragment.class.getSimpleName().equals(targetName) ) {
com.tubb.argknife.MyFragmentBuilder.injectArguments( ( com.tubb.argknife.MainActivity.MyFragment ) target);
return;
}
}
}
fragmentargs使用squareup的javawriter來(lái)生成java源文件的,只需要配置下就行
compile 'com.squareup:javawriter:2.2.1'
但我自己在實(shí)踐的過(guò)程中遇到一個(gè)很奇怪的問(wèn)題,明明javawriter代碼已經(jīng)導(dǎo)入了,但就是引用不了,后來(lái)參考作者的做法把JavaWriter類(lèi)拷貝到本地repackage了一下,問(wèn)題解決
具體代碼的生成過(guò)程挺繁瑣和抽象的,首先會(huì)去得到注解的類(lèi)(Fragment)和注解的屬性(Fragment中的屬性)的Element信息,然后根據(jù)這些Elements來(lái)動(dòng)態(tài)生成類(lèi)和類(lèi)中的方法和屬性,具體的源碼大家可以看下我剝離出來(lái)的代碼
fragmentargs的調(diào)用過(guò)程
fragmentargs通過(guò)annotation processor在工程編譯完成后生成很多的FragmentBuilder.java源文件和一個(gè)AutoFragmentArgInjector。java源文件,當(dāng)然最終也會(huì)編譯成相應(yīng)的class文件
fragmentargs的使用非常簡(jiǎn)單README,那它內(nèi)部是如何工作的呢
通過(guò)FragmentArgs.inject(this)的調(diào)用,會(huì)進(jìn)入到FragmentArgs類(lèi)中的injectFromBundle(Object target)方法
package com.hannesdorfmann.fragmentargs;
/**
* The root class to inject arguments to a fragment
*
* @author Hannes Dorfmann
*/
public class FragmentArgs {
public static final String AUTO_MAPPING_CLASS_NAME = "AutoFragmentArgInjector";
public static final String AUTO_MAPPING_PACKAGE = "com.hannesdorfmann.fragmentargs";
public static final String AUTO_MAPPING_QUALIFIED_CLASS =
AUTO_MAPPING_PACKAGE + "." + AUTO_MAPPING_CLASS_NAME;
private static FragmentArgsInjector autoMappingInjector;
public static void inject(Object fragment) {
injectFromBundle(fragment);
}
static void injectFromBundle(Object target) {
if (autoMappingInjector == null) {
// Load the automapping class
try {
Class<?> c = Class.forName(AUTO_MAPPING_QUALIFIED_CLASS);
autoMappingInjector = (FragmentArgsInjector) c.newInstance();
} catch (Exception e) {
// Since 2.0.0 we don't throw an exception because of android library support.
// Instead we print this exception as warning message
/*
Exception wrapped = new Exception("Could not load the generated automapping class. "
+ "However, that may be ok, if you use FragmentArgs in library projects", e);
wrapped.printStackTrace();
*/
}
}
if (autoMappingInjector != null) {
autoMappingInjector.inject(target);
}
}
}
通過(guò)源碼我們可以發(fā)現(xiàn),會(huì)去反射(注意是static,性能上基本上不會(huì)有影響)實(shí)例化一個(gè)實(shí)現(xiàn)了FragmentArgsInjector接口的類(lèi)的實(shí)例,其實(shí)就是我們之前介紹的AutoFragmentArgInjector,然后調(diào)用AutoFragmentArgInjector的AutoFragmentArgInjector.inject(target)方法,我們可以來(lái)看看這個(gè)方法中具體做了些啥
public void inject(Object target) {
Class<?> targetClass = target.getClass();
String targetName = targetClass.getSimpleName();
if ( com.tubb.argknife.OuterFragment.class.getSimpleName().equals(targetName) ) {
com.tubb.argknife.OuterFragmentBuilder.injectArguments( ( com.tubb.argknife.OuterFragment ) target);
return;
}
if ( com.tubb.argknife.MainActivity.MyFragment.class.getSimpleName().equals(targetName) ) {
com.tubb.argknife.MyFragmentBuilder.injectArguments( ( com.tubb.argknife.MainActivity.MyFragment ) target);
return;
}
}
看到這些代碼是不是突然明悟了:) 在這個(gè)方法中把FragmentBuilder和我們自己的Fragment聯(lián)系起來(lái)了,其實(shí)就是通過(guò)Fragment.getArguments()來(lái)為我們的屬性賦值,也就是Auto Inject
public static final void injectArguments(MyFragment fragment) {
Bundle args = fragment.getArguments();
if(args == null) {
throw new IllegalStateException("No arguments set");
} else if(!args.containsKey("dis")) {
throw new IllegalStateException("required argument dis is not set");
} else {
fragment.dis = args.getInt("dis");
}
}
Note
在自己使用fragmentargs的過(guò)程中遇到一個(gè)比較坑的東西,發(fā)現(xiàn)當(dāng)Fragment為內(nèi)部類(lèi)時(shí)不能正常工作,自己測(cè)試用的代碼嘗試修復(fù)了這個(gè)問(wèn)題,也給作者提了一個(gè)issuse,估計(jì)作者下個(gè)版本會(huì)進(jìn)行修復(fù)的
還有一點(diǎn),ArgKnife并不能用在工作項(xiàng)目中,只是測(cè)試代碼??