Android Loader官方使用說(shuō)明

Android_Loader

PS:對(duì)于Loader并不是很常用,但是在google的mvp示例中有使用loader加載數(shù)據(jù)的demo,本人覺(jué)得Loader有一定的嘗試價(jià)值,v4有提供低版本支持不用擔(dān)心API版本兼容問(wèn)題。

前言

Loader API讓你從ContentProvider或者其他數(shù)據(jù)源讀取數(shù)據(jù)展示在FragmentActivity或者Fragment上。如果你不理解你為什么要用Loader API執(zhí)行看似平凡的操作,那么首先要考慮沒(méi)有l(wèi)oader的情況下可能遇到這些問(wèn)題:

  • 如果你直接在activity或fragment中獲取數(shù)據(jù),由于在UI線程執(zhí)行查詢過(guò)慢,用戶將遇到缺乏響應(yīng)。
  • 如果你從另一個(gè)線程獲取數(shù)據(jù),也許是AsyncTask,那么你將負(fù)責(zé)通過(guò)各個(gè)activity或fragment生命周期
    管理線程和UI線程,例如onDestroy()和configurations變更。

Loader解決了這些問(wèn)題,包括其他好處。例如:

  • Loader單獨(dú)運(yùn)行在隔離的線程放置janky或UI無(wú)響應(yīng)
  • 當(dāng)事件發(fā)生時(shí),Loader提供回調(diào)方法簡(jiǎn)化線程管理
  • 當(dāng)configuration變更時(shí),Loader保留并緩存結(jié)果,防止重復(fù)查詢
  • Loader能實(shí)現(xiàn)一個(gè)Oberver監(jiān)聽(tīng)底層數(shù)據(jù)變化。例如,CursorLoader自動(dòng)注冊(cè)一個(gè)ContentObserver,當(dāng)數(shù)據(jù)變化時(shí)觸發(fā)重新加載

Loader API摘要

在app中使用Loader是可能涉及多個(gè)類和接口。

LoaderManager

一個(gè)與FragmentActivity和Fragment相關(guān)的抽象類,管理一個(gè)或多個(gè)Loader實(shí)例。 每個(gè)activity或fragment中只有一個(gè)LoaderManager,但是一個(gè)LoaderManager能管理多個(gè)Loader。

Loader調(diào)用initLoader()或restartLoader()開(kāi)始讀取數(shù)據(jù)。系統(tǒng)自動(dòng)確定具有相同ID的Loader是否存在,并將創(chuàng)建新的Loader或者重新使用現(xiàn)用的Loader。

LoaderManager.LoaderCallbacks

這個(gè)接口包含回調(diào)Loader事件觸發(fā)時(shí)回調(diào)的方法。接口定義三個(gè)回調(diào)方法:

  • onCreateLoader(int,Bundle) - 當(dāng)需要一個(gè)新Loader被創(chuàng)建時(shí)調(diào)用此方法。代碼應(yīng)該創(chuàng)建一個(gè)Loader對(duì)象并返回給系統(tǒng)。
  • onLoadFinished(Loader<D>,D) - 當(dāng)Loader加載數(shù)據(jù)完成后調(diào)用此方法。通常,應(yīng)該展示數(shù)據(jù)給用戶。
  • onLoaderReset(Loader<D>) - 當(dāng)一個(gè)已經(jīng)創(chuàng)建的Loader被重置是(當(dāng)你調(diào)用destroyLoader(int))或activity(或fragment)已經(jīng)銷毀時(shí)調(diào)用此方法。 代碼應(yīng)該移除任何對(duì)Loader的數(shù)據(jù)的引用。

此接口通常通過(guò)activity或fragment實(shí)現(xiàn)并注冊(cè),當(dāng)調(diào)用initLoader()或restartLoader()。

Loader

Loader執(zhí)行加載數(shù)據(jù)。該類是一個(gè)抽象的,作為基礎(chǔ)類服務(wù)與所有Loader。你可以直接使用子類Loeader或者使用內(nèi)置子類之一來(lái)簡(jiǎn)單實(shí)現(xiàn)。

  • AsyncTaskLoader - 一個(gè)抽象Loader,提供一個(gè)AsyncTask在單獨(dú)的線程執(zhí)行加載操作。
  • Cursoroader - 一個(gè)AsyncTaskLoader用于異步加載來(lái)自ContentProvider的數(shù)據(jù)。它請(qǐng)求一個(gè)ContentResolver并返回一個(gè)Cursor。

