《一個Android工程的從零開始》階段總結(jié)與修改3-BaseActivity上(抽象處理)

先扯兩句

這次已經(jīng)記不清距離上次發(fā)博客有多久了,總歸是好久了吧葵礼。而這次要寫的內(nèi)容槽地,之前也多次開始迁沫,不過又都隨著自己后面的應(yīng)用烹卒,重新作出了調(diào)整,如果收藏了我的demo的或許能夠看到修改的過程弯洗,說實在的旅急,現(xiàn)在的base封裝與之前的實在是相差太多,多到我自己都快找不到之前的痕跡了牡整。
雖然也不能肯定之后還會不會繼續(xù)調(diào)整藐吮,不過也不能這么無休止的拖下去了,如果有需要的可以收藏一下我的demo逃贝,我的修改都會第一時間發(fā)到上面的谣辞,以后我也盡量在修改的同時一同發(fā)博客說明。
閑言少敘沐扳,老規(guī)矩還是先上我的Git庫泥从,然后開始正文吧。
MyBaseApplication (https://github.com/BanShouWeng/MyBaseApplication)

正文

正文的開頭沪摄,我先說說自己這次修改base的初衷躯嫉,實際上目錄中的2~7篇博客,加上階段總結(jié)與修改1杨拐,已經(jīng)能夠基本實現(xiàn)了當前的功能祈餐,所以之前的博客我并沒有刪除掉,依然留給大家做個參考哄陶。
而既然基本能夠完成這功能帆阳,我這里為什么要多費事還要重新封裝底層呢?除了一些性能上的優(yōu)化以外屋吨,更重要的原因了解我的人一定都知道蜒谤,就因為一個字“懶”!至于這么個懶法至扰,下面我就為大家說明一下鳍徽。

抽象類

如果是看過我之前demo的朋友,再對比一下當前的demo渊胸,一定會發(fā)現(xiàn)現(xiàn)在的BaseActivity旬盯、BaseNetActivity、BaseFragment翎猛、BaseNetFragment以及新增加的BaseFragmentActivity都從原本的普通類變成了如今的抽象類胖翰。作為一個菜鳥,你如果讓我說明一下切厘,從性能上這么封裝會不會有什么優(yōu)勢萨咳,我還真一點也說不出來,我就只從“懶”上來做解釋了疫稿。
之前的普通類培他,當我們需要創(chuàng)建一個Activity或者Fragment去繼承Base的時候鹃两,往往需要我們自己去記,需要把setContentView改成setBaseContentView(布局嵌套)舀凛,然后還需要自己去創(chuàng)建一些initView(初始化視圖)俊扳、initData(初始化數(shù)據(jù))等方法,這些雖然說麻煩也不是很麻煩猛遍,但是對于一個懶人來說馋记,實在是太繁瑣了。而使用抽象類封裝懊烤,就可以把這些方法變成抽象方法梯醒,在繼承的時候,只需要我們實現(xiàn)即可腌紧。

BaseActivity

抽象方法如下:

/**
 * 獲取布局ID
 *
 * @return 獲取的布局ID
 */
protected abstract int getLayoutId();

/**
 * 獲取所有View信息
 */
protected abstract void findViews();

/**
 * 初始化布局信息
 */
protected abstract void formatViews();

/**
 * 初始化數(shù)據(jù)信息
 */
protected abstract void formatData();

/**
 * 初始化Bundle
 */
protected abstract void getBundle(Bundle bundle);

除此之外茸习,這些抽象方法,需要在onCreate中進行調(diào)用:

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    activities.add(this);
    context = getApplicationContext();
    activity = this;
    event = this;

    setContentView(R.layout.activity_base);
    initSDK();
    setBaseContentView(getLayoutId());
    findViews();
    getBundle(getIntent().getBundleExtra("bundle"));
    formatViews();
    formatData();
}

