99热99这里只有精品6国产,亚洲中文字幕在线天天更新,在线观看亚洲精品国产福利片 ,久久久久综合网

歡迎加入QQ討論群258996829
麥子學(xué)院 頭像
蘋果6袋
6
麥子學(xué)院

Android開發(fā)中的一些注意事項(xiàng)

發(fā)布時間:2016-10-13 23:16  回復(fù):0  查看:2223   最后回復(fù):2016-10-13 23:16  

開發(fā)中,我們很有必要掌握一些套路和技巧,提升代碼開發(fā)效率,今天和大家分享的就是android開發(fā)中的一些套路,一起來看看吧。

 

initView()與updateView()

 

通常,我會添加一個 initView() 方法來初始化所有的 View 對象,在這個方法的具體實(shí)現(xiàn)中,可能會有兩種不同的細(xì)微差別。第一種是僅僅做 findViewById() 就好了,也就是僅僅是去找到每一個 View 對象,而不去給它們設(shè)置屬性,比如 setText() 之類的。另一種則是在 findViewById() 后,順便給它們設(shè)置初始值。

 

我更傾向于第一種做法,因?yàn)槿绻阍?nbsp;initView ()方法中給 View 設(shè)置一些屬性,那么當(dāng)一些數(shù)據(jù)變更時,你可能也需要去變更 View 的一些屬性,你必然會有一個 updateView() 這樣的方法。 updateView() 方法中,需要根據(jù)當(dāng)前頁面的狀態(tài)和數(shù)據(jù)去給 View 設(shè)值,問題就在于,當(dāng)需求發(fā)生變化的時候,你可能需要改兩個地方, initView() 和 updateView() 。考慮到這一點(diǎn)。最佳的做法就是你需要一個 initView() 方法和一個 updateView() 方法。

 

initView() 方法只做初始化操作,也就是僅僅只會發(fā)生一次的操作,比如 findViewById() , setListener() 之類的。而 updateView() 方法中,則是去做一些根據(jù)某些成員變量,flag,boolean值之類的去變更 View 的屬性,會被反復(fù)調(diào)用的操作。

 

關(guān)于 updateView() 方法,我又有兩種不同的思路,在此之前,先具體的說明一下 updateView() 中要干的工作。比如我們有一些成員變量 dataA , dataB ,有一些會隨之變化的 View , ViewA1 , ViewA2 , ViewB1 , ViewB2 ……然后當(dāng)數(shù)據(jù) dataA 改變時,我們需要更改 ViewA1 , ViewA2 的屬性,當(dāng)數(shù)據(jù) dataB 改變時,我們要更改 ViewB* 的屬性,于是,我們通常寫的 updateView() 方法是這樣的。

 

private void updateView() {

 

    ...

    viewA1.setText(dataA.getContent());

    viewA2.setTextColor(dataA.getTextColor());

    

    viewB1.setImage(dataB.getImage());

    viewB2.setText(dataB.getTitle());

    ...

}

在我們的 Activity / Fragment 比較簡單的時候,這樣寫應(yīng)該沒有什么問題,但是當(dāng)頁面的邏輯因需求的變更而變得越來越復(fù)雜,我們可能需要維持很多很多的成員變量(數(shù)據(jù))和 View 。那么 updateView() 方法可能里面做了很多很多的工作,這樣調(diào)用一次必然是效率低下的。因此,我認(rèn)為另一種比較好的方式是將數(shù)據(jù)A所關(guān)聯(lián)的Views都封裝成一個方法,數(shù)據(jù)B所關(guān)聯(lián)的 Views 都封裝成另一個方法,像這樣。

 

private void updateAViews() {

    viewA1.setText(dataA.getContent());

    viewA2.setTextColor(dataA.getTextColor());

    ...

}

 

private void updateBViews() {

    viewB1.setImage(dataB.getImage());

    viewB2.setText(dataB.getTitle());

    ...

}

 

private void updateAllViews() {

    updateAViews();

    updateBViews();

    ...

}

顯然,第二種方式是效率最好的一種方式,也是維護(hù)起來最麻煩的一種方式,但我個人還是比較傾向于第二種寫法。因?yàn)橛幸恍?nbsp;View 它的 onDraw() 方法本身真的會消耗比較長的時間,如果簡單粗暴的更新所有的 View ,可能會讓UI的流暢度大打折扣。

 

使用boolean值來避免updateView()中的空指針異常

 

