你好LiveData

首先放一張官方推薦的app設計架構圖,想要了解更多( 芝麻之門

LocalBroadcastManager

冷落的LBM

LocalBroadcastManager有點冷落,一個是不多人知道而且合理使用廣播,不少人要麼使用的是系統的全局廣播BraoadCastRecever,要麼使用EventBusRxAndroid等等其餘觀察者模式的三方庫,慢慢的就失寵了。然鵝,並不僅是這樣,當如今你的項目遷移到Androidx時,會發現官方竟然把它廢棄了,隨後官方明確指出最適合的替代品LiveDatareactive streams,見如下描述 (官方傳送門html

關於LBM

LocalBroadcastManager顧名思義就是本地廣播,也是基於觀察者模式的事件總線,用於應用內通訊,比較安全和高效,雖然遷移到androidx後,在1.1.0高版本LocalBroadcastManager被標記過期,可是它的用處仍是蠻大的,相比系統的廣播,本地廣播有着沒法比擬的優越性,並且很是高效,相比第三方觀察者模式庫,知足條件下我寧願使用1.0.0版本的LocalBroadcastManagerjava

與系統廣播的區別

  • 範圍上:LocalBroadcastManager爲本地廣播,只能接受自身App發送的廣播,只能用於應用內之間的通訊,範圍相對較小;而系統的BraoadCastRecever能夠實現跨進程通信,範圍更大。
  • 效率上:LocalBroadcastManager通訊核心是Handler,因此只能用於應用內通訊,安全和效率都很高;而系統的BraoadCastRecever通訊核心是Binder機制, 實現跨進程通訊,範圍更廣,致使運行效率稍微遜一點。
  • 安全上:LocalBroadcastManager因爲核心是Handler,並且只能動態註冊,只能用於app內通訊,安全上更加的有保障;而系統的BraoadCastRecever容易被利用,安全上相對較弱一點。

使用方式

註冊廣播:react

IntentFilter filter = new IntentFilter();
filter.addAction(「你的Action」);
LocalBroadcastManager.getInstance(Context context).registerReceiver(BroadcastReceiver receiver, filter);
複製代碼

發送廣播:android

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機制進行通訊。bash

private final HashMap<BroadcastReceiver, ArrayList<ReceiverRecord>> mReceivers = new HashMap<>();
 private final HashMap<String, ArrayList<ReceiverRecord>> mActions = new  HashMap<>();
 private final ArrayList<BroadcastRecord> mPendingBroadcasts = new ArrayList<>();
 
 public void registerReceiver(@NonNull BroadcastReceiver receiver,
            @NonNull IntentFilter filter) {
        synchronized (mReceivers) {
            ReceiverRecord entry = new ReceiverRecord(filter, receiver);
            ArrayList<ReceiverRecord> filters = mReceivers.get(receiver);
            if (filters == null) {
                filters = new ArrayList<>(1);
                mReceivers.put(receiver, filters);
            }
            filters.add(entry);
            for (int i=0; i<filter.countActions(); i++) {
                String action = filter.getAction(i);
                ArrayList<ReceiverRecord> entries = mActions.get(action);
                if (entries == null) {
                    entries = new ArrayList<ReceiverRecord>(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<String> categories = intent.getCategories();
            ==========================省略部分===================
                if (receivers != null) {
                    for (int i=0; i<receivers.size(); i++) {
                        receivers.get(i).broadcasting = false;
                    }
                    mPendingBroadcasts.add(new BroadcastRecord(intent, receivers));
                    if (!mHandler.hasMessages(MSG_EXEC_PENDING_BROADCASTS)) {
                        mHandler.sendEmptyMessage(MSG_EXEC_PENDING_BROADCASTS);
                    }
                    return true;
                }
            }
        }
        return false;
    }
複製代碼

明顯看出本地廣播是經過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<brs.length; i++) {
                final BroadcastRecord br = brs[i];
                final int nbr = br.receivers.size();
                for (int j=0; j<nbr; j++) {
                    final ReceiverRecord rec = br.receivers.get(j);
                    if (!rec.dead) {
                        rec.receiver.onReceive(mAppContext, br.intent);
                    }
                }
            }
        }
    }
複製代碼

當工做線程接收到Handler發送的消息時候,會執行executePendingBroadcasts()方法,發送廣播的時候mPendingBroadcasts來存儲Intent意圖與廣播接收器ReceiverRecord,當處理廣播的時候,從mPendingBroadcasts中取出信息存放到廣播接收器數組BroadcastRecord[]中,經過循環調用廣播接收的抽象方法onReceive(Context context,Intent intent)進行消息發送到前臺主線程,就這樣完成了應用內通訊。

初識LiveData

如下翻譯可能有槽點,去官網查看更多更詳細的信息( 芝麻之門

介紹

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<? super T> observer) 添加觀察者對象到列表,配合生命週期
void observeForever(Observer<? super T> observer) 添加觀察者對象到列表,無生命週期
void removeObserver(Observer<? super T> 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<String> currentName;
    public MutableLiveData<String> getCurrentName() {
        if (currentName == null) {
            currentName = new MutableLiveData<String>();
        }
        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<String> nameObserver = new Observer<String>() {
            @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<BigDecimal> {
    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<BigDecimal> myPriceListener = ...;
        myPriceListener.observe(this, price -> {
            // Update the UI.
        });
    }
}
複製代碼

方法將fragment(它是LifecycleOwner的一個實例)做爲第一個參數傳遞。這樣作意味着這個觀察者被綁定到與全部者關聯的Lifecycle對象上,這意味着:

若是Lifecycle對象沒有處於活動狀態,那麼即便值發生了更改,也不會調用觀察者 銷燬生命週期對象後,將自動刪除觀察者。

LiveData對象是生命週期感知的,這意味着您能夠在多個ActivitysFragmentsservice之間共享它們。爲了保持示例的簡單性,能夠將LiveData類實現爲單例,以下所示:

public class StockLiveData extends LiveData<BigDecimal> {
    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<User> userLiveData = ...;
LiveData<String> userName = Transformations.map(userLiveData, user -> {
    user.name + " " + user.lastName
});
複製代碼

Transformations.switchMap()

與map()相似,將函數應用於存儲在LiveData對象中的值,並將結果解包並向下分派。傳遞給switchMap()的函數必須返回LiveData對象,以下例所示:

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

LiveData<String> userId = ...;
LiveData<User> 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<String> 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<String> addressInput = new MutableLiveData();
    public final LiveData<String> 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組件的描述,不只可以提升開發效率,並且讓應用變得更加優質,若是你還未體驗過,趕忙試一下吧。

客官觀賞一下其餘文章

相關文章
相關標籤/搜索