欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

實現(xiàn)一個Android鎖屏App功能的難點總結(jié)

 更新時間:2016年11月11日 10:54:53   作者:李爭獻  
這篇文章主要介紹了實現(xiàn)一個Android鎖屏App功能的難點總結(jié),可以有效的解決鎖屏開發(fā)的問題,有需要的可以參考一下。

自定義一個漂亮實用的鎖屏app,如果能贏得用戶的認(rèn)可,替換系統(tǒng)自帶的鎖屏,絕對是一個不小的日活入口。這段時間正好總結(jié)一下最近調(diào)研的Android平臺的鎖屏app開發(fā)中的難點。

一、前言

鎖屏的大概實現(xiàn)原理都很簡單。監(jiān)聽系統(tǒng)的亮屏廣播,在亮屏的時候展示自己的鎖屏界面,用戶在鎖屏界面上進行一系列的動作才能解鎖。有的手機啟動鎖屏界面的過程會很卡,所以會明顯看到亮屏之后鎖屏界面的啟動有延時,因此也可以選擇監(jiān)聽系統(tǒng)滅屏的廣播,屏幕關(guān)掉的時候就將鎖屏界面準(zhǔn)備好,直接亮屏展示(滅屏后你的app會比較容易被殺死,這點要注意做?;睿?/p>

還需要注意,亮屏和滅屏廣播,SCREEN_ON/SCREEN_OFF都是只能動態(tài)監(jiān)聽的,所以要另開一個Service來注冊,這個Service的自啟動和保活也要做好。

基本的實現(xiàn)細節(jié)就不多講了,這篇文章只會講遇到的幾個難點。

二、鎖屏實現(xiàn)中的難點

1.屏蔽Home鍵

既然是鎖屏界面,當(dāng)然只能通過界面上的一些滑動或者輸入動作來解開鎖屏,不能簡單的直接被Home鍵一按,就解開了。從4.0開始,Home直接在framework層就被系統(tǒng)響應(yīng)到,強退到桌面,第三方應(yīng)用里已經(jīng)無法再通過Activity.onKeyDown方法來監(jiān)聽和攔截Home鍵,盡管還象征性的保留了Home鍵的KeyCode來向前兼容,但是Home鍵按下去,并不會回調(diào)這個方法。

除了onKeyDown,有沒有其他辦法監(jiān)聽Home鍵,有的。前臺App退到后臺會有廣播ACTION_CLOSE_SYSTEM_DIALOGS,收到廣播攜帶的intent之后,解析里面的"reason"參數(shù),就可以知道退出原因是什么了。home鍵按下后,reason是"homekey",最近任務(wù)鍵按下后,reason是"recentapps"。

這當(dāng)然不是最終方案,因為有些三星ROM里并不會有這個廣播。而且廣播的意思只是通知你一下,人家framework層已經(jīng)把你的應(yīng)用退回桌面了,你能監(jiān)聽home鍵,但沒有辦法攔截home鍵。也許想到了可以監(jiān)聽到home鍵的時候,馬上把自己的Activity又重新打開展示,我試了一下,home鍵按下后startActivity會有延時3秒左右,這應(yīng)該是Google早就想到了我們會這么干,做了這么一個延時方案。

直接攔截行不通了,想想別的路子。按Home鍵是讓系統(tǒng)退回到Launcher(即桌面啟動器),那么如果我們的鎖屏Activity本身就是Launcher的話,那按Home鍵不就等于回到我們的鎖屏Activity,也就可以阻止它把鎖屏Activity關(guān)掉了。

怎么把自己的Activity聲明為Launcher,在Activity中添加intent-filter:

<intent-filter>
 <action android:name="android.intent.action.MAIN" />
 <category android:name="android.intent.category.HOME" />
 <category android:name="android.intent.category.DEFAULT" />
</intent-filter>

這樣,新安裝的app會是一個能夠作為launcher的app,所以首次按Home鍵的時候,就會有彈窗提示你選擇要進入哪個launcher,選擇我們自己的Activity,這樣home鍵就被我們接管了。

不過這樣有一個很明顯的問題,如果不在我們的鎖屏界面按Home鍵,同樣會進入到鎖屏Activity。當(dāng)然,解決的方式也簡單,當(dāng)我們按Home時進入鎖屏Activity的onCreate里做一個判斷,如果前一個前臺Activity是鎖屏Activity,那就不用對Home鍵處理,如果不是鎖屏Activity,那就要關(guān)閉鎖屏Activity,跳到用戶真正的桌面啟動器去了。真正的桌面啟動器是哪一個,我們可以這樣來找:

List<String> pkgNamesT = new ArrayList<String>();
List<String> actNamesT = new ArrayList<String>();
List<ResolveInfo> resolveInfos = context.getPackageManager().queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);  
for (int i = 0; i < resolveInfos.size(); i++) {
 String string = resolveInfos.get(i).activityInfo.packageName;
 if (!string.equals(context.getPackageName())) {//自己的launcher不要      
  pkgNamesT.add(string);
  string = resolveInfos.get(i).activityInfo.name;
  actNamesT.add(string);
 }
}

