前陣子收到客戶要求,要做一款安卓手機和平板上使用的屏保,其實蠻奇怪的,電腦用屏保倒是見得多了,可是手機不使用的時候關掉螢幕不就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這部分之前用到過,不在詳述,這次把這部分單獨寫成一個工具類,簡化代碼[java] 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的代碼:[java] 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); }; } 屏保展示頁面代碼:[java] <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> 總結和注意:1.系統屏保的屏蔽方法以及螢幕喚醒的知識參考API就可以了,比較簡單的新知識2.如果很多張圖片來載入的話,很可能出現令人最頭疼的OOM,由於屏保就需求全螢幕顯示,所以圖片縮放的方式不合適,沒想到好辦法。不過為了避免反覆解鎖過程中出現OOM,在onDestroy()方法中對圖片進行了recycle。3.在模擬器上運行沒有發現問題,但可能在不同的機型上會出現問題,比如有些手機將安卓的源碼改掉了,甚至不允許使用屏蔽系統屏保的方法。還是原生態的android系統最好了,都瞎改什麼呢。