Android Architecture Components

本文探讨了Android应用开发中常见的问题,如内存泄漏、组件通信困难等,并介绍了LifeCycle、LiveData和ViewModel等框架组件来解决这些问题,提升应用质量和用户体验。

开发者经常面临的问题

Android应用由四大组件构成,各组件可以被独立且无序的调起,用户会在各个App之间来回切换。组件启动后,生命周期会受用户的操作和系统影响,不完全受开发者控制。而由于设备内存问题,进程随时可能被系统强杀,所以不要将数据和状态直接存储在组件中,也不要让组件互相依赖。

问题实例
  • 内存泄漏:在Activity中发起网络请求,在网络请求返回之前退出Activity, 那么Activity就被泄漏了。

  • 崩溃:Activity destroy后,还被其他类操作,从而引发崩溃,例如Glide图片库;

  • Activity类臃肿:数据,逻辑,控件代码都堆积在Activity中,导致Activity臃肿,不易维护和测试。

  • Fragment通信困难:开发中经常会遇到Activity中含有多个Fragment的情况, 并且Fragment之间需要通信。通常会利用Activity转发Fragment之间的通信,而且Fragment之间还需要依赖对方的生命周期和通信细节。

  • 数据易销毁:如果将内存中的数据保存在Activity中,由于Activity很容易被销毁重建(配置改变,内存不够),那么数据也就很容易被销毁。

通用的框架原则

  • 关注点分离:不要在Activity/Fragment中添加非UI控制、非系统接口调用的代码。尽量让他们保持精炼,以免引起生命周期相关的问题。这些类是系统创建和管理的,并不完全受开发者控制。

  • 模型驱动UI:应该用数据模型驱动UI展示,最好是持久模型,因为当系统强杀进程或者网络不稳定时,持久模型能让程序继续工作。模型独立于组件之外,不会受到生命周期的影响。

应用框架组件

Android官方在17年IO大会上发布了一套框架组件,帮助开发者开发优质的App.

LifeCycle

将Activity/Fragment的生命周期剥离到其他类中,减少组件类中的代码。

LifeCycle用两个枚举类追踪组件的状态。

  • Event:从Framework层分发的,匹配Activity和Fragment中的生命周期方法回调。
  • State:当前组件的状态。

使用方式
public class LearnLifeCycleObserver implements LifecycleObserver {

    @OnLifecycleEvent(Lifecycle.Event.ON_CREATE)
    public void onCreate() {

    }

    @OnLifecycleEvent(Lifecycle.Event.ON_STOP)
    public void onStop() {
    
    }
}

public class LearnLifeCycleActivity extends AppCompatActivity {

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        getLifecycle().addObserver(new LearnLifeCycleObserver());
    }
}
实践
  • 将UI和数据分离,UI层不需要持有数据。

  • 构建数据驱动UI的应用,UI控制器负责根据数据更新UI以及将用户的操作反馈给数据层。

  • 将数据逻辑放在ViewModel中,将数据拉取逻辑放在数据仓库(Data Repository)中.

  • 使用MVP模式,引入Presenter, 让程序更加容易被测试。

LiveData

一个自带观察者模式的数据封装类,不同的是LiveData有生命周期,会利用LifeCycle自动监听与它绑定的组件的生命周期。当数据发生改变时,Activity或者Fragment要处于活动状态,观察者才会接收到数据改变的回调,此时界面就可以安全的进行渲染,而不会出现Activity销毁后还更改界面的情况。

优点
  • 不会造成泄漏内存。
  • 不会由于操作销毁的Activity而发生崩溃。
  • 不需要手动处理生命周期。
  • 数据总能实时更新。
  • 数据不会受configuration更改的影响。
  • 易于共享数据。
使用

final MutableLiveData<String> userNameLiveData = new MutableLiveData<>();

userNameLiveData.observe(this, new Observer<String>() {
    @Override
    public void onChanged(@Nullable String s) {
        //Activity的lifecycle state处于STARTED或RESUMED时才会回调
        mUserNameTV.setText(s);
    }
});