如果實際的launcher只有一個,那直接跳轉(zhuǎn)過去就可以了:

ComponentName componentName = new ComponentName(pkgName, actName); 
Intent intent = new Intent(); 
intent.setComponent(componentName); 
context.startActivity(intent); 
((Activity) context).finish();

如果手機安裝有多個launcher(如360桌面一類的app)就會麻煩一點,需要展示一個列表讓用戶來選取用哪個launcher,這個在產(chǎn)品形態(tài)上可能會讓用戶覺得有點不解。

現(xiàn)在,如果在其他APP里按一下Home鍵,會跳到我們的鎖屏Activity然后跳轉(zhuǎn)到真正的launcher。這里可能會有Activity閃現(xiàn)一下的場景,影響用戶體驗。最優(yōu)的辦法其實是另外弄一個Activity來作為Home鍵跳轉(zhuǎn)的Activity,這個Activity設(shè)為透明的,就不會被用戶感知。如此,產(chǎn)品形態(tài)就變成了,鎖屏Activity中按Home鍵,跳轉(zhuǎn)到透明Activity,跳轉(zhuǎn)回鎖屏Activity,相當(dāng)于Home鍵無效;其他APP中按Home鍵,跳轉(zhuǎn)到透明Activity,跳轉(zhuǎn)到真正的桌面。

實現(xiàn)透明的Activity,只需要在xml中聲明

android:theme="@android:style/Theme.Translucent.NoTitleBar"

這樣的界面是透明的,實際上有占位在屏幕的頂層,所以跳轉(zhuǎn)后記得要finish掉,不然會阻斷跳轉(zhuǎn)后的界面的交互。另外,Theme.NoDisplay也能將Activity設(shè)置為不可見,而且不占位,但是筆者實現(xiàn)的時候發(fā)現(xiàn),NoDisplay的Activity無法被系統(tǒng)設(shè)置為launcher(設(shè)置后會彈窗讓你重新設(shè)置,如此反復(fù))

2.懸浮窗的實現(xiàn)方式

由于受Home鍵無法直接攔截的限制,Activity實現(xiàn)的鎖屏?xí)枰@較多的路。所以有的鎖屏應(yīng)用會使用懸浮窗來實現(xiàn),懸浮窗能夠無視Home鍵,在按下home鍵的時候不會退到后臺。所以不需要在home鍵的問題上糾結(jié)。懸浮窗統(tǒng)一由WindowManager來管理,具體的實現(xiàn)比較簡單,筆者就不贅述了,有個坑要注意,懸浮窗需要聲明權(quán)限:

   

<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />

有的手機設(shè)置里,默認(rèn)是不給應(yīng)用授權(quán)懸浮窗使用權(quán)的,所以應(yīng)用里還要考慮引導(dǎo)用戶授權(quán)懸浮窗使用。

此外,有些應(yīng)急解鎖的場景,比如來電接聽,鬧鈴處理,對于Activity實現(xiàn)的鎖屏界面,系統(tǒng)會自動把所有的前臺Activity隱藏,讓用戶直接去處理這些場景。但是懸浮窗會蓋住場景,所以遇到這些場景,懸浮窗實現(xiàn)的鎖屏界面要自己去處理這些特殊場景的自動解鎖。

3.禁用系統(tǒng)鎖屏

有了自己的鎖屏界面,還需要禁用掉系統(tǒng)的鎖屏,以免造成用戶需要解鎖兩次的局面。

首先我們需要知道用戶是否設(shè)置了鎖屏,方法如下:

對于API Level 16及以上SDK,可以使用如下方法判斷是否有鎖:

((KeyguardManager) getSystemService(Context.KEYGUARD_SERVICE)).isKeyguardSecure()

對API Level 15及以下SDK,可以使用反射來判斷:

try {
 Class<?> clazz = Class.forName("com.android.internal.widget.LockPatternUtils");
 Constructor<?> constructor = clazz.getConstructor(Context.class);
 constructor.setAccessible(true);
 Object utils = constructor.newInstance(this);
 Method method = clazz.getMethod("isSecure");
 return (Boolean) method.invoke(utils);
}catch (Exception e){
 e.printStackTrace();
}

好了,得知用戶設(shè)置了系統(tǒng)鎖屏,怎么關(guān)掉呢?有前人建議了這種方法

KeyguardManager km = (KeyguardManager) getSystemService(Context.KEYGUARD_SERVICE);
KeyguardManager.KeyguardLock keyguardLock = km.newKeyguardLock("");
keyguardLock.disableKeyguard();

需要權(quán)限

<uses-permission android:name="android.permission.DISABLE_KEYGUARD" />

但經(jīng)筆者測驗,這種方法只能禁用滑動鎖,如果用戶設(shè)置的是圖案或者PIN的鎖的話,是無法直接取消的。禁用掉密碼鎖或者圖案鎖是一個很危險的行為,基于此,Google應(yīng)該是不會把它開放給開發(fā)者的,所以現(xiàn)在的鎖屏應(yīng)用的禁用鎖的辦法,都是直接跳到系統(tǒng)鎖屏設(shè)置界面,直接引導(dǎo)用戶去手動關(guān)閉??梢酝ㄟ^如下代碼跳到用戶鎖屏設(shè)置界面:

Intent in = new Intent(Settings.ACTION_SECURITY_SETTINGS);
startActivity(in);

這個也會有些許的兼容性問題,比如,360手機的ROM并沒有把設(shè)置系統(tǒng)鎖屏的功能放在安全設(shè)置中,所以打開安全設(shè)置的界面找不到取消系統(tǒng)鎖屏的地方,這個在一眾鎖屏應(yīng)用中并沒有做兼容。

三、附加功能中的難點

上面的功能都是直接針對鎖屏本身的實現(xiàn)來說的。鎖屏應(yīng)用除了本身能夠有“鎖住屏幕”的功能外,還應(yīng)該有其他一些漂亮又實用的功能,最起碼應(yīng)該是盡量往系統(tǒng)鎖屏的樣式上靠攏并發(fā)揮,才方便被用戶接受。

1.獲取通知

新的Notification到來時應(yīng)該展示在鎖屏界面上,所以我們需要對通知欄進行監(jiān)聽。從Android 4.3(api 18)開始,Google給我們提供了一個NotificationListenerService類,第三方應(yīng)用可以更方便的獲得通知欄使用權(quán)(Notification Access),當(dāng)然,這么敏感的權(quán)限得要應(yīng)用自己聲明,同時還要引導(dǎo)用戶手動授權(quán)。如下,建立一個NotificationMonitor類繼承于NotificationListenerService,并聲明權(quán)限:

<service android:name=".NotificationMonitor"
 android:permission="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE">
 <intent-filter>
  <action android:name="android.service.notification.NotificationListenerService" />
 </intent-filter>
</service>

然后同引導(dǎo)用戶關(guān)閉系統(tǒng)鎖屏一樣,要引導(dǎo)用戶來授權(quán)通知欄使用權(quán):

startActivity(new Intent(Settings.ACTION_NOTIFICATION_LISTENER_SETTINGS));

可以通過如下代碼檢查到通知欄使用權(quán)是否已經(jīng)拿到:

private boolean isNotificationListenEnabled(){
  String pkgName = getPackageName();
  final String flat = Settings.Secure.getString(getContentResolver(),
    "enabled_notification_listeners");
  if (!TextUtils.isEmpty(flat)) {
   final String[] names = flat.split(":");
   for (int i = 0; i < names.length; i++) {
    final ComponentName cn = ComponentName.unflattenFromString(names[i]);
    if (cn != null) {
     if (TextUtils.equals(pkgName, cn.getPackageName())) {
      return true;
     }
    }
   }
  }
  return false;
 }

拿到通知欄使用權(quán)后,系統(tǒng)通知欄的變化就可以在NotificationMonitor里面監(jiān)聽到了:

public class NotificationMonitor extends NotificationListenerService {
 @Override
 public IBinder onBind(Intent intent) {
  // TODO: Return the communication channel to the service.
  return null;
 }

 @Override
 public int onStartCommand(Intent intent, int flags, int startId) {
  return super.onStartCommand(intent,flags,startId);
 }

