Development Framework Construction: Encapsulation of Common Base Classes

Posted by afbase on Sun, 07 Jul 2019 00:31:45 +0200

We want to develop an APP. The first task is to build the package structure, build the framework, encapsulate the base class, import the third-party libraries into the project and so on. Today we will talk about how to complete this process.
(Note: Everyone has their own code style, so there is no good or bad code that suits them.)

Through this article, you can Get to the following skills:

1. How to build the package structure of new projects;
2. Encapsulation of common base classes for new projects.

text

Construction of package structure

When it comes to subcontracting, everyone must have their own way. The most common way is to put activities together, fragment s together and so on. Of course, I also subcontract in this way (hey hey). Below is my subcontract chart:

Here I use MVP mode, so there is a MVP in the subcontract. If the custodians don't need mvp, it's simpler. Then replace MVP and put model, activity and fragment outside.

Here constants are constants and widget s are custom view s. Looking at the name of other bags, you can see what's going to be put in them, so there's no need to explain too much here. Just take a look at it, gentlemen.

Encapsulation of Common Base Classes

The following is the focus of this article, encapsulation of common base classes. Let's start with Application, in order, without much nonsense, with code first:

/**
 * @author: X_Meteor
 * @description: Customized BaseApplication
 * @version: V_1.0.0
 * @date: 2017/4/21 16:34
 * @email: lx802315@163.com
 */
public class BaseApplication extends Application {

    /**
     * Maintaining a global context object
     */
    public Context context;

    /**
     * Used to store current users (if any) 
     */
//    private static UserInfo currentUser;

    //Singleton pattern
    private static BaseApplication myApplication = null;

    public static BaseApplication getInstance() {
        return myApplication;
    }

    /**
     * Get the current user object
     *
     * @param currentUser    
     */
//    public UserInfo getCurrentUser() {
//        UserInfo user = currentUser;
//        if (user != null) {
//            return user;
//        }
//        return null;
//    }

    /**
     * Setting the current user object
     *
     */
//    public void setCurrentUser(UserInfo currentUser) {
//        this.currentUser = currentUser;
//    }

    /**
     * Define a tag
     */
    private static String TAG;

    @Override
    public void onCreate() {
        super.onCreate();
        //Define TAG as the class name of the current class
        TAG = this.getClass().getSimpleName();
        //Since the Application class itself is singleton, it can be processed directly as follows.
        myApplication = this;
        context = getApplicationContext();

        //Global exception handling
        if(Constants.isCollectException){
            CrashHandlerUtils crashHandler = CrashHandlerUtils.getInstance();
            crashHandler.init(getApplicationContext());
        }
    }
}

As you can see, there are three main things we do in Base Application:

  1. Get the current system user (if there is a user module)
  2. A TAG for printing log s is defined, with the class name of the current class as the value.
  3. A global error exception class is defined to catch exceptions when an exception occurs in a program. (Exception handling classes will be posted later)

Next, BaseActivity:

/**
 * @author: Meteor
 * @description: Base Classes of All Activities
 * @version: V 1.0
 * @date: 2016/12/28 0028 15:33
 * @company:
 * @email: lx802315@163.com
 */
public abstract class BaseActivity extends AppCompatActivity {

    //Declare a build object to create a warning dialog
    private AlertDialog.Builder builder;
    //Used to create a progress bar dialog box
    private ProgressDialog dialog;
    //Used for printing log
    private final String TAG = "BaseActivity";
    //Declare an activity management stack
    private static Stack<Activity> activities = new Stack<>();

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        //Add activities to the activity stack
        activities.add(this);

        //Fixed screen orientation
        this.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
        //Set the input method not to open by default when the activity starts
        getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN);

        initRootView();
        ButterKnife.bind(this);

        initView();
        initData();
        initListener();

        //Print the current class name
        String msg = this.getClass().getName();
        LogUtils.print(msg);
    }

    /**
     * Initialize the root layout file
     */
    public abstract void initRootView();

    /**
     * Initialization control
     */
    public abstract void initView();

    /**
     * Initialization data
     */
    public abstract void initData();

    /**
     * Initialization interface
     */
    public abstract void initListener();

    /**
     * Implement an immersive notification bar so that the color of the notification bar is the same as that of the APP title.
     */
    protected void immersiveNotification() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
            //Navigation Bar Transparency
            getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
            //Bottom virtual button transparent
