AndroidAnnotations简介

AndroidAnnotations(下简称AA)是一个可以提高开发速度的开源框架。如同Spring一样,AA致力于减少样板代码。它提供依赖注入,封装各类常见操作,能让用户通过注解的方式简化代码。使用AA,用户可以忽略各类繁琐,重复的代码,将精力集中在业务逻辑上。

我们来看一个AA的代码示例:

@Fullscreen
@EActivity(R.layout.bookmarks)
@WindowFeature(Window.FEATURE_NO_TITLE)
public class BookmarksToClipboardActivity extends Activity {
  
  BookmarkAdapter adapter;
  
  @ViewById
  ListView bookmarkList;
 
  @ViewById
  EditText search;
  
  @App
  BookmarkApplication application;
  
  @RestService
  BookmarkClient restClient;
 
  @AnimationRes
  Animation fadeIn;
  
  @SystemService
  ClipboardManager clipboardManager;
 
  @AfterViews
  void initBookmarkList() {
    adapter = new BookmarkAdapter(this);
    bookmarkList.setAdapter(adapter);
  }
  
  @Click({R.id.updateBookmarksButton1, R.id.updateBookmarksButton2})
  void updateBookmarksClicked() {
    searchAsync(search.getText().toString(), application.getUserId());
  }
  
  @Background
  void searchAsync(String searchString, String userId) {
    Bookmarks bookmarks = restClient.getBookmarks(searchString, userId);
    updateBookmarks(bookmarks);
  }
 
  @UiThread
  void updateBookmarks(Bookmarks bookmarks) {
    adapter.updateBookmarks(bookmarks);
    bookmarkList.startAnimation(fadeIn);
  }
  
  @ItemClick
  void bookmarkListItemClicked(Bookmark selectedBookmark) {
    clipboardManager.setText(selectedBookmark.getUrl());
  }
 
}
@Rest("http://www.bookmarks.com")
public interface BookmarkClient {
  
  @Get("/bookmarks/{userId}?search={search}")
  Bookmarks getBookmarks(@Path String search, @Path String userId);
 
}

这其中展示了AA的几种功能:

  • View注入,如@ViewById
  • 事件监听,如@Click@ItemClick
  • 线程管理,如@Background@UiThread
  • 依赖注入,如@SystemService``@RestService
  • Rest Client生成,如@Get("/bookmarks/{userId}?search={search}")

如果我们不使用AA,那么同样的功能需要写成一下代码:

public class BookmarksToClipboardActivity extends Activity {
  
  BookmarkAdapter adapter;
 
  ListView bookmarkList;
 
  EditText search;
 
  BookmarkApplication application;
 
  Animation fadeIn;
 
  ClipboardManager clipboardManager;
 
  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
 
    requestWindowFeature(Window.FEATURE_NO_TITLE);
    getWindow().setFlags(FLAG_FULLSCREEN, FLAG_FULLSCREEN);
 
    setContentView(R.layout.bookmarks);
 
    bookmarkList = (ListView) findViewById(R.id.bookmarkList);
    search = (EditText) findViewById(R.id.search);
    application = (BookmarkApplication) getApplication();
    fadeIn = AnimationUtils.loadAnimation(this, anim.fade_in);
    clipboardManager = (ClipboardManager) getSystemService(CLIPBOARD_SERVICE);
 
    View updateBookmarksButton1 = findViewById(R.id.updateBookmarksButton1);
    updateBookmarksButton1.setOnClickListener(new OnClickListener() {
 
      @Override
      public void onClick(View v) {
        updateBookmarksClicked();
      }
    });
 
    View updateBookmarksButton2 = findViewById(R.id.updateBookmarksButton2);
    updateBookmarksButton2.setOnClickListener(new OnClickListener() {
 
      @Override
      public void onClick(View v) {
        updateBookmarksClicked();
      }
    });
 
    bookmarkList.setOnItemClickListener(new OnItemClickListener() {
 
      @Override
      public void onItemClick(AdapterView p, View v, int pos, long id) {
        Bookmark selectedBookmark = (Bookmark) p.getAdapter().getItem(pos);
        bookmarkListItemClicked(selectedBookmark);
      }
    });
 
    initBookmarkList();
  }
 
  void initBookmarkList() {
    adapter = new BookmarkAdapter(this);
    bookmarkList.setAdapter(adapter);
  }
 
  void updateBookmarksClicked() {
    UpdateBookmarksTask task = new UpdateBookmarksTask();
 
    task.execute(search.getText().toString(), application.getUserId());
  }
  
  private static final String BOOKMARK_URL = //
  "http://www.bookmarks.com/bookmarks/{userId}?search={search}";
  
  
  class UpdateBookmarksTask extends AsyncTask {
 
    @Override
    protected Bookmarks doInBackground(String... params) {
      String searchString = params[0];
      String userId = params[1];
 
      RestTemplate client = new RestTemplate();
      HashMap args = new HashMap();
      args.put("search", searchString);
      args.put("userId", userId);
      HttpHeaders httpHeaders = new HttpHeaders();
      HttpEntity request = new HttpEntity(httpHeaders);
      ResponseEntity response = client.exchange( //
          BOOKMARK_URL, HttpMethod.GET, request, Bookmarks.class, args);
      Bookmarks bookmarks = response.getBody();
 
      return bookmarks;
    }
 
    @Override
    protected void onPostExecute(Bookmarks result) {
      adapter.updateBookmarks(result);
      bookmarkList.startAnimation(fadeIn);
    }
    
  }
 
  void bookmarkListItemClicked(Bookmark selectedBookmark) {
    clipboardManager.setText(selectedBookmark.getUrl());
  }
 
}

可以看到这里有非常多的样板代码,包括但不限于:findViewById,setOnClickListener以及匿名内部类的繁琐语法,onItemClick(AdapterView p, View v, int pos, long id)烦人的参数,以及无穷无尽的AsyncTask.更不用说发送网络请求时复杂的设置了.

AA让我们的代码缩短了大约一半,即减少了程序员一半的体力劳动.不过更重要的是,它让我们代码的可读性增强了.回想最开始的那段代码,即使没有用过AA的开发者也可以读懂它的意思,这归功于语义化的各类注解.

那AA是如何做到这一切的呢?熟悉Spring的开发者的脑中可能已经出现了几个名词BeanFactory,BeanDefinitions等.然而AA采用与Spring完全不同的方式完成依赖注入.Spring是运行时增强,AA是编译期增强.

AA采用标准的Java Annotation Processing Tool,在编译期生成源代码.以@EActivity为例,AA在编译期生成一个该Activity的子类,子类名为原类名加下划线.

package com.some.company;
@EActivity
public class MyActivity extends Activity {
  // ...
}

将生成如下子类

package com.some.company;
public final class MyActivity_ extends MyActivity {
  // ...
}

子类中为机器为我们生成的样板代码.

当然,AA也有一个明显的缺点,就是侵入性太强.在Manifest中,我们必须加入


而不是


在启动新的Activity时,我们必须

startActivity(this, MyListActivity_.class);

而不是

startActivity(this, MyListActivity.class);

或者使用它提供的builder方法

MyListActivity_.intent(context).start();

这样带来的代码侵入性是不能忽视的,尤其是当我们决定弃用AA时,必须改无数个地方才能摆脱类名后的下划线.
相比于Spring在J2EE界的统治地位,AA并不是一个主流的框架,这与其侵入性强是有关系的.

你可能感兴趣的:(AndroidAnnotations简介)