 //新的Notification到達
 @Override
 public void onNotificationPosted(StatusBarNotification sbn) {
  super.onNotificationPosted(sbn);
 }

 //新的Notification到達,api 21新增
 @Override
 public void onNotificationPosted(StatusBarNotification sbn, RankingMap rankingMap) {
  super.onNotificationPosted(sbn, rankingMap);
 }

 //Notification被移除
 @Override
 public void onNotificationRemoved(StatusBarNotification sbn) {
  super.onNotificationRemoved(sbn);
 }

 //Notification被移除,api 21新增
 @Override
 public void onNotificationRemoved(StatusBarNotification sbn, RankingMap rankingMap) {
  super.onNotificationRemoved(sbn, rankingMap);
 }

 //Notification排序變動,api 21新增
 @Override
 public void onNotificationRankingUpdate(RankingMap rankingMap) {
  super.onNotificationRankingUpdate(rankingMap);
 }

 //Service與系統(tǒng)通知欄完成綁定時回調(diào),綁定后才能收到通知欄回調(diào),api 21新增
 @Override
 public void onListenerConnected() {
  super.onListenerConnected();
 }
}

同時,NotificationListenerService還提供了cancelNotification和cancelAllNotification方法,用于移除通知欄的通知,可以很方便的實現(xiàn)在自定義的鎖屏界面移除掉通知了。

筆者實現(xiàn)這個類的時候發(fā)現(xiàn)了一個坑,所有的代碼都是OK的,通知欄使用權(quán)也授權(quán)了,但是來通知時始終沒有回調(diào)onNotificationPosted,查問題查了很久,后來看到網(wǎng)上有人遇到同樣的問題,另外新建了一個類把代碼復(fù)制過去,就OK了,這樣看來應(yīng)該是編譯器的問題。

獲取了通知欄使用權(quán)的Service天然就能被?;?,如果被殺死,Android系統(tǒng)能夠?qū)⑺貑?。所以平時看到一些應(yīng)用要求獲取通知欄使用權(quán)時,要注意這類應(yīng)用會永久駐存后臺的。當(dāng)然,如果這個Service所在進程崩潰達到一定次數(shù)的話,Android系統(tǒng)也會灰心,在下次關(guān)機重啟前不會再將Service重啟,所以,開發(fā)中最好能將這個Service放在一個輕量獨立的進程中。

2.獲取HotSeat區(qū)快捷方式

桌面快捷方式分為兩類,Desktop區(qū),指隨著屏幕滾動的那部分,HotSeat區(qū),指放置于桌面底部不隨屏幕滾動的部分。用戶自定義的HotSeat區(qū)里的快捷方式屬于常用的應(yīng)用。如果能夠在鎖屏界面也添加這部分的快捷啟動,會是一個比較友好的功能。這個的主要問題是,怎么獲取到HotSeat區(qū)的快捷方式呢。

系統(tǒng)快捷方式存儲在數(shù)據(jù)庫文件launcher.db中的favorites表中,如圖所示:
可以看到有對應(yīng)的快捷方式的id,title和intent,這個container屬性是用來指示所在文件夾的id,然而可以看到有的container為負數(shù)。這是為什么,筆者查看了一下Android Launcher相關(guān)的源碼,找到這么兩句:

/**
* The icon is a resource identified by a package name and an integer id.
*/
public static final int CONTAINER_DESKTOP = -100;
public static final int CONTAINER_HOTSEAT = -101;

也就是說,container為-100的是Desktop區(qū)的快捷方式,container為-101的正是要找的HotSeat區(qū)的快捷方式。

現(xiàn)在知道了快捷方式的存儲方式,接下來的問題就是去找launcher.db文件的路徑。

在不同版本的Android原生api中,由于默認(rèn)使用的launcher啟動器的包名不一樣,launcher.db存儲的路徑也不一樣。

Android API 7及以下:/data/data/com.android.launcher/databases/laucher.db
Android API 8~18:/data/data/com.android.launcher2/databases/laucher.db
Android API 19及以上:/data/data/com.android.launcher3/databases/laucher.db

而對于各式各樣的第三方ROM,使用了千奇百怪的laucher包名,這個路徑就更亂了:

HTC: /data/data/com.htc.launcher/databases/laucher.db
360: /data/data/net.qihoo360.launcher/databases/laucher.db
華為: /data/data/com.huawei.launcher3/databases/laucher.db
小米: /data/data/com.miui.mihome2/databases/laucher.db
...