關(guān)于onCreate方法中的其他參數(shù)activities壁肋、context号胚、activity、event墩划,以及方法setBaseContentView的用處涕刚,如果不知道的,請參見我之前的博客《一個Android工程的從零開始》-5乙帮、base(四) BaseActivity——方法封裝,里面會有詳細的描述的极景。

getLayoutId()

顧名思義察净,獲取布局Id,也就是原本的setBaseContentView()方法盼樟,而實際上氢卡,我們在onCreate()方法中也很容易發(fā)現(xiàn),它就是setBaseContentView(getLayoutId());作為一個懶漢的我晨缴,在使用的時候译秦,只需要將我們的布局Id作為返回值返回即可。
例如:

@Override
protected int getLayoutId() {
    return R.layout.activity_main;
}

這樣击碗,我們就實現(xiàn)了布局的設(shè)置筑悴,同時也能使用之前封裝好的Title。

findViews()

獲取所有View信息稍途,也就是剛剛前面說到的initView的一部分功能(至于為什么說是一部分功能阁吝,我們后面會說到),而在其中的操作其實也很簡單械拍,就是我們熟悉的不能在熟悉的findViewById突勇。當然装盯,這個findViewById我們也進行了進一步的封裝,具體怎么封裝的甲馋,我們這里先賣個關(guān)子埂奈,后面會為大家提到。

formatViews()

初始化布局信息定躏,這就是initView的令一部分功能挥转,比如設(shè)置圖片啊,設(shè)置文字啊共屈,設(shè)置布局適配器绑谣,設(shè)置一些監(jiān)聽接口之類的方法,我個人都將其劃分到這個部分來完成了拗引。

formatData()

初始化數(shù)據(jù)信息借宵,這里我個人的定義是設(shè)置一些數(shù)據(jù)的初始化信息,例如定時器之類的初始化矾削、數(shù)據(jù)庫工具的初始化壤玫、或者圖形驗證碼等工具類的初始化,不過該方法使用頻率較低

getBundle(Bundle bundle)

初始化Bundle哼凯,這里之所以選擇初始化bundle欲间,最重要的一個原因是我在Activity跳轉(zhuǎn)的部分做了一個封裝,所以Activity之間傳值就被約束成了通過bundle傳遞断部,而如果你是直接使用的Intent傳值猎贴,那么初始化intent也可以,這個方法就不加以限制了蝴光。而通過上面的說明她渴,想必大家也知道了,這個方法的主要用處就是Activity之間傳遞參數(shù)的一個取值方法蔑祟,當然趁耗,為了防止不必要的麻煩,在實現(xiàn)getBundle()方法的時候疆虚,還是建議大家添加一步bundle的判空以及相對應(yīng)的處理苛败。

initSDK()

當然,細心的還會發(fā)現(xiàn)径簿,在onCreate方法中罢屈,我調(diào)用的方法其實并不僅僅是前面說到的抽象方法,還有一個就是initSDK()方法牍帚,說來儡遮,這個方法的添加也是讓我很無奈啊,其實原本是沒有這個方法的暗赶,直到我使用Baidu地圖的時候鄙币。
看過百度地圖開發(fā)文檔的想必都看到過這么一段代碼:

@Override  
protected void onCreate(Bundle savedInstanceState) {  
    super.onCreate(savedInstanceState);   
    //在使用SDK各組件之前初始化context信息肃叶,傳入ApplicationContext  
    //注意該方法要再setContentView方法之前實現(xiàn)  
    SDKInitializer.initialize(getApplicationContext());  
    setContentView(R.layout.activity_main);  
    //獲取地圖控件引用  
    mMapView = (MapView) findViewById(R.id.bmapView);  
}  

而排出掉可以移動到前面說的抽象方法中的方法后,其中一行就顯得尤為突兀:SDKInitializer.initialize(getApplicationContext()); 它的功能在官方注釋中也可以很看得出來十嘿,就是SDK的初始化因惭。這個方法,我們?nèi)绻胖迷谧覣ctivity的onCreate方法中绩衷,那你很榮幸的就會在調(diào)用mMapView = (MapView) findViewById(R.id.bmapView); 看到萬里江上一片紅蹦魔,全都是錯誤日志,翻譯過來就是讓我們?nèi)コ跏蓟俣萐DK咳燕。至于放到onCreate的super前面勿决,邏輯上確實是提前初始化了,不過依然會報錯崩潰掉招盲,至于日志是什么低缩,有興趣的話,大家可以自行嘗試一下曹货。