應(yīng)用中使用Loader

app使用Loader通常包括以下內(nèi)容:

  • FragmentActiivty或fragment
  • 一個(gè)LoaderManager實(shí)例
  • 一個(gè)CursorLoader通過(guò)ContentProvider加載數(shù)據(jù)?;蛘?,你可以實(shí)現(xiàn)你的子類Loader或AsyncTaskLoader加載其他來(lái)源的數(shù)據(jù)。
  • 一個(gè)LoaderManager.LoaderCallbacks的實(shí)現(xiàn)。在你創(chuàng)建一個(gè)新Loader和管理已經(jīng)存在Loader引用的地方。
  • 一個(gè)數(shù)據(jù)源,例如ContentProvider,當(dāng)使用一個(gè)CursorLoader

開(kāi)啟Loader

一個(gè)FragmentActivity或Fragmentyou只有一個(gè)LoaderManager管理一個(gè)或多個(gè)Loader實(shí)例。通常在Activity的onCreate()方法或者在fragment的onActivityCreateed()方法中初始化Loader,如下:

// Prepare the loader.  Either re-connect with an existing one,
// or start a new one.
getSupportLoaderManager().initLoader(0, null, this);

initLoader()方法參數(shù)如下:

  • 標(biāo)識(shí)Loader的唯一的ID,這里ID是0
  • 提供給Loader的可選參數(shù),這里傳null
  • LoaderManager.LoaderCallbacks的實(shí)現(xiàn)對(duì)象,LoaderManager會(huì)調(diào)用報(bào)告給Loader事件

initLoader()調(diào)用確保Loader被初始化和活躍。他有兩種可能:

  • 如果ID指定的Loader已經(jīng)存在,則使用最后創(chuàng)建的Loader
  • 如果ID指定的Loader不存在,initLoader()觸發(fā)LoaderManager.LoaderCallbacks的onCreateLoader()方法。這是實(shí)現(xiàn)代碼來(lái)實(shí)例化并返回Loader的地方。

在任何一種情況下,給的LoaderManager.LoaderCallbacks實(shí)現(xiàn)都與Loader相關(guān)聯(lián),在Loader狀態(tài)改變時(shí)被調(diào)用。如果調(diào)用的時(shí)候調(diào)用者處于開(kāi)始狀態(tài),請(qǐng)求已經(jīng)存在的Loader并生成了它的數(shù)據(jù),系統(tǒng)會(huì)立馬調(diào)用onLoadFinished()方法(在iniLoader()之間),你必須準(zhǔn)備好這些的發(fā)生。

注意initLoader()方法返回的被創(chuàng)建Loader,但是你不需要捕獲它的引用。Loadermanager自動(dòng)管理Loader的生命周期。必要時(shí)LoaderManager開(kāi)始和停止加載數(shù)據(jù),可以維持Loader相關(guān)內(nèi)容的狀態(tài)。這意味著,你很少和Loader直接交互(盡管使用Loader方法來(lái)微調(diào)Loader的行為)。

重啟Loader

當(dāng)你使用initLoader(),如上所示,如果ID指定的Loader已經(jīng)存在,用這個(gè)。如果不是就創(chuàng)建一個(gè)。但是有時(shí)候你想丟棄老數(shù)據(jù)并重新開(kāi)始。

要丟棄你的老數(shù)據(jù),你用restartLoader()。例如,當(dāng)用得查詢隊(duì)列變更時(shí),這個(gè)SearchView.OnQueryTextListenerde實(shí)現(xiàn)會(huì)重啟Loader。Loader需要重啟,以便接受搜索過(guò)濾器做一次新的查詢:

public boolean onQueryTextChanged(String newText) {
    // Called when the action bar search text has changed.  Update
    // the search filter, and restart the loader to do a new query
    // with this filter.
    mCurFilter = !TextUtils.isEmpty(newText) ? newText : null;
    getSupportLoaderManager().restartLoader(0, null, this);
    return true;
}