當(dāng)然,我們不會通過直接讀取數(shù)據(jù)庫的方式來獲取快捷方式的信息,系統(tǒng)自帶的laucher會提供ContentProvider給外部讀取。避開了對數(shù)據(jù)庫路徑做兼容的大坑,轉(zhuǎn)眼就掉進了另一個大坑,通過Provider來讀取快捷方式,所需要的權(quán)限和URI也需要做兼容。

從快捷方式的存儲可見,Android 的碎片化是多么的嚴(yán)重,所以最后筆者決定不再深入去兼容實現(xiàn),這是得不償失的行為,有興趣實現(xiàn)的可以看看這篇文章,判斷一個快捷方式是否存在是多么的難:http://www.jianshu.com/p/dc3d...

3.獲取壁紙

鎖屏界面的背景和手機桌面壁紙保持一致,不至于讓用戶覺得突兀,這里有兩種辦法實現(xiàn)獲取壁紙。

Activity Style模式

如果是Activity實現(xiàn)的鎖屏界面,可以直接設(shè)置Activity的theme就可以用壁紙做背景了。

android:theme="@android:style/Theme.Wallpaper.NoTitleBar"

WallPaperManager模式

懸浮窗模式的鎖屏界面無法用theme,那么可以通過WallPaperManager來獲取壁紙。

// 獲取壁紙管理器
WallpaperManager wallpaperManager = WallpaperManager
    .getInstance(this);
// 獲取當(dāng)前壁紙
Drawable wallpaperDrawable = wallpaperManager.getDrawable();
// 將Drawable,轉(zhuǎn)成Bitmap
Bitmap bm = ((BitmapDrawable) wallpaperDrawable).getBitmap();
mRootView.setBackgroundDrawable(new BitmapDrawable(bm));

這種方式在小米等仿iOS的一屏桌面上是OK的,但是在原生Android那樣的兩屏桌面(快捷方式與全部app分別在不同屏),快捷方式那屏獲取的壁紙是一整張大壁紙,而實際laucher顯示的是切割后的壁紙。所以以上方式會把尺寸不符的壁紙設(shè)為了背景。需要自己去根據(jù)laucher的屏數(shù)和當(dāng)前是第幾屏來進行切圖,laucher的總屏數(shù)可以在上述launcher.db里的workspaceScreens表里找到,而具體當(dāng)前在第幾屏是存在launcher app內(nèi)存實例中的,無法獲取。如果真要切的話,建議直接按照屏幕寬高切下整張壁紙的左邊一屏就好了。

以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

  • Android編程實現(xiàn)將壓縮數(shù)據(jù)庫文件拷貝到安裝目錄的方法

    Android編程實現(xiàn)將壓縮數(shù)據(jù)庫文件拷貝到安裝目錄的方法

    這篇文章主要介紹了Android編程實現(xiàn)將壓縮數(shù)據(jù)庫文件拷貝到安裝目錄的方法,涉及Android處理壓縮文件的相關(guān)技巧,具有一定參考借鑒價值,需要的朋友可以參考下
    2015-10-10
  • Android編程輸入事件流程詳解

    Android編程輸入事件流程詳解

    這篇文章主要介紹了Android編程輸入事件流程,較為詳細的分析了Android輸入事件原理、相關(guān)概念與具體操作流程,需要的朋友可以參考下
    2016-10-10
  • 詳解Android更改APP語言模式的實現(xiàn)過程

    詳解Android更改APP語言模式的實現(xiàn)過程

    本文詳細介紹如何更改Android中APP的語言模式,這個功能對于大家開發(fā)Android APP很有幫助,本文運用文字介紹和代碼示例把過程寫的很詳細,有需要的可以參考借鑒。
    2016-08-08
  • Android Web3j OOM解決詳解

    Android Web3j OOM解決詳解

    這篇文章主要介紹了Android Web3j OOM解決詳解,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2018-07-07
  • Android實現(xiàn)文件存儲并讀取的示例代碼

    Android實現(xiàn)文件存儲并讀取的示例代碼

    本篇文章主要介紹了Android實現(xiàn)文件存儲的示例代碼,文件內(nèi)容可以分別存儲在手機內(nèi)存和外存中,并且都可以讀去取出來,有興趣的可以了解一下。
    2017-01-01
  • Android手機衛(wèi)士之設(shè)置密碼對話框

    Android手機衛(wèi)士之設(shè)置密碼對話框

    這篇文章主要為大家詳細介紹了Android手機衛(wèi)士之設(shè)置密碼對話框,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2016-10-10
  • 最新評論