mUserNameSetBtn.setOnClickListener(new View.OnClickListener(){

    @Override
    public void onClick(View v) {
        //更新LiveData的数据,以尝试触发观察者的通知
        userNameLiveData.setValue("jayden");
    }
});
两个扩展类
MutableLiveData

开放了setValue和postValue方法,用于主动改变LiveData的值,并通知观察者。

MediatorLiveData

可以同时观察多个LiveData, 当被观察的LiveData发生改变时,可以对LiveData的数据进行加工后再通知MediatorLiveData.

转换LiveData
Transformations#map

利用MediatorLiveData将LiveData的数据加工后再通知给观察者。

LiveData<User> userLiveData = ...;
LiveData<String> userName = Transformations.map(userLiveData, user -> {
    user.name + " " + user.lastName
});
Transformations#switchMap

和map方法类似,而且也是用MediatorLiveData实现。但是switchMap方法的第二个形参接口的返回值是LiveData. 如下代码块中的例子,当userId发生改变后,getUser(id)返回另一个LiveData: liveData1, user的观察者开始观察liveData1的变化。

举个例子:id为1的用户切换成id为2的用户后,getUser(id)方法返回的LiveData就是id为2的用户的用户信息,以后如果id为2的用户信息发生改变,user的观察者就会接收到通知。

private LiveData<User> getUser(String id) {
  ...;
}

LiveData<String> userId = ...;
LiveData<User> user = Transformations.switchMap(userId, id -> getUser(id) );
ViewModel

用来存储和管理和UI相关的有生命周期的数据。当Configuration改变,例如屏幕旋转,语言切换时,ViewModel中的数据不会被销毁,是一个不会被滥用的单例。

创建ViewModel

利用LiveData持有数据

public class MyViewModel extends ViewModel {
    private MutableLiveData<List<User>> mObservableUsers;
    
    public MyViewModel(){
        mObservableUsers = new MutableLiveData<List<Users>>();
    }
    
    public LiveData<List<User>> getUsers() {
        return mObservableUsers;
    }

    private void loadUsers() {
        // 异步加载数据后通知到users中。
        ...
        mObservableUsers.setValue(users)
    }
}

在Activity中监听数据改变

public class MyActivity extends AppCompatActivity {
    public void onCreate(Bundle savedInstanceState) {
        MyViewModel model = ViewModelProviders.of(this).get(MyViewModel.class);
        //监听LiveData
        model.getUsers().observe(this, users -> {
            // update UI
        });
    }
}

如果由于屏幕旋转或语言切换导致Activity重建,ViewModelProviders.of(this).get(MyViewModel.class);获取的ViewModel还是Activity首次创建时所构建的,只有当Activity销毁后,ViewModel才会被清除。

ViewModel的生命周期

在多个Fragment中共享数据
public class SharedViewModel extends ViewModel {
    private final MutableLiveData<Item> selected = new MutableLiveData<Item>();

    public void select(Item item) {
        selected.setValue(item);
    }

    public LiveData<Item> getSelected() {
        return selected;
    }
}

public class MasterFragment extends Fragment {
    private SharedViewModel model;
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        model = ViewModelProviders.of(getActivity()).get(SharedViewModel.class);
        itemSelector.setOnClickListener(item -> {
            model.select(item);
        });
    }
}

public class DetailFragment extends Fragment {
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        SharedViewModel model = ViewModelProviders.of(getActivity()).get(SharedViewModel.class);
        model.getSelected().observe(this, { item ->
           // Update the UI.
        });
    }
}

两个Fragment拿到的ViewModel对象是同一个。

这种方式的优点:

  • Activity不需要了解Fragment之间的通信,完全松耦合。
  • 两个Fragment都不需要依赖对方的生命周期和通信细节,即使一个Fragment被销毁,也不会影响另一个Fragment.

最终的框架

