Android基类的设计

这两天开始放寒假,在家呆着也是呆着,遂决定找星空以前Android方向大佬写的项目学习下,看看别人代码的结构以及有什么方法可以降低代码的耦合度,然后在师兄的项目里接触到了BaseActivity,在这里记录一下

为什么设计基类

  • 方便代码编写,减少重复代码和冗余逻辑,优化代码
  • 优化程序架构,降低耦合度,方便拓展、修改
  • 版面更干净,减少了诸如生命周期日志等重复逻辑的占用版面

设计的基本思路

  • 生命周期的调试日志输出
  • 绑定视图
  • 常用OnClick方法
  • Back方法,Toast以及Activity等操作
  • 常用第三方工具(ButterKnife等)
  • 标题栏是否显示,是否全屏,初始化数据等

具体基类的封装

BaseActivity

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
public abstract class BaseActivity<P extends BasePresenter> extends AppCompatActivity
implements BaseView<P> {

@BindView(R.id.toolbar)
Toolbar mToolbar;

Unbinder bind;

/**
* 进度对话框
*/
protected ProgressDialog mProgressDialog;
/**
* 泛型确定Presenter
*/
protected P mPresenter;

@Override
protected final void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 设置为竖屏
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
setContentView(bindLayout());
// ButterKnife绑定布局
bind = ButterKnife.bind(this);

mPresenter = createPresenter();
if (mPresenter != null) {
// 调用Presenter初始化方法
mPresenter.onStart();
}

// 准备数据
prepareData();
// 初始化标题栏
initToolbar();
// 初始化视图
initView();
// 初始化数据
initData(savedInstanceState);
// 初始化事件监听
initEvent();
}


/**
* 创建Presenter
*
* @return 泛型Presenter
*/
protected abstract P createPresenter();

/**
* 实现BasePresenter接口的setPresenter方法
*
* @param presenter createPresenter()创建的Presenter
*/
@Override
public void setPresenter(P presenter) {
mPresenter = presenter;
}

/**
* 初始化Toolbar
*/
private void initToolbar() {
setSupportActionBar(mToolbar);
}

/**
* 设置Toolbar标题
*
* @param title 标题
*/
protected void setToolbarTitle(String title) {
if (getSupportActionBar() != null) {
getSupportActionBar().setTitle(title);
}
}

/**
* 设置Toolbar显示返回按钮及标题
*
* @param title 标题
*/
protected void setToolbarBackEnable(String title) {
if (getSupportActionBar() != null) {
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
getSupportActionBar().setHomeAsUpIndicator(R.drawable.ic_arrow_white_24dp);
setToolbarTitle(title);
}

}

/**
* 绑定布局
*
* @return 布局文件的资源ID
*/
protected abstract int bindLayout();

/**
* 准备数据(从Intent获取上一个界面传过来的数据或其他需要初始化的数据)
*/
protected abstract void prepareData();

/**
* 初始化视图,findViewById等等
*/
protected abstract void initView();

/**
* 初始化数据,从本地或服务器开始获取数据
*
* @param savedInstanceState 界面非正常销毁时保存的数据
*/
protected abstract void initData(Bundle savedInstanceState);

/**
* 初始化事件监听,setOnClickListener等等
*/
protected abstract void initEvent();

/**
* 实现BaseView的showToast(CharSequence msg)
*
* @param msg 吐司显示的信息
*/
@Override
public void showToast(CharSequence msg) {
ToastUtils.shortToast(this, msg);
}

/**
* 实现BaseView的showToast(int msgId)
*
* @param msgId 吐司显示的字符串资源id
*/
@Override
public void showToast(int msgId) {
ToastUtils.shortToast(this, msgId);
}

/**
* 实现BaseView的showLoadingDialog(CharSequence msg)
* 显示加载对话框
*
* @param msg 对话框的提示内容
*/
@Override
public void showLoadingDialog(CharSequence msg) {
if (mProgressDialog == null) {
mProgressDialog = new ProgressDialog(this);
mProgressDialog.setTitle(R.string.title_dialog_tips);
mProgressDialog.setMessage(msg);
} else {
mProgressDialog.setTitle(R.string.title_dialog_tips);
}
mProgressDialog.show();
}

/**
* 实现BaseView的hideLoadingDialog()
* 隐藏加载对话框
*/
@Override
public void hideLoadingDialog() {
if (mProgressDialog != null && mProgressDialog.isShowing()) {
mProgressDialog.dismiss();
}
}

/**
* Activity销毁时清理资源
*/
@Override
protected void onDestroy() {
// ButterKnife解除绑定
bind.unbind();
// 销毁Presenter
if (mPresenter != null) {
mPresenter.onDestroy();
}
super.onDestroy();
}

/**
* 隐藏键盘
*/
public void hideKeyboard() {
View view = getCurrentFocus();
if (view != null) {
((InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE)).
hideSoftInputFromWindow(view.getWindowToken(), InputMethodManager.HIDE_NOT_ALWAYS);
}
}

@Override
protected void onPause() {
super.onPause();
// (仅有Activity的应用中SDK自动调用,不需要单独写)
// 保证 onPageEnd 在onPause之前调用,因为 onPause 中会保存信息。
MobclickAgent.onPageEnd(this.getClass().getSimpleName());
MobclickAgent.onPause(this);
}

@Override
protected void onResume() {
super.onResume();
//统计页面(仅有Activity的应用中SDK自动调用,不需要单独写。)
MobclickAgent.onPageStart(this.getClass().getSimpleName());
//统计时长
MobclickAgent.onResume(this);
}

@Override
public boolean onOptionsItemSelected(MenuItem item) {
if (item.getItemId() == android.R.id.home) {
finish();
}
return super.onOptionsItemSelected(item);
}
}

BaseFragment

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
public abstract class BaseFragment<P extends BasePresenter> extends Fragment
implements BaseView<P> {

@BindView(R.id.toolbar)
Toolbar mToolbar;

private Unbinder mUnbinder;
protected P mPresenter;

protected ProgressDialog mProgressDialog;

private ActionBar mActionbar;

@Nullable
@Override
public final View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
mPresenter = createPresenter();
if (mPresenter != null) {
mPresenter.onStart();
}
View root = inflater.inflate(bindLayout(), container, false);
mUnbinder = ButterKnife.bind(this, root);
prepareData(savedInstanceState);
initToolbar();
// 初始化视图
initView(root);
initData(savedInstanceState);
initEvent();
return root;
}


protected abstract P createPresenter();


@Override
public void setPresenter(P presenter) {
mPresenter = presenter;
}

/**
* 准备数据
*
* @param savedInstanceState
*/
protected abstract void prepareData(Bundle savedInstanceState);

/**
* 绑定fragment的布局文件
*
* @return
*/
protected abstract int bindLayout();

/**
* 初始化数据
*
* @param savedInstanceState
*/
protected abstract void initData(Bundle savedInstanceState);

/**
* 初始化界面
*
* @param rootView
*/
protected abstract void initView(View rootView);

/**
* 初始化事件监听器
*/
protected abstract void initEvent();


/**
* 初始化Toolbar
*/
private void initToolbar() {
((AppCompatActivity) getActivity()).setSupportActionBar(mToolbar);
mActionbar = ((AppCompatActivity) getActivity()).getSupportActionBar();
}

/**
* 设置Toolbar标题
*
* @param title 标题
*/
protected void setToolbarTitle(String title) {
if (mActionbar != null) {
mActionbar.setTitle(title);
}
}

/**
* 设置Toolbar显示返回按钮及标题
*
* @param title 标题
*/
protected void setToolbarBackEnable(String title) {
if (mActionbar != null) {
mActionbar.setDisplayHomeAsUpEnabled(true);
mActionbar.setHomeAsUpIndicator(R.drawable.ic_arrow_white_24dp);
}
}

@Override
public void showToast(CharSequence msg) {
ToastUtils.shortToast(APP.getAppContext(), msg);
}

@Override
public void showToast(int msgId) {
ToastUtils.shortToast(APP.getAppContext(), msgId);
}

