前陣子收到客戶要求,要做一款安卓手機和平板上使用的屏保,其實蠻奇怪的,電腦用屏保倒是見得多了,可是手機不使用的時候關掉屏幕不就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;
}
}
}
-------------------------------------------------------------------------------------------------------------------------------------------
其實最主要的應該就是上面的內容,後續就是編寫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 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系統最好了,都瞎改什麼呢。