官方文档:https://developer.android.com/topic/libraries/architecture/index.html
官方实例:https://github.com/googlesamples/android-architecture-components
注意:目前该架构还处于alpha阶段,涉及到的相关Api,比如ViewModel、Lifecycle、LiveData等都有可能变更,目前还不建议商业应用。
一、架构原则
1、Separation of concerns:代码分离,比如应用组件是系统与应用程序之间的一种协议体现,因此Activity、Fragment中编写的应该是只与UI或者系统调用相关的代码,而其他的与这些无关的代码都应当从中分离。2、Model drive UI:Model与View以及应用组件都是相互独立的,简化UI代码、分离应用逻辑可以更加方便的进行代码的管理与维护。
二、架构与示例
1、UI与控制器:可以是Activity或Fragment,目前该架构提供了相关API,LifecycleActivity和LifecycleFragment。代码如下:
(1)引入顶层build.gradle配置
allprojects {
repositories {
jcenter()
maven { url 'https://maven.google.com' }
}
}
(2)引入模块build.gradle配置
dependencies {
//Architecture Components
compile "android.arch.lifecycle:runtime:1.0.0-alpha3"
compile "android.arch.lifecycle:extensions:1.0.0-alpha3"
annotationProcessor "android.arch.lifecycle:compiler:1.0.0-alpha3"
//retrofit
compile 'com.squareup.retrofit2:retrofit:2.3.0'
//dagger
compile 'com.google.dagger:dagger:2.11-rc2'
annotationProcessor 'com.google.dagger:dagger-compiler:2.11-rc2'
compile 'com.google.dagger:dagger-android:2.11-rc2'
compile 'com.google.dagger:dagger-android-support:2.11-rc2'
annotationProcessor 'com.google.dagger:dagger-android-processor:2.11-rc2'
//room
compile "android.arch.persistence.room:runtime:1.0.0-alpha3"
annotationProcessor "android.arch.persistence.room:compiler:1.0.0-alpha3"
//butterknife
compile 'com.jakewharton:butterknife:8.6.0'
annotationProcessor 'com.jakewharton:butterknife-compiler:8.6.0'
}
(3)Activiry代码如下:
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
showUserEnteringFragment();
showUserProfileFragment();
}
private void showUserProfileFragment() {
UserProfileFragment fragment = UserProfileFragment.getUserProfileFragment();
getSupportFragmentManager().beginTransaction().replace(R.id.profile_container, fragment).commit();
}
private void showUserEnteringFragment() {
UserEnteringFragment fragment = UserEnteringFragment.getUserEnteringFragment();
getSupportFragmentManager().beginTransaction().replace(R.id.enter_container, fragment).commit();
}
}
(4)两个fragment如下:
public class UserEnteringFragment extends LifecycleFragment {
@BindView(R.id.first_name)
EditText etFirstName;
@BindView(R.id.last_name)
EditText etLastName;
@BindView(R.id.save)
Button btnSave;
private Unbinder unbinder;
@Inject
UserEnteringViewModel viewModel;
public static UserEnteringFragment getUserEnteringFragment() {
return new UserEnteringFragment();
}
@Override
public void onAttach(Context context) {
AndroidSupportInjection.inject(this);
super.onAttach(context);
}
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle
savedInstanceState) {
return inflater.inflate(R.layout.user_entering, container, false);
}
@Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
unbinder = ButterKnife.bind(this, view);
btnSave.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
String fn = etFirstName.getText().toString();
String ln = etLastName.getText().toString();
viewModel.enteringNewUser(fn, ln);
}
});
}
@Override
public void onDestroyView() {
super.onDestroyView();
unbinder.unbind();
}
}
public class UserProfileFragment extends LifecycleFragment {
private static final String UID_KEY = "uid";
@Inject
UserProfileViewModel viewModel;
@BindView(R.id.user)
TextView textView;
private Unbinder unbinder;
public static UserProfileFragment getUserProfileFragment() {
UserProfileFragment userProfileFragment = new UserProfileFragment();
Bundle bundle = new Bundle();
bundle.putString(UID_KEY, "0");
userProfileFragment.setArguments(bundle);
return userProfileFragment;
}
@Override
public void onAttach(Context context) {
AndroidSupportInjection.inject(this);
super.onAttach(context);
}
@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
String uid = getArguments().getString(UID_KEY, "0");
viewModel.init(uid);
viewModel.getUser().observe(this, new Observer<User>() {
@Override
public void onChanged(@Nullable User user) {
if (user != null) {
textView.setText(user.toString());
}
}
});
}
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle
savedInstanceState) {
return inflater.inflate(R.layout.user_profile, container, false);
}
@Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
unbinder = ButterKnife.bind(this, view);
}
@Override
public void onDestroyView() {
super.onDestroyView();
unbinder.unbind();
}
}
2、ViewModel:作用是为Activity或Fragment提供数据以及调用业务的代码,目前该架构提供了相关API,注意这个ViewModel是与Activity或Fragment的生命周期绑在一起的,注意不会持有View引用以及Activity的直接或间接引用,对于Application context有专门的AndroidViewModel。代码如下:
public class UserEnteringViewModel extends ViewModel {
private LiveData<User> user;
private UserRepository userRepository;
@Inject
public UserEnteringViewModel(UserRepository userRepository) {
this.userRepository = userRepository;
}
public void enteringNewUser(String firstName, String lastName) {
userRepository.setUser(firstName, lastName);
}
public LiveData<User> getUser() {
return this.user;
}
}
public class UserProfileViewModel extends ViewModel {
private LiveData<User> user;
private UserRepository userRepo;
@Inject // UserRepository parameter is provided by Dagger 2
public UserProfileViewModel(UserRepository userRepo) {
this.userRepo = userRepo;
}
public void init(String userId) {
if (this.user != null) {
// ViewModel is created per Fragment so
// we know the userId won't change
return;
}
user = userRepo.getUser(userId);
}
public LiveData<User> getUser() {
return this.user;
}
}
3、LiveData:为ViewModel中数据的变化去通知更新UI,本质上是一个数据观察者,它也有相关生命周期的回调,LiveData不需主动去停止数据的观察。注意RxJava或Agera也可以实现LiveData的功能,但是需要注意生命周期的正确处理。
@Singleton // informs Dagger that this class should be constructed once
public class UserRepository {
private final Webservice webservice;
private final UserDao userDao;
private final Executor executor;
@Inject
public UserRepository(Webservice webservice, UserDao userDao, Executor executor) {
this.webservice = webservice;
this.userDao = userDao;
this.executor = executor;
}
public LiveData<User> getUser(String userId) {
refreshUser(userId);
// return a LiveData directly from the database.
return userDao.load(userId);
}
public void setUser(final String firstName, final String lastName) {
executor.execute(new Runnable() {
@Override
public void run() {
User user = new User();
user.setFirstName(firstName);
user.setLastName(lastName);
userDao.save(user);
}
});
}
private void refreshUser(final String userId) {
executor.execute(new Runnable() {
@Override
public void run() {
// running in a background thread
// check if user was fetched recently
/*boolean userExists = userDao.hasUser(FRESH_TIMEOUT);*/
boolean userExists = true;
if (!userExists) {
// refresh the data
Response<User> response = null;
try {
response = webservice.getUser(userId).execute();
userDao.save(response.body());
} catch (Exception e) {
e.printStackTrace();
userDao.save(mockUser());
}
// TODO check for error etc.
// Update the database.The LiveData will automatically refresh so
// we don't need to do anything else here besides updating the database
}
}
});
}
private static User mockUser() {
User user = new User();
user.setFirstName("Jack");
user.setLastName("Harry");
return user;
}
}
5、Persisting data:使用room或者realm等库进行数据存储。因为Repository的引入,可以灵活的用于测试,而不影响到View和ViewModel。代码如下:
(1)首先是User对象
@Entity
public class User {
@PrimaryKey
private int id;
@ColumnInfo(name = "first_name")
private String firstName;
@ColumnInfo(name = "last_name")
private String lastName;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
@Override
public String toString() {
return "User[id=" + id + ",firstName=" + firstName + ",lastName=" + lastName + "]";
}
}
(2)dao对象
@Dao
public interface UserDao {
@Query("SELECT * FROM user WHERE id = :userId")
LiveData<User> load(String userId);
@Query("SELECT * FROM user WHERE first_name LIKE :first AND "
+ "last_name LIKE :last LIMIT 1")
LiveData<User> findByName(String first, String last);
@Query("SELECT * FROM user")
List<User> getAll();
@Insert(onConflict = REPLACE)
void save(User user);
@Insert
void insertAll(User... users);
@Delete
void delete(User user);
}
(3)数据库对象
@Database(entities = {User.class}, version = 1)
public abstract class AppDatabase extends RoomDatabase {
public abstract UserDao userDao();
}
三、总结:相对通用的android应用架构模板的雏形,并且google为框架中的每一层次都提供了相关Api或者library。