Android IOC思想入門と実戦
36665 ワード
簡単に言えば、IOCはプログラムコードからアクティブに取得されたリソースであり、サードパーティが取得して元のコードを受動的に受信する方式を転換し、デカップリングの効果を達成することを制御反転と呼ぶ.
例:ButterKnifeのシミュレーションを例に挙げます.レイアウトファイルとviewのfindViewイベントとonClickイベントを注入します.
// BaseActivity
public abstract class BaseActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
InjectUtils.inject(this);
}
}
// MainActivity
@ContentView(R.layout.activity_main)
public class MainActivity extends BaseActivity {
// ` `
@InjectView(R.id.btn1)
private Button btn1;
@InjectView(R.id.btn2)
private Button btn2;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.e(TAG, btn1.toString());
Log.e(TAG, btn2.toString());
}
//
@OnClick({R.id.btn1, R.id.btn2})
public void click(View view) {
Toast.makeText(this, "click", Toast.LENGTH_SHORT).show();
}
}
// layoutId class
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface ContentView {
int value();
}
// view viewId
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface InjectView {
int value();
}
//
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface BaseEvent {
// setOnClickListener / setonLongClick
String setListenerType();
// new Onclick / onLongClick
Class<?> newListenerType();
}
//
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
// setOnClickListener setOnLongClickListener
@BaseEvent(setListenerType = "setOnClickListener"
, newListenerType = View.OnClickListener.class)
public @interface OnClick {
// viewid ButterKnife @Click({R.id.btn,R.id.text})
int[] value();
}
/**
* ioc
*/
class InjectUtils {
static void inject(Object context) {
injectLayout(context);
injectView(context);
injectEvent(context);
}
/**
* layout
*
* @param context context
*/
private static void injectLayout(Object context) {
// calss
Class clazz = context.getClass();
//
ContentView contentView = (ContentView) clazz.getAnnotation(ContentView.class);
if (contentView != null) {
try {
// id
int layoutId = contentView.value();
// activity setContentView , id
Method method = clazz.getMethod("setContentView", int.class);
method.invoke(context, layoutId);
} catch (Exception e) {
e.printStackTrace();
}
}
}
/**
* view
*
* @param context context
*/
private static void injectView(Object context) {
// activity class
Class clazz = context.getClass();
//
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
//
InjectView annotation = field.getAnnotation(InjectView.class);
if (annotation != null) {
try {
// private
field.setAccessible(true);
// viewId
int viewId = annotation.value();
// activity findViewById
Method method = clazz.getMethod("findViewById", int.class);
View view = (View) method.invoke(context, viewId);
// findViewById view
field.set(context, view);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
/**
*
*/
private static void injectEvent(Object context) {
Class clazz = context.getClass();
//
Method[] declaredMethods = clazz.getDeclaredMethods();
for (Method method : declaredMethods) {
//
Annotation[] declaredAnnotations = method.getDeclaredAnnotations();
for (Annotation annotation : declaredAnnotations) {
Class<?> aClass = annotation.annotationType();
// baseEvent
BaseEvent baseEvent = aClass.getAnnotation(BaseEvent.class);
if (baseEvent == null) {
continue;
}
// onClick onLongClick
String listenerType = baseEvent.setListenerType();
// new View.OnclickListener
Class<?> newListenerType = baseEvent.newListenerType();
try {
// view
Method valueMethod = aClass.getDeclaredMethod("value");
int[] viewIds = (int[]) valueMethod.invoke(annotation);
for (int viewId : viewIds) {
Method findViewById = clazz.getMethod("findViewById", int.class);
View view = (View) findViewById.invoke(context, viewId);
if (view == null) {
continue;
}
// setOnClickListener setOnLongClickListener
// InvocationHandler
ListenerInvocationHandler invocationHandler = new ListenerInvocationHandler(context, method);
//
Object proxy = Proxy.newProxyInstance(newListenerType.getClassLoader(), new Class[]{newListenerType}, invocationHandler);
Method eventMethod = view.getClass().getMethod(listenerType, newListenerType);
eventMethod.invoke(view, proxy);
}
} catch (Exception e) {
Log.e("exception", e.toString());
}
}
}
}
}
public class ListenerInvocationHandler implements InvocationHandler {
// activity dialog
private Object object;
//
private Method method;
public ListenerInvocationHandler(Object object, Method method) {
this.object = object;
this.method = method;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
return this.method.invoke(object, args);
}
}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
// baseEvent
@BaseEvent(setListenerType = "setOnLongClickListener"
, newListenerType = View.OnLongClickListener.class)
public @interface OnLongClick {
int[] value() default -1;
}
// onLongClick true
@OnLongClick({R.id.btn1, R.id.btn2})
public boolean onLongClick(View view){
Toast.makeText(this, "longClick", Toast.LENGTH_SHORT).show();
return true;
}
IOCの基本思想はもう終わりました.IOCは私たちがよく知っているButterKnife、javaのSpringMVCなど、多くのフレームワークに応用されたいと考えています.これにより結合度を低減することができる.今後のメンテナンスコストを削減します.しかし、オブジェクトを作成するプロセスはやや複雑で、通常の書き方よりも効率がやや低い.