你好LiveData

image

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

LocalBroadcastManager

冷落的LBM

LocalBroadcastManager有點冷落,一個是很少人知道并且合理使用廣播,很多人要么使用的是系統(tǒng)的全局廣播BraoadCastRecever,要么使用EventBusRxAndroid等等其他觀察者模式的三方庫,慢慢的就失寵了。然鵝,并不只是這樣,當現(xiàn)在你的項目遷移到Androidx時,會發(fā)現(xiàn)官方居然把它廢棄了,隨后官方明確指出最適合的替代品LiveDatareactive streams,見以下描述 (官方傳送門

image
image

關(guān)于LBM

LocalBroadcastManager顧名思義就是本地廣播,也是基于觀察者模式的事件總線,用于應(yīng)用內(nèi)通信,比較安全和高效,雖然遷移到androidx后,在1.1.0高版本LocalBroadcastManager被標記過時,但是它的用處還是蠻大的,相比系統(tǒng)的廣播,本地廣播有著無法比擬的優(yōu)越性,而且非常高效,相比第三方觀察者模式庫,滿足條件下我寧愿使用1.0.0版本的LocalBroadcastManager。

與系統(tǒng)廣播的區(qū)別

  • 范圍上:LocalBroadcastManager為本地廣播,只能接受自身App發(fā)送的廣播,只能用于應(yīng)用內(nèi)之間的通信,范圍相對較??;而系統(tǒng)的BraoadCastRecever可以實現(xiàn)跨進程通訊,范圍更大。
  • 效率上:LocalBroadcastManager通信核心是Handler,所以只能用于應(yīng)用內(nèi)通信,安全和效率都很高;而系統(tǒng)的BraoadCastRecever通信核心是Binder機制,
    實現(xiàn)跨進程通信,范圍更廣,導致運行效率稍微遜一點。
  • 安全上:LocalBroadcastManager由于核心是Handler,而且只能動態(tài)注冊,只能用于app內(nèi)通信,安全上更加的有保障;而系統(tǒng)的BraoadCastRecever容易被利用,安全上相對較弱一點。

使用方式

注冊廣播:

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

發(fā)送廣播:

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開發(fā)手冊(正式版)_v1.0.0》中有明確指出關(guān)于廣播的使用規(guī)范問題,以下規(guī)范等級為強制,所以我們應(yīng)用合理正確的使用廣播,遵循綠色公約,避免信息泄露和攔截意圖的風險,以下為規(guī)范描述:

如果廣播僅限于應(yīng)用內(nèi),則可以使用LocalBroadcastManager#sendBroadcast()實現(xiàn),避免敏感信息外泄和 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明顯采取的是懶漢模式的單例模式,并且初始化的時候創(chuàng)建了一個Handler對象,在主線程上進行工作,用于處理發(fā)送的廣播,這就是和系統(tǒng)BroadCastReciver的最大區(qū)別,系統(tǒng)廣播則是通過Binder機制進行通信的,而本地廣播采取的是Handler機制進行通信。

 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來進行協(xié)調(diào)運作的,而mReceivers則是以receiver作為key,存儲的是一個接收記錄ReceiverRecord集合,而ReceiverRecord是用來存儲篩選器IntentFilter與廣播接收器BroadcastReceiver及廣播運行狀態(tài)的,但是由于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進行發(fā)送消息的,檢查排隊隊列是否有未發(fā)布的MSG_EXEC_PENDING_BROADCASTS,沒有的話發(fā)送消息。

    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發(fā)送的消息時候,會執(zhí)行executePendingBroadcasts()方法,發(fā)送廣播的時候mPendingBroadcasts來存儲Intent意圖與廣播接收器ReceiverRecord,當處理廣播的時候,從mPendingBroadcasts中取出信息存放到廣播接收器數(shù)組BroadcastRecord[]中,通過循環(huán)調(diào)用廣播接收的抽象方法onReceive(Context context,Intent intent)進行消息發(fā)送到前臺主線程,就這樣完成了應(yīng)用內(nèi)通信。

初識LiveData

image

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

介紹

LiveData是官方架構(gòu)組件,是Jetpack眾多組件的一個,它是一個可觀察的數(shù)據(jù)持有者類。與常規(guī)的可觀察對象不同,LiveData是生命周期感知的,這意味著它尊重其他應(yīng)用程序組件的生命周期,比如activitiesfragments或者services。這種意識確保LiveData只更新處于活動生命周期狀態(tài)的應(yīng)用程序組件觀察者。

LiveData認為,如果一個觀察者的生命周期處于STARTEDRESUMED狀態(tài),那么這個觀察者(由observer類表示)就處于活動狀態(tài)。LiveData只將更新通知活動觀察者。注冊為監(jiān)視LiveData對象的非活動觀察者不會收到有關(guān)更改的通知。

您可以注冊一個與實現(xiàn)LifecycleOwner接口的對象配對的觀察者。當相應(yīng)的生命周期對象的狀態(tài)更改為DESTROYED時,此關(guān)系允許刪除觀察者。這對于活動和片段特別有用,因為它們可以安全地觀察LiveData對象,而不用擔心泄漏——當activitiesfragments的生命周期被破壞時,它們會立即取消訂閱。

優(yōu)勢

  • UI界面與數(shù)據(jù)實時保證一致
    LiveData遵循觀察者模式。當生命周期狀態(tài)發(fā)生變化時,LiveData通知觀察者Observer對象。您可以合并代碼來更新這些觀察者對象中的UI。您的觀察者可以在每次發(fā)生更改時更新UI,而不是每次應(yīng)用程序數(shù)據(jù)更改時都更新UI

  • 不會造成內(nèi)存泄漏
    觀察者被綁定到生命周期Lifecycle對象,并在其關(guān)聯(lián)的生命周期被destroyed后進行清理。

  • 當界面銷毀或者停止活動不會造成崩潰
    如果觀察者的生命周期是不活動的,例如在后堆棧中的活動,那么它不會接收任何LiveData事件。

  • 不需要手動處理生命周期
    UI組件只觀察相關(guān)數(shù)據(jù),不停止或恢復觀察。LiveData自動管理所有這些,因為它在觀察過程中知道相關(guān)的生命周期狀態(tài)變化。

  • 總是保證最新的數(shù)據(jù)
    如果一個生命周期變?yōu)椴换顒拥?,它將在再次活動時接收最新的數(shù)據(jù)。例如,在后臺的活動在返回到前臺后立即接收最新的數(shù)據(jù)。

  • 適當?shù)呐渲酶?br> 如果某個activitysfragments由于配置更改(如設(shè)備旋轉(zhuǎn))而重新創(chuàng)建,它將立即接收最新可用數(shù)據(jù)。

  • 共享資源和數(shù)據(jù)
    您可以使用singleton模式擴展LiveData對象來包裝系統(tǒng)服務(wù),以便在您的應(yīng)用程序中共享它們。LiveData對象連接到系統(tǒng)服務(wù)一次,然后任何需要該資源的觀察者都可以查看LiveData對象。有關(guān)更多信息,請參見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() 當活動觀察者的數(shù)量從0變?yōu)?時調(diào)用
void onInactive() 當活動觀察者的數(shù)量從1變?yōu)?時調(diào)用
void postValue(T value) 將任務(wù)發(fā)布到主線程以設(shè)置給定值
void setValue(T value) 設(shè)置更新數(shù)據(jù)源

使用步驟

  • 依賴
// 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"
  • 創(chuàng)建LiveData實例來保存特定類型的數(shù)據(jù)。通常會結(jié)合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?,F(xiàn)在這些UI控制器負責顯示數(shù)據(jù),而不是保存數(shù)據(jù)狀態(tài)。

將LiveData實例與特定的Activity或Fragment實例解耦,并允許LiveData對象在配置更改后仍然存在。
  • 創(chuàng)建一個觀察者對象Observer,該對象定義onChanged()方法,該方法控制LiveData對象持有的數(shù)據(jù)更改時發(fā)生的情況。通常在UI控制器中創(chuàng)建一個觀察者對象,例如一個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);
    }
}
在大多數(shù)情況下,應(yīng)用程序組件的onCreate()方法是開始觀察LiveData對象的合適位置,原因如下:  

以確保系統(tǒng)不會從活動或fragment的onResume()方法發(fā)出冗余調(diào)用。  

確?;顒踊蚱尉哂袛?shù)據(jù),可以在活動時立即顯示這些數(shù)據(jù)。一旦應(yīng)用程序組件處于啟動狀態(tài),它就會從它所觀察的LiveData對象接收到最近的值。只有在設(shè)置了要觀察的LiveData對象時才會發(fā)生這種情況。

