Step1:在根目录.build文件下添加
repositories {
maven { url "https://jitpack.io" }
}
Step2:在具体项目.build目录下添加
implemention 'com.github.yuyuyu123:ZCommon:1.3.1'
ZCommon中添加了对Aspectj的依赖,支持AOP编程。
1.添加依赖
首先需要在根目录的build文件下添加依赖:
repositories {
google()
}
dependencies {
classpath 'org.aspectj:aspectjtools:1.9.1'
classpath 'org.aspectj:aspectjweaver:1.9.1'
}
然后在app目录的build文件下添加如下代码:
import org.aspectj.bridge.IMessage
import org.aspectj.bridge.MessageHandler
import org.aspectj.tools.ajc.Main
final def log = project.logger
final def variants = project.android.applicationVariants
variants.all { variant ->
//打Release包的时候,整个if判断注释掉,否则AOP会不起作用。
if (!variant.buildType.isDebuggable()) {
log.debug("Skipping non-debuggable build type '${variant.buildType.name}'.")
return;
}
JavaCompile javaCompile = variant.javaCompile
javaCompile.doLast {
String[] args = ["-showWeaveInfo",
"-1.8",
"-inpath", javaCompile.destinationDir.toString(),
"-aspectpath", javaCompile.classpath.asPath,
"-d", javaCompile.destinationDir.toString(),
"-classpath", javaCompile.classpath.asPath,
"-bootclasspath", project.android.bootClasspath.join(File.pathSeparator)
]
log.debug "ajc args: " + Arrays.toString(args)
MessageHandler handler = new MessageHandler(true);
new Main().run(args, handler);
for (IMessage message : handler.getMessages(null, true)) {
switch (message.getKind()) {
case IMessage.ABORT:
case IMessage.ERROR:
case IMessage.FAIL:
log.error message.message, message.thrown
break;
case IMessage.WARNING:
log.warn message.message, message.thrown
break;
case IMessage.INFO:
log.info message.message, message.thrown
break;
case IMessage.DEBUG:
log.debug message.message, message.thrown
break;
}
}
}
}
注:若网络比较慢,建议Fan Qiang(你懂得)依赖,否则可能会失败。
2.使用
关于aspectj的具体使用方法请自行查找教程并学习
3.ZCommon中提供的aspect功能
3.1 处理快速点击
若需要处理快速点击,只需要在对应的方法中添加@FastClick注解接口,例:
@FastClick
public void fastClick(View view) {
Log.e(TAG, "fastClick-----------");
}
3.2 权限申请
ZCommon中针对Android M提供了动态权限申请服务,包含Java代码的方式(见下文)和注解的方式,注解包含三个部分:
1.GPermission(Get Permission),申请权限;
2.CPermission(Permission Canceled),权限被用户拒绝,但允许再次提示;
3.DPermission(Permission Denied),权限被用户拒绝,且不允许再次提示。
下面分别对这三个注解进行讲解:
3.2.1 申请单个权限
@GPermission(value = {Manifest.permission.ACCESS_FINE_LOCATION},requestCode = 1)
private void location() {
T.showShort(this, "定位权限通过");
}
3.2.2 申请多个权限
@GPermission(value = {Manifest.permission.CAMERA, Manifest.permission.WRITE_EXTERNAL_STORAGE}, requestCode = 10)
private void takePhoto() {
T.showShort(this, "拍照和文件读写权限通过");
}
3.2.3 权限被拒绝,但允许再次提示
@CPermission(requestCode = 10)
public void dealCancelPermission(PermissionCanceled bean) {
Toast.makeText(this, "requestCode:" + bean.getRequestCode(), Toast.LENGTH_SHORT).show();
}
3.2.4 权限被拒绝,且不允许再次提示
@DPermission
public void dealPermission(PermissionDenied bean) {
if (bean == null) return;
Toast.makeText(this, "requestCode:" + bean.getRequestCode()
+ ",Permissions: " + Arrays.toString(bean.getDenyList().toArray()), Toast.LENGTH_SHORT).show();
switch (bean.getRequestCode()) {
//权限被拒绝后,一般提示用户到设置界面手动打开权限
case 1:
//多单个权限申请返回结果
break;
case 10:
//多个权限申请返回结果
break;
default:
break;
}
}
3.3 登录验证
我们在开发过程中有许多地方需要验证用户是否已经登录,对此,ZCommon对其作了统一处理:
首先需要在Application类中对LoginManager类进行初始化:
@Override
public void onCreate() {
super.onCreate();
LoginManager.get().init(this, new OnLoginListener() {
@Override
public void login(Context context, int val) {
Log.e(TAG, "login-------->" + val);
//这里写登录逻辑,比如跳转到登录界面
if(val == 5) {//根据变量的值可以作不同的操作
}
}
@Override
public boolean isLogin(Context context) {
Log.e(TAG, "isLogin-------->");
//这里判断是否已经登录
return false;
}
});
}
然后在需要验证登录的地方添加Login注解:
@Login(val = 100)
public void login(View view) {
T.showShort(this, "登录了哦");
}
1.约定:数据请求一律采用RxJava+Retrofit
2.配置
1.请求单个base url
ConfigurationHelper.setBaseUrl(URLHelper.BASE_URL);
ConfigurationHelper.setOkhttpClient(okHttpClient);
2.请求多个base url
Map<String , String> baseUrlMap = new HashMap<>();
String url1 = "";
String url2 = "";
baseUrlMap.put(key1, url1);
baseUrlMap.put(key2, url2);
ConfigurationHelper.setBaseUrlMap(baseUrlMap);
Map<String,OkHttpClient> clientMap = new HashMap<>();
clientMap.put(key1, okHttpClient1);
clientMap.put(key2, okHttpClient2);
ConfigurationHelper.setOkhttpClientMap(clientMap);
说明:
1.虽然多个base url可以共用一个OkHttpClient对象,但是为了适应不同的配置,即使是相同的OkHttpClient也要放进Map中;
2.设置base url的map和设置client的key必须一一对应
3.使用
1.请求单个base url
BaiduService service = ManagerFactory.getFactory().getManager(BaiduService.class);
2.请求多个base url
OneService service1 = ManagerFactory.getFactory().getManager(OneService.class, key1);
TwoService service2 = ManagerFactory.getFactory().getManager(TwoService.class, key2);
说明:
1.单个base url的使用可以不传key,对应只设置base url;
2.多个base url的使用必须传key区分,对应设置base url map;
3.上面只是罗列了要点,具体使用可以参考内部app。
如果需要打印网络请求,建议用OkHttp3提供的拦截器HttpLoggingInterceptor,只需要在请求前配置即可,例:
HttpLoggingInterceptor logging = new HttpLoggingInterceptor(new HttpLoggingInterceptor.Logger() {
@Override
public void log(String message) {
Log.d("okhttp", message);
}
});
logging.setLevel(HttpLoggingInterceptor.Level.BASIC);
new OkHttpClient
.Builder()
.addInterceptor(logging);
OkHttp支持Http和Https两种协议的请求,只需要在请求前用ZCommon内的HttpsUtils配置一下即可,例:
HttpsUtils.SSLParams sslParams = HttpsUtils.getSslSocketFactory(null, null, null);
new OkHttpClient
.Builder()
.sslSocketFactory(sslParams.sSLSocketFactory, sslParams.trustManager);
为了防止NullPointerException等Bug导致程序崩溃,可以在自定义Application中使用CrashHandler类,例:
public class App extends BaseApplication {
@Override
public void onCreate() {
super.onCreate();
initCrashHandler();
}
private void initCrashHandler() {
CrashHandler.init((thread, throwable) -> {
new Handler(Looper.getMainLooper()).post(() -> {
try {
//处理错误,可以上报,也可以不处理
} catch (Throwable e) {
}
});
});
}
}
ZCommon中提供了状态栏适配的Util叫StatusBarUtil,建议在基类里面作统一适配,例:
public abstract class DkBaseActivity<V extends RxBaseView, P extends RxBasePresenter<V>>
extends RxBasePermissionActivity<V, P> {
@Override
protected void init(Bundle savedInstanceState) {
super.init(savedInstanceState);
StatusBarUtil.showStatusBarWithLightMode(this, com.ccclubs.common.R.color.material_light_white);
}
}
建议在业务baseLib中可以根据自己的需求写一到多个基类Activity,继承RxBaseActivity或者RxBasePermissionActivity(封装了权限请求),例新建一个处理Umeng统计的基类:
public abstract class DkBaseActivity<V extends RxBaseView, P extends RxBasePresenter<V>>
extends RxBaseActivity<V, P> {
@Override
protected void onResume() {
super.onResume();
MobclickAgent.onPageStart(TAG);
MobclickAgent.onResume(this);
}
@Override
protected void onPause() {
super.onPause();
MobclickAgent.onPageEnd(TAG);
MobclickAgent.onPause(this);
}
}
例写一个处理滑动返回的基类:
public abstract class DkBaseSwipeBackActivity<V extends RxBaseView, P extends RxBasePresenter<V>> extends DkBaseActivity<V,P> {
@Override
protected void init(Bundle savedInstanceState) {
super.init(savedInstanceState);
if(isSwipeBackEnabled()) {
SwipeBackManager.onCreate(this);
SwipeBackManager.getCurrentPage(this)//get current instance
.setSwipeBackEnable(true)//on-off
.setSwipeEdge(mScreenWidth / 5)
.setSwipeEdgePercent(0.2f)
.setSwipeSensitivity(0.6f)
.setClosePercent(0.5f)
.setSwipeRelateEnable(false)
.setSwipeRelateOffset(500);
}
}
@Override
protected void onPostCreate(Bundle savedInstanceState) {
super.onPostCreate(savedInstanceState);
if(isSwipeBackEnabled()) {
SwipeBackManager.onPostCreate(this);
}
}
@Override
protected void onDestroy() {
super.onDestroy();
if(isSwipeBackEnabled()) {
SwipeBackManager.onDestroy(this);
}
}
}
1.setContentView 当Activity需要setContentView时,可以复写下面两个方法:
@Override
protected abstract int getLayoutId() {
return R.layout.activity_home_layout;
};
@Override
protected View getLayoutView() {
return new FrameLayout(this);
}
如果复写了getLayoutView方法,getLayoutId方法将失效。
2.初始化数据
@Override
protected void init(Bundle savedInstanceState) {
super.init(savedInstanceState);
//初始化,包括findViewById
}
3.Activity内创建Presenter
Activity或Fragment内需要创建Presenter需要复写createPresenter方法
@Override
protected SplashPresenter createPresenter() {
return new SplashPresenter();
}
4.加载列表 参考内部App
Activity需要滑动返回效果时,可以创建一个基类用SwipeBackManager来设置相应的滑动返回参数。
注:
1.有地图的界面不允许设置滑动返回效果;
2.支持滑动切换多个Tab的界面不允许设置滑动返回。
1.基类和Activity类似;
2.初始化提供两个方法:
1)初始化View
@Override
protected void init(Bundle savedInstanceState, View view) {
super.init(savedInstanceState, view);
AppCompatImageView imgClose = view.findViewById(R.id.id_img_close);
imgClose.setOnClickListener(v -> titleLeftClicked());
}
2)初始化数据
@Override
protected void initData() {
super.initData();
final Bundle bundle = getArguments();
mOrderId = bundle.getLong("orderId", -1);
mCarLat = bundle.getDouble("carLat", 0d);
mCarLng = bundle.getDouble("carLng", 0d);
mIsLongOrder = bundle.getBoolean("isLongOrder", false);
}
3.设置布局文件和创建Presenter和Fragment类似
4.处理back键
需要处理back键的Fragment首先需要实现IFragmentBackPressed接口,并复写其中的方法,例:
public class PhotoGalleryFragment extends Fragment implements IFragmentBackPressed {
@Override
public void onFragmentBackPressed() {
//处理返回键
}
}
然后在Fragment的宿主Activity中复写onBackPressed方法,如下:
@Override
public void onBackPressed() {
if (!FragmentBackManager.handleBackPress(this)) {
super.onBackPressed();
}
}
1.Activity申请权限
1)申请单个权限
Activity若需要申请权限,需要继承RxBasePermissionActivity,例:
public class SplashActivity extends RxBasePermissionActivityimplements RxBasePermissionActivity.OnSinglePermissionRequestCallBack {
@Override
protected void onStart() {
super.onStart();
if (dealWithPermission(android.Manifest.permission.ACCESS_FINE_LOCATION, “被拒绝后的提示”)){
//如果权限已经通过dealWithPermission方法会返回true
}
}
@Override
public void onPermissionAllowed(String permission) {//权限申请通过回调
if (permission.equals(android.Manifest.permission.ACCESS_FINE_LOCATION)) {
//定位权限被打开
}
}
@Override
public void onPermissionDenied(String permission) {//权限被拒绝回调
if( !shouldShowPermissionRationale(permission)) {
String permissionTip = "您拒绝了定位权限,将无法正常使用App,请前往\"权限管理\"界面手动允许定位权限。";
} else {
finish();
}
}
}
2)申请多个权限
申请多个权限和单个权限类似,参考内部App
2.Fragment申请权限
Fragment申请权限和Activity类似,只要继承RxBasePermissionFragment并实现OnSinglePermissionRequestCallBack或OnMultiPermissionRequestCallBack接口并实现其中的方法即可。
Activity中默认注册EventBus,Fragment中想要用EventBus,需要复写该方法:
@Override
protected boolean isBindEventBusHere() {
return true;
}
发送EventBus消息:
EventBusHelper.post(new AppUpdateEvent());
1.获取网络类型、判断网络是否可用等集合在NetworkUtils工具类中。
2.网络状态变更时获取网络连接状态
step1.让Activity实现NetChangeObserver接口,复写其中的方法
step2.在onResume和onPause方法中分别调用相应的注册方法和反注册方法:
private void registerNetReceiver() {
NetStateReceiver.registerObserver(this);
NetStateReceiver.registerNetworkStateReceiver(this);
}
private void unRegisterNetReceiver() {
NetStateReceiver.removeRegisterObserver(this);
NetStateReceiver.unRegisterNetworkStateReceiver(this);
}
1.加载普通图片
无论是加载网络图片、File中的图片还是Asset资源中的图片,均可调用
displayImg(ImageView imageView, String path)
当然也可以调用
displayImg(ImageView imageView, File file)
加载文件中的图片,调用
displayAssetImg(ImageView imageView, String assetName)
加载assets中的图片,示例:
ZImageLoader.get(imageView.getContext()).displayImg(imageView, path);
2.加载图片时需要设置相应的error和holder图
ZImageLoader loader = ZImageLoader.get(imageView.getContext());
loader.setResPlaceHolderId(R.mipmap.bg_img_load_failed);//设置加载中的占位图
loader.setResErrorId(R.mipmap.bg_img_load_failed);//设置加载错误时的占位图
loader.displayImg(imageView, path);
3.加载圆形或圆角图片
1)加载圆形图片:
ZImageLoader loader = ZImageLoader.get(imageView.getContext());
loader.setType(ZImageLoader.IMG_TYPE.CIRCLE);
loader.displayImg(imageView, path);
2)加载圆角图片
ZImageLoader loader = ZImageLoader.get(imageView.getContext());
loader.setType(ZImageLoader.IMG_TYPE.ROUND_CORNER);
loader.setRoundingRadius(20);
loader.displayImg(imageView, path);
4.加载图片支持设置回调
ZImageLoader loader = ZImageLoader.get(imageView.getContext());
loader.setType(ZImageLoader.IMG_TYPE.ROUND_CORNER);
loader.setRoundingRadius(20);
loader.displayImg(imageView, path, new OnImgLoadListener() {
@Override
public void onLoadComplete() {
//图片加载完成
}
@Override
public void onLoadFailure(Exception e) {
//图片加载失败
}
});
快速点击处理
1.用ViewClickUtils处理快速点击,间隔默认500ms,支持自定义时间,例:
@Override
public void onClick(View v) {
if (ViewClickUtils.isFastClick(v)) {//默认500ms,建议用这个
return;
}
}
@Override
public void onClick(View v) {
if(ViewClickUtils.isSpecificTimeClick(v, 600)) {//指定600ms
return;
}
}
2.也可以用FastClickAgent来处理快速点击,实际上他就是一个OnClickListener的扩展类,例:
private FastClickAgent mFastClickListener = new FastClickAgent(view -> {
final long id = view.getId();
if(id == R.id.id_item_2) {
//具体的点击事件
}
});
view.setOnClickListener(mFastClickListener);
ZCommon中提供了SQLite的帮助类来实现数据的持久化存储,支持直接以Model的方式存储和获取。
1.使用前提:用于存储和读取的Model类必须继续ZCommon中的BaseModel。
2.使用步骤:
1)调用ZDbConfigHelper类来对数据库名称和数据库版本号,以及相应的表和对应的列进行初始化,示例:
//初始化数据库名
ZDbConfigHelper.getInstance(this).setDatabaseName(TablesHelper.DATABASE_NAME);
//初始化数据库版本号
ZDbConfigHelper.getInstance(this).setDatabaseVersion(TablesHelper.DATABASE_VERSION);
//用户表
List<String> userColumns = new ArrayList<>();
//表的列名+列名对应的类型
userColumns.add(TablesHelper.USER_CSM_ID + ZDbConfigHelper.TEXT_TYPE);
userColumns.add(TablesHelper.USER_TOKEN + ZDbConfigHelper.TEXT_TYPE);
userColumns.add(TablesHelper.USER_MOBILE + ZDbConfigHelper.TEXT_TYPE);
userColumns.add(TablesHelper.USER_HEADER + ZDbConfigHelper.TEXT_TYPE);
userColumns.add(TablesHelper.USER_NICKNAME + ZDbConfigHelper.TEXT_TYPE);
userColumns.add(TablesHelper.USER_BALANCE + ZDbConfigHelper.TEXT_TYPE);
//添加表,表名+所有的列
ZDbConfigHelper.getInstance(this).addTable(TablesHelper.USER_INFO_TABLE, userColumns);
//地址搜索表
List<String> searchColumns = new ArrayList<>();
searchColumns.add(TablesHelper.USER_SEARCH_DATE + ZDbConfigHelper.LONG_TYPE);
searchColumns.add(TablesHelper.USER_SEARCH_LAT + ZDbConfigHelper.LONG_TYPE);
searchColumns.add(TablesHelper.USER_SEARCH_LON + ZDbConfigHelper.DOUBLE_TYPE);
searchColumns.add(TablesHelper.USER_SEARCH_ADDRESS + ZDbConfigHelper.TEXT_TYPE);
searchColumns.add(TablesHelper.USER_SEARCH_NAME + ZDbConfigHelper.TEXT_TYPE);
ZDbConfigHelper.getInstance(this).addTable(TablesHelper.USER_SEARCH_TABLE, searchColumns);
2)编写具体的Dao类来对数据库进行操作,示例如下:
public class SearchDao implements ISearchDao{
@Override
public long insertData(UserSearchModel userSearchModel) {
if(userSearchModel == null || TextUtils.isEmpty(userSearchModel.search_addr)
|| userSearchModel.search_lat == 0 || userSearchModel.search_lon == 0) {
Log.d(TAG, "the data to insert is invalid.");
return -1;
}
if(isDataExisted(userSearchModel)) {
return ZDbDataHelper.getInstance().update(TablesHelper.USER_SEARCH_TABLE, TablesHelper.USER_SEARCH_ADDRESS + "=?", new String[]{userSearchModel.search_addr}, userSearchModel);
} else {
return ZDbDataHelper.getInstance().insert(TablesHelper.USER_SEARCH_TABLE, userSearchModel);
}
}
}
具体用法可以参考内部App
1.下载apk文件,示例:
private void downloadApk(String url) {
File savingFile = new File(getExternalFilesDir(null) + File.separator + "rainbow.apk");
RetrofitDownloadConfig retrofitDownloadConfig = new RetrofitDownloadConfig.Builder(this)
.setSavingFile(savingFile)//设置保存的文件
.setNotification(null)//通知栏显示下载进度,没有设置为null
.setPackageName(getPackageName())//下载apk文件需要安装时建议手动设置包名,当然也可以不设置
.setAutoInstallApk(true)//是否自动安装apk
.setRetrofitDownloadAdapter(new RetrofitDownloadAdapter() {//下载进度监听
@Override
public void onDownloading(int code, String message, long fileTotalSize, long fileSizeDownloaded) {
super.onDownloading(code, message, fileTotalSize, fileSizeDownloaded);
if (fileTotalSize <= 0) return;
final int percent = (int) ((fileSizeDownloaded * 1.f / fileTotalSize) * 100);
showDownloadProgressDialog(percent);
}
@Override
public void onDownloadSuccess(int code, String message) {
//下载成功
super.onDownloadSuccess(code, message);
dismissDownloadProgressDialog();
}
@Override
public void onDownloadFailure(int code, String message) {
//下载失败
super.onDownloadFailure(code, message);
dismissDownloadProgressDialog();
T.showShort(HomeActivity.this, getStringResource(R.string.download_failure) + ":" + message);
}
@Override
public void onDownloadError(Throwable t) {
//下载出错
super.onDownloadError(t);
dismissDownloadProgressDialog();
}
}).build();
RetrofitDownloadManager retrofitDownloadManager = new RetrofitDownloadManager(retrofitDownloadConfig);
retrofitDownloadManager.downloadFile(url);
}
注意,在Android7.0以上的手机,需要提供相应的provider文件,并在AndroidManifest文件中注册,具体可以参考我们的项目。
2.下载普通文件
下载普通文件和apk类似
支持上传单文件和多文件,例:
private void uploadFiles(File[] files) {//上传单文件只需要写一个file即可
Map<String, Object> map = new HashMap<>();
map.put("app", URLHelper.IMG_APP_VALUE);
map.put("key", URLHelper.IMG_KEY_VALUE);
Map<String, Object> paramMap = new HashMap<>();
paramMap.put("json", new Gson().toJson(map).toString());//上传的参数
RetrofitUploadConfig retrofitUploadConfig = new RetrofitUploadConfig.Builder(context)
.setUploadUrl(URLHelper.IMG_SERVER).setParamsMap(paramMap).setFileKey("file")
.setDescriptionString("image for getting evidence")
.setRetrofitUploadAdapter(new RetrofitUploadAdapter<PhotoModel>() {
@Override
public void onUploadSuccess(int code, PhotoModel bean, boolean allFinished) {
//上传成功
}
@Override
public void onUploadFailure(int code, String message, boolean allFinished) {
//上传失败
}
@Override
public void onUploadError(Throwable t, boolean allFinished) {
//上传发生错误
}
}).build();
List<File> list = new ArrayList<>();
for (File file : files) {
if (null == file) {
continue;
}
list.add(file);
}
RetrofitUploadManager retrofitUploadManager = new RetrofitUploadManager
(retrofitUploadConfig);
retrofitUploadManager.uploadFiles(list);
}
1.拍照
startActivityForResult(CameraAndPictureActivity.newIntent(this, CameraAndPictureActivity.TYPE_TAKING_PHOTO, "hello"), 1);
2.拍照并裁剪
startActivityForResult(CameraAndPictureActivity.newIntent(this, CameraAndPictureActivity.TYPE_TAKING_PHOTO_AND_CROP, "hello"), 1);
3.选择图片
startActivityForResult(CameraAndPictureActivity.newIntent(this, CameraAndPictureActivity.TYPE_CHOOSING_IMG, "hello"), 1);
4.选择图片并裁剪
startActivityForResult(CameraAndPictureActivity.newIntent(this, CameraAndPictureActivity.TYPE_CHOOSING_IMG_AND_CROP, "hello"), 1);
5.接收数据
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if(data == null) {
return;
}
if(requestCode == 1) {
final String path = data.getStringExtra("path");
Bitmap bitmap = BitmapFactory.decodeFile(path);
img.setImageBitmap(bitmap);
}
}
注意:
1.需要在AndroidManifest中注册CameraAndPictureActivity,并设置为透明主题;
2.CameraAndPictureActivity内已经对6.0权限作了适配,但推荐在外部申请好相应的权限之后在调用;
3.拍摄单个图片可以不传图片名,但拍摄多张图片时必须传入图片名以作区分;
4.对于Android7.0需要提供相应的provider文件,具体参考Simple或内部App。
暂时只提供ACache这个类作轻量级的缓存,后续会引入接口缓存的方案。
暂时忽略
ZCommon下面提供了许多有用的Utils,比如跟Log相关的L类,跟toast相关的T类等等,待进一步完善。