@Override
public void onDestroy() {
super.onDestroy();
}

@Override
public void showLoadingDialog(CharSequence msg) {
if (mProgressDialog == null) {
mProgressDialog = new ProgressDialog(getContext());
mProgressDialog.setMessage(msg);
} else {
mProgressDialog.setTitle(R.string.title_dialog_tips);
}
mProgressDialog.show();
}

@Override
public void hideLoadingDialog() {
if (mProgressDialog != null && mProgressDialog.isShowing()) {
mProgressDialog.dismiss();
}
}

@Override
public void onDetach() {
mUnbinder.unbind();
super.onDetach();
}

@Override
public void onDestroyView() {
if (mPresenter != null) {
mPresenter.onDestroy();
}
super.onDestroyView();
}

@Override
public void onResume() {
super.onResume();
MobclickAgent.onPageStart(this.getClass().getSimpleName());
}

@Override
public void onPause() {
super.onPause();
MobclickAgent.onPageEnd(this.getClass().getSimpleName());
}

}

BasePresenter

1
2
3
4
5
6
7
public interface BasePresenter {

void onStart();

void onDestroy();

}

BaseView

1
2
3
4
5
6
7
8
9
10
11
12
13
public interface BaseView<P> {

void setPresenter(P presenter);

void showToast(CharSequence msg);

void showToast(int msgId);

void showLoadingDialog(CharSequence msg);

void hideLoadingDialog();

}

Impl类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public abstract class BasePresenterImpl implements BasePresenter {
protected CompositeDisposable mSubscriptions;

@Override
public void onStart() {
if (mSubscriptions == null) {
mSubscriptions = new CompositeDisposable();
}
}

@Override
public void onDestroy() {
if (mSubscriptions != null) {
mSubscriptions.dispose();
mSubscriptions.clear();
}
}
}

APP类

用于获取全局的context

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
public class APP extends Application {

private static Context appContext;
private static long exitTime = 0;

/**
* 获取Application的Context
*
* @return 全局Context
*/
public static Context getAppContext() {
return appContext;
}

@Override
public void onCreate() {
super.onCreate();
appContext = getApplicationContext();
}

/**
* 退出APP
*/
public static void exitApp() {
if (System.currentTimeMillis() - exitTime > 2000) {
ToastUtils.shortToast(getAppContext(), appContext.getString(R.string.text_press_again));
exitTime = System.currentTimeMillis();
} else {
android.os.Process.killProcess(android.os.Process.myPid());
}
}
}

具体使用

Contract 契约类

契约类用于定义同一个界面的view和presenter的接口,通过规范的方法命名或注释,可以清晰的看到整个页面的逻辑。

1
2
3
4
5
6
7
8
9
public interface myContract {

interface View extends BaseView<Presenter> {
}

interface Presenter extends BasePresenter {

}
}

Presenter

1
2
3
4
5
6
7
8
public class SamplePresenter extends BasePresenterImpl implements myContract.Presenter {

private final myContract.View mView;
public SamplePresenter(myContract.View view) {
mView = view;
this.mView.setPresenter(this);
}
}

Activiy

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
public class SampleActivity extends BaseActivity<myContract.Presenter> implements myContract.View {

@BindView(R.id.tv_sample_text)
TextView mTvSample;

@Override
protected myContract.Presenter createPresenter() {
return new SamplePresenter(this);
}

@Override
protected int bindLayout() {
//TODO:添加视图,记得添加androidmanifest
return R.layout.activity_sample;
}

@Override
protected void prepareData() {
//TODO:准备数据 比如:从数据库加载数据,或者网络请求数据等等
}

@Override
protected void initView() {
//TODO:初始化视图 比如:recycleview的准备,添加adapter等等
mTvSample.setText("这是一个sample");
}

@Override
protected void initData(Bundle savedInstanceState) {
//TODO:初始化数据 比如:将数据加入到view中
}

@Override
protected void initEvent() {
//TODO:初始化事件监听 比如:增加监听器,下拉刷新,加载更多等等
}
}