原文地址: https://juejin.im/post/590f09ec128fe100584ee6b0android
距離 上篇文章 的發表時間已通過去兩個多月了,這兩個月時間裏我沒寫文章但一直在更新着個人 MVPArms 框架,讓他逐漸朝着 可配置化集成框架 發展git
就在前段時間我在 鴻洋公衆號 上看到了一篇文章,大概是介紹怎麼封裝 BaseActivity ,讓 Activity 經過幾行代碼就能夠實現 ToolBargithub
恰好個人 MVPArms 框架也更新了一個功能:bash
經過非繼承 Activity Fragment 來實現之前須要封裝進 BaseActivity BaseFragment 經過繼承來實現的一些公共邏輯,以及監聽整個 App 全部 Activity 以及 Fragment 的生命週期(包括三方庫),並可向其生命週期內插入代碼app
** 那我就來講說我怎麼在不使用繼承的狀況下讓 Activty 一行代碼都不寫就能實現 Toolbar **框架
BaseActivity 封裝多了,除了很差管理外,還有最重要的一點就是, Java
只能單繼承,當若是你的 Activity 須要使用到某個三方庫,那個三方庫必須讓你繼承於它的 Activity 可是你又須要你本身封裝的 BaseActivity 的某些特性,這時你怎麼辦? 你不能改三方庫的 Activity 因此你只有改你的 BaseActivity 讓它去繼承三方庫的 Activity,可是當改了 BaseActivity 後,發現有不少繼承於 BaseActivity 的 Activity 並不須要這個三方庫,卻被迫繼承了它的 Activity ,這時你又要從新封裝一個 BaseActivityide
當這種狀況發生的多了,你的 BaseActiviy 也愈來愈多,這樣就惡性循環了,因此我不只提倡 App 開發者少封裝 BaseActivity 少用繼承,也提倡 三方庫 開發者少封裝 BaseActivity 少用繼承,爲何呢?由於當 App 開發者的 Activity 須要使用到兩個三方庫,兩個三方庫都須要繼承它的 Activity,這時你讓 App 開發者怎麼辦?因此做爲一個可配置化集成框架做者,我不能讓開發者去直接改個人 BaseActivity 我必須經過其餘擴展的方式去解決這個問題佈局
好了進入正題,要想解決上面提到的問題,咱們就要思考咱們爲何必定要封裝 BaseActivity 經過繼承來實現一些 Activity 的公共邏輯,而不能將公共邏輯封裝到其餘類裏面?post
答案很簡單,由於咱們必須使用到 Activity 的一些生命週期,在對應的生命週期裏執行對應的邏輯,這個就是咱們不能經過封裝其餘類來實現的緣由,找到了問題關鍵,那咱們就從生命週期上下手來解決問題學習
提到 Activity 的生命週期,這時我就要介紹一個接口了,它叫 ActivityLifecycleCallbacks ,不知道有同窗以前瞭解過它嗎?不瞭解沒關係,我如今來介紹介紹它
ActivityLifecycleCallbacks 是 Application 中聲明的一個內部接口,咱們來看看它的結構
public interface ActivityLifecycleCallbacks {
void onActivityCreated(Activity activity, Bundle savedInstanceState);
void onActivityStarted(Activity activity);
void onActivityResumed(Activity activity);
void onActivityPaused(Activity activity);
void onActivityStopped(Activity activity);
void onActivitySaveInstanceState(Activity activity, Bundle outState);
void onActivityDestroyed(Activity activity);
}
複製代碼
Application 提供有一個 registerActivityLifecycleCallbacks() 的方法,須要傳入的參數就是這個 ActivityLifecycleCallbacks 接口,做用和你猜的沒錯,就是在你調用這個方法傳入這個接口實現類後,系統會在每一個 Activity 執行完對應的生命週期後都調用這個實現類中對應的方法,請記住是每一個!
這個時候咱們就會想到一個需求實現,關閉全部 Activity !你還在經過繼承 BaseActivity 在 BaseActivity 的 onCreate 中將這個 Activity 加入集合???
那我如今就告訴你這樣的弊端,若是你 App 中打開有其餘三方庫的 Activity ,這個三方庫確定不可能繼承你的 BaseActivity ,這時你怎麼辦?怎麼辦?
這時 ActivityLifecycleCallbacks 就派上用場了, App 中的全部 Activity 只要執行完生命週期就必定會調用這個接口實現類的對應方法, 那你就能夠在 onActivityCreated 中將全部 Activity 加入集合,這樣無論你是否是三方庫的 Activity 我均可以遍歷集合 finish 全部的 Activity
<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item>
</style>
複製代碼
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme">
...
</application>
複製代碼
你們不要糾結個人 Toolbar 佈局方式,這裏只是實現思想,你能夠本身改爲本身的佈局方式
<me.jessyan.art.widget.autolayout.AutoToolbar
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="120px"
android:background="?attr/colorPrimary"
app:contentInsetStart="0dp"
>
// toolbar 中的返回按鈕
<com.zhy.autolayout.AutoRelativeLayout
android:id="@+id/toolbar_back"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:gravity="left"
>
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="30px"
android:layout_marginRight="30px"
android:layout_centerVertical="true"
android:src="@mipmap/login_return"/>
</com.zhy.autolayout.AutoRelativeLayout>
// toolbar 中的標題名
<TextView
android:id="@+id/toolbar_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="18dp"
android:textColor="#fff"
android:layout_gravity="center"
tools:text="MVPArt"/>
</me.jessyan.art.widget.autolayout.AutoToolbar>
複製代碼
在你須要 Toolbar 的 Activity 佈局中 include 上面的佈局
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
>
//將 Toolbar 包裹進來
<include layout="@layout/include_title"/>
<android.support.v4.widget.SwipeRefreshLayout
android:id="@+id/swipeRefreshLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingLeft="15px"
android:paddingTop="15px"
>
<android.support.v7.widget.RecyclerView
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scrollbars="vertical"
tools:listitem="@layout/recycle_list"
/>
</android.support.v4.widget.SwipeRefreshLayout>
</LinearLayout>
複製代碼
下面只是對 ToolBar 簡單的設置,你能夠本身配置更多複雜的功能,你想象力有多豐富,這裏就有多強大
public class WEApplication extends BaseApplication{
@Override
public void onCreate() {
super.onCreate();
registerActivityLifecycleCallbacks(new ActivityLifecycleCallbacks() {
@Override
public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
//這裏全局給Activity設置toolbar和title,你想象力有多豐富,這裏就有多強大,之前放到BaseActivity的操做均可以放到這裏
if (activity.findViewById(R.id.toolbar) != null) { //找到 Toolbar 而且替換 Actionbar
if (activity instanceof AppCompatActivity) {
((AppCompatActivity) activity).setSupportActionBar((Toolbar) activity.findViewById(R.id.toolbar));
((AppCompatActivity) activity).getSupportActionBar().setDisplayShowTitleEnabled(false);
} else {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
activity.setActionBar((android.widget.Toolbar) activity.findViewById(R.id.toolbar));
activity.getActionBar().setDisplayShowTitleEnabled(false);
}
}
}
if (activity.findViewById(R.id.toolbar_title) != null) { //找到 Toolbar 的標題欄並設置標題名
((TextView) activity.findViewById(R.id.toolbar_title)).setText(activity.getTitle());
}
if (activity.findViewById(R.id.toolbar_back) != null) { //找到 Toolbar 的返回按鈕,而且設置點擊事件,點擊關閉這個 Activity
activity.findViewById(R.id.toolbar_back).setOnClickListener(v -> {
activity.onBackPressed();
});
}
}
...
});
}
}
複製代碼
之後你想讓一個 Activity 實現 ToolBar ,只用作兩件事情:
<include layout="@layout/include_title"/>
複製代碼
<activity
android:name=".mvp.ui.activity.SplashActivity"
android:label="@string/app_name"
/>
複製代碼
再介紹給你們一個全局配置全部 Activity 進入退出過分動畫的方法,設置 Theme 屬性
<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
<!-- Customize your theme here. -->
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item>
<item name="android:windowAnimationStyle">@style/AnimaActivity</item>
</style>
<style name="AnimaActivity">
<item name="android:activityOpenEnterAnimation">@anim/translate_right_to_center</item>
<item name="android:activityOpenExitAnimation">@anim/translate_center_to_left</item>
<item name="android:activityCloseEnterAnimation">@anim/translate_left_to_center</item>
<item name="android:activityCloseExitAnimation">@anim/translate_center_to_right</item>
</style>
複製代碼
不少公共邏輯均可以寫到 ActivityLifecycleCallbacks 中,只要勇於嘗試,你想象力有多豐富,這裏就有多強大
由於全部 Activity 在執行對應生命週期時, ActivityLifecycleCallbacks 對應的方法都會被調用,有些 Activity 可能不須要 Toolbar ,好比三方庫的 Activity ,雖然在 onActivityCreated 方法中,判斷了 ToolBar 的 Id 找不到就不執行設置 ToolBar 的邏輯,可是未免不夠優雅
其實咱們可讓 Activity 實現對應的自定義接口, 在 onActivityCreated 中 instanceof 這個自定義接口,若是不實現這個自定義接口,就說明不須要設置 ToolBar ,這樣就優雅不少
在 ActivityLifecycleCallbacks 中,全部 Activity 執行對應的生命週期後,它對應的方法都會被調用,因此咱們必須區分這個 Activity 究竟是哪一個 Activity ,因此 ActivityLifecycleCallbacks 每一個方法都會傳入 Activity 作爲參數,咱們就能夠用來區分 Activity
public void onActivityCreated(Activity activity, Bundle savedInstanceState){
if (activity instanceof xxxActivity){
....
}
}
複製代碼
可是又有一個問題出現這個 ActivityLifecycleCallbacks 是公用的,當一個 Activity 在 onCreate 方法產生了一個對象 ,咱們須要在這個 Activity 執行 onDestroy 時用到這個對象,怎麼辦?由於每一個 Activity 都要產生這個對象,咱們不可能把這個對象存儲在 ActivityLifecycleCallbacks 中啊
如今就能夠用到 Activity.getIntent 來存儲一些數據, Intent 中持有一個 Bundle 對象能夠存儲一些數據,
咱們須要使用 ActivityLifecycleCallbacks 實現給全部 Activity 執行 ButterKnife.bind(activity)
Bundle 中能夠存儲 Parcelable 對象
public class ActivityBean extends Parcelable {
private Unbinder unbinder;
public void setUnbinder(Unbinder unbinder){
thid.unbinder = unbinder;
}
public Unbinder getUnbinder(){
return unbinder;
}
}
複製代碼
在 ActivityLifecycleCallbacks 執行對應邏輯
public class WEApplication extends BaseApplication{
@Override
public void onCreate() {
super.onCreate();
registerActivityLifecycleCallbacks(new ActivityLifecycleCallbacks() {
@Override
public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
ActivityBean bean = new ActivityBean();
Unbinder unbinder = ButterKnife.bind(activity);
bean.setUnbinder(unbinder);
activity.getIntent().putExtra("ActivityBean", bean);
}
...
@Override
public void onActivityDestroyed(Activity activity) {
ActivityBean bean = activity.getIntent().getParcelableExtra("ActivityBean");
bean.getUnbinder().unbind();
}
}
});
}
}
複製代碼
BaseActivity 有些時候須要,子 Activity 實現某些方法,或者提供某些數據,如須要子 Activity 實現 initView 返回 setContentView() 中的佈局 ID ,實現 initData 初始化一些數據,這樣就能夠不須要 Activity 再重寫 onCreate ,達到規範的目的, 這樣使用 ActivityLifecycleCallbacks 一樣能作到,那我該怎麼作呢?
只須要 Activity 實現某個自定義接口
public interface IActivity {
int initView();
void initData();
}
複製代碼
而後在 ActivityLifecycleCallbacks 的 onActivityCreated 中調用這些方法,就能夠實現
public class WEApplication extends BaseApplication{
@Override
public void onCreate() {
super.onCreate();
registerActivityLifecycleCallbacks(new ActivityLifecycleCallbacks() {
@Override
public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
if (activity instanceof IActivity) {
activity.setContentView(((IActivity)activity).initView());
((IActivity)activity).initData();
}
}
...
}
});
}
}
複製代碼
因爲 ActivityLifecycleCallbacks 中全部方法的調用時機都是在 Activity 對應生命週期的 Super 方法中進行的,因此在 Activity 的 onCreate 方法中使用 setContentView 必須在 super.onCreate(savedInstanceState); 以前,否則在 onActivityCreated 方法中 findViewById 會發現找不到
@Override
protected void onCreate(Bundle savedInstanceState) {
setContentView(R.layout.activity_home);
super.onCreate(savedInstanceState);
}
複製代碼
也能夠結合上面的方式使用自定義接口,調用 initView 後,在 findViewById() 找到 ToolBar
一不當心實現了這麼多之前必須寫到 BaseActivity 中經過繼承才能達到的功能,發現沒有 BaseActivity ,也並無什麼不舒服的地方,忽然感受本身好牛逼 🤒,趕快關注我吧,雖然我並不會常常更新博客,可是我更新的文章在質量上絕對有保證!
值得注意的是 ActivityLifecycleCallbacks 能夠註冊多個,能夠針對不一樣狀況添加各類 ActivityLifecycleCallbacks 按照須要進行組合從而達到不一樣的需求 ,和 Okhttp
的 Interceptor 相似
如今整個 App 的全部 Activity 和 Fragment 只要生命週期調用都會被攔截,應用到我框架上就能夠動態的向全部 Activity 和 Fragment 的對應生命週期插入任意代碼,好比說 LeakCanary 的 RefWatcher.watch(fragment) 也能夠直接插入到三方庫的 Fragment 中,而且若是代碼有任何改動也不用再去改基類,有點Aop的意思
以上提到的思想以及解決方案已經使用到了個人 MVPArms 框架中,想知道更詳細的用法能夠去看看個人框架實現,我上面提到的全部的實現,其實都是最簡單的一些需求,相信已經顛覆了之前的實現方式了,並且更加優雅(由於我是站在三方庫設計者的角度提出這個功能的,我必須將唯一繼承的機會留給其餘 Activity ,你若是相較於以前的方式以爲不夠優雅,那就當我沒說),更不用擔憂 Java 單繼承的束縛
可是千萬不要覺得, ActivityLifecycleCallbacks 就只能實現這些簡單的需求,它還能夠用到更多更復雜的功能之上,仍是我以前的話, 只要勇於嘗試,你想象力有多豐富,這裏就有多強大 ,思想以及解決方案已經介紹的很清楚了,至於更多需求的實現就靠你們去嘗試咯,雖然我不敢保證之前封裝 BaseActivity 經過繼承實現的全部功能都能被 ActivityLifecycleCallbacks 所替代,可是我想大多數功能仍是能實現的
之前你們都是分享本身封裝的 BaseActivity ,說不定有一天你們就開發分享本身寫的 ActivityLifecycleCallbacks 呢! 當之前的方式已經不能知足咱們的需求,勇於跳出傳統的方式,嘗試不一樣的解決方案,才能擴寬本身的視野,增加本身的技術, MVPArms 正在不斷的努力着!
這裏還要說一句,每一個人的思路不同,考慮的角度也不同,你認同我也好, 不認同我也好,都不會影響個人腳步,至少我是在用個人思路創新,解決一些我認爲有必要解決的問題,和上一篇的文章同樣,我就是喜歡使用不同的思路解決一樣的問題,無論你是否以爲可行,我至少用這個你以爲不可行的思路實現了我想達到的效果,仁者見仁智者見智,若是咱們思惟沒有碰撞,那也請珍惜個人勞動成果
對於一些評論我再說一句, registerActivityLifecycleCallbacks() 內部是把 ActivityLifecycleCallbacks 加到一個集合中,因此 ActivityLifecycleCallbacks 能夠添加多個,而且 ActivityLifecycleCallbacks 只是在項目初始化的時候被裝到集合中,並不會初始化任何東西,和添加監聽器一個道理,使用的是觀察者模式,因此不要說 Application 代碼這麼多會怎麼怎麼樣, Okhttp
的 Interceptor 的代碼更多,也是在 Okhttp
初始化時被添加,你以爲會有什麼影響嗎?
掃碼關注個人公衆號 JessYan,一塊兒學習進步,若是框架有更新,我也會在公衆號上第一時間通知你們
Hello 我叫 JessYan,若是您喜歡個人文章,能夠在如下平臺關注我
-- The end