LoaderManager.LoaderCallbacks的使用

LoaderManager.LoaderCallbacks是一個(gè)回調(diào)接口,讓客戶端通過(guò)LoaderManager與之交互。

CursorLoader預(yù)計(jì)Loader在停止之后將保留其數(shù)據(jù)。允許應(yīng)用保持activity或fragment的onStop()和onStart()方法相關(guān)的數(shù)據(jù),當(dāng)用戶返回應(yīng)用程序時(shí)他們不必等待數(shù)據(jù)重載。你可以再LoaderManager.LoaderCallbacks方法合適使用這些方法創(chuàng)建一個(gè)新的Loader,并告訴應(yīng)用合適停止使用Loader的數(shù)據(jù)。

  • onCreateLoader() - 實(shí)例化并Loader為給定的ID 返回一個(gè)新的。
  • onLoadFinished() - 當(dāng)以前創(chuàng)建的加載程序完成加載時(shí)調(diào)用。
  • onLoaderReset() - 當(dāng)以前創(chuàng)建的加載程序正在重置時(shí)調(diào)用,從而使其數(shù)據(jù)不可用。
    這些方法在下面的章節(jié)中有更詳細(xì)的描述。

onCreateLoader

當(dāng)你試圖訪問(wèn)一個(gè)Loader(例如通過(guò)initLoader())時(shí),它會(huì)檢查該ID指定的Loader是否存在。如果沒(méi)有,則觸發(fā)LoaderManager.LoaderCallbacks的onCreateLoader()方法。這是你創(chuàng)建一個(gè)新的Loader的地方。通常將是CursorLoader(),但你能實(shí)現(xiàn)Loader的子類。

在這個(gè)例子中,onCreateLoader()回調(diào)方法創(chuàng)建一個(gè)CursorLoader。你必須用CursorLoader的構(gòu)造方法創(chuàng)建CursorLoader,這需要完整地信息集來(lái)執(zhí)行查詢ContentProvider。具體來(lái)說(shuō),它需要:

  • uri - 藥檢所的內(nèi)容的URI
  • projection - 要返回的列的列表。傳遞null將返回所有列,這是低效的。
  • selection - 過(guò)濾器,聲明返回哪些行,格式化為SQL WHERE子句(不包括WHERE本身)。傳遞null將返回給定URI的所有行。
  • selectionArgs - 你可以在選擇中包括 '?s' ,它們將按照它們出現(xiàn)在選擇中的順序由selectionArgs中的值替換。這些值將被綁定為字符串。
  • sortOrder - 如何對(duì)行進(jìn)行排序,格式為SQL ORDER BY子句(不包括ORDER BY本身)。傳遞null將使用默認(rèn)的排序順序,可能是無(wú)序的。

例如:

public Loader<Cursor> onCreateLoader(int id, Bundle args) {
    // This is called when a new Loader needs to be created.  This
    // sample only has one Loader, so we don't care about the ID.
    // First, pick the base URI to use depending on whether we are
    // currently filtering.
    Uri baseUri;
    if (mCurFilter != null) {
        baseUri = Uri.withAppendedPath(Contacts.CONTENT_FILTER_URI,
                  Uri.encode(mCurFilter));
    } else {
        baseUri = Contacts.CONTENT_URI;
    }

    // Now create and return a CursorLoader that will take care of
    // creating a Cursor for the data being displayed.
    String select = "((" + Contacts.DISPLAY_NAME + " NOTNULL) AND ("
            + Contacts.HAS_PHONE_NUMBER + "=1) AND ("
            + Contacts.DISPLAY_NAME + " != '' ))";
    return new CursorLoader(getActivity(), baseUri,
            CONTACTS_SUMMARY_PROJECTION, select, null,
            Contacts.DISPLAY_NAME + " COLLATE LOCALIZED ASC");
}

onLoadFinished

當(dāng)之前創(chuàng)建的Loader已經(jīng)完成加載時(shí)此方法被調(diào)用。這個(gè)方法保證Loader提供最后的數(shù)據(jù)之前被調(diào)用。此時(shí),你應(yīng)該移除所有的老數(shù)據(jù)(因?yàn)樗芸毂会尫牛?,但是不?yīng)該你自己釋放數(shù)據(jù),因?yàn)樗腖oader擁有它并會(huì)照顧。