想必大家看到的我的順序咆繁,在看到官方的注釋就會發(fā)現(xiàn),我并沒有放到setContentView的前面顶籽,不過我的順序在執(zhí)行過程中玩般,確實也沒有報錯,同時也能夠正常使用礼饱,原因我也沒有去深究坏为,不過還是建議大家用官方建議的順序去執(zhí)行。

不過由于SDK的初始化慨仿,在使用頻率上久脯,比formatData()方法還要低,甚至一部分APP使用的都是類似于極光推送之類的SDK镰吆,只需要進行一次全局的初始化,而并不需要在使用它的Activity中進行初始化跑慕。所以這里選擇了在BaseActivity方法中寫了一個空方法万皿,在需要的時候重寫即可。

/**
 * SDK初始化
 */
protected void initSDK() {
}
onCreate方法中的順序

前面的屬性部分核行,大家隨著心情來就好了牢硅,是沒有關(guān)系的,不過一定要放到這些抽象方法的調(diào)用前面芝雪,雖然setContentView()與getBundle(Bundle bundle)是不受這些參數(shù)影響的减余,但也是為了代碼排版更好看容易理解一些。
下面惩系,我這里setContentView()位岔,不過前面斜體字已經(jīng)說明了如筛,還是建議大家先使用initSDK(),在使用setContentView()抒抬。
下一個杨刨,setBaseContentView(getLayoutId())或者getBundle(Bundle bundle),這兩個方法則沒有順序前后的要求擦剑,只不過這兩個方法都需要放在findViews()方法與formatViews()方法之前妖胀,因為在formatViews()賦值時,很可能使用的是getBundle(Bundle bundle)傳遞來的值惠勒,而邏輯上findViews()最好與formatViews()相鄰赚抡。而最后,則是使用頻率相對較低的formatData()纠屋。

其中可以看得出來涂臣,有一些方法的順序并不是一成不變的,大家也是可以根據(jù)自己的需求進行增刪改

其他

首先需要說明一點巾遭,大家可以看一下標題肉康,這一篇博客所說的內(nèi)容只是BaseActivity的上,所以這個其他并不是除了上述抽象方法的其他所有灼舍,而是與抽象方法相關(guān)的一些其他方法吼和,總結(jié)起來有兩類三個方法:

 /**
     * 簡化獲取View
     *
     * @param viewId View的ID
     * @param <T>    將View轉(zhuǎn)化為對應(yīng)泛型,簡化強轉(zhuǎn)的步驟
     * @return ID對應(yīng)的View
     */
    @SuppressWarnings("unchecked")
    public <T extends View> T getView(int viewId) {
        return (T) findViewById(viewId);
    }

    /**
     * 簡化獲取View
     *
     * @param view   父view
     * @param viewId View的ID
     * @param <T>    將View轉(zhuǎn)化為對應(yīng)泛型骑素,簡化強轉(zhuǎn)的步驟
     * @return ID對應(yīng)的View
     */
    @SuppressWarnings("unchecked")
    public <T extends View> T getView(View view, int viewId) {
        return (T) view.findViewById(viewId);
    }

    /**
     * 設(shè)置點擊事件
     *
     * @param layouts 點擊控件Id
     */
    protected void setOnClickListener(int... layouts) {
        for (int layout : layouts) {
            getView(layout).setOnClickListener(this);
        }
    }
getView

