1. 程式人生 > >android專案開發遇到的問題以及處理結果

android專案開發遇到的問題以及處理結果

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