Android開發(fā)Tips(8)

歡迎Follow我的GitHub, 關(guān)注我的簡(jiǎn)書. 其余參考Android目錄.

Tips

本文的合集已經(jīng)編著成書,高級(jí)Android開發(fā)強(qiáng)化實(shí)戰(zhàn)踪央,歡迎各位讀友的建議和指導(dǎo)臀玄。在京東即可購(gòu)買:https://item.jd.com/12385680.html

Android

本文記錄一些有趣的知識(shí)點(diǎn), 再加一些有用的代碼段, 精心準(zhǔn)備, 來(lái)源于實(shí)踐. 技術(shù)來(lái)源于積累, 一點(diǎn)一滴, 掌握魔法的奧秘.

其余: 第一篇, 第二篇, 第三篇, 第四篇, 第五篇, 第六篇, 第七篇.


Android 5.0 Status Bar 圖標(biāo)顯示白色方塊

Android 5.0 是樣式改版, 引入Material Design, 統(tǒng)一風(fēng)格, 其中就包含狀態(tài)欄(Status Bar)的風(fēng)格統(tǒng)一. 所有圖標(biāo), 均以相同顏色覆蓋, 如果是方形圖標(biāo), 就會(huì)顯示一個(gè)均色方塊, 需要重新設(shè)計(jì), 非重點(diǎn)部分使用透明處理.

代碼位置不變, 仍是setSmallIcon方法.

builder.setSmallIcon(R.drawable.notification_icon);

Gradle Daemon 異常

AS 2.0 + Gradle 2.10, 報(bào)錯(cuò):

To run dex in process, the Gradle daemon needs a larger heap.

原因: 在Gradle 2.10中, Dex運(yùn)行在Gradle構(gòu)建進(jìn)程而不是分離進(jìn)程.

Dex runs inside gradle build process as opposed to a separate process.

解決方案:

增大守護(hù)進(jìn)程的內(nèi)存, 在gradle.properties中設(shè)置

org.gradle.jvmargs=-Xmx4g -XX:MaxPermSize=512m

JVM最大使用4G內(nèi)存, 每次增大512M.

在build.gradle中設(shè)置

android {
    dexOptions {
        preDexLibraries true
        javaMaxHeapSize "3g"
        incremental true
        dexInProcess = true
    }
}

允許預(yù)增Dex, Dex的大小是3G, 允許Dex在進(jìn)程中運(yùn)行.

直接屏蔽.

android {
     dexOptions {
        dexInProcess = false
    }
}

Dex不在進(jìn)程中運(yùn)行.


毫秒轉(zhuǎn)換具體時(shí)間

時(shí)間(小時(shí):分鐘:秒), 使用TimeUnit.MILLISECONDS工具轉(zhuǎn)換.

/**
 * 轉(zhuǎn)換毫秒到具體時(shí)間, 小時(shí):分鐘:秒
 * 參考: http://stackoverflow.com/questions/625433/how-to-convert-milliseconds-to-x-mins-x-seconds-in-java
 *
 * @param millis 毫秒
 * @return 時(shí)間字符串
 */
public static String convertMillis2Time(long millis) {
    return String.format("%02d:%02d:%02d",
            TimeUnit.MILLISECONDS.toHours(millis),
            TimeUnit.MILLISECONDS.toMinutes(millis) - TimeUnit.HOURS.toMinutes(TimeUnit.MICROSECONDS.toHours(millis)),
            TimeUnit.MILLISECONDS.toSeconds(millis) - TimeUnit.HOURS.toSeconds(TimeUnit.MICROSECONDS.toHours(millis)) - TimeUnit.MINUTES.toSeconds(TimeUnit.MILLISECONDS.toMinutes(millis))
    );
}

判斷服務(wù)是否啟動(dòng)

服務(wù)檢測(cè)比較具有迷惑性, 不能直接通過(guò)類名檢查, 一定要判斷UID是否相同, 否則多個(gè)應(yīng)用使用相同的服務(wù), 會(huì)出現(xiàn)檢查錯(cuò)誤, 有一個(gè)啟動(dòng)就會(huì)成功. 添加UID檢查, 才可以正確使用.

