新项目经过两个月的开发,遇到了不少问题,很多都是基础相关. 还有一些是自己之前没有接触到的内容,现在趁着有时间做一点整理记录.
1 关于.gradle文件自己定义
--------------------------------------------------------------------------------------------------------------------------
dependencies.gradle 文件内容:
ext {
//Android SDK版本
androidBuildToolsVersion = "26.0.1"
androidMinSdkVersion = 18
androidTargetSdkVersion = 26
androidCompileSdkVersion = 26
//包名/版本信息
androidApplicationId = 'com.xxx.xxxx'
androidVersionCode = 1
androidVersionName = "1.0.0"
testInstrumentationRunner = "android.support.test.runner.AndroidJUnitRunner"
testApplicationId = "com.fernandocejas.android10.sample.presentation.test"
//依赖版本号
okhttp3_version = "3.8.1"
retrofit2_version = "2.3.0"
moshi_version = "1.5.0"
flycoroundview ="1.3.0"
appDependencies = [
//依赖全路径
okHttp3 : "com.squareup.okhttp3:okhttp:${okhttp3_version}",
retrofit2 : "com.squareup.retrofit2:retrofit:${retrofit2_version}",
moshi : "com.squareup.moshi:moshi:${moshi_version}",
flycoroundview : "com.flyco.roundview:FlycoRoundView_Lib:${FlycoRoundView_version}@aar",
]
appDebugDependencies = [
]
appTestDependencies = [
junit: "junit:junit:${junit_version}"
]
gitSha = 'git rev-parse --short HEAD'.execute([], project.rootDir).text.trim()//获取git代码库版本号
buildTime = new Date().format("yyyy-MM-dd'T'HH:mm:ss'Z'", TimeZone.getTimeZone("UTC"))
}
--------------------------------------------------------------------------------------------------------------------------
build.gradle 中配置:
apply plugin: 'com.android.application'
apply plugin: 'com.jakewharton.butterknife'
apply plugin: 'me.tatarka.retrolambda'
def globalConfiguration = rootProject.extensions.getByName("ext")
android {
compileSdkVersion globalConfiguration.androidCompileSdkVersion
buildToolsVersion globalConfiguration.androidBuildToolsVersion
signingConfigs {
debug {
def Properties props = new Properties()
File propFile = file('../buildconfig/debug_signing.properties');
if (propFile.exists()) {
props.load(propFile.newInputStream())
}
//ALIAS
storeFile file(props.getProperty('STORE_FILE'))
storePassword props.getProperty('STORE_PASSWORD')
keyAlias props.getProperty('KEY_ALIAS')
keyPassword props.getProperty('KEY_PASSWORD')
}
release {
storeFile
storePassword
keyAlias
keyPassword
}
}
packagingOptions {
exclude 'META-INF/NOTICE'
exclude 'META-INF/LICENSE'
}
lintOptions {
abortOnError false
textReport true
textOutput 'stdout'
fatal 'UnusedResources'
}
defaultConfig {
applicationId globalConfiguration.androidApplicationId
minSdkVersion globalConfiguration.androidMinSdkVersion
targetSdkVersion globalConfiguration.androidTargetSdkVersion
versionCode globalConfiguration.androidVersionCode
versionName globalConfiguration.androidVersionName
testInstrumentationRunner globalConfiguration.testInstrumentationRunner
buildConfigField 'String', 'GIT_SHA', "\"${gitSha()}\""
buildConfigField 'long', 'GIT_TIMESTAMP', "${gitTimestamp()}L"
buildConfigField "String", "BUILD_TIME", "\"${globalConfiguration.buildTime}\""
resValue "string", "app_name", "AppName"
multiDexEnabled true
resConfigs "zh"
ndk {
abiFilters 'armeabi', 'armeabi-v7a', 'armeabi-v8a'
}
}
buildTypes {
release {
debuggable false
zipAlignEnabled true //是否zip对齐
shrinkResources true //移除无用的resource文件
minifyEnabled true //是否进行混淆
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
buildConfigField "boolean", "ONLINE", "true" //线上online
buildConfigField "boolean", "OFFLINE", "false" //关闭debug
manifestPlaceholders = [
AMAP_KEY : "Key",
JPUSH_PKGNAME : "包名",
JPUSH_APPKEY : "Key",
JPUSH_CHANNEL : "developer-default",
BUGLY_APPID : "AppId",
BUGLY_APP_VERSION : globalConfiguration.androidVersionName,
BUGLY_ENABLE_DEBUG: "true",
]
signingConfig signingConfigs.release
File propFile = file('../buildconfig/release.properties');
if (propFile.exists()) {
def Properties props = new Properties()
props.load(new FileInputStream(propFile))
for (prop in props) {
buildConfigField "String", prop.key, prop.value
}
}
}
internal {
//内部网络线上版
applicationIdSuffix ".debug"
debuggable true
minifyEnabled true
zipAlignEnabled false
shrinkResources false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
buildConfigField "boolean", "ONLINE", "false" //线下offline
buildConfigField "boolean", "OFFLINE", "true" //开启debug
resValue "string", "app_name", "App名称"
manifestPlaceholders = [
AMAP_KEY : "Key",
JPUSH_PKGNAME : "包名",
JPUSH_APPKEY : "Key", //JPush上注册的包名对应的appkey.
JPUSH_CHANNEL : "developer-default", //暂时填写默认值即可.
BUGLY_APPID : "AppId",
BUGLY_APP_VERSION : globalConfiguration.androidVersionName,
BUGLY_ENABLE_DEBUG: "true",
]
signingConfig signingConfigs.debug
File propFile = file('../buildconfig/debug.properties');
if (propFile.exists()) {
def Properties props = new Properties()
props.load(new FileInputStream(propFile))
for (prop in props) {
buildConfigField "String", prop.key, prop.value
}
}
}
debug {
//内部网络测试版
applicationIdSuffix ".debug"
debuggable true
minifyEnabled true
zipAlignEnabled false
shrinkResources false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
buildConfigField "boolean", "ONLINE", "false" //线下offline
buildConfigField "boolean", "OFFLINE", "true" //开启debug
resValue "string", "app_name", "AppName"
manifestPlaceholders = [
AMAP_KEY : "KEY",
JPUSH_PKGNAME : "包名",
JPUSH_APPKEY : "KEY", //JPush上注册的包名对应的appkey.
JPUSH_CHANNEL : "developer-default", //暂时填写默认值即可.
BUGLY_APPID : "xxxx",
BUGLY_APP_VERSION : globalConfiguration.androidVersionName,
BUGLY_ENABLE_DEBUG: "true",
]
signingConfig signingConfigs.debug
File propFile = file('../buildconfig/debug.properties');
if (propFile.exists()) {
def Properties props = new Properties()
props.load(new FileInputStream(propFile))
for (prop in props) {
buildConfigField "String", prop.key, prop.value
}
}
}
}
dexOptions {
preDexLibraries = false
javaMaxHeapSize "4g"
}
packagingOptions {
exclude 'META-INF/rxjava.properties'
}
sourceSets {
main {
jniLibs.srcDirs = ['libs']
}
}
repositories {
flatDir {
dirs 'libs' // this way we can find the .aar file in libs folder 到libs文件夹下寻找.aar包
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
//从 dependencies.gradle中获取
dependencies {
def appDependencies = rootProject.ext.appDependencies
def appDebugCompile = rootProject.ext.appDebugDependencies
def appTestDependencies = rootProject.ext.appTestDependencies
compile fileTree(dir: 'libs', include: ['*.jar'])
androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
exclude group: 'com.android.support', module: 'support-annotations'
})
compile appDependencies.okHttp3
compile appDependencies.okhttpLog
compile appDependencies.retrofit2
compile appDependencies.moshi
compile appDependencies.flycoroundview
// 测试内存泄漏
// debugCompile 'com.squareup.leakcanary:leakcanary-android:1.5'
// releaseCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.5'
// testCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.5'
}
}
android.applicationVariants.all { variant ->
variant.outputs.each { output ->
def outputFile = output.outputFile
if (outputFile != null && outputFile.name.endsWith('.apk')) {
File outputDirectory = new File(outputFile.parent);
def date = new Date().format("yyyyMMddHHmmss", TimeZone.getDefault())
//获取系统时间
def fileName
fileName = "deliver_${globalConfiguration.androidVersionName}" +
// "_${variant.productFlavors[0].name}_${variant.buildType.name}" +
"_${variant.buildType.name}" +
"_versioncode" +
"_${globalConfiguration.androidVersionCode}" +
"_${date}"+".apk"
output.outputFile = new File(outputDirectory, fileName)
}
}
}
File propFile = file('../buildconfig/release_signing.properties');
if (propFile.exists()) {
def Properties props = new Properties()
props.load(new FileInputStream(propFile))
if (props.containsKey('STORE_FILE') && props.containsKey('STORE_PASSWORD') &&
props.containsKey('KEY_ALIAS') && props.containsKey('KEY_PASSWORD')) {
android.signingConfigs.release.storeFile = file(props['STORE_FILE'])
println android.signingConfigs.release.storeFile
android.signingConfigs.release.storePassword = props['STORE_PASSWORD']
android.signingConfigs.release.keyAlias = props['KEY_ALIAS']
android.signingConfigs.release.keyPassword = props['KEY_PASSWORD']
} else {
android.buildTypes.release.signingConfig = null
}
} else {
android.buildTypes.release.signingConfig = null
}
def gitSha() {
def p = 'git rev-parse --short HEAD'.execute([], project.rootDir)
p.waitFor()
if (p.exitValue() != 0) {
throw new RuntimeException(p.errorStream.text)
}
return p.text.trim()
}
def gitTimestamp() {
def p = 'git log -n 1 --format=%at'.execute([], rootDir)
p.waitFor()
if (p.exitValue() != 0) {
throw new RuntimeException(p.errorStream.text)
}
return p.text.trim()
}
dependencies {
compile files('libs/zbardecoder.jar')
compile files('libs/AMap_Search_V5.3.1_20170817.jar')
}
--------------------------------------------------------------------------------------------------------------------------
2 关于popWindow在小米六 即7.1版本出现全屏问题 在低版本能够在控件下方正常显示
适配方案:
if (Build.VERSION.SDK_INT >= 24) {
int[] location = new int[2];
ivMore.getLocationOnScreen(location);
int offsetY = location[1] + ivMore.getHeight();
if (Build.VERSION.SDK_INT == 25) {
int screenHeight = ScreenUtils.getScreenHeight(getActivity());
popupWindow.setHeight(screenHeight - offsetY);
}
popupWindow.showAtLocation(ivMore, Gravity.NO_GRAVITY, 0, offsetY);
} else {
popupWindow.showAsDropDown(ivMore);
}
--------------------------------------------------------------------------------------------------------------------------
3.关于高德地图 相关
遇到的问题是:
a 需求是实现 配送员当前位置 \市场位置\ 用户位置 三个点实现骑行路线规划 ,而高德地图提供的api
只有驾车模式能够实现三点路径规划, 驾车提供了途径点. 其它路径规划只有两点间的路径规划,而特别坑的是产品设计
出的UI是一段实线,一段虚线 这就与高德地图API相冲突,坑的是IOS竟然能够实现!!!. 方式为驾车和骑行进行拼接.
可能是第一次做,没有找到正确的方法.如果哪位大神看到了我的问题有解决办法请告知,谢谢.
b项目需要在订单列表上显示两段路径规划的实际距离,通过三个经纬度计算.
坑的是高德计算
mAMapNavi = AMapNavi.getInstance(context.getApplicationContext());
mAMapNavi.addAMapNaviListener(this);
这个计算方式是每次初始化成功后进行计算,坑爹的是他计算要放在初始化成功的回调接口当中进行.
这就出现了一个问题, 比如我需要计算一个集合数据 通过for循环进行调用. 结果会出现只计算到了最后一个数据.
原因是 当你遍历完集合可能还没初始化完成, 结果就是当在初始化成功回调接口中获取数据时 数据已经遍历到最后一个了.
//初始化成功回调
@Override
public void onInitNaviSuccess() {
//计算方法
mAMapNavi.calculateDriveRoute(mList, uList, null, strategy);
}
@Override
public void onCalculateRouteSuccess(int[] ints) {
//计算成功回调
AMapNaviPath mAMapNaviPath = mAMapNavi.getNaviPath();
if (mAMapNaviPath != null) {
//距离
Double distance = Double.valueOf(mAMapNaviPath.getAllLength());
}
}
--------------------------------------------------------------------------------------------------------------------------
4 关于极光推送 自己到官方看配置文档
public class JMessageReceiver extends BroadcastReceiver {
private static final String TAG = JMessageReceiver.class.getSimpleName();
private Context context;
private String extraData;
@Override
public void onReceive(Context context, Intent intent) {
this.context = context;
printExtras(intent);
if (SpUtil.isEmptySp(SpUtil.read(context, SpUtil.LOGIN_INFO_SP_NAME, Global.LOGIN_INFO_KEY))) {
return;
}
if (intent == null) {
return;
}
String action = intent.getAction();
Bundle bundle = intent.getExtras();
if (bundle == null) {
return;
}
extraData = bundle.getString(JPushInterface.EXTRA_EXTRA);
if (TextUtils.isEmpty(extraData)) {
return;
}
JpMessageParser jpMessageParser = JsonUtils.fromJson(extraData, JpMessageParser.class);
if (jpMessageParser == null) {
return;
}
String flag = jpMessageParser.getFlag();
String type = jpMessageParser.getType();
if (flag == null || type == null) {
return;
}
if (JPushInterface.ACTION_REGISTRATION_ID.equals(action)) {
//SDK 向 JPush Server 注册所得到的注册 ID
handleRegistrationId(bundle);
} else if (JPushInterface.ACTION_MESSAGE_RECEIVED.equals(action)) {
handleCustomMessage(bundle);
} else if (JPushInterface.ACTION_CONNECTION_CHANGE.equals(action)) {
handleConnectionChange(bundle);
} else if (JPushInterface.ACTION_NOTIFICATION_OPENED.equals(intent.getAction())) {
// 通知
if ("1001".equals(flag) && "1".equals(type)) {
JPushOrderManager.get(context).toMessageActivity(intent);
} else if ("2003".equals(flag) && "2".equals(type)) {
Global.newOrderComeIn = true;
JPushOrderManager.get(context).toHomeActivity(intent);
Intent intents = new Intent();
intents.setAction(Global.BROADCAST_ACTION_NEW_COME);
context.sendBroadcast(intents);
}
return;
}
printOrderExtras(jpMessageParser, flag, type);
}
private void printOrderExtras(JpMessageParser jpMessageParser, String flag, String type) {
// 通知
if ("3002".equals(flag) && "2".equals(type)) {
JPushOrderManager.get(context).showOverTimeDialog(jpMessageParser);
} else if ("3001".equals(flag) && "2".equals(type)) {
JPushOrderManager.get(context).showRemindDialog(jpMessageParser);
} else if ("3003".equals(flag) && "2".equals(type)) {
JPushOrderManager.get(context).newOrderRefresh();
}
}
private void handleConnectionChange(Bundle bundle) {
boolean connected = bundle.getBoolean(JPushInterface.EXTRA_CONNECTION_CHANGE, false);
if (connected) {
CcLog.e(TAG, "极光服务已连接");
} else {
CcLog.e(TAG, "极光服务已断开连接");
}
}
private void handleRegistrationId(Bundle bundle) {
String registrationId = bundle.getString(JPushInterface.EXTRA_REGISTRATION_ID);
CcLog.e(TAG, "极光推送初始化成功-[registration id:" + registrationId + "]");
}
private void handleCustomMessage(Bundle bundle) {
String title = bundle.getString(JPushInterface.EXTRA_TITLE);
String extraData = bundle.getString(JPushInterface.EXTRA_MESSAGE);
CcLog.e(TAG, "接收到自定义消息:" +
"\ntitle:" + title
+ "\ndata:" + extraData);
}
private void printExtras(Intent intent) {
String action = intent.getAction();
StringBuilder sb = new StringBuilder();
sb.append("-------JPush Message-------\n");
sb.append("action:").append(action).append("\n");
Bundle bundle = intent.getExtras();
if (bundle != null) {
Set<String> keys = bundle.keySet();
for (String key : keys) {
sb.append(key).append(":").append(bundle.get(key))
.append("\n");
}
}
sb.append("-------End-------");
CcLog.e(TAG, sb.toString());
}
}
包名配置 :android:name="${JPUSH_PKGNAME}.permission.JPUSH_MESSAGE"
--------------------------------------------------------------------------------------------------------------------------
5 二维码扫描速度慢优化处理 在此贴出代码 自行下载进行替换
下载地址 http://download.csdn.net/download/anroidyanyou/10159074
--------------------------------------------------------------------------------------------------------------------------
6.Activity 设置成Dialog样式
<style name="DialogTheme" parent="@android:style/Theme.Dialog">
<item name="android:windowIsTranslucent">true</item>
<item name="android:windowBackground">@android:color/transparent</item>
<item name="android:windowContentOverlay">@null</item>
<item name="android:windowNoTitle">true</item>
<item name="android:windowIsFloating">true</item>
<item name="android:backgroundDimEnabled">true</item>
<item name="android:windowCloseOnTouchOutside">false</item>
</style>
--------------------------------------------------------------------------------------------------------------------------
7. apk更新断点续传实现 http://download.csdn.net/download/anroidyanyou/10159841
--------------------------------------------------------------------------------------------------------------------------
8. 日期选择dialog http://download.csdn.net/download/anroidyanyou/10159867
--------------------------------------------------------------------------------------------------------------------------
9. 接口方式编程:
第一步: 创建接口 例如
public interface JPushOrder {
void showRemindDialog(JpMessageParser jpMessageParser);
void showOverTimeDialog(JpMessageParser jpMessageParser);
void newOrderRefresh();
void toHomeActivity(Intent intent);
void toMessageActivity(Intent intent);
}
第二步创建一个单例类实现接口:
public class JPushOrderManager implements JPushOrder {
@SuppressLint("StaticFieldLeak")
private static volatile JPushOrderManager sInstance;
private Context context;
public JPushOrderManager(Context context) {
this.context = context;
}
public static JPushOrderManager get(Context context) {
if (sInstance == null) {
synchronized (JPushOrderManager.class) {
if (sInstance == null) {
sInstance = new JPushOrderManager(context);
}
}
}
return sInstance;
}
@Override
public void showRemindDialog(JpMessageParser jpMessageParser) {
Bundle b = new Bundle();
String deliveryMode = jpMessageParser.getDeliveryMode();
if (null == deliveryMode) {
return;
}
Intent intent = new Intent(context, RemindService.class);
b.putInt("deliveryMode", Integer.valueOf(deliveryMode));
intent.putExtras(b);
context.startService(intent);
}
@Override
public void showOverTimeDialog(JpMessageParser jpMessageParser) {
String flowID = jpMessageParser.getFlowID();
String coExpressId = jpMessageParser.getCoExpressId();
Intent intents = new Intent(context, OverTimeDialog.class);
Bundle b = new Bundle();
b.putString("flowID", flowID);
b.putString("coExpressId", coExpressId);
intents.putExtras(b);
intents.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(intents);
}
@Override
public void newOrderRefresh() {
VibratorUtil.startAlarm(context, R.raw.psneworder);
//用户在home界面 并且在新订单页
Intent intents = new Intent();
intents.setAction(Global.BROADCAST_ACTION_NEW_ORDER);
context.sendBroadcast(intents);
}
@Override
public void toHomeActivity(Intent intent) {
if (!Global.isFront) {
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(intent.setClass(context, HomeActivity.class));
CcApplication.clear(HomeActivity.class);
} else {
Intent intents = new Intent();
intents.setAction(Global.BROADCAST_ACTION_NEW_ORDER);
context.sendBroadcast(intents);
}
}
@Override
public void toMessageActivity(Intent intent) {
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(intent.setClass(context, MessageActivity.class));
return;
}
}
最后就是调用传参啦:
JPushOrderManager.get(context).showRemindDialog(jpMessageParser);
--------------------------------------------------------------------------------------------------------------------------
10.加载状态页面LoadingView http://download.csdn.net/download/anroidyanyou/10159903
--------------------------------------------------------------------------------------------------------------------------
11. 数据实体类使用技巧
对于列表而每个Item又有一些显示隐藏的操作的时候, 可以在实体类中加入boolean值 创建get set 方法
实现界面逻辑处理.
12. 善用组件生命周期,通过全局变量实现特定情况功能代码的调用.
--------------------------------------------------------------------------------------------------------------------------
13. 验证码获取倒计时 功能
private void showTime() {
btnGetCode.setEnabled(false);
countDownTimer = new CountDownTimer(60 * 1000, 1000) {
@Override
public void onTick(long time) {
btnGetCode.setText(time / 1000 + "(s)");
time--;
}
@Override
public void onFinish() {
btnGetCode.setText("获取验证码");
btnGetCode.setEnabled(true);
time = 0;
}
}.start();
}
--------------------------------------------------------------------------------------------------------------------------
14 阿里云图片上传 相关代码下载 http://download.csdn.net/download/anroidyanyou/10161101
ArrayList<OSSAsyncTask> tasks = new ArrayList<>();
public void uploadToAliyun(final int type, TResult result){
if(null == loadingDialog)loadingDialog = new LoadingDialog(this).setCancel(true);
loadingDialog.show();
setSelectImageEnable(false);
File file = new File(result.getImage().getPath());
OSSAsyncTask task = mOssService.uploadFile(String.valueOf(type), file, new UpdateCallback() {
@Override
public void onProgress(PutObjectRequest request, long currentSize, long totalSize) {
}
@Override
public void onFailure(PutObjectRequest request, ClientException clientException, ServiceException serviceException) {
if (null != loadingDialog)loadingDialog.dismiss();
setSelectImageEnable(true);
if (clientException != null && clientException.isCanceledException()) {
return;
}
Log.e("mander", "upload img failed,path:[" + request.getUploadFilePath() + "]");
CcToast.showShort( getString(R.string.upload_fail_tips));
}
@Override
public void onSuccess(PutObjectRequest request, PutObjectResult result, String url) {
Log.e("mander", "upload img success,path:[" + request.getUploadFilePath() + "]");
Log.e("mander", "image url:" + url);
setSelectImageEnable(true);
onUploadCompleted(type, url, file.getPath());
}
});
tasks.add(task);
}
@Override
protected void onDestroy() {
for(OSSAsyncTask task : tasks){
if(null != task)task.cancel();
}
super.onDestroy();
}
--------------------------------------------------------------------------------------------------------------------------
15 Glide是否加载圆形图片
public static void loadImage(Context context, File file, ImageView imageView, boolean isRounded){
if(null == file || !file.exists() || null == imageView || null == context)return;
Glide.with(context).load(file).asBitmap().centerCrop().into(new BitmapImageViewTarget(imageView) {
@Override
protected void setResource(Bitmap resource) {
RoundedBitmapDrawable circularBitmapDrawable =
RoundedBitmapDrawableFactory.create(context.getResources(), resource);
circularBitmapDrawable.setCircular(isRounded);
imageView.setImageDrawable(circularBitmapDrawable);
}
});
}
1 关于.gradle文件自己定义
--------------------------------------------------------------------------------------------------------------------------
dependencies.gradle 文件内容:
ext {
//Android SDK版本
androidBuildToolsVersion = "26.0.1"
androidMinSdkVersion = 18
androidTargetSdkVersion = 26
androidCompileSdkVersion = 26
//包名/版本信息
androidApplicationId = 'com.xxx.xxxx'
androidVersionCode = 1
androidVersionName = "1.0.0"
testInstrumentationRunner = "android.support.test.runner.AndroidJUnitRunner"
testApplicationId = "com.fernandocejas.android10.sample.presentation.test"
//依赖版本号
okhttp3_version = "3.8.1"
retrofit2_version = "2.3.0"
moshi_version = "1.5.0"
flycoroundview ="1.3.0"
appDependencies = [
//依赖全路径
okHttp3 : "com.squareup.okhttp3:okhttp:${okhttp3_version}",
retrofit2 : "com.squareup.retrofit2:retrofit:${retrofit2_version}",
moshi : "com.squareup.moshi:moshi:${moshi_version}",
flycoroundview : "com.flyco.roundview:FlycoRoundView_Lib:${FlycoRoundView_version}@aar",
]
appDebugDependencies = [
]
appTestDependencies = [
junit: "junit:junit:${junit_version}"
]
gitSha = 'git rev-parse --short HEAD'.execute([], project.rootDir).text.trim()//获取git代码库版本号
buildTime = new Date().format("yyyy-MM-dd'T'HH:mm:ss'Z'", TimeZone.getTimeZone("UTC"))
}
--------------------------------------------------------------------------------------------------------------------------
build.gradle 中配置:
apply plugin: 'com.android.application'
apply plugin: 'com.jakewharton.butterknife'
apply plugin: 'me.tatarka.retrolambda'
def globalConfiguration = rootProject.extensions.getByName("ext")
android {
compileSdkVersion globalConfiguration.androidCompileSdkVersion
buildToolsVersion globalConfiguration.androidBuildToolsVersion
signingConfigs {
debug {
def Properties props = new Properties()
File propFile = file('../buildconfig/debug_signing.properties');
if (propFile.exists()) {
props.load(propFile.newInputStream())
}
//ALIAS
storeFile file(props.getProperty('STORE_FILE'))
storePassword props.getProperty('STORE_PASSWORD')
keyAlias props.getProperty('KEY_ALIAS')
keyPassword props.getProperty('KEY_PASSWORD')
}
release {
storeFile
storePassword
keyAlias
keyPassword
}
}
packagingOptions {
exclude 'META-INF/NOTICE'
exclude 'META-INF/LICENSE'
}
lintOptions {
abortOnError false
textReport true
textOutput 'stdout'
fatal 'UnusedResources'
}
defaultConfig {
applicationId globalConfiguration.androidApplicationId
minSdkVersion globalConfiguration.androidMinSdkVersion
targetSdkVersion globalConfiguration.androidTargetSdkVersion
versionCode globalConfiguration.androidVersionCode
versionName globalConfiguration.androidVersionName
testInstrumentationRunner globalConfiguration.testInstrumentationRunner
buildConfigField 'String', 'GIT_SHA', "\"${gitSha()}\""
buildConfigField 'long', 'GIT_TIMESTAMP', "${gitTimestamp()}L"
buildConfigField "String", "BUILD_TIME", "\"${globalConfiguration.buildTime}\""
resValue "string", "app_name", "AppName"
multiDexEnabled true
resConfigs "zh"
ndk {
abiFilters 'armeabi', 'armeabi-v7a', 'armeabi-v8a'
}
}
buildTypes {
release {
debuggable false
zipAlignEnabled true //是否zip对齐
shrinkResources true //移除无用的resource文件
minifyEnabled true //是否进行混淆
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
buildConfigField "boolean", "ONLINE", "true" //线上online
buildConfigField "boolean", "OFFLINE", "false" //关闭debug
manifestPlaceholders = [
AMAP_KEY : "Key",
JPUSH_PKGNAME : "包名",
JPUSH_APPKEY : "Key",
JPUSH_CHANNEL : "developer-default",
BUGLY_APPID : "AppId",
BUGLY_APP_VERSION : globalConfiguration.androidVersionName,
BUGLY_ENABLE_DEBUG: "true",
]
signingConfig signingConfigs.release
File propFile = file('../buildconfig/release.properties');
if (propFile.exists()) {
def Properties props = new Properties()
props.load(new FileInputStream(propFile))
for (prop in props) {
buildConfigField "String", prop.key, prop.value
}
}
}
internal {
//内部网络线上版
applicationIdSuffix ".debug"
debuggable true
minifyEnabled true
zipAlignEnabled false
shrinkResources false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
buildConfigField "boolean", "ONLINE", "false" //线下offline
buildConfigField "boolean", "OFFLINE", "true" //开启debug
resValue "string", "app_name", "App名称"
manifestPlaceholders = [
AMAP_KEY : "Key",
JPUSH_PKGNAME : "包名",
JPUSH_APPKEY : "Key", //JPush上注册的包名对应的appkey.
JPUSH_CHANNEL : "developer-default", //暂时填写默认值即可.
BUGLY_APPID : "AppId",
BUGLY_APP_VERSION : globalConfiguration.androidVersionName,
BUGLY_ENABLE_DEBUG: "true",
]
signingConfig signingConfigs.debug
File propFile = file('../buildconfig/debug.properties');
if (propFile.exists()) {
def Properties props = new Properties()
props.load(new FileInputStream(propFile))
for (prop in props) {
buildConfigField "String", prop.key, prop.value
}
}
}
debug {
//内部网络测试版
applicationIdSuffix ".debug"
debuggable true
minifyEnabled true
zipAlignEnabled false
shrinkResources false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
buildConfigField "boolean", "ONLINE", "false" //线下offline
buildConfigField "boolean", "OFFLINE", "true" //开启debug
resValue "string", "app_name", "AppName"
manifestPlaceholders = [
AMAP_KEY : "KEY",
JPUSH_PKGNAME : "包名",
JPUSH_APPKEY : "KEY", //JPush上注册的包名对应的appkey.
JPUSH_CHANNEL : "developer-default", //暂时填写默认值即可.
BUGLY_APPID : "xxxx",
BUGLY_APP_VERSION : globalConfiguration.androidVersionName,
BUGLY_ENABLE_DEBUG: "true",
]
signingConfig signingConfigs.debug
File propFile = file('../buildconfig/debug.properties');
if (propFile.exists()) {
def Properties props = new Properties()
props.load(new FileInputStream(propFile))
for (prop in props) {
buildConfigField "String", prop.key, prop.value
}
}
}
}
dexOptions {
preDexLibraries = false
javaMaxHeapSize "4g"
}
packagingOptions {
exclude 'META-INF/rxjava.properties'
}
sourceSets {
main {
jniLibs.srcDirs = ['libs']
}
}
repositories {
flatDir {
dirs 'libs' // this way we can find the .aar file in libs folder 到libs文件夹下寻找.aar包
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
//从 dependencies.gradle中获取
dependencies {
def appDependencies = rootProject.ext.appDependencies
def appDebugCompile = rootProject.ext.appDebugDependencies
def appTestDependencies = rootProject.ext.appTestDependencies
compile fileTree(dir: 'libs', include: ['*.jar'])
androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
exclude group: 'com.android.support', module: 'support-annotations'
})
compile appDependencies.okHttp3
compile appDependencies.okhttpLog
compile appDependencies.retrofit2
compile appDependencies.moshi
compile appDependencies.flycoroundview
// 测试内存泄漏
// debugCompile 'com.squareup.leakcanary:leakcanary-android:1.5'
// releaseCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.5'
// testCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.5'
}
}
android.applicationVariants.all { variant ->
variant.outputs.each { output ->
def outputFile = output.outputFile
if (outputFile != null && outputFile.name.endsWith('.apk')) {
File outputDirectory = new File(outputFile.parent);
def date = new Date().format("yyyyMMddHHmmss", TimeZone.getDefault())
//获取系统时间
def fileName
fileName = "deliver_${globalConfiguration.androidVersionName}" +
// "_${variant.productFlavors[0].name}_${variant.buildType.name}" +
"_${variant.buildType.name}" +
"_versioncode" +
"_${globalConfiguration.androidVersionCode}" +
"_${date}"+".apk"
output.outputFile = new File(outputDirectory, fileName)
}
}
}
File propFile = file('../buildconfig/release_signing.properties');
if (propFile.exists()) {
def Properties props = new Properties()
props.load(new FileInputStream(propFile))
if (props.containsKey('STORE_FILE') && props.containsKey('STORE_PASSWORD') &&
props.containsKey('KEY_ALIAS') && props.containsKey('KEY_PASSWORD')) {
android.signingConfigs.release.storeFile = file(props['STORE_FILE'])
println android.signingConfigs.release.storeFile
android.signingConfigs.release.storePassword = props['STORE_PASSWORD']
android.signingConfigs.release.keyAlias = props['KEY_ALIAS']
android.signingConfigs.release.keyPassword = props['KEY_PASSWORD']
} else {
android.buildTypes.release.signingConfig = null
}
} else {
android.buildTypes.release.signingConfig = null
}
def gitSha() {
def p = 'git rev-parse --short HEAD'.execute([], project.rootDir)
p.waitFor()
if (p.exitValue() != 0) {
throw new RuntimeException(p.errorStream.text)
}
return p.text.trim()
}
def gitTimestamp() {
def p = 'git log -n 1 --format=%at'.execute([], rootDir)
p.waitFor()
if (p.exitValue() != 0) {
throw new RuntimeException(p.errorStream.text)
}
return p.text.trim()
}
dependencies {
compile files('libs/zbardecoder.jar')
compile files('libs/AMap_Search_V5.3.1_20170817.jar')
}
--------------------------------------------------------------------------------------------------------------------------
2 关于popWindow在小米六 即7.1版本出现全屏问题 在低版本能够在控件下方正常显示
适配方案:
if (Build.VERSION.SDK_INT >= 24) {
int[] location = new int[2];
ivMore.getLocationOnScreen(location);
int offsetY = location[1] + ivMore.getHeight();
if (Build.VERSION.SDK_INT == 25) {
int screenHeight = ScreenUtils.getScreenHeight(getActivity());
popupWindow.setHeight(screenHeight - offsetY);
}
popupWindow.showAtLocation(ivMore, Gravity.NO_GRAVITY, 0, offsetY);
} else {
popupWindow.showAsDropDown(ivMore);
}
--------------------------------------------------------------------------------------------------------------------------
3.关于高德地图 相关
遇到的问题是:
a 需求是实现 配送员当前位置 \市场位置\ 用户位置 三个点实现骑行路线规划 ,而高德地图提供的api
只有驾车模式能够实现三点路径规划, 驾车提供了途径点. 其它路径规划只有两点间的路径规划,而特别坑的是产品设计
出的UI是一段实线,一段虚线 这就与高德地图API相冲突,坑的是IOS竟然能够实现!!!. 方式为驾车和骑行进行拼接.
可能是第一次做,没有找到正确的方法.如果哪位大神看到了我的问题有解决办法请告知,谢谢.
b项目需要在订单列表上显示两段路径规划的实际距离,通过三个经纬度计算.
坑的是高德计算
mAMapNavi = AMapNavi.getInstance(context.getApplicationContext());
mAMapNavi.addAMapNaviListener(this);
这个计算方式是每次初始化成功后进行计算,坑爹的是他计算要放在初始化成功的回调接口当中进行.
这就出现了一个问题, 比如我需要计算一个集合数据 通过for循环进行调用. 结果会出现只计算到了最后一个数据.
原因是 当你遍历完集合可能还没初始化完成, 结果就是当在初始化成功回调接口中获取数据时 数据已经遍历到最后一个了.
//初始化成功回调
@Override
public void onInitNaviSuccess() {
//计算方法
mAMapNavi.calculateDriveRoute(mList, uList, null, strategy);
}
@Override
public void onCalculateRouteSuccess(int[] ints) {
//计算成功回调
AMapNaviPath mAMapNaviPath = mAMapNavi.getNaviPath();
if (mAMapNaviPath != null) {
//距离
Double distance = Double.valueOf(mAMapNaviPath.getAllLength());
}
}
--------------------------------------------------------------------------------------------------------------------------
4 关于极光推送 自己到官方看配置文档
public class JMessageReceiver extends BroadcastReceiver {
private static final String TAG = JMessageReceiver.class.getSimpleName();
private Context context;
private String extraData;
@Override
public void onReceive(Context context, Intent intent) {
this.context = context;
printExtras(intent);
if (SpUtil.isEmptySp(SpUtil.read(context, SpUtil.LOGIN_INFO_SP_NAME, Global.LOGIN_INFO_KEY))) {
return;
}
if (intent == null) {
return;
}
String action = intent.getAction();
Bundle bundle = intent.getExtras();
if (bundle == null) {
return;
}
extraData = bundle.getString(JPushInterface.EXTRA_EXTRA);
if (TextUtils.isEmpty(extraData)) {
return;
}
JpMessageParser jpMessageParser = JsonUtils.fromJson(extraData, JpMessageParser.class);
if (jpMessageParser == null) {
return;
}
String flag = jpMessageParser.getFlag();
String type = jpMessageParser.getType();
if (flag == null || type == null) {
return;
}
if (JPushInterface.ACTION_REGISTRATION_ID.equals(action)) {
//SDK 向 JPush Server 注册所得到的注册 ID
handleRegistrationId(bundle);
} else if (JPushInterface.ACTION_MESSAGE_RECEIVED.equals(action)) {
handleCustomMessage(bundle);
} else if (JPushInterface.ACTION_CONNECTION_CHANGE.equals(action)) {
handleConnectionChange(bundle);
} else if (JPushInterface.ACTION_NOTIFICATION_OPENED.equals(intent.getAction())) {
// 通知
if ("1001".equals(flag) && "1".equals(type)) {
JPushOrderManager.get(context).toMessageActivity(intent);
} else if ("2003".equals(flag) && "2".equals(type)) {
Global.newOrderComeIn = true;
JPushOrderManager.get(context).toHomeActivity(intent);
Intent intents = new Intent();
intents.setAction(Global.BROADCAST_ACTION_NEW_COME);
context.sendBroadcast(intents);
}
return;
}
printOrderExtras(jpMessageParser, flag, type);
}
private void printOrderExtras(JpMessageParser jpMessageParser, String flag, String type) {
// 通知
if ("3002".equals(flag) && "2".equals(type)) {
JPushOrderManager.get(context).showOverTimeDialog(jpMessageParser);
} else if ("3001".equals(flag) && "2".equals(type)) {
JPushOrderManager.get(context).showRemindDialog(jpMessageParser);
} else if ("3003".equals(flag) && "2".equals(type)) {
JPushOrderManager.get(context).newOrderRefresh();
}
}
private void handleConnectionChange(Bundle bundle) {
boolean connected = bundle.getBoolean(JPushInterface.EXTRA_CONNECTION_CHANGE, false);
if (connected) {
CcLog.e(TAG, "极光服务已连接");
} else {
CcLog.e(TAG, "极光服务已断开连接");
}
}
private void handleRegistrationId(Bundle bundle) {
String registrationId = bundle.getString(JPushInterface.EXTRA_REGISTRATION_ID);
CcLog.e(TAG, "极光推送初始化成功-[registration id:" + registrationId + "]");
}
private void handleCustomMessage(Bundle bundle) {
String title = bundle.getString(JPushInterface.EXTRA_TITLE);
String extraData = bundle.getString(JPushInterface.EXTRA_MESSAGE);
CcLog.e(TAG, "接收到自定义消息:" +
"\ntitle:" + title
+ "\ndata:" + extraData);
}
private void printExtras(Intent intent) {
String action = intent.getAction();
StringBuilder sb = new StringBuilder();
sb.append("-------JPush Message-------\n");
sb.append("action:").append(action).append("\n");
Bundle bundle = intent.getExtras();
if (bundle != null) {
Set<String> keys = bundle.keySet();
for (String key : keys) {
sb.append(key).append(":").append(bundle.get(key))
.append("\n");
}
}
sb.append("-------End-------");
CcLog.e(TAG, sb.toString());
}
}
包名配置 :android:name="${JPUSH_PKGNAME}.permission.JPUSH_MESSAGE"
--------------------------------------------------------------------------------------------------------------------------
5 二维码扫描速度慢优化处理 在此贴出代码 自行下载进行替换
下载地址 http://download.csdn.net/download/anroidyanyou/10159074
--------------------------------------------------------------------------------------------------------------------------
6.Activity 设置成Dialog样式
<style name="DialogTheme" parent="@android:style/Theme.Dialog">
<item name="android:windowIsTranslucent">true</item>
<item name="android:windowBackground">@android:color/transparent</item>
<item name="android:windowContentOverlay">@null</item>
<item name="android:windowNoTitle">true</item>
<item name="android:windowIsFloating">true</item>
<item name="android:backgroundDimEnabled">true</item>
<item name="android:windowCloseOnTouchOutside">false</item>
</style>
--------------------------------------------------------------------------------------------------------------------------
7. apk更新断点续传实现 http://download.csdn.net/download/anroidyanyou/10159841
--------------------------------------------------------------------------------------------------------------------------
8. 日期选择dialog http://download.csdn.net/download/anroidyanyou/10159867
--------------------------------------------------------------------------------------------------------------------------
9. 接口方式编程:
第一步: 创建接口 例如
public interface JPushOrder {
void showRemindDialog(JpMessageParser jpMessageParser);
void showOverTimeDialog(JpMessageParser jpMessageParser);
void newOrderRefresh();
void toHomeActivity(Intent intent);
void toMessageActivity(Intent intent);
}
第二步创建一个单例类实现接口:
public class JPushOrderManager implements JPushOrder {
@SuppressLint("StaticFieldLeak")
private static volatile JPushOrderManager sInstance;
private Context context;
public JPushOrderManager(Context context) {
this.context = context;
}
public static JPushOrderManager get(Context context) {
if (sInstance == null) {
synchronized (JPushOrderManager.class) {
if (sInstance == null) {
sInstance = new JPushOrderManager(context);
}
}
}
return sInstance;
}
@Override
public void showRemindDialog(JpMessageParser jpMessageParser) {
Bundle b = new Bundle();
String deliveryMode = jpMessageParser.getDeliveryMode();
if (null == deliveryMode) {
return;
}
Intent intent = new Intent(context, RemindService.class);
b.putInt("deliveryMode", Integer.valueOf(deliveryMode));
intent.putExtras(b);
context.startService(intent);
}
@Override
public void showOverTimeDialog(JpMessageParser jpMessageParser) {
String flowID = jpMessageParser.getFlowID();
String coExpressId = jpMessageParser.getCoExpressId();
Intent intents = new Intent(context, OverTimeDialog.class);
Bundle b = new Bundle();
b.putString("flowID", flowID);
b.putString("coExpressId", coExpressId);
intents.putExtras(b);
intents.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(intents);
}
@Override
public void newOrderRefresh() {
VibratorUtil.startAlarm(context, R.raw.psneworder);
//用户在home界面 并且在新订单页
Intent intents = new Intent();
intents.setAction(Global.BROADCAST_ACTION_NEW_ORDER);
context.sendBroadcast(intents);
}
@Override
public void toHomeActivity(Intent intent) {
if (!Global.isFront) {
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(intent.setClass(context, HomeActivity.class));
CcApplication.clear(HomeActivity.class);
} else {
Intent intents = new Intent();
intents.setAction(Global.BROADCAST_ACTION_NEW_ORDER);
context.sendBroadcast(intents);
}
}
@Override
public void toMessageActivity(Intent intent) {
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(intent.setClass(context, MessageActivity.class));
return;
}
}
最后就是调用传参啦:
JPushOrderManager.get(context).showRemindDialog(jpMessageParser);
--------------------------------------------------------------------------------------------------------------------------
10.加载状态页面LoadingView http://download.csdn.net/download/anroidyanyou/10159903
--------------------------------------------------------------------------------------------------------------------------
11. 数据实体类使用技巧
对于列表而每个Item又有一些显示隐藏的操作的时候, 可以在实体类中加入boolean值 创建get set 方法
实现界面逻辑处理.
12. 善用组件生命周期,通过全局变量实现特定情况功能代码的调用.
--------------------------------------------------------------------------------------------------------------------------
13. 验证码获取倒计时 功能
private void showTime() {
btnGetCode.setEnabled(false);
countDownTimer = new CountDownTimer(60 * 1000, 1000) {
@Override
public void onTick(long time) {
btnGetCode.setText(time / 1000 + "(s)");
time--;
}
@Override
public void onFinish() {
btnGetCode.setText("获取验证码");
btnGetCode.setEnabled(true);
time = 0;
}
}.start();
}
--------------------------------------------------------------------------------------------------------------------------
14 阿里云图片上传 相关代码下载 http://download.csdn.net/download/anroidyanyou/10161101
ArrayList<OSSAsyncTask> tasks = new ArrayList<>();
public void uploadToAliyun(final int type, TResult result){
if(null == loadingDialog)loadingDialog = new LoadingDialog(this).setCancel(true);
loadingDialog.show();
setSelectImageEnable(false);
File file = new File(result.getImage().getPath());
OSSAsyncTask task = mOssService.uploadFile(String.valueOf(type), file, new UpdateCallback() {
@Override
public void onProgress(PutObjectRequest request, long currentSize, long totalSize) {
}
@Override
public void onFailure(PutObjectRequest request, ClientException clientException, ServiceException serviceException) {
if (null != loadingDialog)loadingDialog.dismiss();
setSelectImageEnable(true);
if (clientException != null && clientException.isCanceledException()) {
return;
}
Log.e("mander", "upload img failed,path:[" + request.getUploadFilePath() + "]");
CcToast.showShort( getString(R.string.upload_fail_tips));
}
@Override
public void onSuccess(PutObjectRequest request, PutObjectResult result, String url) {
Log.e("mander", "upload img success,path:[" + request.getUploadFilePath() + "]");
Log.e("mander", "image url:" + url);
setSelectImageEnable(true);
onUploadCompleted(type, url, file.getPath());
}
});
tasks.add(task);
}
@Override
protected void onDestroy() {
for(OSSAsyncTask task : tasks){
if(null != task)task.cancel();
}
super.onDestroy();
}
--------------------------------------------------------------------------------------------------------------------------
15 Glide是否加载圆形图片
public static void loadImage(Context context, File file, ImageView imageView, boolean isRounded){
if(null == file || !file.exists() || null == imageView || null == context)return;
Glide.with(context).load(file).asBitmap().centerCrop().into(new BitmapImageViewTarget(imageView) {
@Override
protected void setResource(Bitmap resource) {
RoundedBitmapDrawable circularBitmapDrawable =
RoundedBitmapDrawableFactory.create(context.getResources(), resource);
circularBitmapDrawable.setCircular(isRounded);
imageView.setImageDrawable(circularBitmapDrawable);
}
});
}