通常,LiveData只在數(shù)據(jù)發(fā)生更改時提供更新,并且只向活動觀察者提供更新。這種行為的一個例外是,當觀察者從非活動狀態(tài)更改為活動狀態(tài)時,也會收到更新。此外,如果觀察者第二次從非活動狀態(tài)更改為活動狀態(tài),則只有當值自上次活動以來發(fā)生更改時,才會接收到更新。
  • 使用observe()方法將觀察者對象附加到LiveData對象。方法接受LifecycleOwner對象。這訂閱觀察者對象到LiveData對象,以便在發(fā)生更改時通知它。通常將觀察者對象附加到UI控制器中,例如ActivityFragment
// Observe the LiveData, passing in this activity as the LifecycleOwner and the observer.
model.getCurrentName().observe(this, nameObserver);
您可以使用observeForever(observer)方法注冊一個沒有關(guān)聯(lián)LifecycleOwner對象的觀察者。在這種情況下,觀察者總是被認為是活躍的,因此總是被通知修改。您可以刪除調(diào)用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對象具有活動觀察者時,將調(diào)用onActive()方法。這意味著您需要開始從該方法觀察股票價格更新。

當LiveData對象沒有任何活動的觀察者時,將調(diào)用onInactive()方法。因為沒有觀察者在聽,所以沒有理由保持與StockManager服務(wù)的連接。

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的一個實例)作為第一個參數(shù)傳遞。這樣做意味著這個觀察者被綁定到與所有者關(guān)聯(lián)的Lifecycle對象上,這意味著:

如果Lifecycle對象沒有處于活動狀態(tài),那么即使值發(fā)生了更改,也不會調(diào)用觀察者
銷毀生命周期對象后,將自動刪除觀察者。

LiveData對象是生命周期感知的,這意味著您可以在多個Activitys、Fragmentsservice之間共享它們。為了保持示例的簡單性,可以將LiveData類實現(xiàn)為單例,如下所示:

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);
    }
}

Activity、FragmentService中可以這么調(diào)用,通過單例實現(xiàn)共享數(shù)據(jù)

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

轉(zhuǎn)化 LiveData

在將LiveData對象發(fā)送給觀察者之前,您可能希望更改存儲在LiveData對象中的值,或者您可能需要根據(jù)另一個LiveData實例的值返回另一個LiveData實例。生命周期包提供轉(zhuǎn)換類,該類包含支持這些場景的helper方法。

Transformations.map ()

對存儲在LiveData對象中的值應(yīng)用函數(shù),并將結(jié)果向下傳播

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

Transformations.switchMap()

與map()類似,將函數(shù)應(yīng)用于存儲在LiveData對象中的值,并將結(jié)果解包并向下分派。傳遞給switchMap()的函數(shù)必須返回LiveData對象,如下例所示:

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

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

您可以使用轉(zhuǎn)換方法在觀察者的生命周期中攜帶信息。除非觀察者正在觀察返回的LiveData對象,否則不會計算轉(zhuǎn)換。因為轉(zhuǎn)換是延遲計算的,所以與生命周期相關(guān)的行為是隱式傳遞的,不需要額外的顯式調(diào)用或依賴關(guān)系。

