前阵子收到客户要求,要做一款安卓手机和平板上使用的屏保,其实蛮奇怪的,电脑用屏保倒是见得多了,可是手机不使用的时候关掉屏幕不就OK了吗?话说现在的智能手机电池都不耐用的可怜,还装屏保岂不是很费电。原来客户是用于放在营业厅(手机相关),通过手机或者平板来使用相关设备投射到电子屏幕上展示广告的用途,24小时不断电,只是展示用,故电量不作考虑。要求在服务端上传欲展示的图片,PDA上可以进行获取更新图片,只要不断滚动他们的广告就可以了。起初并不是我来做的,同事已经都写的差不多了,他突然有别的项目很急,留给我来做,也好,以前没做过,顺便了解一下android屏保相关的知识,写下来做积累。
-------------------------------------------------------------------------------------------------------
首先接触到了KeyguardManager,用来对系统的屏保进行屏蔽
public class KeyguardManager extends Object
Class that can be used to lock and unlock the keyboard. Get an instance of this class by calling Context.getSystemService(java.lang.String) with argument Context.KEYGUARD_SERVICE. The actual class to control the keyboard locking is KeyguardManager.KeyguardLock.
一个用于锁屏和解锁的类,通过调用Context.getSystemService(Context.KEYGUARD_SERVICE)来获取实例。实际上用于操控锁屏的是KeyguardManager.KeyguardLock类
KeyguardManager 两个内部类分别是:
(1)KeyguardManager.KeyguardLock(l两个函数)
记得加权限
<uses-permission android:name="android.permission.DISABLE_KEYGUARD"/>
disableKeyguard()函数来解除锁屏
reenableKeyguard()反解除锁屏.reenableKeyguard()反解除锁屏的意思是:如果在调用disableKeyguard()函数之前是锁屏的,那么就进行锁屏,否则不进行任何操作。当然如果之前没调用disableKeyguard()函数,也不进行任何操作。
(2)KeyguardManager.OnKeyguardExitResult(boolean success) :返回true表示exitKeyguardSecurely()函数执行成功,否则表示失败<具体自己没用到,是个做判断和debug用的吧估计>
-----------------------------------------------------------------------------------------------------
而后用到的是开启和关闭屏幕唤醒的内容,PowerManager和WakeLock这部分之前用到过,不在详述,这次把这部分单独写成一个工具类,简化代码
- package com.eyu.screen.util;
- import android.content.Context;
- import android.os.PowerManager;
- import android.os.PowerManager.WakeLock;
- public class PowerManagerWakeLock {
- private static WakeLock wakeLock;
- /**开启 保持屏幕唤醒*/
- public static void acquire(Context context) {
- PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
- wakeLock = pm.newWakeLock(PowerManager.SCREEN_BRIGHT_WAKE_LOCK|PowerManager.ON_AFTER_RELEASE, "PowerManagerWakeLock");
- wakeLock.acquire();
- }
- /**关闭 保持屏幕唤醒*/
- public static void release() {
- if (wakeLock != null) {
- wakeLock.release();
- wakeLock = null;
- }
- }
- }
package com.eyu.screen.util;
import android.content.Context;
import android.os.PowerManager;
import android.os.PowerManager.WakeLock;
public class PowerManagerWakeLock {
private static WakeLock wakeLock;
/**开启 保持屏幕唤醒*/
public static void acquire(Context context) {
PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
wakeLock = pm.newWakeLock(PowerManager.SCREEN_BRIGHT_WAKE_LOCK|PowerManager.ON_AFTER_RELEASE, "PowerManagerWakeLock");
wakeLock.acquire();
}
/**关闭 保持屏幕唤醒*/
public static void release() {
if (wakeLock != null) {
wakeLock.release();
wakeLock = null;
}
}
}
-------------------------------------------------------------------------------------------------------------------------------------------
其实最主要的应该就是上面的内容,后续就是编写Service和发送广播的操作,并实现和服务端的通信。
做完之后抛开服务端和下载更新的内容,自己做了一个单机用的留底,基本思路和操作与公司项目的一致,只是图片需要手动添加而已,使用后会一直监听屏幕的状态,一旦屏幕灭掉会立刻唤醒,使用viewpager和定时器来控制图片自动的翻页,想彻底停掉就需要断掉后台的service,下面是小程序的截图,启动后会先做判断,如果sd卡指定目录没有图片的话,则加载资源文件,有图片加载SD卡中的。
Service的代码:
- package com.eyu.screen.UI;
- import com.eyu.screen.util.PowerManagerWakeLock;
- import android.app.KeyguardManager;
- import android.app.Service;
- import android.content.BroadcastReceiver;
- import android.content.Context;
- import android.content.Intent;
- import android.content.IntentFilter;
- import android.os.IBinder;
- public class ScreenSaverS extends Service {
- @Override
- public IBinder onBind(Intent intent) {
- // TODO Auto-generated method stub
- return null;
- }
- public void onCreate() {
- // 屏蔽系统的屏保
- KeyguardManager manager = (KeyguardManager) getSystemService(Context.KEYGUARD_SERVICE);
- KeyguardManager.KeyguardLock lock = manager
- .newKeyguardLock("KeyguardLock");
- lock.disableKeyguard();
- // 注册一个监听屏幕开启和关闭的广播
- IntentFilter filter = new IntentFilter();
- filter.addAction(Intent.ACTION_SCREEN_ON);
- filter.addAction(Intent.ACTION_SCREEN_OFF);
- registerReceiver(screenReceiver, filter);
- }
- BroadcastReceiver screenReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- // TODO Auto-generated method stub
- String action = intent.getAction();
- if (action.equals(Intent.ACTION_SCREEN_ON)) {
- } else if (action.equals(Intent.ACTION_SCREEN_OFF)) {//如果接受到关闭屏幕的广播
- if (!ScreenSaverShowA.isShow) {
- //开启屏幕唤醒,常亮
- PowerManagerWakeLock.acquire(ScreenSaverS.this);
- }
- PowerManagerWakeLock.acquire(ScreenSaverS.this);
- Intent intent2 = new Intent(ScreenSaverS.this,
- ScreenSaverShowA.class);
- intent2.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- startActivity(intent2);
- PowerManagerWakeLock.release();
- }
- }
- };
- public void onDestroy() {
- PowerManagerWakeLock.release();
- unregisterReceiver(screenReceiver);
- };
- }
package com.eyu.screen.UI;
import com.eyu.screen.util.PowerManagerWakeLock;
import android.app.KeyguardManager;
import android.app.Service;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.IBinder;
public class ScreenSaverS extends Service {
@Override
public IBinder onBind(Intent intent) {
// TODO Auto-generated method stub
return null;
}
public void onCreate() {
// 屏蔽系统的屏保
KeyguardManager manager = (KeyguardManager) getSystemService(Context.KEYGUARD_SERVICE);
KeyguardManager.KeyguardLock lock = manager
.newKeyguardLock("KeyguardLock");
lock.disableKeyguard();
// 注册一个监听屏幕开启和关闭的广播
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_SCREEN_ON);
filter.addAction(Intent.ACTION_SCREEN_OFF);
registerReceiver(screenReceiver, filter);
}
BroadcastReceiver screenReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
// TODO Auto-generated method stub
String action = intent.getAction();
if (action.equals(Intent.ACTION_SCREEN_ON)) {
} else if (action.equals(Intent.ACTION_SCREEN_OFF)) {//如果接受到关闭屏幕的广播
if (!ScreenSaverShowA.isShow) {
//开启屏幕唤醒,常亮
PowerManagerWakeLock.acquire(ScreenSaverS.this);
}
PowerManagerWakeLock.acquire(ScreenSaverS.this);
Intent intent2 = new Intent(ScreenSaverS.this,
ScreenSaverShowA.class);
intent2.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent2);
PowerManagerWakeLock.release();
}
}
};
public void onDestroy() {
PowerManagerWakeLock.release();
unregisterReceiver(screenReceiver);
};
}
屏保展示页面代码:
- <SPAN style="FONT-SIZE: 12px">package com.eyu.screen.UI;
- import java.io.File;
- import java.util.Timer;
- import java.util.TimerTask;
- import com.eyu.screen.R;
- import com.eyu.screen.adapter.ScreenSaverShowAdapter;
- import com.eyu.screen.util.FolderUtil;
- import android.annotation.SuppressLint;
- import android.app.Activity;
- import android.graphics.Bitmap;
- import android.graphics.BitmapFactory;
- import android.graphics.drawable.Drawable;
- import android.os.Bundle;
- import android.os.Handler;
- import android.os.Message;
- import android.support.v4.view.ViewPager;
- import android.util.Log;
- import android.view.View;
- import android.view.View.OnClickListener;
- @SuppressLint("HandlerLeak")
- public class ScreenSaverShowA extends Activity {
- public static boolean isShow = false;
- private ViewPager viewPager;
- private Bitmap[] bmps = null;
- private Drawable[] drawables = new Drawable[5];
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- // TODO Auto-generated method stub
- super.onCreate(savedInstanceState);
- setContentView(R.layout.screenshow);
- isShow = true;
- initControl();
- autoSwitch();
- }
- @Override
- protected void onDestroy() {
- // TODO Auto-generated method stub
- isShow = false;
- //将bitmap回收,尽量避免OOM
- if (bmps != null) {
- for (int i = 0; i < bmps.length; i++) {
- bmps[i].recycle();
- }
- } else {
- return;
- }
- super.onDestroy();
- }
- private void initControl() {
- findViewById(R.id.btn_jiesuo).setOnClickListener(new OnClick());
- viewPager = (ViewPager) findViewById(R.id.viewpager);
- File f = new File(FolderUtil.getSaveFolder());
- File[] files = f.listFiles();// 得到所有子目录
- bmps = new Bitmap[files.length];
- //如果文件夹为空,则从资源文件加载图片
- if (files.length == 0) {
- drawables[0] = getResources().getDrawable(R.drawable.bg_01);
- drawables[1] = getResources().getDrawable(R.drawable.bg_02);
- drawables[2] = getResources().getDrawable(R.drawable.bg_03);
- drawables[3] = getResources().getDrawable(R.drawable.bg_04);
- drawables[4] = getResources().getDrawable(R.drawable.bg_05);
- ScreenSaverShowAdapter adapter = new ScreenSaverShowAdapter(
- ScreenSaverShowA.this, drawables);
- viewPager.setAdapter(adapter);
- } else {
- //文件夹不为空则循环遍历加载sd卡指定目录中图片
- for (int i = 0; i < files.length; i++) {
- String path = files[i].getAbsolutePath();
- bmps[i] = BitmapFactory.decodeFile(path);
- Log.d("PDA", "====H===" + path);
- }
- ScreenSaverShowAdapter adapter = new ScreenSaverShowAdapter(
- ScreenSaverShowA.this, bmps);
- viewPager.setAdapter(adapter);
- }
- }
- /** 图片定时自动切换 */
- private void autoSwitch() {
- int interval = 3000;
- Timer timer = new Timer();
- TimerTask task = new TimerTask() {
- @Override
- public void run() {
- // TODO Auto-generated method stub
- Message message = new Message();
- handler.sendMessage(message);
- }
- };
- timer.schedule(task, interval, interval);
- }
- Handler handler = new Handler() {
- @Override
- public void handleMessage(Message msg) {
- int currentPage = viewPager.getCurrentItem();
- int tempNum = bmps.length == 0 ? drawables.length : bmps.length;
- int nextPage = (currentPage + 1) % tempNum;
- viewPager.setCurrentItem(nextPage);
- super.handleMessage(msg);
- }
- };
- class OnClick implements OnClickListener {
- @Override
- public void onClick(View v) {
- // TODO Auto-generated method stub
- ScreenSaverShowA.this.finish();
- }
- }
- }
- </SPAN>
package com.eyu.screen.UI;
import java.io.File;
import java.util.Timer;
import java.util.TimerTask;
import com.eyu.screen.R;
import com.eyu.screen.adapter.ScreenSaverShowAdapter;
import com.eyu.screen.util.FolderUtil;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.support.v4.view.ViewPager;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
@SuppressLint("HandlerLeak")
public class ScreenSaverShowA extends Activity {
public static boolean isShow = false;
private ViewPager viewPager;
private Bitmap[] bmps = null;
private Drawable[] drawables = new Drawable[5];
@Override
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
setContentView(R.layout.screenshow);
isShow = true;
initControl();
autoSwitch();
}
@Override
protected void onDestroy() {
// TODO Auto-generated method stub
isShow = false;
//将bitmap回收,尽量避免OOM
if (bmps != null) {
for (int i = 0; i < bmps.length; i++) {
bmps[i].recycle();
}
} else {
return;
}
super.onDestroy();
}
private void initControl() {
findViewById(R.id.btn_jiesuo).setOnClickListener(new OnClick());
viewPager = (ViewPager) findViewById(R.id.viewpager);
File f = new File(FolderUtil.getSaveFolder());
File[] files = f.listFiles();// 得到所有子目录
bmps = new Bitmap[files.length];
//如果文件夹为空,则从资源文件加载图片
if (files.length == 0) {
drawables[0] = getResources().getDrawable(R.drawable.bg_01);
drawables[1] = getResources().getDrawable(R.drawable.bg_02);
drawables[2] = getResources().getDrawable(R.drawable.bg_03);
drawables[3] = getResources().getDrawable(R.drawable.bg_04);
drawables[4] = getResources().getDrawable(R.drawable.bg_05);
ScreenSaverShowAdapter adapter = new ScreenSaverShowAdapter(
ScreenSaverShowA.this, drawables);
viewPager.setAdapter(adapter);
} else {
//文件夹不为空则循环遍历加载sd卡指定目录中图片
for (int i = 0; i < files.length; i++) {
String path = files[i].getAbsolutePath();
bmps[i] = BitmapFactory.decodeFile(path);
Log.d("PDA", "====H===" + path);
}
ScreenSaverShowAdapter adapter = new ScreenSaverShowAdapter(
ScreenSaverShowA.this, bmps);
viewPager.setAdapter(adapter);
}
}
/** 图片定时自动切换 */
private void autoSwitch() {
int interval = 3000;
Timer timer = new Timer();
TimerTask task = new TimerTask() {
@Override
public void run() {
// TODO Auto-generated method stub
Message message = new Message();
handler.sendMessage(message);
}
};
timer.schedule(task, interval, interval);
}
Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
int currentPage = viewPager.getCurrentItem();
int tempNum = bmps.length == 0 ? drawables.length : bmps.length;
int nextPage = (currentPage + 1) % tempNum;
viewPager.setCurrentItem(nextPage);
super.handleMessage(msg);
}
};
class OnClick implements OnClickListener {
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
ScreenSaverShowA.this.finish();
}
}
}
总结和注意:
1.系统屏保的屏蔽方法以及屏幕唤醒的知识参考API就可以了,比较简单的新知识
2.如果很多张图片来加载的话,很可能出现令人最头疼的OOM,由于屏保就需求全屏显示,所以图片缩放的方式不合适,没想到好办法。不过为了避免反复解锁过程中出现OOM,在onDestroy()方法中对图片进行了recycle。
3.在模拟器上运行没有发现问题,但可能在不同的机型上会出现问题,比如有些手机将安卓的源码改掉了,甚至不允许使用屏蔽系统屏保的方法。还是原生态的android系统最好了,都瞎改什么呢。