用于在重啟動(dòng)服務(wù)時(shí), 進(jìn)行服務(wù)保活, 防止重復(fù)啟動(dòng).

/**
 * 判斷服務(wù)是否啟動(dòng), 注意只要名稱相同, 會(huì)檢測(cè)任何服務(wù).
 *
 * @param context      上下文
 * @param serviceClass 服務(wù)類
 * @return 是否啟動(dòng)服務(wù)
 */
public static boolean isServiceRunning(Context context, Class<?> serviceClass) {
    if (context == null) {
        return false;
    }

    Context appContext = context.getApplicationContext();
    ActivityManager manager = (ActivityManager) appContext.getSystemService(Context.ACTIVITY_SERVICE);

    if (manager != null) {
        List<ActivityManager.RunningServiceInfo> infos = manager.getRunningServices(Integer.MAX_VALUE);
        if (infos != null && !infos.isEmpty()) {
            for (ActivityManager.RunningServiceInfo service : infos) {
                // 添加Uid驗(yàn)證, 防止服務(wù)重名, 當(dāng)前服務(wù)無(wú)法啟動(dòng)
                if (getUid(context) == service.uid) {
                    if (serviceClass.getName().equals(service.service.getClassName())) {
                        return true;
                    }
                }
            }
        }
    }
    return false;
}

/**
 * 獲取應(yīng)用的Uid, 用于驗(yàn)證服務(wù)是否啟動(dòng)
 *
 * @param context 上下文
 * @return uid
 */
public static int getUid(Context context) {
    if (context == null) {
        return -1;
    }

    int pid = android.os.Process.myPid();
    ActivityManager manager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);

    if (manager != null) {
        List<ActivityManager.RunningAppProcessInfo> infos = manager.getRunningAppProcesses();
        if (infos != null && !infos.isEmpty()) {
            for (ActivityManager.RunningAppProcessInfo processInfo : infos) {
                if (processInfo.pid == pid) {
                    return processInfo.uid;
                }
            }
        }
    }
    return -1;
}

午夜定時(shí)器

午夜定時(shí)器, 午夜12點(diǎn)發(fā)送廣播. 用于計(jì)步器的日期更新, 或者其他與本地日期有關(guān)的功能.

/**
 * 設(shè)置午夜定時(shí)器, 午夜12點(diǎn)發(fā)送廣播, MIDNIGHT_ALARM_FILTER.
 * 實(shí)際測(cè)試可能會(huì)有一分鐘左右的偏差.
 *
 * @param context 上下文
 */
public static void setMidnightAlarm(Context context) {
    Context appContext = context.getApplicationContext();
    Intent intent = new Intent(IntentConsts.MIDNIGHT_ALARM_FILTER);

    PendingIntent pi = PendingIntent.getBroadcast(appContext, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT);
    AlarmManager am = (AlarmManager) appContext.getSystemService(Context.ALARM_SERVICE);

    // 午夜12點(diǎn)的標(biāo)準(zhǔn)計(jì)時(shí), 來(lái)源于SO, 實(shí)際測(cè)試可能會(huì)有一分鐘左右的偏差.
    Calendar calendar = Calendar.getInstance();
    calendar.setTimeInMillis(System.currentTimeMillis());
    calendar.set(Calendar.SECOND, 0);
    calendar.set(Calendar.MINUTE, 0);
    calendar.set(Calendar.HOUR, 0);
    calendar.set(Calendar.AM_PM, Calendar.AM);
    calendar.add(Calendar.DAY_OF_MONTH, 1);

    // 顯示剩余時(shí)間
    long now = Calendar.getInstance().getTimeInMillis();
    showLogs("剩余時(shí)間(秒): " + ((calendar.getTimeInMillis() - now) / 1000));

    // 設(shè)置之前先取消前一個(gè)PendingIntent
    am.cancel(pi);

    // 設(shè)置每一天的計(jì)時(shí)器
    am.setRepeating(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), AlarmManager.INTERVAL_DAY, pi);
}