一旦應(yīng)用長(zhǎng)時(shí)間不使用loader將釋放數(shù)據(jù)。例如,如果數(shù)據(jù)是來(lái)自CursorLoader的一個(gè)cursor,你不應(yīng)該自己調(diào)用close()。如果cursor放在CursorAdapter中,你應(yīng)該用swapCursor()方法這樣老的cursor不關(guān)閉。例如:

// This is the Adapter being used to display the list's data.
SimpleCursorAdapter mAdapter;
...

public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
    // Swap the new cursor in.  (The framework will take care of closing the
    // old cursor once we return.)
    mAdapter.swapCursor(data);
}

onLoaderReset

當(dāng)已經(jīng)創(chuàng)建的Loader正在重置是調(diào)用這個(gè)方法,從而使數(shù)據(jù)不可用。這個(gè)回調(diào)讓你找出數(shù)據(jù)何時(shí)被釋放,這樣你就可以移除它的引用。

// This is the Adapter being used to display the list's data.
SimpleCursorAdapter mAdapter;
...

public void onLoaderReset(Loader<Cursor> loader) {
    // This is called when the last Cursor provided to onLoadFinished()
    // above is about to be closed.  We need to make sure we are no
    // longer using it.
    mAdapter.swapCursor(null);
}

舉例

作為一個(gè)例子,這里實(shí)現(xiàn)了一個(gè)完整地Fragment顯示一個(gè)LIstView,包含ContentProvider查詢聯(lián)系人內(nèi)容的查詢結(jié)果。它使用了CursorLoader管理provider的查詢。

對(duì)于訪問(wèn)用戶聯(lián)系人的應(yīng)用,如本例所示,manifest必須包含READ_CONTACTS權(quán)限。

public static class CursorLoaderListFragment extends ListFragment
        implements OnQueryTextListener, LoaderManager.LoaderCallbacks<Cursor> {

    // This is the Adapter being used to display the list's data.
    SimpleCursorAdapter mAdapter;

    // If non-null, this is the current filter the user has provided.
    String mCurFilter;

    @Override public void onActivityCreated(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);

        // Give some text to display if there is no data.  In a real
        // application this would come from a resource.
        setEmptyText("No phone numbers");

        // We have a menu item to show in action bar.
        setHasOptionsMenu(true);

        // Create an empty adapter we will use to display the loaded data.
        mAdapter = new SimpleCursorAdapter(getActivity(),
                android.R.layout.simple_list_item_2, null,
                new String[] { Contacts.DISPLAY_NAME, Contacts.CONTACT_STATUS },
                new int[] { android.R.id.text1, android.R.id.text2 }, 0);
        setListAdapter(mAdapter);

        // Prepare the loader.  Either re-connect with an existing one,
        // or start a new one.
        getLoaderManager().initLoader(0, null, this);
    }

    @Override public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
        // Place an action bar item for searching.
        MenuItem item = menu.add("Search");
        item.setIcon(android.R.drawable.ic_menu_search);
        item.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
        SearchView sv = new SearchView(getActivity());
        sv.setOnQueryTextListener(this);
        item.setActionView(sv);
    }

    public boolean onQueryTextChange(String newText) {
        // Called when the action bar search text has changed.  Update
        // the search filter, and restart the loader to do a new query
        // with this filter.
        mCurFilter = !TextUtils.isEmpty(newText) ? newText : null;
        getLoaderManager().restartLoader(0, null, this);
        return true;
    }

    @Override public boolean onQueryTextSubmit(String query) {
        // Don't care about this.
        return true;
    }

    @Override public void onListItemClick(ListView l, View v, int position, long id) {
        // Insert desired behavior here.
        Log.i("FragmentComplexList", "Item clicked: " + id);
    }

    // These are the Contacts rows that we will retrieve.
    static final String[] CONTACTS_SUMMARY_PROJECTION = new String[] {
        Contacts._ID,
        Contacts.DISPLAY_NAME,
        Contacts.CONTACT_STATUS,
        Contacts.CONTACT_PRESENCE,
        Contacts.PHOTO_ID,
        Contacts.LOOKUP_KEY,
    };
    public Loader<Cursor> onCreateLoader(int id, Bundle args) {
        // This is called when a new Loader needs to be created.  This
        // sample only has one Loader, so we don't care about the ID.
        // First, pick the base URI to use depending on whether we are
        // currently filtering.
        Uri baseUri;
        if (mCurFilter != null) {
            baseUri = Uri.withAppendedPath(Contacts.CONTENT_FILTER_URI,
                    Uri.encode(mCurFilter));
        } else {
            baseUri = Contacts.CONTENT_URI;
        }

        // Now create and return a CursorLoader that will take care of
        // creating a Cursor for the data being displayed.
        String select = "((" + Contacts.DISPLAY_NAME + " NOTNULL) AND ("
                + Contacts.HAS_PHONE_NUMBER + "=1) AND ("
                + Contacts.DISPLAY_NAME + " != '' ))";
        return new CursorLoader(getActivity(), baseUri,
                CONTACTS_SUMMARY_PROJECTION, select, null,
                Contacts.DISPLAY_NAME + " COLLATE LOCALIZED ASC");
    }

    public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
        // Swap the new cursor in.  (The framework will take care of closing the
        // old cursor once we return.)
        mAdapter.swapCursor(data);
    }

    public void onLoaderReset(Loader<Cursor> loader) {
        // This is called when the last Cursor provided to onLoadFinished()
        // above is about to be closed.  We need to make sure we are no
        // longer using it.
        mAdapter.swapCursor(null);
    }
}