如果您認為在ViewModel對象中需要一個生命周期對象,那么轉(zhuǎn)換可能是一個更好的解決方案。例如,假設(shè)您有一個UI組件,它接受一個地址并返回該地址的郵政編碼。您可以為這個組件實現(xiàn)簡單的視圖模型,如下面的示例代碼所示:

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對象注銷注冊,并在每次調(diào)用getPostalCode()時注冊到新實例。此外,如果重新創(chuàng)建UI組件,它將觸發(fā)對repository.getPostCode()方法的另一個調(diào)用,而不是使用前一個調(diào)用的結(jié)果。

相反,您可以將郵政編碼查詢實現(xiàn)為地址輸入的轉(zhuǎn)換,如下面的示例所示:

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字段被定義為轉(zhuǎn)換addressInput。只要您的應(yīng)用程序具有與該postalCode字段關(guān)聯(lián)的活動觀察者,就會在addressInput更改時重新計算并檢索字段的值 。

此機制允許較低級別的應(yīng)用程序創(chuàng)建LiveData按需延遲計算的對象。甲ViewModel對象可以容易地獲得以引用LiveData的對象,然后在它們的頂部限定的變換規(guī)則。

創(chuàng)建新的轉(zhuǎn)換

有十幾種不同的特定轉(zhuǎn)換可能對您的應(yīng)用有用,但默認情況下不提供。要實現(xiàn)自己的轉(zhuǎn)換,您可以使用MediatorLiveData該類,該類偵聽其他LiveData對象并處理它們發(fā)出的事件。MediatorLiveData正確地將其狀態(tài)傳播到源LiveData對象。要了解有關(guān)此模式的更多信息,請參閱Transformations 該類的參考文檔 。

合并多個LiveData源

MediatorLiveData是一個子類LiveData,允許您合并多個LiveData源。MediatorLiveData 只要任何原始LiveData源對象發(fā)生更改,就會觸發(fā)對象的觀察者。

例如,如果LiveDataUI中有一個可以從本地數(shù)據(jù)庫或網(wǎng)絡(luò)更新的對象,則可以將以下源添加到該 MediatorLiveData對象:

  • LiveData與存儲在數(shù)據(jù)庫中的數(shù)據(jù)關(guān)聯(lián)的對象。

  • LiveData與從網(wǎng)絡(luò)訪問的數(shù)據(jù)關(guān)聯(lián)的對象。

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

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

總結(jié)

讀到這里,是不是發(fā)現(xiàn)LiveData即有點像EventBus,又有點像Rxjava,這正是他的強大之處,另外它還能夠結(jié)合組件的生命周期使用,不僅能夠共享資源與數(shù)據(jù),而且UI與數(shù)據(jù)同步也是非常實時的,正如官方對Jetpack組件的描述,不僅能夠提高開發(fā)效率,而且讓應(yīng)用變得更加優(yōu)質(zhì),如果你還未體驗過,趕緊試一下吧。

image
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • [TOC] LiveData LiveData概述 LiveData是一個可觀察的數(shù)據(jù)持有者類。與常規(guī)observ...
    雪晨杰閱讀 1,195評論 0 8
  • 示例應(yīng)用程序 使用LiveData的優(yōu)點 使用LiveData對象創(chuàng)建LiveData對象觀察LiveData對象...
    yyg閱讀 5,936評論 5 7
  • 在Model - build.gradle添加依賴 LiveData是一個可觀察的數(shù)據(jù)持有者類。與常規(guī)可觀察性不同...
    貝貝beibei96閱讀 1,478評論 0 1
  • 概述 LiveData是一個可觀察的數(shù)據(jù)持有者類。 與常規(guī)observable不同,LiveData是生命周期感知...
    鹿小純0831閱讀 333評論 0 0
  • LiveData Overview LiveData 是一個可觀察的數(shù)據(jù)持有者類。與常規(guī) observable 不...
    Little丶Jerry閱讀 3,317評論 0 3

友情鏈接更多精彩內(nèi)容