本文的合集已經(jīng)編著成書,高級(jí)Android開發(fā)強(qiáng)化實(shí)戰(zhàn)踪央,歡迎各位讀友的建議和指導(dǎo)臀玄。在京東即可購(gòu)買:https://item.jd.com/12385680.html
本文記錄一些有趣的知識(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_ON
或Intent.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!