推荐的框架原则
  • 在Manifest文件中定义的程序入口:Activities, Services, BroadcastReceiver等,都不能作为数据的来源。

  • 明确定义各模块的职责。

  • 尽可能少地暴露每个模块的信息。

  • 定义模块间的交互时,考虑如何让他们易于测试。

  • 将数据持久化,让程序在离线时更加可用。

  • 数据存储库应该指定一个数据源作为单一的数据来源。

推荐阅读

### Android Architecture Components 使用指南与最佳实践 Android Architecture Components 是一组用于构建稳健、可测试和易于维护的应用程序的库集合。这些组件旨在帮助开发者解决常见问题,例如生命周期管理和数据持久化。 --- ### 推荐使用的架构组件及其作用 #### 1. **LiveData** `LiveData` 是一种可观察的数据持有者类,具有感知生命周期的能力。这意味着它可以自动管理订阅者的生命周期,从而避免内存泄漏等问题[^2]。 ```java public class MyViewModel extends ViewModel { private final MutableLiveData<String> data = new MutableLiveData<>(); public void setData(String value) { this.data.setValue(value); } public LiveData<String> getData() { return data; } } ``` #### 2. **ViewModel** `ViewModel` 类的设计目的是存储和管理与界面相关的数据,并确保在配置更改(如屏幕旋转)时不会丢失数据[^5]。 ```java public class MainViewModel extends ViewModel { private Repository repository; public MainViewModel(Repository repo) { this.repository = repo; } public LiveData<List<Item>> getItems() { return repository.getItems(); } } ``` #### 3. **Room 数据库** `Room` 是一个抽象层,位于 SQLite 之上,提供了更简洁的 API 并支持编译期查询验证[^2]。 ```java @Dao public interface ItemDao { @Query("SELECT * FROM items") LiveData<List<Item>> getAllItems(); @Insert(onConflict = OnConflictStrategy.REPLACE) void insert(Item item); } @Entity(tableName = "items") public class Item { @PrimaryKey(autoGenerate = true) public int id; public String name; } ``` #### 4. **Paging Library** 分页库 (`Paging`) 可以高效地加载大量数据集的一部分,而无需一次性加载整个列表[^4]。 ```java @Dao public interface PagingItemDao { @Query("SELECT * FROM paging_items ORDER BY id ASC") PagingSource<Integer, PagingItem> getPagedItems(); } ``` #### 5. **WorkManager** `WorkManager` 提供了一个健壮的框架来调度延迟或周期性的后台任务,即使应用退出或设备重启也能继续执行[^3]。 ```java PeriodicWorkRequest periodicWork = new PeriodicWorkRequest.Builder(MyWorker.class, 15, TimeUnit.MINUTES).build(); WorkManager.getInstance(context).enqueue(periodicWork); ``` --- ### 示例项目与官方文档 为了更好地理解如何组合使用以上提到的各种组件,可以参考以下两个开源示例项目: - **Architecture Samples**[^4]: 这个项目展示了多种不同的架构工具和模式的实际运用方式。地址为 https://gitcode.com/gh_mirrors/ar/architecture-samples 。它涵盖了 MVVM 设计模式下的典型实现方法。 - **Architecture Components Samples**[^1]: 此仓库包含了多个针对单个特性的演示案例,比如 Room、LiveData 等的具体用法。链接见 https://gitcode.com/gh_mirrors/ar/architecture-components-samples 。 同时也可以查阅官方文档获得最新最权威的信息:https://developer.android.com/topic/libraries/architecture/guide.html[^3] --- ### 最佳实践总结 1. **清晰的角色分工**: 将业务逻辑封装到 `Repository` 层中,UI 相关的操作交给 `ViewModel` 处理,保持各层次之间的低耦合度。 2. **利用 Lifecycle-Aware 组件**: 始终优先选用像 `LiveData`, `ViewModel` 这样的能够响应活动或片段生命周期变化的对象,简化资源释放流程。 3. **注重单元测试覆盖范围**: 针对核心功能编写充分的自动化测试脚本,尤其是涉及到异步操作的地方,保证系统的稳定性。 4. **合理安排工作流**: 对于耗时较长的任务考虑采用 WorkManager 来代替传统的 Service 结构,增强可靠性。 ---
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值