更多的例子

以下示例說(shuō)明如何使用裝載機(jī):

  • LoaderCursor - 上面顯示的片段的完整版本。
  • 檢索聯(lián)系人列表 - 使用CursorLoader從演示文稿提供程序檢索數(shù)據(jù)的演練。
  • LoaderThrottle - 如何使用限制來(lái)減少內(nèi)容提供者在其數(shù)據(jù)更改時(shí)執(zhí)行的查詢數(shù)的示例。
  • AsyncTaskLoader- 使用AsyncTaskLoader 從包管理器加載當(dāng)前安裝的應(yīng)用程序的示例。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,362評(píng)論 6 544
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 99,577評(píng)論 3 429
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人,你說(shuō)我怎么就攤上這事?!?“怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 178,486評(píng)論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 63,852評(píng)論 1 317
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 72,600評(píng)論 6 412
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 55,944評(píng)論 1 328
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,944評(píng)論 3 447
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 43,108評(píng)論 0 290
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,652評(píng)論 1 336
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 41,385評(píng)論 3 358
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 43,616評(píng)論 1 374
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,111評(píng)論 5 364
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,798評(píng)論 3 350
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 35,205評(píng)論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 36,537評(píng)論 1 295
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 52,334評(píng)論 3 400
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 48,570評(píng)論 2 379

推薦閱讀更多精彩內(nèi)容

  • 1 背景## 在Android中任何耗時(shí)的操作都不能放在UI主線程中,所以耗時(shí)的操作都需要使用異步實(shí)現(xiàn)。同樣的,在...
    我是昵稱閱讀 1,228評(píng)論 0 3
  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,743評(píng)論 25 708
  • Android開(kāi)發(fā)者都經(jīng)歷過(guò)APP UI開(kāi)發(fā)不當(dāng) 會(huì)造成overDraw,導(dǎo)致APP UI渲染過(guò)慢,但是很多人卻沒(méi)...
    Tamic閱讀 15,975評(píng)論 30 104
  • 1.她過(guò)去從未這樣努力思考過(guò)。她已經(jīng)不再是個(gè)孩子了,但也還沒(méi)有真正長(zhǎng)大。蘇菲意識(shí)到她已經(jīng)開(kāi)始朝著兔子(就是從宇宙的...
    南之嘉木閱讀 477評(píng)論 0 0
  • 1 一開(kāi)學(xué),學(xué)校就組織了家長(zhǎng)學(xué)校的講座,所有一年級(jí)的老師都參加了(就是我說(shuō)的周六加班)。不說(shuō)客氣話,我的收獲真的很...
    三分之一2015閱讀 211評(píng)論 0 0