android專案開發遇到的問題以及處理結果
阿新 • • 發佈:2019-01-11
新專案經過兩個月的開發,遇到了不少問題,很多都是基礎相關. 還有一些是自己之前沒有接觸到的內容,現在趁著有時間做一點整理記錄.
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);
}
});
}