Android Architecture Components

卫子平
2023-12-01

官方文档: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的功能,但是需要注意生命周期的正确处理。
4、Fetching data(Repository):网络数据的请求不应直接在ViewModel中调用这些api,否则随着业务的增长会使得ViewModel越来越难以维护。Repository引入是专门处理这种数据操作,可以认为是各种数据源(persistent model, web service, cache)的介质。代码如下:

@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。

 类似资料:

相关阅读

相关文章

相关问答