當(dāng)我們使用 initView() 和 updateView() 兩個方法來變更 View 的時候,要注意空指針的情況,因?yàn)檎{(diào)用 updateView 的時機(jī)不是自己能控制的, updateView 可能是在網(wǎng)絡(luò)數(shù)據(jù)返回時調(diào)用,那么如果 onCreate 的時候先請求數(shù)據(jù),數(shù)據(jù)馬上返回了并調(diào)用 updateView 方法,這個時候, initView 還沒有執(zhí)行,那么 updateView 中對 View 的操作就會報空指針異常。

 

我們可以使用一個 boolean 值來解決這個問題。

 

提前考慮Activity和Fragment的復(fù)用

 

當(dāng)我們寫 Activity 或 Fragment 的時候需要考慮到這個頁面可能會從哪些地方調(diào)過來。比如說,我們要完成一個需求,這個需求是顯示一個列表,列表里面有特定的數(shù)據(jù),這個頁面必須要自己全新寫一個 Activity 或 Fragment來完成,入口也只有一個,那么我們幾乎是可以“為所欲為”的實(shí)現(xiàn)這個頁面,想怎么寫就怎么寫。

 

但是當(dāng)需求發(fā)生了變化,比如其他地方也可以點(diǎn)擊進(jìn)入你這個頁面,并且還顯示了不一樣的數(shù)據(jù),考慮到頁面復(fù)用這一點(diǎn),我們應(yīng)該通過傳入不同的參數(shù),來改變這個頁面的行為(應(yīng)該顯示怎么樣的數(shù)據(jù),或者UI上有哪些其他的變化)。

 

所以,在我們?nèi)聦戇@個頁面的時候,就應(yīng)該有所收斂,要主動思考一下,因?yàn)檫@個頁面如果是被復(fù)用的,那么一般來說,是這個頁面的樣式,行為會被復(fù)用。不一樣的地方往往是數(shù)據(jù),頁面的復(fù)用,就要考慮到在 onCreate的時候可以傳入不同的參數(shù),完成不同的要求和顯示。

 

我們應(yīng)該在 Activity 或 Fragment 中添加幾個成員變量,用來標(biāo)記狀態(tài),比如:

 

public class DataListActivity extends Activity {

 

    public static final int DATA_TYPE_ALL = 1;

    public static final int DATA_TYPE_PART = 2;

 

    private int mDataType = DATA_TYPE_ALL;

    

    ...

}

這樣,我們內(nèi)部獲取數(shù)據(jù)的時候就根據(jù)這個 mDataType 來做具體的處理就好了。考慮到復(fù)用這一點(diǎn),后面擴(kuò)展的時候就會更游刃有余。并且這個 mDataType 也許會影響到UI上的一些表現(xiàn), updateView 系列方法可能也需要關(guān)心這個(些)變量的情況。

 

通過封裝好的靜態(tài)方法啟動Activity

 

初學(xué)的時候,我們總是是用下面類似的代碼啟動 Activity 。

 

Intent i = new Intent();

i.setClass(context, TargetActivity.class);

context.startActivity(i);

但是,根據(jù)上一個小主題上面所說的,往往我們需要告訴要啟動的 Activity 一些特定的信息,然后展示出不同的行為,一般有兩種常見的寫法。

 

方式A:

 

public class TargetActivity extends Activity {

 

    public static final String INTENT_KEY_DATA_TYPE = "INTENT_KEY_DATA_TYPE";

    

    public static final int DATA_TYPE_ALL = 1;

    public static final int DATA_TYPE_PART = 2;

    

    public static void start(Context c, int dataType) {

        Intent i = new Intent();

        i.setClass(c, TargetActivity.class);

        i.putExtras(INTENT_KEY_DATA_TYPE, dataType);

        c.startActivity(i);

    }

}

 

//in other Activity

TargetActivity.start(context, TargetActivity.DATA_TYPE_ALL);

方式B:

 

public class TargetActivity extends Activity {

 

    public static final String INTENT_KEY_DATA_TYPE = "INTENT_KEY_DATA_TYPE";

    

    public static final int DATA_TYPE_ALL = 1;

    public static final int DATA_TYPE_PART = 2;

    

    public static Intent obtainIntent(Context, int dataType) {

        Intent i = new Intent();

        i.setClass(c, TargetActivity.class);

        i.putExtras(INTENT_KEY_DATA_TYPE, dataType);

        return i;

    }

}

 

//in other Activity.

startActivity(TargetActivity.obtainIntent(this, TargetActivity.DATA_TYPE_ALL));

方式A更簡潔,方式B更繁瑣一些,但是方式B更好,因?yàn)橛袝r候我們需要啟動的 Activity 結(jié)束時返回一些東西,那么我們需要調(diào)用到 startActivityForResult() 方法來啟動,在當(dāng)前的 Activity 調(diào)用這個方法,必須要獲取到 Intent 對象,所以,方式B的 obtainIntent 使用情況就更廣泛了。

 

但在編寫 obtianIntent 方法的時候,建議讓它帶上你需要傳遞的參數(shù),當(dāng)前的demo是只有一個 int 型的 dataType ,也許你還有很多其他的參數(shù),但都請?jiān)?nbsp;obtainIntent 方法中就給 Intent 填上,這樣外面(其他)的 Activity就不需要去填寫這些額外的信息了,你的 INTENT_KEY 可以完全的定義在要用它的內(nèi)部,這樣做真是又干凈又漂亮。

 