還記得前面我說過吧炫乓,在這次BaseActivity的封裝中,我對findViewById也進行了封裝献丑,這也就是兩個getView方法末捣。
這里我們使用到了一個神奇的內(nèi)容“T”,在注釋中也能看出來创橄,這貨叫泛型箩做,當然別看我這里寫的T,大家就當成“T”就是泛型妥畏,T只不過是一個代指邦邦,就好像如果我把“T”替換成“半壽翁”,那么我“半壽翁”就成了泛型(別說你還不知道在AS中可以使用中文變量醉蚁,并嘲諷我使用中文)

/**
 * 簡化獲取View
 *
 * @param viewId View的ID
 * @param <半壽翁>    將View轉(zhuǎn)化為對應(yīng)泛型燃辖,簡化強轉(zhuǎn)的步驟
 * @return ID對應(yīng)的View
 */
@SuppressWarnings("unchecked")
public <半壽翁 extends View> 半壽翁 getView(int viewId) {
    return (半壽翁) findViewById(viewId);
}

其使用方法就是,找到所有泛型類的一個共同父類网棍,然后用泛型指代變量(“T”或者“半壽翁”黔龟,或者你喜歡的其他什么稱呼),<T extends View>繼承這個父類,這樣氏身,當我們用這個指代“T”表示這個父類的任何一個子類巍棱,都不需要進行強轉(zhuǎn),又或者說我們已經(jīng)這里進行了強轉(zhuǎn):

(T) findViewById(viewId);

這里需要說明一下观谦,當我們使用泛型的時候拉盾,我們可以使用TextView textView = (T)view,然后使用textView.setText()方法豁状,而不能直接使用(T)view.setText()捉偏,因為泛型并不知道你究竟要將這個父類轉(zhuǎn)換成具體的哪個子類,所以你無法直接使用子類所特有的方法泻红。所以我們需要用特有的子類去進行接收夭禽,接收后就可以使用其特有的方法了。

所以使用getView方法谊路,我們就能在formatViews()方法中獲取其對應(yīng)的控件了讹躯。至于為什么會有g(shù)etView(View view, int viewId),畢竟不是所有的情況下都可以直接使用findViewById()缠劝,至少在BaseAdapter的getView方法中潮梯,我們就難免使用到convertView.findViewById(),所以這里我也添加了一條:

return (T) view.findViewById(viewId);

當然惨恭,getView(View view, int viewId)放在demo中的Const工具類中也是可以的秉馏,又或者在我們的view只需要獲取一次的時候,我們也可以getView(View view, int viewId)方法進行進一步的封裝:

/**
 * 簡化獲取View
 *
 * @param layoutId 父布局Id
 * @param viewId   View的ID
 * @param <T>      將View轉(zhuǎn)化為對應(yīng)泛型脱羡,簡化強轉(zhuǎn)的步驟
 * @return ID對應(yīng)的View
 */
@SuppressWarnings("unchecked")
public <T extends View> T getView(int layoutId, int viewId) {
    return (T) LayoutInflater.from(context).inflate(layoutId, null).findViewById(viewId);
}
setOnClickListener

這個想必大家一眼就能看出來萝究,單純的就是一個點擊事件的設(shè)置,沒錯锉罐,它的功能同樣也是設(shè)置點擊事件帆竹,只是與普通的View.setOnClickListener()不同的是,這部分我是拿來批量設(shè)置點擊事件脓规。
首先栽连,需要我們的BaseActivity實現(xiàn)View.OnClickListener接口,不過onClick方法卻不必須實現(xiàn)侨舆,誰讓BaseActivity是抽象類呢升酣,只需要與上面那些抽象方法一樣,在繼承BaseActivity的子Activity中實現(xiàn)就好了态罪。
其次就是setOnClickListener的參數(shù)int... layouts,這個參數(shù)的意思就是傳入一個類型為int數(shù)組下面,并且這個數(shù)組的數(shù)量是不確定复颈,范圍是“大于等于0”,也就是說我們可以不傳值,也可以傳很多值耗啦,只要類型都是“...”前的即可凿菩。既然是數(shù)組,只需要對傳入的view id參數(shù)進行一個foreach循環(huán)設(shè)置點擊事件即可:

@Override
protected void formatViews() {
    setOnClickListener(R.id.button1, R.id.button2, R.id.button3, R.id.button4)
}

我一般是在formatViews()方法中使用setOnClickListener的帜讲。

注意事項

由于網(wǎng)絡(luò)請求的部分我拆解出來放置在了BaseNetActivity中衅谷,所以當我們執(zhí)行網(wǎng)絡(luò)請求時,切記在onCreate之后執(zhí)行似将,如果在上述抽象方法的實現(xiàn)中執(zhí)行获黔,由于BaseNetActivity的onCreate方法要在BaseActivity的onCreate方法之后執(zhí)行,將會導(dǎo)致空指針的發(fā)生在验。
當然玷氏,這部分也可以在BaseNetActivity中添加一個網(wǎng)絡(luò)訪問的抽象方法,不過有很多Activity將會有重新獲取焦點時刷新頁面的需求腋舌,也就是網(wǎng)絡(luò)請求需要在onResume()方法中調(diào)用盏触,所以在BaseNetActivity中就沒有設(shè)置對應(yīng)的抽象方法,如果大家在使用過程中沒有這個需求块饺,不放BaseNetActivity中封裝一個網(wǎng)絡(luò)請求的方法赞辩,畢竟能偷懶何必那么勤快不是。

附錄

《一個Android工程的從零開始》- 目錄

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末授艰,一起剝皮案震驚了整個濱河市辨嗽,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌想诅,老刑警劉巖召庞,帶你破解...
    沈念sama閱讀 221,273評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異来破,居然都是意外死亡篮灼,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,349評論 3 398
  • 文/潘曉璐 我一進店門徘禁,熙熙樓的掌柜王于貴愁眉苦臉地迎上來诅诱,“玉大人,你說我怎么就攤上這事送朱∧锏矗” “怎么了?”我有些...
    開封第一講書人閱讀 167,709評論 0 360
  • 文/不壞的土叔 我叫張陵驶沼,是天一觀的道長炮沐。 經(jīng)常有香客問我,道長回怜,這世上最難降的妖魔是什么大年? 我笑而不...
    開封第一講書人閱讀 59,520評論 1 296
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上翔试,老公的妹妹穿的比我還像新娘轻要。我一直安慰自己,他們只是感情好垦缅,可當我...
    茶點故事閱讀 68,515評論 6 397
  • 文/花漫 我一把揭開白布冲泥。 她就那樣靜靜地躺著,像睡著了一般壁涎。 火紅的嫁衣襯著肌膚如雪凡恍。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,158評論 1 308
  • 那天粹庞,我揣著相機與錄音咳焚,去河邊找鬼。 笑死庞溜,一個胖子當著我的面吹牛革半,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播流码,決...
    沈念sama閱讀 40,755評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼又官,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了漫试?” 一聲冷哼從身側(cè)響起六敬,我...
    開封第一講書人閱讀 39,660評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎驾荣,沒想到半個月后外构,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,203評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡播掷,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,287評論 3 340
  • 正文 我和宋清朗相戀三年审编,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片歧匈。...
    茶點故事閱讀 40,427評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡垒酬,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出件炉,到底是詐尸還是另有隱情勘究,我是刑警寧澤,帶...
    沈念sama閱讀 36,122評論 5 349
  • 正文 年R本政府宣布斟冕,位于F島的核電站口糕,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏磕蛇。R本人自食惡果不足惜走净,卻給世界環(huán)境...
    茶點故事閱讀 41,801評論 3 333
  • 文/蒙蒙 一券时、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧伏伯,春花似錦、人聲如沸捌袜。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,272評論 0 23
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽虏等。三九已至弄唧,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間霍衫,已是汗流浹背候引。 一陣腳步聲響...
    開封第一講書人閱讀 33,393評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留敦跌,地道東北人澄干。 一個月前我還...
    沈念sama閱讀 48,808評論 3 376
  • 正文 我出身青樓,卻偏偏與公主長得像柠傍,于是被迫代替她去往敵國和親麸俘。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 45,440評論 2 359

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