Android Architecture Components
1.BasicSample - Shows how to persist data using a SQLite database and Room. Also uses ViewModels and LiveData.
此 sample 需求非常简单, 使用 list 展示(持久化)数据库中的数据, 点击每个 item 可以进入到详情页(展示详情信息)
1.1MainActivity.java -- main_activity.xml
作为一个 fragment 的载体, 仅仅包括一个 fragment: 1.2ProductListFragment.java
还有个 public method 展示ProductFragment.java(一会再来看)
1.2ProductListFragment.java -- list_fragment.xml
此界面中有2个 view, 一个是加载完成前的 loading 提示view, 一个是加载完成后的 recyclerView
1.2ProductListFragment 中使用了android.databinding 进行 fragment, xml 和数据的绑定
onCreateView() 中 init:
private ListFragmentBinding mBinding;
mBinding = DataBindingUtil.inflate(inflater, R.layout.list_fragment, container, false);
在 list_fragment.xml 中,
--1.使用<data> 标签,声明 var
--2.使用 app:visibleGone="@{isLoading}" 对 view 进行动态控制
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<data>
<variable
name="isLoading"
type="boolean" />
</data>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/cardview_light_background"
android:orientation="vertical">
<TextView
android:id="@+id/loading_tv"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center_vertical|center_horizontal"
android:text="@string/loading_products"
android:textAlignment="center"
app:visibleGone="@{isLoading}"
/>
<android.support.v7.widget.RecyclerView
android:id="@+id/products_list"
android:contentDescription="@string/cd_products_list"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layoutManager="LinearLayoutManager"
app:visibleGone="@{!isLoading}"/>
</LinearLayout>
</layout>
--3.${visibleGone} 关键字在 BindingAdapters.java 中被自定义, 然后 showHide() 方法对 view 进行动态控制
public class BindingAdapters {
@BindingAdapter("visibleGone")
public static void showHide(View view, boolean show) {
view.setVisibility(show ? View.VISIBLE : View.GONE);
}
}
--4. 而 showHide() 是在ListFragmentBinding.java 中被调用, 是自动生成的
public class ListFragmentBinding extends android.databinding.ViewDataBinding {
...
com.example.android.persistence.ui.BindingAdapters.showHide(this.loadingTv, isLoading);
com.example.android.persistence.ui.BindingAdapters.showHide(this.productsList, IsLoading1);
...
}
--5.适配器 ProductAdapter.java 对数据和 fragment 进行关联
mProductAdapter = new ProductAdapter(mProductClickCallback);
--6.获取数据
在onActivityCreated() 中, 获取数据:
final ProductListViewModel viewModel = ViewModelProviders.of(this).get(ProductListViewModel.class);
// Update the list when the data changes
viewModel.getProducts().observe(this, new Observer<List<ProductEntity>>() {
@Override
public void onChanged(@Nullable List<ProductEntity> myProducts) {
if (myProducts != null) {
mBinding.setIsLoading(false);
mProductAdapter.setProductList(myProducts);
} else {
mBinding.setIsLoading(true);
}
}
});
这段代码使用了新的方式获取数据库中的数据:ViewModelProviders.of(this).get(ProductListViewModel.class);
--7.ProductListViewModel.java
ViewModelProviders为arch 中的方法;
此处我们需要实现如下: 在 apply() 中有 loadAllProducts()的方法, 此方法实现在 Dao 中
public class ProductListViewModel extends AndroidViewModel {
private static final MutableLiveData ABSENT = new MutableLiveData();
{
//noinspection unchecked
ABSENT.setValue(null);
}
private final LiveData<List<ProductEntity>> mObservableProducts;
public ProductListViewModel(Application application) {
super(application);
final DatabaseCreator databaseCreator = DatabaseCreator.getInstance(this.getApplication());
LiveData<Boolean> databaseCreated = databaseCreator.isDatabaseCreated();
mObservableProducts = Transformations.switchMap(databaseCreated,
new Function<Boolean, LiveData<List<ProductEntity>>>() {
@Override
public LiveData<List<ProductEntity>> apply(Boolean isDbCreated) {
if (!Boolean.TRUE.equals(isDbCreated)) { // Not needed here, but watch out for null
//noinspection unchecked
return ABSENT;
} else {
//noinspection ConstantConditions
return databaseCreator.getDatabase().productDao().loadAllProducts();
}
}
});
databaseCreator.createDb(this.getApplication());
}
/**
* Expose the LiveData Products query so the UI can observe it.
*/
public LiveData<List<ProductEntity>> getProducts() {
return mObservableProducts;
}
}
--8.ProductEntity.java
@Entity 是 新的包 arch.room 中的
此类是Product 的实体类:
@Entity(tableName = "products"): 表名为"products"
@PrimaryKey private int id; : primary key 为 id
@Entity(tableName = "products")
public class ProductEntity implements Product {
@PrimaryKey
private int id;
private String name;
private String description;
private int price;
@Override
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
@Override
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
@Override
public int getPrice() {
return price;
}
public void setPrice(int price) {
this.price = price;
}
public ProductEntity() {
}
public ProductEntity(Product product) {
this.id = product.getId();
this.name = product.getName();
this.description = product.getDescription();
this.price = product.getPrice();
}
}
--9.ProductDao.java
@Dao 是新的包 arch.room 中的
形如 retrofit2的接口请求, 实现起来的方式和retrofit 很像
@Dao
public interface ProductDao {
@Query("SELECT * FROM products")
LiveData<List<ProductEntity>> loadAllProducts();
@Insert(onConflict = OnConflictStrategy.REPLACE)
void insertAll(List<ProductEntity> products);
@Query("select * from products where id = :productId")
LiveData<ProductEntity> loadProduct(int productId);
@Query("select * from products where id = :productId")
ProductEntity loadProductSync(int productId);
}
--10.上一张图简单理解下 View ViewModel model 的关系
final-architecture.png2.GithubBrowserSample - An advanced sample that uses the Architecture components, Dagger and the Github API. Requires Android Studio 3.0 canary 1
--1.MainActivity.java
implements LifecycleRegistryOwner
@Override
public LifecycleRegistry getLifecycle() {
return lifecycleRegistry;
}
implements HasSupportFragmentInjector
@Override
public DispatchingAndroidInjector<Fragment> supportFragmentInjector() {
return dispatchingAndroidInjector;
}
通过注入NavigationController 控制 fragment
@Inject
NavigationController navigationController;
@Inject
public NavigationController(MainActivity mainActivity) {
this.containerId = R.id.container;
this.fragmentManager = mainActivity.getSupportFragmentManager();
}
public void navigateToSearch() {
SearchFragment searchFragment = new SearchFragment();
fragmentManager.beginTransaction()
.replace(containerId, searchFragment)
.commitAllowingStateLoss();
}
--2.SearchFragment.java
dataBinding
<data>
<import type="com.android.example.github.vo.Repo"/>
<import type="java.util.List"/>
<import type="com.android.example.github.vo.Status"/>
<import type="com.android.example.github.vo.Resource"/>
<variable name="resultCount" type="int"/>
<variable name="query" type="String"/>
<variable name="loadingMore" type="boolean"/>
<variable name="searchResource" type="Resource"/>
<variable name="callback" type="com.android.example.github.ui.common.RetryCallback"/>
</data>
SearchFragmentBinding dataBinding = DataBindingUtil
.inflate(inflater, R.layout.search_fragment, container, false,
dataBindingComponent);
extends LifecycleFragment
功能1:searching function:
private void doSearch(View v) {
//get query key word
String query = binding.get().input.getText().toString();
// Dismiss keyboard
dismissKeyboard(v.getWindowToken());
binding.get().setQuery(query);
searchViewModel.setQuery(query);
}
--3.SearchViewModel.java
extends ViewModel
功能1:refresh()
void refresh() {
if (query.getValue() != null) {
query.setValue(query.getValue());
}
}
功能2:loadNextPage()
void loadNextPage() {
String value = query.getValue();
if (value == null || value.trim().length() == 0) {
return;
}
nextPageHandler.queryNextPage(value);
}
--4.DAO @Dao
RepoDao.java
UserDao.java
--5.Entity @Entity
RepoEntity.java
UserEntity.java
RepoSearchResultEntity.java
ContributorEntity.java
--6.GithubService.java
@GET("users/{login}")
LiveData<ApiResponse<User>> getUser(@Path("login") String login);
@GET("users/{login}/repos")
LiveData<ApiResponse<List<Repo>>> getRepos(@Path("login") String login);
@GET("repos/{owner}/{name}")
LiveData<ApiResponse<Repo>> getRepo(@Path("owner") String owner, @Path("name") String name);
@GET("repos/{owner}/{name}/contributors")
LiveData<ApiResponse<List<Contributor>>> getContributors(@Path("owner") String owner, @Path("name") String name);
@GET("search/repositories")
LiveData<ApiResponse<RepoSearchResponse>> searchRepos(@Query("q") String query);
@GET("search/repositories")
Call<RepoSearchResponse> searchRepos(@Query("q") String query, @Query("page") int page);
--7.repository
RepoRepository.java
GithubDb db;
//操作数据库
RepoDao repoDao;
//网络操作
GithubService githubService;
AppExecutors appExecutors;
UserRepository.java
//操作数据库
UserDao userDao;
//网络操作
GithubService githubService;
AppExecutors appExecutors;
--8.AppComponent.java
@Singleton
@Component(modules = {
AndroidInjectionModule.class,
AppModule.class,
MainActivityModule.class
})
public interface AppComponent {
@Component.Builder
interface Builder {
@BindsInstance Builder application(Application application);
AppComponent build();
}
void inject(GithubApp githubApp);
}
--9.AppModule.java
@Module(includes = ViewModelModule.class)
@Singleton @Provides
GithubService provideGithubService() {
return new Retrofit.Builder()
.baseUrl("https://api.github.com/")
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(new LiveDataCallAdapterFactory())
.build()
.create(GithubService.class);
}
@Singleton @Provides
GithubDb provideDb(Application app) {
return Room.databaseBuilder(app, GithubDb.class,"github.db").build();
}
@Singleton @Provides
UserDao provideUserDao(GithubDb db) {
return db.userDao();
}
@Singleton @Provides
RepoDao provideRepoDao(GithubDb db) {
return db.repoDao();
}
--10.MainActivityModule.java
@ContributesAndroidInjector(modules = FragmentBuildersModule.class)
abstract MainActivity contributeMainActivity();
--11.FragmentBuildersModule.java
@ContributesAndroidInjector
abstract RepoFragment contributeRepoFragment();
@ContributesAndroidInjector
abstract UserFragment contributeUserFragment();
@ContributesAndroidInjector
abstract SearchFragment contributeSearchFragment();