非重復(fù)數(shù)

用于多個(gè)通知的ID, 或者其他非重復(fù)ID.

// 獲取通知的ID, 防止重復(fù), 可以用于通知的ID
public static class NotificationID {
    // 隨機(jī)生成一個(gè)數(shù)
    private final static AtomicInteger c = new AtomicInteger(0);

    // 獲取一個(gè)不重復(fù)的數(shù), 從0開始
    public static int getID() {
        return c.incrementAndGet() 
    }
}

檢測(cè)屏幕是否開啟

除了此方法, 也可以通過(guò)監(jiān)聽系統(tǒng)廣播, 判斷屏幕的亮滅, 即Intent.ACTION_SCREEN_ONIntent.ACTION_SCREEN_OFF.

/**
 * 檢測(cè)屏幕是否開啟
 *
 * @param context 上下文
 * @return 是否屏幕開啟
 */
public static boolean isScreenOn(Context context) {
    Context appContext = context.getApplicationContext();
    PowerManager pm = (PowerManager) appContext.getSystemService(Context.POWER_SERVICE);

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT_WATCH) {
        return pm.isInteractive();
    } else {
        // noinspection all
        return pm.isScreenOn();
    }
}

判斷計(jì)步傳感器是否可用

判斷的時(shí)候需要注意, 畢竟與硬件相關(guān), 隨時(shí)可能出現(xiàn)異常, 而且每個(gè)廠商都不同, 需要綜合考慮.

/**
 * 檢測(cè)計(jì)步傳感器是否可以使用
 *
 * @param context 上下文
 * @return 是否可用計(jì)步傳感器
 */
public static boolean hasStepSensor(Context context) {
    if (context == null) {
        return false;
    }

    Context appContext = context.getApplicationContext();
    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {
        return false;
    } else {
        boolean hasSensor = false;
        Sensor sensor = null;
        try {
            hasSensor = appContext.getPackageManager().hasSystemFeature("android.hardware.sensor.stepcounter");
            SensorManager sm = (SensorManager) appContext.getSystemService(Context.SENSOR_SERVICE);
            sensor = sm.getDefaultSensor(Sensor.TYPE_STEP_COUNTER);
        } catch (Exception e) {
            e.printStackTrace();
        }

        return hasSensor && sensor != null;
    }
}

獲取進(jìn)程名稱

獲取當(dāng)前進(jìn)程名稱, 用于進(jìn)程背澹活.

/**
 * 獲取進(jìn)程名稱
 *
 * @param context 上下文
 * @return 進(jìn)程名稱
 */
public static String getProcessName(Context context) {
    int pid = android.os.Process.myPid();
    ActivityManager manager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
    List<ActivityManager.RunningAppProcessInfo> infos = manager.getRunningAppProcesses();
    if (infos != null) {
        for (ActivityManager.RunningAppProcessInfo processInfo : infos) {
            if (processInfo.pid == pid) {
                return processInfo.processName;
            }
        }
    }
    return null;
}

檢測(cè)應(yīng)用是否運(yùn)行

判斷應(yīng)用是否存活, 通過(guò)唯一包名判斷.

/**
 * 檢測(cè)應(yīng)用是否運(yùn)行
 *
 * @param packageName 包名
 * @param context     上下文
 * @return 是否存在
 */
public static boolean isAppAlive(String packageName, Context context) {
    if (context == null || TextUtils.isEmpty(packageName)) {
        return false;
    }

    ActivityManager activityManager = (ActivityManager)
            context.getSystemService(Context.ACTIVITY_SERVICE);

    if (activityManager != null) {
        List<ActivityManager.RunningAppProcessInfo> procInfos = activityManager.getRunningAppProcesses();
        if (procInfos != null && !procInfos.isEmpty()) {
            for (int i = 0; i < procInfos.size(); i++) {
                if (procInfos.get(i).processName.equals(packageName)) {
                    return true;
                }
            }
        }
    }

    return false;
}

轉(zhuǎn)換16進(jìn)制字符串

