The original address: https://juejin.cn/post/6844903477169111047
preface
It’s been more than two months since the last article was published, and I haven’t written in the past two months, but I’ve been updating my MVPArms framework and moving it toward a configurable integration framework
How to package BaseActivity with the ToolBar in just a few lines of code
My MVPArms framework has also been updated with a new feature:
Implement some common logic that needs to be encapsulated in a BaseActivity Fragment by inheriting it, and listen for all activities and fragments in the App , including tripartite libraries, and can insert code into its life cycle
How can I make Activty implement the Toolbar ** without writing a line of code without using inheritance
Why do I advocate less encapsulation and BaseActvity inheritance
One of the most important things about BaseActivity encapsulation, besides being difficult to manage, is that Java can only be inherited. What do you do if your Activity needs to use a tripartite library that must let you inherit from its Activity, but you also need some of the BaseActivity features that you’ve wrapped yourself? You can’t change the Activity from the tripartite library, so you have to change your BaseActivity to inherit from the tripartite library, but when you change BaseActivity, you find that there are many activities that inherit from BaseActivity When you’re forced to inherit an Activity from a library that you don’t need, you have to rewrap a BaseActivity
When this happens more and more, you have more and more BaseActiviys, so it’s a vicious circle. So I advocate not only App developers to wrap BaseActivity less and use inheritance less, but also three-party library developers to wrap BaseActivity less and use inheritance less. Why? Because when an App developer’s Activity needs to use two tripartite libraries, both of which need to inherit their Activity, what do you tell the App developer to do? So as the author of a configurable integration framework, I can’t ask developers to change my BaseActivity directly and I have to solve this problem through other extensions
Get into the business
To solve the above problem, we need to think about why we must encapsulate BaseActivity by inheriting some of the Activity’s public logic, but can’t encapsulate the public logic in other classes?
The answer is simple, because we must use some lifecycle of the Activity and execute the corresponding logic in the corresponding lifecycle. This is why we cannot implement it by encapsulating other classes. Finding the key to the problem, let’s start with the lifecycle
ActivityLifecycleCallbacks
Refers to the Activity of the life cycle, then I will introduce an interface, it is called ActivityLifecycleCallbacks, don’t know how students learn it before? It doesn’t matter if you don’t know it. Let me introduce it now
ActivityLifecycleCallbacks is declared in the Application of an internal interface, let’s take a look at its structure
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);
}
Copy the code
What’s the use of this interface?
Application provides a registerActivityLifecycleCallbacks () method, need the incoming parameters is the ActivityLifecycleCallbacks Interface, as you guessed it, is that after you call this method and pass in the interface implementation class, the system will call the corresponding method in the implementation class after each Activity completes the corresponding lifecycle, remember each!
At this point we’ll come up with a requirement implementation that shuts down all activities! You are also adding BaseActivity to the collection in BaseActivity onCreate by inheriting BaseActivity.
If you open an Activity from another library in your App, that library cannot inherit your BaseActivity, what do you do? How to do?
ActivityLifecycleCallbacks will come in handy at this moment, all Activity as long as the execution of the life cycle of the App will call the corresponding method of interface implementation class, Then you can add all activities to the collection in onActivityCreated, so I can iterate through the collection and finish all activities, whether you’re an Activity in a tripartite library or not
Using ActivityLifecycleCallbacks achieve the ToolBar
Set the NoActionBar theme
<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>
Copy the code
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme">... </application>Copy the code
Create the layout for the ToolBar and reference it
Don’t worry about the layout of my Toolbar, this is just the implementation of the idea, you can change the layout of your own
<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"> / / return button in the 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 > / / < title name in the 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>
Copy the code
Include the layout above the Activity layout you need for the Toolbar
<? 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"> // Wrap the Toolbar in <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>
Copy the code
Implement ActivityLifecycleCallbacks and register to the Application
The following is a simple setup for the ToolBar, and you can configure more complex functionality yourself, as powerful as your imagination
public class WEApplication extends BaseApplication{
@Override
public void onCreate() {
super.onCreate();
registerActivityLifecycleCallbacks(new ActivityLifecycleCallbacks() { @Override public void onActivityCreated(Activity activity, Bundle savedInstanceState) {// Set the Activity toolbar and title globally. This is as powerful as you can imagineif(activity.findViewById(R.id.toolbar) ! // Find the Toolbar and replace the Actionbarif (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) {// Find the Toolbar title bar and set the title name ((TextView) activity.findViewById(R.id.toolBar_title)).settext (activity.getTitle()); }if(activity.findViewById(R.id.toolbar_back) ! FindViewById (R.id.toolbar_back).setonClickListener (v -> {// Locate the Toolbar back button and set the click event to close the Activity. activity.onBackPressed(); }); }}... }); }}Copy the code
Set the Label in androidmanifest.xml
In the future, if you want an Activity to implement the ToolBar, do two things:
- In the layout file
<include layout="@layout/include_title"/>
Copy the code
- In androidmanifest.xml, you give the Activity a Label. This Label is the title name
<activity
android:name=".mvp.ui.activity.SplashActivity"
android:label="@string/app_name"
/>
Copy the code
Here’s a global way to configure all activities to enter and exit animation. Set the Theme property
<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>
Copy the code
inActivityYou don’t have to inherit anythingActivityWithout writing any line of code, you can achieve a lot of complicated functions
Many public logic can be written to ActivityLifecycleCallbacks, as long as dare to try, you have more rich imagination, there is more powerful
extension
Because when all activities are executing their life cycles, ActivityLifecycleCallbacks corresponding method will be called, some Activity may not need the Toolbar, such as tripartite library Activity, although the onActivityCreated method, determine the Toolbar The logic for setting the ToolBar is not executed if the Id of the ToolBar is not found, but it is not elegant enough
Custom Interface
In this case, instanceof is a custom interface. If you don’t implement this custom interface, then you don’t need to set the ToolBar, which is much more elegant
To store data
In ActivityLifecycleCallbacks, all the Activity execute the corresponding life cycle, its corresponding methods can be invoked, so we have to distinguish between which the Activity, the Activity is so ActivityLifecycleCallbacks each method will be introduced to the Activity as a parameter, we can be used to distinguish the Activity
public void onActivityCreated(Activity activity, Bundle savedInstanceState){
if(activity instanceof xxxActivity){ .... }}Copy the code
But there is a problem the ActivityLifecycleCallbacks is common, when an Activity in the onCreate method creates an object, we need in this Activity onDestroy execution How to use this object? Since each Activity to produce this object, we can’t store the object in the ActivityLifecycleCallbacks ah
Now you can use the activity. getIntent to store some data. The Intent holds a Bundle object that stores some data.
For example
We need to use ActivityLifecycleCallbacks implementation to all Activity execution ButterKnife. Bind (Activity)
A Bundle can store Parcelable objects
public class ActivityBean extends Parcelable {
private Unbinder unbinder;
public void setUnbinder(Unbinder unbinder){
thid.unbinder = unbinder;
}
public Unbinder getUnbinder() {returnunbinder; }}Copy the code
In ActivityLifecycleCallbacks perform corresponding logic
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(); }}}); }}Copy the code
The Activity needs to initialize something or provide some data
BaseActivity sometimes requires a child Activity to implement some method or provide some data, such as initView, which returns the layout ID in setContentView() and implements initData Initialize some data, so that you can do not need the Activity to re-write the onCreate, achieve the purpose of the specification, such use ActivityLifecycleCallbacks also can do it, so what should I do?
You only need the Activity to implement a custom interface
public interface IActivity {
int initView();
void initData();
}
Copy the code
Then in ActivityLifecycleCallbacks onActivityCreated call these methods, can be achieved
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(); }}... }}); }}Copy the code
Matters needing attention
Due to all the methods in the ActivityLifecycleCallbacks call time is the Activity of corresponding Super method of life cycle, so in the Activity’s onCreate method using the setContentView Must be super. OnCreate (savedInstanceState); Before, otherwise findViewById would not be found in the onActivityCreated method
@Override
protected void onCreate(Bundle savedInstanceState) {
setContentView(R.layout.activity_home);
super.onCreate(savedInstanceState);
}
Copy the code
You can also use a custom interface in combination with the above method by calling initView and finding the ToolBar in findViewById()
Accidentally implement so many functions that you had to write into BaseActivity to achieve through inheritance, and find that there is no BaseActivity, and there is no uncomfortable place, suddenly feel so awesome 🤒, please follow me, although I do not update my blog regularly, but the quality of my updated articles is absolutely guaranteed!
conclusion
It is worth noting that the ActivityLifecycleCallbacks can register multiple, can be added according to different situations according to needs to carry on the combination of various ActivityLifecycleCallbacks so as to achieve the different needs, and Okhttp Interceptor similar
Now all Activity and Fragment calls in the entire App will be intercepted as long as they are called during their lifetimes. Applying to my framework can dynamically insert arbitrary code into the corresponding lifetimes of all activities and fragments, such as LeakCanary RefWatcher. Watch (fragment) can also be inserted directly into the fragment of a three-party library, and there is no need to modify the base class if any code changes
The ideas and solutions mentioned above have been used in my MVPArms Framework, want to know more detailed usage can go to see my framework implementations, all I mentioned above, are some of the most simple requirements, believe that has toppled the implementation of the way before, and more elegant (because I am standing on three sides put forward the function of the library designers point of view, I must be the only inheritance opportunities for other activities If you feel less elegant than the previous approach, forget it.) and don’t worry about Java’s single-inheritance constraints
But don’t think, ActivityLifecycleCallbacks can only achieve these simple requirements, it can also used more complex functions, one still before me, As long as you dare to try, how rich your imagination, here is how powerful, ideas and solutions have been introduced very clearly, as for the realization of more requirements depends on everyone to try, although I am not sure that all the functions of the previous package BaseActivity through inheritance can be ActivityLifecycleCallbacks replaced, but I think most of the functionality or can achieve
Everyone is sharing their packaging BaseActivity before, maybe one day we will develop to share their writing ActivityLifecycleCallbacks! When the previous way can not meet our needs, dare to jump out of the traditional way, try different solutions, to broaden their horizons, grow their own technology, MVPArms is constantly working hard!
I would also like to say that everyone has different ideas and perspectives. It is good if you agree with me. Don’t agree with me or not, will not affect my steps, at least I was using my train of thought innovation, to solve some I think it is necessary to solve the problem, and the previous article, I just like to use different ideas to solve the same problem, whether you think feasible, I thought at least you think with this unacceptable to realize the effect that I want to reach, has seen a wise man Zhi, if our thinking is not collision, that also please cherish the fruits of my labor
I’ll say one more thing about some of the comments,registerActivityLifecycleCallbacks()Internal is to put theActivityLifecycleCallbacksTo a set, soActivityLifecycleCallbacksMultiple can be added, andActivityLifecycleCallbacksIt just installs into the collection when the project initializes, and does not initialize anything, just like adding listeners, using observer mode, so don’t sayApplicationWhat happens when you have so much code,Okhttp
çš„ InterceptorMore code, also inOkhttp
Do you think it will make any difference when it is added during initialization?
The public,
Scan the code to follow my official account JessYan to learn and make progress together. If there is any update to the framework, I will inform you immediately on my official account
Hello, my name is JessYan. If you like my articles, you can follow me on the following platforms
- Personal homepage: Jessyan.me
- Making: github.com/JessYanCodi…
- The Denver nuggets: juejin. Cn/user / 976022…
- Jane: www.jianshu.com/u/1d0c0bc63…
- Weibo: weibo.com/u/178626251…
— The end