//            getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION);
        }
    }

    /**
     * Display a warning dialog box, no button, you need to set it yourself
     *
     * @param title Title
     * @param msg   content
     * @param flag  Can we cancel it?
     * @return
     */
    protected AlertDialog.Builder showAlertDialog(String title, String msg, boolean flag) {
        if (builder == null) {
            //Create a builder object
            builder = new AlertDialog.Builder(this);
            builder.setTitle(title).setMessage(msg).setCancelable(flag);
        }
        return builder;
    }

    /**
     * Function: Cancel Warning Dialog Box
     */
    protected void dismissAlertDialog(android.app.AlertDialog alertDialog) {
        if (alertDialog != null) {
            //Cancel warning dialog box
            alertDialog.dismiss();
        }
    }

    /**
     * Function: Display a progress bar dialog box
     */
    protected void showProcessDialog(String title, String msg, boolean falg) {
        if (dialog == null) {
            dialog = new ProgressDialog(this);
        }
        dialog.setTitle(title);
        dialog.setMessage(msg);
        dialog.setCancelable(falg);
        dialog.show();
    }

    /**
     * Function: Cancel a progress bar dialog box
     */
    protected void dismissProcessDialog() {
        if (dialog != null) {
            dialog.dismiss();
        }
    }

    /**
     * Determine whether the user has logged in
     *
     * @return true For successful landing false for no landing
     */
    protected boolean isLogin() {
    //Do your own logical processing here
        return true;
    }

    /**
     * Exit all activities and exit the current application
     */
    public static void exitApplication() {
        for (Activity activity : activities) {
            if (activity != null) {
                activity.finish();
            }
        }
        System.exit(0);
        android.os.Process.killProcess(android.os.Process.myPid());
    }
}

As you can see, we did a lot of work in BaseActivity:

  1. Define an Alert Dialog, Warning Dialog
  2. Define a ProgressDialog, Progress Dialog
  3. A method to judge whether a user is logged in or not is implemented.
  4. Maintaining an activity stack and opening up a way to exit all activities
  5. Four abstract methods are defined: initRootVeiw();initView;initData();initListener.
    (Note: The initRootView here is specifically defined to use butterknife, and we need to put setContentView in an integrated subclass.)

Specifically, you can see the code: onCreate () of BaseActivity

@Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        //Add activities to the activity stack
        activities.add(this);

        //Fixed screen orientation
        this.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
        //Set the input method not to open by default when the activity starts
        getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN);

        initRootView();
        //Add ButterKnife Annotation Framework
        ButterKnife.bind(this);

        initView();
        initData();
        initListener();

        //Print the current class name
        String msg = this.getClass().getName();
        LogUtils.print(msg);
    }

Inherited subclass, MainActivity:

public class MainActivity extends BaseActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
    }

    @Override
    public void initRootView() {
        setContentView(R.layout.activity_main);
    }

    @Override
    public void initView() {

    }

    @Override
    public void initData() {

    }

    @Override
    public void initListener() {

    }
}

Next, BaseFragment:

/**
 * @author: Meteor
 * @description: Base Classes of All Fragment s
 * @version:
 * @date: 2016/12/28 0028 16:16
 * @company:
 * @email: lx802315@163.com
 */
public abstract class BaseFragment extends Fragment {

    //Define a recycle pool for repeated views
    private View contentView = null;