含有字母的字符串, 轉(zhuǎn)換16進(jìn)制的數(shù)字字符串, 用于鑒別判斷.

/**
 * String轉(zhuǎn)換40位16進(jìn)制.
 *
 * @param arg 字母字符串
 * @return 16進(jìn)制數(shù)字字符串
 */
private String toHex(String arg) {
    if (TextUtils.isEmpty(arg)) {
        return null;
    }
    return String.format("%040x", new BigInteger(1, arg.getBytes(/*YOUR_CHARSET?*/)));
}

OK, that's all! Enjoy it!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末健无,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子液斜,更是在濱河造成了極大的恐慌睬涧,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,386評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件旗唁,死亡現(xiàn)場(chǎng)離奇詭異畦浓,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)检疫,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,142評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門讶请,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人,你說(shuō)我怎么就攤上這事夺溢÷畚。” “怎么了?”我有些...
    開封第一講書人閱讀 164,704評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵风响,是天一觀的道長(zhǎng)嘉汰。 經(jīng)常有香客問(wèn)我,道長(zhǎng)状勤,這世上最難降的妖魔是什么鞋怀? 我笑而不...
    開封第一講書人閱讀 58,702評(píng)論 1 294
  • 正文 為了忘掉前任,我火速辦了婚禮持搜,結(jié)果婚禮上密似,老公的妹妹穿的比我還像新娘。我一直安慰自己葫盼,他們只是感情好残腌,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,716評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著贫导,像睡著了一般抛猫。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上孩灯,一...
    開封第一講書人閱讀 51,573評(píng)論 1 305
  • 那天闺金,我揣著相機(jī)與錄音,去河邊找鬼钱反。 笑死,一個(gè)胖子當(dāng)著我的面吹牛匣距,可吹牛的內(nèi)容都是我干的面哥。 我是一名探鬼主播,決...
    沈念sama閱讀 40,314評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼毅待,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼尚卫!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起尸红,我...
    開封第一講書人閱讀 39,230評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤吱涉,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后外里,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體怎爵,經(jīng)...
    沈念sama閱讀 45,680評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,873評(píng)論 3 336
  • 正文 我和宋清朗相戀三年盅蝗,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了鳖链。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,991評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡墩莫,死狀恐怖芙委,靈堂內(nèi)的尸體忽然破棺而出锹杈,到底是詐尸還是另有隱情蝶涩,我是刑警寧澤,帶...
    沈念sama閱讀 35,706評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站影钉,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏芜飘。R本人自食惡果不足惜泪蔫,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,329評(píng)論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望慨菱。 院中可真熱鬧焰络,春花似錦、人聲如沸符喝。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,910評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)协饲。三九已至畏腕,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間茉稠,已是汗流浹背描馅。 一陣腳步聲響...
    開封第一講書人閱讀 33,038評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留而线,地道東北人铭污。 一個(gè)月前我還...
    沈念sama閱讀 48,158評(píng)論 3 370
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像膀篮,于是被迫代替她去往敵國(guó)和親嘹狞。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,941評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,139評(píng)論 25 707
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理誓竿,服務(wù)發(fā)現(xiàn)磅网,斷路器,智...
    卡卡羅2017閱讀 134,657評(píng)論 18 139
  • 第十章:Android的消息機(jī)制 Handler是Android消息機(jī)制的上層接口筷屡,開發(fā)人員只需要與它交互即可涧偷,底...
    loneyzhou閱讀 680評(píng)論 0 1
  • 這本書看了很久,因?yàn)楹茈s很瑣碎很輕松毙死。你可以隨時(shí)燎潮,隨地,隨意的從任何一頁(yè)開始閱讀扼倘,不給你任何壓力跟啤。此刻,我聽著蘇打...
    Vividww閱讀 4,361評(píng)論 0 1
  • 1. 夜深人靜的時(shí)刻竿奏,被雷聲驚醒,熙遲從床上坐了起來(lái)腥放,漆黑的夜色里泛啸,聽到窗外的雨聲。 那樣大的雨秃症,一如多年前候址,在沈...
    原墅閱讀 300評(píng)論 0 0