你好LiveData

image

首先放一张官方推荐的app设计架构图,想要了解更多(芝麻之门)

LocalBroadcastManager

冷落的LBM

LocalBroadcastManager有点冷落,一个是很少人知道并且合理使用广播,很多人要么使用的是系统的全局广播BraoadCastRecever,要么使用EventBusRxAndroid等等其他观察者模式的三方库,慢慢的就失宠了。然鹅,并不只是这样,当现在你的项目迁移到Androidx时,会发现官方居然把它废弃了,随后官方明确指出最适合的替代品LiveDatareactive streams,见以下描述 (官方传送门)

image
image

关于LBM

LocalBroadcastManager顾名思义就是本地广播,也是基于观察者模式的事件总线,用于应用内通信,比较安全和高效,虽然迁移到androidx后,在1.1.0高版本LocalBroadcastManager被标记过时,但是它的用处还是蛮大的,相比系统的广播,本地广播有着无法比拟的优越性,而且非常高效,相比第三方观察者模式库,满足条件下我宁愿使用1.0.0版本的LocalBroadcastManager

与系统广播的区别

  • 范围上:LocalBroadcastManager为本地广播,只能接受自身App发送的广播,只能用于应用内之间的通信,范围相对较小;而系统的BraoadCastRecever可以实现跨进程通讯,范围更大。
  • 效率上:LocalBroadcastManager通信核心是Handler,所以只能用于应用内通信,安全和效率都很高;而系统的BraoadCastRecever通信核心是Binder机制,
    实现跨进程通信,范围更广,导致运行效率稍微逊一点。
  • 安全上:LocalBroadcastManager由于核心是Handler,而且只能动态注册,只能用于app内通信,安全上更加的有保障;而系统的BraoadCastRecever容易被利用,安全上相对较弱一点。

使用方式

注册广播:

IntentFilter filter = new IntentFilter();
filter.addAction(“你的Action”);
LocalBroadcastManager.getInstance(Context context).registerReceiver(BroadcastReceiver receiver, filter);

发送广播:

LocalBroadcastManager.getInstance(Context context).sendBroadcast(Intent intent);

取消注册:

LocalBroadcastManager.getInstance(this).unregisterReceiver(BroadcastReceiver receiver);

接收广播:

final class MyBroadcastReceiver extends BroadcastReceiver{
        @Override
        public void onReceive(Context context, Intent intent) {
            if(intent!=null && “你的Action”.equals(intent.getAction())){
               // 处理逻辑
            }
        }
    }

在《阿里巴巴Android开发手册(正式版)_v1.0.0》中有明确指出关于广播的使用规范问题,以下规范等级为强制,所以我们应用合理正确的使用广播,遵循绿色公约,避免信息泄露和拦截意图的风险,以下为规范描述:

如果广播仅限于应用内,则可以使用LocalBroadcastManager#sendBroadcast()实现,避免敏感信息外泄和 Intent 拦截的风险。

源码分析

    @NonNull
    public static LocalBroadcastManager getInstance(@NonNull Context context) {
        synchronized (mLock) {
            if (mInstance == null) {
                mInstance = new LocalBroadcastManager(context.getApplicationContext());
            }
            return mInstance;
        }
    }
    private LocalBroadcastManager(Context context) {
        mAppContext = context;
        mHandler = new Handler(context.getMainLooper()) {
            @Override
            public void handleMessage(Message msg) {
                switch (msg.what) {
                    case MSG_EXEC_PENDING_BROADCASTS:
                        executePendingBroadcasts();
                        break;
                    default:
                        super.handleMessage(msg);
                }
            }
        };
    }

