一、概述
1.框架图:整体架构分为UI层、数据层,并结合MVC模式的使用
2.项目包结构:按模块进行划分
application:主要是自定义的Application
config:一些配置文件,如url、常量…
exception:自定义的异常
util:一些工具类
core:应用的核心部分,包含了各个跟业务相关的模块
3.core说明:作为应用的核心,包含了所有与业务相关的模块,主要有
activity、fragment:代表控制器,处理逻辑、事件交互、并作为 View、Model之间的桥梁
model:数据实体,分为resp、resq
view:视图,主要是自定义的视图,大部分都放到res/layout下
adapter:适配器
datalayer:数据层,又分为接口层、本地数据层
其它诸如receiver、service、provider等… 都应单独放在模块文件夹下
gradle依赖添加:
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
testCompile 'junit:junit:4.12'
compile files('libs/gson-2.3.1.jar')
compile 'com.android.support:appcompat-v7:23.4.0'
compile 'com.facebook.fresco:fresco:0.10.0'
compile 'com.squareup.okhttp3:okhttp:3.2.0'
compile 'de.greenrobot:greendao:2.1.0'
compile 'com.android.support:design:23.4.0'
compile 'com.android.support:support-v4:23.4.0'
}
二、UI层设计
BaseActivity、BaseFragment的封装:
public abstract class BaseActivity extends FragmentActivity implements View.OnClickListener{
public String TAG = null;
protected Context context;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//将当前activity压入栈顶
ActivityStackManager.getInstance().pushActivity(this);
//无标题栏
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
context = this;
TAG = getClass().getSimpleName();
}
/**
* 初始化变量
* 对于一个activity,通常会在onCreate中执行三个操作,分别是初始化成员变量,初始化视图,加载数据
* 因此在基类定义好模板,由具体子类实现
*/
protected void initVariable(){
}
/**
* 初始化视图
*/
protected void initViews(){
}
/**
* 加载数据
*/
protected void loadData(){
}
/**
* 返回按键处理处理
* @param keyCode
* @param event
* @return
*/
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if(keyCode == KeyEvent.KEYCODE_BACK){
back();
return true;
}
return super.onKeyDown(keyCode, event);
}
/**
* 返回键处理,默认是移除当前界面
*/
protected void back(){
ActivityStackManager.getInstance().popActivity();
}
@Override
public void onClick(View v) {
}
/**
* 显示输入键盘
*/
public void displaySoftKeyboard() {
InputMethodManager inputMethodManager = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
if (inputMethodManager != null) {
View localView = getCurrentFocus();
if (localView != null && localView.getWindowToken() != null) {
inputMethodManager.toggleSoftInput(0, InputMethodManager.HIDE_NOT_ALWAYS);
}
}
}
/**
* 关闭输入键盘
*/
public void hideSoftKeyboard() {
InputMethodManager inputMethodManager = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
if (inputMethodManager != null) {
View localView = getCurrentFocus();
if (localView != null && localView.getWindowToken() != null) {
IBinder windowToken = localView.getWindowToken();
inputMethodManager.hideSoftInputFromWindow(windowToken, 0);
}
}
}
public abstract class BaseFragment extends Fragment{
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
return super.onCreateView(inflater, container, savedInstanceState);
}
/**
* 初始化变量
* 通常会在fragment的onCreate中执行三个操作,分别是初始化成员变量,初始化视图,加载数据
* 因此在基类定义好模板,由具体子类实现
*/
protected void initVariable(){
}
/**
* 初始化视图
*/
protected void initViews(){
}
/**
* 加载数据
*/
protected void loadData(){
}
}
三、Http工具设计
自定义OkHttp的回调:
/**
* User: zjt
* DateTime: 16/5/23 07:12
*
* 默认情况下okhttp的回调是在子线程,无法实现更新UI
* 解决方案:重写onResponse()、onFailure()方法,并通过Handler转发到主线程即可
*
*/
public abstract class OkHttpCallback implements okhttp3.Callback{
/**
* 主线程的Handler,全局
* 只有UI线程默认Loop.prepare()、Loop.loop()过,如果在子线程中创建则需要手动处理
*/
private static Handler mainHanlder = new Handler(Looper.getMainLooper());
@Override
public void onResponse(Call call, final Response response) throws IOException {
final String jsonStr = response.body().string(); //读取响应数据
mainHanlder.post(new Runnable() {
@Override
public void run() {
onResponse(jsonStr); //读取完后通过handler转发到UI线程
}
});
}
@Override
public void onFailure(Call call, final IOException e) {
mainHanlder.post(new Runnable() {
@Override
public void run() {
String errorMsg = "";
if(e instanceof NotNetworkException){
errorMsg = "请检查您的网络!";
}
else if(e instanceof SocketTimeoutException){
errorMsg = "请求超时,请重试!";
}
else if(e instanceof ConnectException){
errorMsg = "无法连接到服务器,请稍后再试!";
}
else{
errorMsg = "未知错误!";
}
onFailure(e,errorMsg); //通过handler转发到UI线程
}
});
}
/**
* 请求成功 - UI线程回调
* @param jsonStr
*/
public abstract void onResponse(String jsonStr);
/**
* 请求失败 - UI线程回调
* @param e
*/
public abstract void onFailure(IOException e,String errorMsg);
}
OKHttpUtil:
/**
* User: zjt
* DateTime: 16/5/20 15:36
*
* http工具类
*/
public class OkHttpUtil {
public static OkHttpClient okHttpClient; //作为单例,只创建一次
static {
okHttpClient = new OkHttpClient();
}
/**
* get异步请求,参数拼接到url后
* @param url 请求路径
* @param callback 回调
*/
public static void doGet(String url, OkHttpCallback callback){
if(isNetworkConnected(CustomApplication.getAppContext())){
//1,封装请求Request
Request request = new Request.Builder().url(url).build();
//2,发送http请求并获取响应结果
okHttpClient.newCall(request).enqueue(callback);
}
else{
callback.onFailure(new NotNetworkException(),"请检查您的网络!");
}
}
/**
* post异步请求(将请求参数以json格式提交)
* @param url 请求路径
* @param params 参数,可为object、map
* @param callback 回调
*/
public static void doPostWithJson(String url,Object params,OkHttpCallback callback){
Log.i("OkHttpUtil", "doPostWithJson: " + isNetworkConnected(CustomApplication.getAppContext()));
if(isNetworkConnected(CustomApplication.getAppContext())){
//1,设置content-type, post请求体
MediaType mimeType = MediaType.parse("application/json; charset=utf-8");
RequestBody body = RequestBody.create(mimeType, JsonUtil.parseToJsonStr(params));
Request request = new Request.Builder().url(url).post(body).build();
//2,发送http post请求
okHttpClient.newCall(request).enqueue(callback);
}
else{
callback.onFailure(new NotNetworkException(),"请检查您的网络!");
}
}
/**
* POST请求(key、value作为表单提交)
*/
public void doPostWithForm(String url, Map<String,String> params, OkHttpCallback callback) throws IOException {
if(isNetworkConnected(CustomApplication.getAppContext())){
//封装请求参数
FormBody.Builder formBuilder = new FormBody.Builder();
for(Map.Entry<String,String> entry : params.entrySet()){
formBuilder.add(entry.getKey(),entry.getValue());
}
RequestBody formBody = formBuilder.build();
Request request = new Request.Builder().url(url).post(formBody).build();
//发送http post请求
okHttpClient.newCall(request).enqueue(callback);
}
else{
callback.onFailure(new NotNetworkException(),"请检查您的网络!");
}
}
/**
* 是否有网络连接
*/
public static boolean isNetworkConnected(Context context) {
ConnectivityManager manager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo info = manager.getActiveNetworkInfo();
return info != null && info.isAvailable();
}
模拟接口调用:
/**
* 登录操作
*/
private void login(){
String phone = editUserName.getText().toString();
String pwd = editPwd.getText().toString();
loadingView.show();
userApiManager.loginForPassword(phone, pwd, new OkHttpCallback() {
@Override
public void onResponse(String jsonStr) {
loadingView.close();
PromptUtil.toast(context,"登录成功!");
startActivity(new Intent(context, IndexActivity.class));
}
@Override
public void onFailure(IOException e, String errorMsg) {
loadingView.close();
PromptUtil.toast(context,errorMsg);
loadingErrorView.show(errorMsg);
}
});
}
三、Http统一错误处理
在onFailure中进行处理,并转发到UI线程,传入异常类型、以及描述
@Override
public void onFailure(Call call, final IOException e) {
mainHanlder.post(new Runnable() {
@Override
public void run() {
String errorMsg = "";
if(e instanceof NotNetworkException){
errorMsg = "请检查您的网络!";
}
else if(e instanceof SocketTimeoutException){
errorMsg = "请求超时,请重试!";
}
else if(e instanceof ConnectException){
errorMsg = "无法连接到服务器,请稍后再试!";
}
else{
errorMsg = "未知错误!";
}
onFailure(e,errorMsg); //通过handler转发到UI线程
}
});
四、自定义加载动画
/**
* User: zjt
* DateTime: 16/7/10 15:03
*
* 加载视图(仿美团)
*/
public class LoadingView extends RelativeLayout{
private ImageView ivLoading; //用于播放gif加载动画
private TextView tvLoadingText; //显示加载文字
public LoadingView(Context context) {
super(context);
initView(context);
}
public LoadingView(Context context, AttributeSet attrs) {
super(context, attrs);
initView(context);
}
/*
* 初始化视图
*/
private void initView(Context context){
View.inflate(context,R.layout.loading_view,this); //加载视图,并挂载到当前节点下
ivLoading = (ImageView) findViewById(R.id.iv_loading);
tvLoadingText = (TextView) findViewById(R.id.tv_loading_text);
}
/*
* 显示加载视图
*/
public void show(){
setVisibility(View.VISIBLE);
//开启加载动画
AnimationDrawable drawables = (AnimationDrawable) ivLoading.getDrawable();
if (!drawables.isRunning()) {
drawables.start();
}
}
/*
* 关闭加载视图
*/
public void close(){
setVisibility(View.GONE);
//关闭加载动画
AnimationDrawable drawables = (AnimationDrawable) ivLoading.getDrawable();
if (drawables.isRunning()) {
drawables.stop();
}
}
}
显示错误信息的视图:
/**
* User: zjt
* DateTime: 16/7/10 15:40
*
* 调用http请求中,加载出错的提示
*/
public class LoadingErrorView extends RelativeLayout {
private TextView tvErrorText; //错误信息提示
public Button btnReload; //重新加载按钮
public LoadingErrorView(Context context) {
super(context);
initView(context);
}
public LoadingErrorView(Context context, AttributeSet attrs) {
super(context, attrs);
initView(context);
}
/*
* 初始化视图
*/
private void initView(Context context){
View.inflate(context, R.layout.loading_error_view,this);
tvErrorText = (TextView) findViewById(R.id.tv_error_text);
btnReload = (Button) findViewById(R.id.btn_reload);
}
/*
* 显示加载视图
*/
public void show(String errorMsg){
setVisibility(View.VISIBLE);
tvErrorText.setText(errorMsg);
}
/*
* 关闭加载视图
*/
public void close(){
setVisibility(View.GONE);
}
加载框的配置: