Android-锁屏的应用
来源:互联网 发布:js滑动到底部加载更多 编辑:程序博客网 时间:2024/05/16 05:33
锁屏效果图:
记录一个锁屏应用的实现
二月底回到公司实习,开始做锁屏应用的小项目,耗时大概一周多,期间遇到挺多问题的。分享出来,希望同样在迷茫的人能找到解决方法。
整个小项目分为:锁屏界面的实现和锁屏的应用,锁屏界面的实现其实不难,可以看代码,这里主要介绍如何应用锁屏, 因为自己在实现的过程中,也是这一部分遇到的问题最多。
锁屏的应用难点
(1) 锁住Home键,这是比较有难点的地方
预期效果: 锁屏界面出现的时候,除非成功解锁,否则无论如何点击Home键,锁屏界面都不会消失
无效方案: 4.0之前,可以通过以下方法屏蔽Home键,但是4.0之后,Google官方为了安全性考虑,把Home键的响应放到的框架层(Framework)。因此,按下Home键,会直接跳到主页。
A.重写以下两个方法
@Overridepublic boolean onKeyDown(int keyCode, KeyEvent event) { if(KeyEvent.KEYCODE_HOME==keyCode){ return true; } return super.onKeyDown(keyCode, event);}@Overridepublic void onAttachedToWindow(){ this.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD); super.onAttachedToWindow();}
B.加入对应权限
<uses-permission android:name="android.permission.DISABLE_KEYGUARD"/>
可用方案: 设置一个activity作为Home页,方法如下:
既然无法屏蔽Home键,那我们只能顺着它的意图跳到主页,不过,可以设置我们自己的“主页”。这里的主页不是系统默认的主页,我们可以在工程里增加一个
Activity,作为用来代替系统主页的“主页”,当按下Home键,自然会跳到我们定义的“主页”,这时候,我们可以在我们的“主页” 上操作了,可以在这里重启维持我们的锁屏界面。
但是,由于Activity的跳转会闪动的效果,导致用户体验不佳,因此这个方案也是不可取的。
A.创建一个Activity并且设置属性,将其设置为主页
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.HOME" />
<category android:name="android.intent.category.DEFAULT" />
B.设置该Activity的主题为无界面
C.在我们设置的“主页”中操作,可以通过startActivity()的方式重启锁屏界面,可以维持锁屏界面存在。
有网友说通过moveTaskToBack(false)可以让“主页”Activity自然撤到后台,但是亲测似乎没效果。
最佳方案: 把锁屏页改成悬浮窗,当屏幕点亮,全屏显示悬浮窗
悬浮框主要是通过WindowManager中的addView,updateView,removeView实现
WindowManager.LayoutParams这个类用于提供悬浮窗所需的参数
WindowManager.LayoutParams参数说明:
type 用于确定悬浮窗的类型(window类型,window有三种类型,应用window,子window,系统window,其中悬浮窗中使用的是系统window)
TYPE_PHONE,表示在所有应用程序之上,状态栏之下
TYPE_STATUS_BAR(状态栏)
TYPE_SEARCH_BAR(搜索框)
TYPE_SYSTEM_ALERT(系统提示框,例如电量很低时提示)等等
根据需求去选择 flags 用于确定悬浮窗的行为,我们这里选择的是TYPE_SYSTEM_ALERT类型。
flag 用于设置悬浮窗的属性
FLAG_NOT_FOCUSABLE(window不需要获得焦点,也不需要接收各种输入事件)
FLAG_NOT_TOUCHABLE(不可点击)
FLAG_NOT_TOUCH_MODAL(系统会通过当前window区域以外的单击事件传递给底层的window,当前window区域以内的单击事件则自己处理)
FLAG_SHOW_WHEN_LOCKED(显示在锁屏的界面上)等等
gravity 用于确定悬浮窗的对齐方式
x 用于确定悬浮窗的横坐标
y 用于确定悬浮窗的纵坐标
width 值用于指定悬浮窗的宽度
height 值用于指定悬浮窗的高度
接下来看实现:
A. xml中添加悬浮窗的权限
<!-- 悬浮窗 -->
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
B. 屏幕点亮时,开启锁屏悬浮窗
RootView mRootView = new RootView(getApplicationContext()); WindowManager.LayoutParams param = new WindowManager.LayoutParams(); param.type = WindowManager.LayoutParams.TYPE_SYSTEM_ALERT; if(Config.sIsFull) param.flags = WindowManager.LayoutParams.FLAG_FULLSCREEN | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN; param.format = PixelFormat.RGBA_8888; param.width = WindowManager.LayoutParams.MATCH_PARENT; param.height = WindowManager.LayoutParams.MATCH_PARENT; mWindowManager.addView(mRootView, param);
C. 现在的华为魅族等手机,应用安装之后都是默认关闭了悬浮窗的权限,因此,如果是这类型的手机,我们还需要手动去开启悬浮窗的权限。
(2)锁住Back键
相比Home键,这是比较好实现的,Back键比较容易屏蔽掉,重写onKeyDown或onBackPressed方法即可,几乎在所有Android版本上都是可以用的。
(3)监听屏幕点亮
系统在按下电源键关闭屏幕或点亮屏幕时会发出相应的广播,如Intent.ACTION_SCREEN_OFF和Intent.ACTION_SCREEN_ON,我们可以注册一个BroadcastReceiver来接收这些广播
。这里我们采取动态注册的方式,建立一个常驻内存的Service,在开启时动态注册一个BroadcastReceiver来监听,当销毁时,移除这个BroadcastReceiver。
当接受到广播,我们需要先屏蔽系统默认的锁屏,再启动自己的锁屏
/** * 通过悬浮窗实现锁屏 * @author lin * @version 1.0 * @date 16-2-25 */public class LockerService extends Service{ private static final String TAG = "LockerService"; private ScreenListener screenListener; private KeyguardManager mKeyguardManager; private KeyguardManager.KeyguardLock mKeyguardLock; private WindowManager mWindowManager; private RootView mRootView; private BroadcastReceiver mReceiver = null; ...//中间代码省略 .../** * init all the data */ private void init(){ if(mWindowManager == null){ mWindowManager = (WindowManager) getApplicationContext().getSystemService(Context.WINDOW_SERVICE); } if(screenListener == null) { screenListener = new ScreenListener(LockerService.this); screenListener.begin(new ScreenListener.ScreenStateListener() { public void onScreenOn() { disableDefaultLock();} @Override public void onScreenOff() { enableDefaultLock(); if (mRootView == null) mRootView = new RootView(getApplicationContext()); addLockView(); createConfig(); createReceiver(); } @Override public void onUnlock() { } }); } } /** * add the rootView 显示悬浮窗 */ private void addLockView(){ if(mWindowManager == null) return ; mRootView = new RootView(getApplicationContext()); WindowManager.LayoutParams param = new WindowManager.LayoutParams(); param.type = WindowManager.LayoutParams.TYPE_SYSTEM_ALERT; if(Config.sIsFull) param.flags = WindowManager.LayoutParams.FLAG_FULLSCREEN | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN; param.format = PixelFormat.RGBA_8888; param.width = WindowManager.LayoutParams.MATCH_PARENT; param.height = WindowManager.LayoutParams.MATCH_PARENT; mWindowManager.addView(mRootView, param); } /** * remove the rootView 移除悬浮窗 */ private void removeLockView(){ if(mWindowManager == null) return ; mWindowManager.removeView(mRootView); }}
(4)锁屏自启动
当手机重启时,我们定义的锁屏需要手动开启,才能使用,这样的体验非常不好。因此我们需要让其自启动,当手机开启时,锁屏服务自动开启。
A.在xml中添加自启动权限
<!-- 自启动 -->
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
B.全局注册一个广播接收器
<!-- 注册自启动接收器 --><receiver android:name="com.zero.locker.lock.LockerBootReceiver"> <intent-filter > <!-- 开机启动 --> <action android:name="android.intent.action.BOOT_COMPLETED"/> <!-- 后台启动 --> <action android:name="android.intent.action.QUICKBOOT_POWERON" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter></receiver>
C.编写广播接收器类
/** * Locker 自启动接收器 * @author lin * @version 1.0 * @date 16-2-29 */public class LockerBootReceiver extends BroadcastReceiver{ @Override public void onReceive(Context context, Intent intent) { if(Config.sIsLock && !LockerService.isStarted){ context.startService(new Intent(context, LockerService.class)); } }}
(5)锁屏服务持久驻扎
方案一: 在Service的onDestory方法里重启Service
理论上是可行的,但是经过一段时间测试,服务莫名其妙的消失了,在本人的魅族手机上是这种情况,因此放弃。
方案二: 把Service设置为前台服务
Android将进程分为6个等级,它们按优先级顺序由高到低依次是:
1.前台进程( FOREGROUND_APP)
2.可视进程(VISIBLE_APP )
3.次要服务进程(SECONDARY_SERVER )
4.后台进程 (HIDDEN_APP)
5.内容供应节点(CONTENT_PROVIDER)
6.空进程(EMPTY_APP)
在Service的onStartCommand方法里,调用startForeground方法,将锁屏服务设置为前台服务。
这样实际上就是提高了进程的优先级,当内存不足的时候,系统会优先回收那些优先级低的进程和服务,因此Service得以常驻内存
但是,即使如此,一旦遇到内存非常吃紧,Service还是有可能会被回收杀死,同时如果调用一些内存清理的功能,Service同样会被回收。
代码上的改动如下:
A.在Service的onStartCommand方法里设置
/** * 设置前台运行,使Service常驻内存 */Notification.Builder builder = new Notification.Builder(this);PendingIntent contentIntent = PendingIntent.getActivity(this, 0, new Intent(LockerService.this, MainActivity.class), 0);builder.setContentIntent(contentIntent);builder.setSmallIcon(R.mipmap.ic_launcher);builder.setTicker(sNotificationTickerText);builder.setContentTitle(sNotificationContentTitle);builder.setContentText(sNotificationContentText);Notification notification = builder.build();startForeground(sNotificationIdentify, notification);
B.在Service的onDestroy里设置 stopForeground(true);
方案三:
然而,在方案二的基础上,依然会出现锁屏服务使用一段时间后,被系统销毁的情况。
使用双服务守护
原理:通常来说,Service内存开销是比较小的,默认情况下,Service会附在UI进程下运行,这样就很容易在整体内存占用过大的情况被一起清理掉。而单独的进程可以有效避免系统回收。
让Service运行在单独的进程之外,我们还需要一个轻量级的Service,作为唤醒的守护Service.
步骤:
A.在xml中设置Service运行于单独的进程中
<service android:name=".lock.LockerService" android:process=":ServiceProcess" android:enabled="true" android:exported="true"> </service> <service android:name=".lock.LockerProtectService" android:enabled="true" android:process=":ServiceProcess" android:exported="true" > </service>
B.编写守护服务,这是一个需要一直保持的服务,因此我们尽量让其保持简单,尽量使其内存开销接近无,尽量不去占用资源,以免对内存的造成影响。
如何让守护服务一直保持呢,可以通过AlarmManager定时唤醒守护服务,在守护服务唤醒同时,检查锁屏服务是否销毁,如果销毁,则重启锁屏服务。此外,由于让服务长期驻扎内存是非常不被推荐的行为,因此,我们应该尽量保持服务不吃内存开销,同时在不想使用锁屏的时候,应该通过stopService()主动销毁这个守护服务.
/** * 锁屏守护服务 * * @author * @version 1.0 * @date 16-3-2 */public class LockerProtectService extends Service { private static final String TAG = "LockerProtectService"; …//中间代码省略 @Override public int onStartCommand(Intent intent, int flags, int startId) { Log.e(TAG, "onStart"); restoreLockerService(); return START_STICKY; } //恢复锁屏服务 private void restoreLockerService(){ if(!isServiceRunning("com.zero.locker.lock.LockerService")){ Log.e(TAG,"restore LockerService"); startService(new Intent(this,LockerService.class)); } }}
记录一下Log,检查一下效果:
15:28 锁屏服务启动,同时守护服务启动
03-02 15:28:56.606 24871-24871/com.zero.locker:ServiceProcess E/LockerProtectService: onCreate
03-02 15:28:56.606 24871-24871/com.zero.locker:ServiceProcess E/LockerProtectService: onStart
03-02 15:28:56.612 24871-24871/com.zero.locker:ServiceProcess E/LockerService: onCreate
03-02 15:28:56.616 24871-24871/com.zero.locker:ServiceProcess E/LockerService: onStart
15:31 锁屏服务和守护服务都被销毁,守护服务重启,同时检查锁屏服务,唤醒锁屏服务
03-02 15:31:54.110 26854-26854/? E/LockerProtectService: onCreate
03-02 15:31:54.111 26854-26854/? E/LockerProtectService: onStart
03-02 15:31:54.116 26854-26854/? E/LockerProtectService: restore LockerService
03-02 15:31:54.127 26854-26854/? E/LockerService: onCreate
03-02 15:31:54.130 26854-26854/? E/LockerService: onStart
15:34 守护服务继续监护
03-02 15:35:54.026 26854-26854/com.zero.locker:ServiceProcess E/LockerProtectService: onStart
03-02 15:36:54.038 26854-26854/com.zero.locker:ServiceProcess E/LockerProtectService: onStart
03-02 15:37:54.023 26854-26854/com.zero.locker:ServiceProcess E/LockerProtectService: onStart
03-02 15:38:54.034 26854-26854/com.zero.locker:ServiceProcess E/LockerProtectService: onStart
16:10 守护服务和锁屏服务都存在,预期效果达到
03-02 16:08:54.028 8194-8194/com.zero.locker:ServiceProcess E/LockerProtectService: onStart
03-02 16:09:54.041 8194-8194/com.zero.locker:ServiceProcess E/LockerProtectService: onStart
03-02 16:10:54.036 8194-8194/com.zero.locker:ServiceProcess E/LockerProtectService: onStart
总归起来,这些方案只能尽量提高Service的持久性,面对内存清理助手等,现在没有应用做的到进程被完全杀死还能唤醒的情况,包括微信,亲测即便其双进程互相守护的方式,在手动关闭微信进程的情况下,也就无法继续收到消息了。
顺便附上源码:locker.tar
..........
好吧,刚以为可以Service可以持久运行了,坚持了5个小时,锁屏服务又销毁了,关键是手机内存还剩下20%多,这分明是内存还足够,具体什么原因,还需要继续研究~ 未完待续...
- Android-锁屏的应用
- Android应用锁屏
- android应用——一款精致的锁屏界面设计
- Android中应用层面对于锁屏lock的操作
- Android 横竖屏+碎片的应用
- Android应用的构成
- Android应用的构成
- Android应用的构成
- Android应用的构成
- Android应用的构成
- Android processBar的应用
- android Button的应用
- android 的Application应用
- android应用的组成
- android AlertDialog的应用
- Android的网络应用
- android 条形码的应用
- Android ViewStub的应用
- Find the Duplicate Number
- AndroidStudio中如何打开hierarchyviewer
- 核心动画之图片折叠和倒影
- eventlet引发的学习:python:单线程、多线程、多进程在计算方面的性能对比
- Stanford Segment 使用笔记
- Android-锁屏的应用
- 初次接触vue
- ‘asm’ operand has impossible constraints
- 学习OpenCV2 —— 直方图匹配
- 使用springmvc做项目时资源的的引入问题 (CSS/JS/IMAGES)
- *LeetCode 94. Binary Tree Inorder Traversal
- 学习OpenCV2——opencv2.4.8和opencv3的安装和配置
- 三种工厂模式的分析以及C++实现
- [理论原理][数学]逻辑运算