父類應(yīng)該減輕子類的負(fù)擔(dān),而不是給子類添加約束

 

上面幾個話題,我們講了幾個常見的套路做法,這樣可以使代碼更加清晰,更加易于維護(hù)。

 

但是我們習(xí)慣的套路中那些 initView , updateView , obtainIntent 等方法,并不適合移動到父類去,因?yàn)檫@不是邏輯,如果你挪到父類中寫成抽象方法,方法就是限定死了,所有的子類都要有這個 initView 方法,這樣是不合適的,不同的人也許有不同的代碼習(xí)慣,因此將多余的流程挪到父類,就會形成對子類的約束。子類中如果有重復(fù)的邏輯,才是應(yīng)該移動到父類的。

 

監(jiān)聽器,觀察者模式,回調(diào)

 

其實(shí)監(jiān)聽器和觀察者模式,回調(diào)都是一樣的東西,表面上看,它們就是一群叫 OnXxxxx 的一群方法或者接口。

 

它們負(fù)責(zé)告訴你一些事件發(fā)生了,比如系統(tǒng)給你的 onClick , onTouch , onSrcoll ……還可以是在新的線程發(fā)起一個網(wǎng)絡(luò)請求,當(dāng)請求結(jié)果返回時,告訴你,像 onResult , onPush ……這樣的形式。

 

總之,當(dāng)你理解了這個東西,你就可以熟練的使用,當(dāng)你想寫一個控件,這個控件要完成一個功能或者一些特性,你需要提供一些回調(diào)接口來供客戶程序員使用。比如我之前寫過一個底部有l(wèi)oading的控件,滾動到底部的時候,會出現(xiàn)一個loading(轉(zhuǎn)菊花),然后給你一個“時機(jī)”來讓你請求數(shù)據(jù),然后讓 adapter 更新數(shù)據(jù)。這里有是具體的代碼: BottomLoadListView.java in github

 

通常,我們可以把這個回調(diào)接口都讓 Activity 或者 Fragment 來實(shí)現(xiàn),像這樣:

 

public class MyActivity extends Activity implement OnClickListener, OnNetworkChangeListener, IOnRequestCallback{

    ...

}

這樣,這個 Activity 內(nèi)部的一些對象需要回調(diào)接口的時候,直接給它 this 即可,就不需要那么多匿名內(nèi)部類了,而這些回調(diào)方法都放在 Activity 中,當(dāng)它們被調(diào)用的時候,也能很好的控制整個 Activity 的行為,是很方便的。

 

多個頁面共用數(shù)據(jù)與回調(diào)

 

通常,我們某一個頁面( Activity / Fragment )需要顯示一些數(shù)據(jù),這些數(shù)據(jù)的引用都是讓 Activity 自己持有的,如果僅僅是一個頁面需要這些數(shù)據(jù),這么做沒有什么問題,當(dāng)我們有兩個頁面需要對同一份數(shù)據(jù)進(jìn)行操作的時候,這樣做就不太方便了。通常可以寫一個名為 XxxxEngine 的東西,xxx具體是什么跟所關(guān)聯(lián)的業(yè)務(wù)邏輯有關(guān),比如說是消息列表,那么就叫 MessageEngine 好了。

 

這個 Engine 一般會寫成單例模式,然后讓它來持有數(shù)據(jù)的引用,而兩個或多個頁面需要對這份消息列表(message list)進(jìn)行操作的時候,就通過這個 Engine 來獲取就行了。

 

使用 Engine 還有另一個場景,就是兩個頁面都需要監(jiān)聽某一個網(wǎng)絡(luò)push,比如說在多終端的情況下,我們有一個個人信息頁面,個人信息是可以在別的終端被修改的,那么我們的頁面就會收到一個通知,有時候,通知回調(diào)是不帶數(shù)據(jù)的,我們需要手動去拉去數(shù)據(jù),就算帶上了數(shù)據(jù),如果兩個頁面都監(jiān)聽這個網(wǎng)絡(luò)回調(diào),也會有問題,因?yàn)檫@樣就有兩份數(shù)據(jù),或者說有兩個地方會對數(shù)據(jù)進(jìn)行操作。我用來代碼來演示。

 

public class ProfileActivity extends Activity implement OnProfileChangedListener, OnResultForProfileRequest {

    

    private Profile mProfile = null;

    

    //當(dāng)別的終端更新了個人信息后調(diào)用這里

    @override

    public void onProfileChanged() {

        ProfileManager.getInstance().requestProfile(this);  //傳入OnResultForProfileRequest接口

    }

    

    //當(dāng)requestProfile()請求結(jié)果返回時調(diào)用