初始化上LocalBroadcastManager明显采取的是懒汉模式的单例模式,并且初始化的时候创建了一个Handler对象,在主线程上进行工作,用于处理发送的广播,这就是和系统BroadCastReciver的最大区别,系统广播则是通过Binder机制进行通信的,而本地广播采取的是Handler机制进行通信。

 private final HashMap> mReceivers = new HashMap<>();
 private final HashMap> mActions = new  HashMap<>();
 private final ArrayList mPendingBroadcasts = new ArrayList<>();
 
 public void registerReceiver(@NonNull BroadcastReceiver receiver,
            @NonNull IntentFilter filter) {
        synchronized (mReceivers) {
            ReceiverRecord entry = new ReceiverRecord(filter, receiver);
            ArrayList filters = mReceivers.get(receiver);
            if (filters == null) {
                filters = new ArrayList<>(1);
                mReceivers.put(receiver, filters);
            }
            filters.add(entry);
            for (int i=0; i entries = mActions.get(action);
                if (entries == null) {
                    entries = new ArrayList(1);
                    mActions.put(action, entries);
                }
                entries.add(entry);
            }
        }
    }

注册广播则明显能够看出是通过2个Map嵌套集合mReceiversmActions来进行协调运作的,而mReceivers则是以receiver作为key,存储的是一个接收记录ReceiverRecord集合,而ReceiverRecord是用来存储筛选器IntentFilter与广播接收器BroadcastReceiver及广播运行状态的,但是由于LocalBroadcastManager是单例模式,可以存放多个广播接收器BroadcastReceiver故采用的集合嵌套,同理mActions是以筛选器中的action作为key,存储的也是ReceiverRecord集合。

  public boolean sendBroadcast(@NonNull Intent intent) {
        synchronized (mReceivers) {
            final String action = intent.getAction();
            final String type = intent.resolveTypeIfNeeded(
                    mAppContext.getContentResolver());
            final Uri data = intent.getData();
            final String scheme = intent.getScheme();
            final Set categories = intent.getCategories();
            ==========================省略部分===================
                if (receivers != null) {
                    for (int i=0; i

明显看出本地广播是通过Handler进行发送消息的,检查排队队列是否有未发布的MSG_EXEC_PENDING_BROADCASTS,没有的话发送消息。

    void executePendingBroadcasts() {
        while (true) {
            final BroadcastRecord[] brs;
            synchronized (mReceivers) {
                final int N = mPendingBroadcasts.size();
                if (N <= 0) {
                    return;
                }
                brs = new BroadcastRecord[N];
                mPendingBroadcasts.toArray(brs);
                mPendingBroadcasts.clear();
            }
            for (int i=0; i

当工作线程接收到Handler发送的消息时候,会执行executePendingBroadcasts()方法,发送广播的时候mPendingBroadcasts来存储Intent意图与广播接收器ReceiverRecord,当处理广播的时候,从mPendingBroadcasts中取出信息存放到广播接收器数组BroadcastRecord[]中,通过循环调用广播接收的抽象方法onReceive(Context context,Intent intent)进行消息发送到前台主线程,就这样完成了应用内通信。

初识LiveData

image

以下翻译可能有槽点,去官网查看更多更详细的信息(芝麻之门)

介绍

LiveData是官方架构组件,是Jetpack众多组件的一个,它是一个可观察的数据持有者类。与常规的可观察对象不同,LiveData是生命周期感知的,这意味着它尊重其他应用程序组件的生命周期,比如activitiesfragments或者services。这种意识确保LiveData只更新处于活动生命周期状态的应用程序组件观察者。

LiveData认为,如果一个观察者的生命周期处于STARTEDRESUMED状态,那么这个观察者(由observer类表示)就处于活动状态。LiveData只将更新通知活动观察者。注册为监视LiveData对象的非活动观察者不会收到有关更改的通知。

您可以注册一个与实现LifecycleOwner接口的对象配对的观察者。当相应的生命周期对象的状态更改为DESTROYED时,此关系允许删除观察者。这对于活动和片段特别有用,因为它们可以安全地观察LiveData对象,而不用担心泄漏——当activitiesfragments的生命周期被破坏时,它们会立即取消订阅。

优势

  • UI界面与数据实时保证一致
    LiveData遵循观察者模式。当生命周期状态发生变化时,LiveData通知观察者Observer对象。您可以合并代码来更新这些观察者对象中的UI。您的观察者可以在每次发生更改时更新UI,而不是每次应用程序数据更改时都更新UI

  • 不会造成内存泄漏
    观察者被绑定到生命周期Lifecycle对象,并在其关联的生命周期被destroyed后进行清理。

  • 当界面销毁或者停止活动不会造成崩溃
    如果观察者的生命周期是不活动的,例如在后堆栈中的活动,那么它不会接收任何LiveData事件。

  • 不需要手动处理生命周期
    UI组件只观察相关数据,不停止或恢复观察。LiveData自动管理所有这些,因为它在观察过程中知道相关的生命周期状态变化。

  • 总是保证最新的数据
    如果一个生命周期变为不活动的,它将在再次活动时接收最新的数据。例如,在后台的活动在返回到前台后立即接收最新的数据。

  • 适当的配置更改
    如果某个activitysfragments由于配置更改(如设备旋转)而重新创建,它将立即接收最新可用数据。

  • 共享资源和数据
    您可以使用singleton模式扩展LiveData对象来包装系统服务,以便在您的应用程序中共享它们。LiveData对象连接到系统服务一次,然后任何需要该资源的观察者都可以查看LiveData对象。有关更多信息,请参见Extend LiveData。

属性及方法

返回类型 方法 说明
T getValue() 返回T类型的对象值
boolean hasActiveObservers() 当前是否有活动的观察者observers
boolean hasObservers() 当前是否有观察者observers
void observe(LifecycleOwner owner, Observer observer) 添加观察者对象到列表,配合生命周期
void observeForever(Observer observer) 添加观察者对象到列表,无生命周期
void removeObserver(Observer observer) 从观察者列表中移除给定的观察者
void removeObservers(LifecycleOwner owner) 删除与给定生命周期所有者绑定的所有观察者
void onActive() 当活动观察者的数量从0变为1时调用
void onInactive() 当活动观察者的数量从1变为0时调用
void postValue(T value) 将任务发布到主线程以设置给定值
void setValue(T value) 设置更新数据源

使用步骤

  • 依赖
// ViewModel and LiveData
implementation "android.arch.lifecycle:extensions:1.1.1"
// just ViewModel
implementation "android.arch.lifecycle:viewmodel:1.1.1"
// just LiveData
implementation "android.arch.lifecycle:livedata:1.1.1"
  • 创建LiveData实例来保存特定类型的数据。通常会结合ViewModel一起使用,也可以单独使用。
public class NameViewModel extends ViewModel {
// Create a LiveData with a String
private MutableLiveData currentName;
    public MutableLiveData getCurrentName() {
        if (currentName == null) {
            currentName = new MutableLiveData();
        }
        return currentName;
    }
// Rest of the ViewModel...
}
注意:确保将更新UI的LiveData对象存储在ViewModel对象中,而不是存储在Activity或Fragment中,原因如下:  

以避免膨胀的Activity和Fragment。现在这些UI控制器负责显示数据,而不是保存数据状态。

将LiveData实例与特定的Activity或Fragment实例解耦,并允许LiveData对象在配置更改后仍然存在。
  • 创建一个观察者对象Observer,该对象定义onChanged()方法,该方法控制LiveData对象持有的数据更改时发生的情况。通常在UI控制器中创建一个观察者对象,例如一个Activity或者fragment
public class NameActivity extends AppCompatActivity {

    private NameViewModel model;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        // Other code to setup the activity...
        // Get the ViewModel.
        model = ViewModelProviders.of(this).get(NameViewModel.class);
        // Create the observer which updates the UI.
        final Observer nameObserver = new Observer() {
            @Override
            public void onChanged(@Nullable final String newName) {
                // Update the UI, in this case, a TextView.
                nameTextView.setText(newName);
            }
        };

        // Observe the LiveData, passing in this activity as the LifecycleOwner and the observer.
        model.getCurrentName().observe(this, nameObserver);
    }
}
在大多数情况下,应用程序组件的onCreate()方法是开始观察LiveData对象的合适位置,原因如下:  

以确保系统不会从活动或fragment的onResume()方法发出冗余调用。  

确保活动或片段具有数据,可以在活动时立即显示这些数据。一旦应用程序组件处于启动状态,它就会从它所观察的LiveData对象接收到最近的值。只有在设置了要观察的LiveData对象时才会发生这种情况。

通常,LiveData只在数据发生更改时提供更新,并且只向活动观察者提供更新。这种行为的一个例外是,当观察者从非活动状态更改为活动状态时,也会收到更新。此外,如果观察者第二次从非活动状态更改为活动状态,则只有当值自上次活动以来发生更改时,才会接收到更新。
  • 使用observe()方法将观察者对象附加到LiveData对象。方法接受LifecycleOwner对象。这订阅观察者对象到LiveData对象,以便在发生更改时通知它。通常将观察者对象附加到UI控制器中,例如ActivityFragment
// Observe the LiveData, passing in this activity as the LifecycleOwner and the observer.
model.getCurrentName().observe(this, nameObserver);
您可以使用observeForever(observer)方法注册一个没有关联LifecycleOwner对象的观察者。在这种情况下,观察者总是被认为是活跃的,因此总是被通知修改。您可以删除调用removeObserver(Observer)方法的这些观察者。
  • LiveData扩展用法
public class StockLiveData extends LiveData {
    private StockManager stockManager;

    private SimplePriceListener listener = new SimplePriceListener() {
        @Override
        public void onPriceChanged(BigDecimal price) {
            setValue(price);
        }
    };

    public StockLiveData(String symbol) {
        stockManager = new StockManager(symbol);
    }

    @Override
    protected void onActive() {
        stockManager.requestPriceUpdates(listener);
    }

    @Override
    protected void onInactive() {
        stockManager.removeUpdates(listener);
    }
}

当LiveData对象具有活动观察者时,将调用onActive()方法。这意味着您需要开始从该方法观察股票价格更新。

当LiveData对象没有任何活动的观察者时,将调用onInactive()方法。因为没有观察者在听,所以没有理由保持与StockManager服务的连接。

setValue(T)方法更新LiveData实例的值,并将更改通知任何活动的观察者。

public class MyFragment extends Fragment {
    @Override
    public void onActivityCreated(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        LiveData myPriceListener = ...;
        myPriceListener.observe(this, price -> {
            // Update the UI.
        });
    }
}

方法将fragment(它是LifecycleOwner的一个实例)作为第一个参数传递。这样做意味着这个观察者被绑定到与所有者关联的Lifecycle对象上,这意味着:

如果Lifecycle对象没有处于活动状态,那么即使值发生了更改,也不会调用观察者
销毁生命周期对象后,将自动删除观察者。

LiveData对象是生命周期感知的,这意味着您可以在多个ActivitysFragmentsservice之间共享它们。为了保持示例的简单性,可以将LiveData类实现为单例,如下所示:

public class StockLiveData extends LiveData {
    private static StockLiveData sInstance;
    private StockManager stockManager;

    private SimplePriceListener listener = new SimplePriceListener() {
        @Override
        public void onPriceChanged(BigDecimal price) {
            setValue(price);
        }
    };

    @MainThread
    public static StockLiveData get(String symbol) {
        if (sInstance == null) {
            sInstance = new StockLiveData(symbol);
        }
        return sInstance;
    }

    private StockLiveData(String symbol) {
        stockManager = new StockManager(symbol);
    }

    @Override
    protected void onActive() {
        stockManager.requestPriceUpdates(listener);
    }

    @Override
    protected void onInactive() {
        stockManager.removeUpdates(listener);
    }
}

ActivityFragmentService中可以这么调用,通过单例实现共享数据

public class MyFragment extends Fragment {
    @Override
    public void onActivityCreated(Bundle savedInstanceState) {
        StockLiveData.get(symbol).observe(this, price -> {
            // Update the UI.
        });
    }
}

转化 LiveData

在将LiveData对象发送给观察者之前,您可能希望更改存储在LiveData对象中的值,或者您可能需要根据另一个LiveData实例的值返回另一个LiveData实例。生命周期包提供转换类,该类包含支持这些场景的helper方法。

Transformations.map ()

对存储在LiveData对象中的值应用函数,并将结果向下传播

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

Transformations.switchMap()

与map()类似,将函数应用于存储在LiveData对象中的值,并将结果解包并向下分派。传递给switchMap()的函数必须返回LiveData对象,如下例所示:

private LiveData getUser(String id) {
  ...;
}

LiveData userId = ...;
LiveData user = Transformations.switchMap(userId, id -> getUser(id) );

您可以使用转换方法在观察者的生命周期中携带信息。除非观察者正在观察返回的LiveData对象,否则不会计算转换。因为转换是延迟计算的,所以与生命周期相关的行为是隐式传递的,不需要额外的显式调用或依赖关系。

如果您认为在ViewModel对象中需要一个生命周期对象,那么转换可能是一个更好的解决方案。例如,假设您有一个UI组件,它接受一个地址并返回该地址的邮政编码。您可以为这个组件实现简单的视图模型,如下面的示例代码所示:

class MyViewModel extends ViewModel {
    private final PostalCodeRepository repository;
    public MyViewModel(PostalCodeRepository repository) {
       this.repository = repository;
    }

    private LiveData getPostalCode(String address) {
       // DON'T DO THIS,不推荐
       return repository.getPostCode(address);
    }
}

然后,UI组件需要从以前的LiveData对象注销注册,并在每次调用getPostalCode()时注册到新实例。此外,如果重新创建UI组件,它将触发对repository.getPostCode()方法的另一个调用,而不是使用前一个调用的结果。

相反,您可以将邮政编码查询实现为地址输入的转换,如下面的示例所示:

class MyViewModel extends ViewModel {
    private final PostalCodeRepository repository;
    private final MutableLiveData addressInput = new MutableLiveData();
    public final LiveData postalCode =
            Transformations.switchMap(addressInput, (address) -> {
                return repository.getPostCode(address);
             });

  public MyViewModel(PostalCodeRepository repository) {
      this.repository = repository
  }

  private void setInput(String address) {
      addressInput.setValue(address);
  }
}

在这种情况下,该postalCode字段被定义为转换addressInput。只要您的应用程序具有与该postalCode字段关联的活动观察者,就会在addressInput更改时重新计算并检索字段的值 。

此机制允许较低级别的应用程序创建LiveData按需延迟计算的对象。甲ViewModel对象可以容易地获得以引用LiveData的对象,然后在它们的顶部限定的变换规则。

创建新的转换

有十几种不同的特定转换可能对您的应用有用,但默认情况下不提供。要实现自己的转换,您可以使用MediatorLiveData该类,该类侦听其他LiveData对象并处理它们发出的事件。MediatorLiveData正确地将其状态传播到源LiveData对象。要了解有关此模式的更多信息,请参阅Transformations 该类的参考文档 。

合并多个LiveData源

MediatorLiveData是一个子类LiveData,允许您合并多个LiveData源。MediatorLiveData 只要任何原始LiveData源对象发生更改,就会触发对象的观察者。

例如,如果LiveDataUI中有一个可以从本地数据库或网络更新的对象,则可以将以下源添加到该 MediatorLiveData对象:

  • LiveData与存储在数据库中的数据关联的对象。

  • LiveData与从网络访问的数据关联的对象。

 LiveData liveData1 = ...;
 LiveData liveData2 = ...;

 MediatorLiveData liveDataMerger = new MediatorLiveData<>();
 liveDataMerger.addSource(liveData1, value -> liveDataMerger.setValue(value));
 liveDataMerger.addSource(liveData2, value -> liveDataMerger.setValue(value));

总结

读到这里,是不是发现LiveData即有点像EventBus,又有点像Rxjava,这正是他的强大之处,另外它还能够结合组件的生命周期使用,不仅能够共享资源与数据,而且UI与数据同步也是非常实时的,正如官方对Jetpack组件的描述,不仅能够提高开发效率,而且让应用变得更加优质,如果你还未体验过,赶紧试一下吧。

image

你可能感兴趣的:(你好LiveData)