    /**
     * Fragment Is the current state visible
     */
    protected boolean isVisible;
    private AlertDialog.Builder builder;
    private AlertDialog alertDialog;
    private ProgressDialog dialog;
    //Binding for Butterknife
    Unbinder unbinder;
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
    }

    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        if (contentView == null) {//Judging whether the recycling pool is empty
            contentView = initLayout(inflater, container, false);
            unbinder = ButterKnife.bind(this, contentView);
        }
        if (contentView != null) {
            unbinder = ButterKnife.bind(this, contentView);
            return contentView;
        }
        return super.onCreateView(inflater, container, savedInstanceState);
    }

    /**
     * Initialize the layout of Fragment, which is called when an attempt is to be created
     *
     * @param inflater Layout Filler
     * @param container ViewGroup
     * @param b     sign
     * @return view Return View
     */
    protected abstract View initLayout(LayoutInflater inflater, ViewGroup container, boolean b);

    @Override
    public void onActivityCreated(@Nullable Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        //Initialization data
        initData(savedInstanceState);
    }

    /**
     * Initialize data and call this method when ViewCreate is created
     * @param savedInstanceState
     */
    protected abstract void initData(Bundle savedInstanceState);

    @Override
    public final void setUserVisibleHint(boolean isVisibleToUser) {
        super.setUserVisibleHint(isVisibleToUser);

        if (getUserVisibleHint()) {
            isVisible = true;
            onVisible();
        } else {
            isVisible = false;
            onInvisible();
        }
    }

    /**
     * So
     */
    protected void onVisible() {
        lazyLoad();
    }

    /**
     * Invisible
     */
    protected void onInvisible() {

    }

    /**
     * Delayed loading
     * Subclasses must override this method
     */
    protected abstract void lazyLoad();

    @Override
    public final void onDestroyView() {
        //Remove the current view to prevent repeated loading of the same view causing the program to flip back
        ((ViewGroup) contentView.getParent()).removeView(contentView);
        super.onDestroyView();
        unbinder.unbind();
    }

    /**
     * Determine whether the user has logged in
     *
     * @return true For successful landing false for no landing
     */
    protected boolean isLogin() {
        return false;
    }

    /**
     *  Function: Display a warning dialog box
     */
    protected void showAlertDialog(String title,String text){
        if (builder==null){
            //Create a builder object
            builder = new AlertDialog.Builder(getContext());
            builder.setTitle(title).setMessage(text).setCancelable(false);
            builder.setPositiveButton("Set up", new DialogInterface.OnClickListener() {
                @Override
                public void onClick(DialogInterface dialog, int which) {
                    //Jump to System Network Settings
                    Intent intent = new Intent(Settings.ACTION_WIRELESS_SETTINGS);
                    startActivity(intent);
                }
            }).setNegativeButton("cancel", new DialogInterface.OnClickListener() {
                @Override
                public void onClick(DialogInterface dialog, int which) {
                    //Exit Virtual Machine
                    System.exit(0);
                }
            });
        }
        alertDialog = builder.show();
    }

    /**
     * Function: Cancel Warning Dialog Box
     */
    protected void dismissAlertDialog(){
        if (alertDialog!=null){
            //Cancel warning dialog box
            alertDialog.dismiss();
        }
    }
    /**
     * Function: Display a progress bar dialog box
     */
    protected void showProcessDialog(String title,String msg,boolean falg){
        if(dialog==null){
            dialog = new ProgressDialog(getContext());
        }
        dialog.setTitle(title);
        dialog.setMessage(msg);
        dialog.setCancelable(falg);
        dialog.show();
    }

    /**
     * Function: Cancel a progress bar dialog box
     */
    protected void dismissProcessDialog(){
        if(dialog!=null){
            dialog.dismiss();
        }
    }

    public interface BackHandledInterface {
        public abstract void setSelectedFragment(BaseFragment selectedFragment);
    }
}

In addition to the display dialog box similar to BaseActivity, there are mainly several lazy loading methods, and the knowledge about lazy loading can be explained by Baidu.

Subclass inheritance is used as follows:

/**
 * @author: X_Meteor
 * @description: Class Description
 * @version: V_1.0.0
 * @date: 2017/4/22 16:37
 * @email: lx802315@163.com
 */
public class homeFragment extends BaseFragment {
    @Override
    protected View initLayout(LayoutInflater inflater, ViewGroup container, boolean b) {
        View rootView = inflater.inflate(R.layout.activity_main, null);
        return rootView;
    }

    @Override
    protected void initData(Bundle savedInstanceState) {

    }

    @Override
    protected void lazyLoad() {

    }
}

The most commonly used base classes here are encapsulated. Of course, there is no best one but the most suitable one. The project has been uploaded to the following address:
Demo

That's all for today. I'll write about the simple encapsulation of rxjava2 + retrofit2's network request framework later. Welcome to discuss learning together.

Topics: ButterKnife Fragment Android network