    @override

    public void onResult(Profile profile) {

        mProfile = profile;

        updateView();

    }

}

上面代碼展示了一個頁面收到數(shù)據(jù)變更的通知以及請求數(shù)據(jù)的情況,那么當(dāng)我們有兩個頁面都需要關(guān)心數(shù)據(jù)發(fā)生變化的時候,如果兩個頁面都像上面這樣寫,那么我們就有兩處來請求數(shù)據(jù),這樣是不好的,因?yàn)閮蓚€地方用的是同一份數(shù)據(jù),這樣根據(jù)上面說的,我們需要一個 ProfileEngine 來維持這份數(shù)據(jù)的引用,另一方面,我們可以把 profile changed 的監(jiān)聽,放在 ProfileEngine 上,這樣就只有它一個地方收到變化的通知,一個地方來拉取最新數(shù)據(jù),更新好了之后,再通知兩個(多個)頁面通過單例來獲取最新的數(shù)據(jù)。這種情形下,我們需要定義一個本地的接口。

 

public class ProfileEngine implement OnRemoteProfileChangedListener, OnResultForProfileRequest {

    

    public interface OnLocalProfileChangedListener {

    

        void onLocalProfileChanged(Profile newProfile);

    }

    

    private Profile mProfile = null;

    

    //監(jiān)聽列表

    private ArrayList<OnLocalProfileChangedListener> mListeners = new ArrayList<>();

    

    //當(dāng)別的終端更新了個人信息后調(diào)用這里

    @override

    public void onProfileChanged() {

        ProfileManager.getInstance().requestProfile(this);  //傳入OnResultForProfileRequest接口

    }

    

    //當(dāng)requestProfile()請求結(jié)果返回時調(diào)用

    @override

    public void onResult(Profile profile) {

        mProfile = profile;

    }

    

    //通知所有的頁面,profile發(fā)生了變更,并且已經(jīng)取好了最新的數(shù)據(jù)了,拿過去更新UI就好了

    private void notifyListener() {

        for (OnLocalProfileChangedListener l : mListeners) {

            l.onLocalProfileChanged(mProfile);

        }

    }

}

這個套路感覺真的很簡潔干練,但我們需要注意一個問題就是本地的監(jiān)聽的注冊與反注冊。

 

單例一旦被創(chuàng)建就不會被銷毀了,除非進(jìn)程被干掉,或者我們主動置空( null )并且GC。也就是說,這個單例通常情況下會一直在內(nèi)存中的,也會一直監(jiān)聽remoteprofile變化,并且會去拉去最新的數(shù)據(jù),請注意這里的 mListeners ,里面存放的兩個頁面( Activity / Fragment ),如果我們沒有在頁面銷毀( onDestory )的時候?qū)⒆约簭谋O(jiān)聽列表中移除,那么 mListeners 就會一直持有Activity的引用,但是頁面卻已經(jīng)是消失了,這樣就造成了內(nèi)存泄露。因此一定要嚴(yán)格的在 onCreate 和 onDestory 中調(diào)用注冊與反注冊方法。

 

一種網(wǎng)絡(luò)請求套路

 

這種網(wǎng)絡(luò)請求套路也是最近才學(xué)習(xí)到的,感覺非常的簡單巧妙。

 

//發(fā)起一個請求檢查一下數(shù)據(jù)是否有變更,如果有變更,會通過通知onChanged()告訴客戶端,無參數(shù)無返回值

void check();

 

//通知,告知客戶端數(shù)據(jù)有變更,要拉取最新數(shù)據(jù)需要另一個接口,無參數(shù),無返回值

void onChanged();

 

//通過網(wǎng)絡(luò)拉取數(shù)據(jù),無返回值,傳入回調(diào)接口,因?yàn)槭钱惒椒祷財?shù)據(jù)

void request(onRequestResult);

 

//請求數(shù)據(jù)的回調(diào)接口,參數(shù)中是最新的數(shù)據(jù)

void onRequestResult(Data)

 

//通過網(wǎng)絡(luò)更新數(shù)據(jù),無返回值,通過參數(shù)傳入新數(shù)據(jù)和回調(diào)接口

void set(Data, OnSetResult);

 

//更新數(shù)據(jù)的回調(diào)接口,參數(shù)表示有沒有成功,以及最新的數(shù)據(jù),同時也會調(diào)用onChanged()方法

void onSetResult(int, Data);

可以發(fā)現(xiàn),數(shù)據(jù)變化的時候,總是會調(diào)用 onChanged() 方法,而這僅僅是通知,獲取數(shù)據(jù)需要自己手動去拉取一次。這樣我們有統(tǒng)一的時機(jī)可以獲取最新的數(shù)據(jù)。

 

 

文章來源:SegmentFault

您還未登錄,請先登錄

熱門帖子

最新帖子

?