Android中的壁纸分为静态壁纸和动态壁纸, 这两类壁纸本质都是一样的, 都是通过继承WallpaperService来实现的,只不过是绘制方面的差异。WallpaperManagerService用于管理壁纸的运行与切换,并通过WallpaperManager类向外界提供操作壁纸的接口,主要体现了对壁纸的管理方式。WallpaperService则对应壁纸的具体实现,实现壁纸服务相关的核心是WallpaperService中的Engine类

1 简单壁纸案列

1.1 新建壁纸服务
public class MyWallPaperService extends WallpaperService {private static final String TAG = "MyWallPaperService";@Overridepublic Engine onCreateEngine() {return new MyEngine();}class MyEngine extends Engine {@Overridepublic void onSurfaceCreated(SurfaceHolder holder) {super.onSurfaceCreated(holder);Canvas canvas = holder.lockCanvas();canvas.drawColor(Color.GREEN);holder.unlockCanvasAndPost(canvas);}}
}
1.2 配置文件中配置相关
<!-- AndroidManifest.xml中service配置   -->
<service android:name=".MyWallPaperService"android:enabled="true"android:permission="android.permission.BIND_WALLPAPER"><intent-filter ><action android:name="android.service.wallpaper.WallpaperService"/></intent-filter><meta-dataandroid:name="android.service.wallpaper"android:resource="@xml/wallpaper_resource"/>
</service><!-- wallpaper_resource.xml配置  -->
<?xml version="1.0" encoding="utf-8"?>
<wallpaper xmlns:android="http://schemas.android.com/apk/res/android"/>
1.3 通过壁纸选择器设置壁纸,最终壁纸显示为绿色背景
void setWallpaper() {Intent intent = new Intent(WallpaperManager.ACTION_CHANGE_LIVE_WALLPAPER);ComponentName componentName = new ComponentName(this, MyWallPaperService.class);intent.putExtra(WallpaperManager.EXTRA_LIVE_WALLPAPER_COMPONENT, componentName);startActivity(intent);
}

2 WallpaperManager相关介绍

// 主屏幕壁纸flag
public static final int FLAG_SYSTEM = 1 << 0;// 锁屏壁纸flag
public static final int FLAG_LOCK = 1 << 1;
// 设置动态壁纸
public boolean setWallpaperComponent(ComponentName name)// 清除锁屏壁纸
public void clear(WallpaperManager.FLAG_LOCK)// 清除所有壁纸
public void clearWallpaper()// 获取静态壁纸图片
public Bitmap getBitmap()// 设置静态壁纸
public void setBitmap(Bitmap bitmap)
2.1 WallpaperService和WallpaperManagerService之间的关系

2.2 WallpaperManager和WallpaperManagerService之间的关系

2.3 壁纸涉及到的类介绍
// 壁纸服务信息封装
WallpaperInfo// 提供接口对壁纸服务的访问
WallpaperManager// IWallpaperEngine接口服务端对象
// class IWallpaperEngineWrapper extends IWallpaperEngine.Stub
IWallpaperEngineWrapper// IWallpaperService接口服务端对象
// IWallpaperServiceWrapper extends IWallpaperService.Stub
IWallpaperServiceWrapper// 用户壁纸相关信息存储
WallpaperData// 监听WallPaperService之间的连接状态和实现IWallpaperConnection接口服务端实现
// class WallpaperConnection extends IWallpaperConnection.Stub
//             implements ServiceConnection
WallpaperConnection// 壁纸服务
// public class WallpaperManagerService extends IWallpaperManager.Stub
WallpaperManagerService// 壁纸核心和壁纸绘制相关
Engine

3 壁纸相关代码分析

WallpaperManager的setWallpaperComponent函数为设置动态壁纸函数,接下来以动态壁纸为入口分析相关代码。Globals实现了IWallpaperManagerCallback接口,sGlobals.mService为WallpaperManagerService的代理对象,sGlobals.mService最终通过binder调用WallpaperManagerService的setWallpaperComponent函数,name为对应壁纸服务的包名。

private static class Globals extends IWallpaperManagerCallback.Stub {private final IWallpaperManager mService;
}public boolean setWallpaperComponent(ComponentName name, int userId) {if (sGlobals.mService == null) {Log.w(TAG, "WallpaperService not running");throw new RuntimeException(new DeadSystemException());}try {// step 1, 调用WallpaperManagerService的setWallpaperComponentChecked函数sGlobals.mService.setWallpaperComponentChecked(name, mContext.getOpPackageName(),userId);return true;} catch (RemoteException e) {throw e.rethrowFromSystemServer();}
}
3.1 WallpaperManagerService::setWallpaperComponent

拥有android.Manifest.permission.SET_WALLPAPER_COMPONENT权限的apk才能设置动态壁纸,壁纸选择器有这个设置权限,首先进行权限的相关检查, 然后获取用户设置的壁纸相关信息, 错误判断等, 接下来bindWallpaperComponentLocked函数才是核心,对WallpaperService的条件判断以及绑定

private void setWallpaperComponent(ComponentName name, int userId) {userId = ActivityManager.handleIncomingUser(getCallingPid(), getCallingUid(), userId,false /* all */, true /* full */, "changing live wallpaper", null /* pkg */);// step 1, 权限检查checkPermission(android.Manifest.permission.SET_WALLPAPER_COMPONENT);int which = FLAG_SYSTEM;boolean shouldNotifyColors = false;WallpaperData wallpaper;synchronized (mLock) {if (DEBUG) Slog.v(TAG, "setWallpaperComponent name=" + name);// step 2, 获取用户壁纸相关信息wallpaper = mWallpaperMap.get(userId);if (wallpaper == null) {throw new IllegalStateException("Wallpaper not yet initialized for user " + userId);}final long ident = Binder.clearCallingIdentity();// step 3 , 如果锁屏壁纸没有添加,则将锁屏壁纸flag添加上if (mLockWallpaperMap.get(userId) == null) {which |= FLAG_LOCK;}try {wallpaper.imageWallpaperPending = false;boolean same = changingToSame(name, wallpaper);// step 3, 绑定WallpaperService服务if (bindWallpaperComponentLocked(name, false, true, wallpaper, null)) {if (!same) {wallpaper.primaryColors = null;}wallpaper.wallpaperId = makeWallpaperIdLocked();notifyCallbacksLocked(wallpaper);shouldNotifyColors = true;}} finally {Binder.restoreCallingIdentity(ident);}}if (shouldNotifyColors) {// step 4, 通知壁纸状态改变notifyWallpaperColorsChanged(wallpaper, which);notifyWallpaperColorsChanged(mFallbackWallpaper, FLAG_SYSTEM);}
}
3.2 WallpaperManagerService::bindWallpaperComponentLocked
  • bindWallpaperComponentLocked函数较长,分开来分析。
  • step 1, 如果设置的壁纸服务包名和现在设置的包名相同则返回不做处理
  • step 2, 根据componentName来查询对应的服务相关信息,即ServiceInfo,si为空说明对应包名的服务不存在,也返回不做处理了
  • step 3, 对应包名的WallpaperService没有包含android.Manifest.permission.BIND_WALLPAPER权限,返回不做后续处理
// step 1 , 壁纸对应的包名是否改变,不是则是相同的壁纸返回,不做处理
if (!force && changingToSame(componentName, wallpaper)) {return true;
}int serviceUserId = wallpaper.userId;
// step 2, 通过PackageManager查找是否存在对应包名的Service相关信息,不存在则返回
ServiceInfo si = mIPackageManager.getServiceInfo(componentName,PackageManager.GET_META_DATA | PackageManager.GET_PERMISSIONS, serviceUserId);
if (si == null) {Slog.w(TAG, "Attempted wallpaper " + componentName + " is unavailable");return false;
}// step 3, 如果service不包含android.Manifest.permission.BIND_WALLPAPER权限则返回
if (!android.Manifest.permission.BIND_WALLPAPER.equals(si.permission)) {String msg = "Selected service does not have "+ android.Manifest.permission.BIND_WALLPAPER+ ": " + componentName;if (fromUser) {throw new SecurityException(msg);}Slog.w(TAG, msg);return false;
}
3.3 WallpaperManagerService::bindWallpaperComponentLocked

ris为查询系统中所有的WallpaperService,筛选componentName包名相同的WallpaperService,并创建相关WallpaperInfo,wi为null的情况, 则说明对应的WallpaperService不存在,返回

WallpaperInfo wi = null;Intent intent = new Intent(WallpaperService.SERVICE_INTERFACE);
if (componentName != null && !componentName.equals(mImageWallpaper)) {// Make sure the selected service is actually a wallpaper service.List<ResolveInfo> ris =mIPackageManager.queryIntentServices(intent,intent.resolveTypeIfNeeded(mContext.getContentResolver()),PackageManager.GET_META_DATA, serviceUserId).getList();for (int i=0; i<ris.size(); i++) {ServiceInfo rsi = ris.get(i).serviceInfo;// step 1, 如果componentName包名对应的service存在,创建WallpaperInfo保存ServiceInfo信息if (rsi.name.equals(si.name) &&rsi.packageName.equals(si.packageName)) {try {wi = new WallpaperInfo(mContext, ris.get(i));} catch (XmlPullParserException e) {if (fromUser) {throw new IllegalArgumentException(e);}Slog.w(TAG, e);return false;} catch (IOException e) {if (fromUser) {throw new IllegalArgumentException(e);}Slog.w(TAG, e);return false;}break;}}// step 2, componentName包名对应的service不存在, 直接返回if (wi == null) {String msg = "Selected service is not a wallpaper: "+ componentName;if (fromUser) {throw new SecurityException(msg);}Slog.w(TAG, msg);return false;}
}
3.4 WallpaperManagerService::bindWallpaperComponentLocked

wi不为空, 对应的WallpaperService存在,后续做绑定服务的工作,创建WallpaperConnection对象,WallpaperConnection继承IWallpaperConnection接口, 并且实现了ServiceConnection接口。接下来就是通过mContext.bindServiceAsUser来绑定componentName对应的WallpaperService,绑定服务成功后会回调ServiceConnection接口的onServiceConnected函数,即WallpaperConnection的onServiceConnected函数,onServiceConnected函数放到后面分析,同一个用户,并且上次用户设置的信息不为空(mLastWallpaper != null),取消掉上一次的壁纸设定。

class WallpaperConnection extends IWallpaperConnection.StubServiceConnection {}final int componentUid = mIPackageManager.getPackageUid(componentName.getPackageName(),MATCH_DIRECT_BOOT_AUTO, wallpaper.userId);
// step 1, 创建WallpaperConnection对象
WallpaperConnection newConn = new WallpaperConnection(wi, wallpaper, componentUid);
intent.setComponent(componentName);
intent.putExtra(Intent.EXTRA_CLIENT_LABEL,com.android.internal.R.string.wallpaper_binding_label);
intent.putExtra(Intent.EXTRA_CLIENT_INTENT, PendingIntent.getActivityAsUser(mContext, 0,Intent.createChooser(new Intent(Intent.ACTION_SET_WALLPAPER),mContext.getText(com.android.internal.R.string.chooser_wallpaper)),0, null, new UserHandle(serviceUserId)));// step 2, 绑定服务, 服务绑定成功会回调WallpaperConnection的onServiceConnected函数
if (!mContext.bindServiceAsUser(intent, newConn,Context.BIND_AUTO_CREATE | Context.BIND_SHOWING_UI| Context.BIND_FOREGROUND_SERVICE_WHILE_AWAKE| Context.BIND_INCLUDE_CAPABILITIES,new UserHandle(serviceUserId))) {String msg = "Unable to bind service: "+ componentName;if (fromUser) {throw new IllegalArgumentException(msg);}Slog.w(TAG, msg);return false;
}// step 3, 取消上一次的壁纸服务
if (wallpaper.userId == mCurrentUserId && mLastWallpaper != null&& !wallpaper.equals(mFallbackWallpaper)) {detachWallpaperLocked(mLastWallpaper);
}
wallpaper.wallpaperComponent = componentName;
wallpaper.connection = newConn;
newConn.mReply = reply;
if (wallpaper.userId == mCurrentUserId && !wallpaper.equals(mFallbackWallpaper)) {mLastWallpaper = wallpaper;
}
3.5 detachWallpaperLocked

wallpaper.connection不为空, 说明WallPaperService还在运行,wallpaper.connection.mService为IWallpaperService接口,detach函数Android9.0以前不存在,回调IWallpaperService的detach函数,最终调用WallPaperService的detach函数来回收相关资源,和attach函数相对应,通过mContext.unbindService(wallpaper.connection)取消绑定服务来销毁服务。WallpaperConnection.DisplayConnector::disconnectLocked善后工作

private void detachWallpaperLocked(WallpaperData wallpaper) {// step 1, wallpaper.connection不为空, 说明WallPaperService还在运行if (wallpaper.connection != null) {try {// step 2, 回调WallpaperService的detach函数if (wallpaper.connection.mService != null) {wallpaper.connection.mService.detach();}} catch (RemoteException e) {Slog.w(TAG, "Failed detaching wallpaper service ", e);}// step 3, 取消绑定WallpaperServicemContext.unbindService(wallpaper.connection);// step 4调用WallpaperConnection.DisplayConnector::disconnectLocked函数// 将壁纸的WindowToken从WindowManagerService中移除,壁纸将无法显示wallpaper.connection.forEachDisplayConnector(WallpaperConnection.DisplayConnector::disconnectLocked);wallpaper.connection.mService = null;wallpaper.connection.mDisplayConnector.clear();wallpaper.connection = null;if (wallpaper == mLastWallpaper) mLastWallpaper = null;}
}
3.6 WallpaperConnection::DisplayConnector::disconnectLocked
void WallpaperConnection::DisplayConnector::disconnectLocked() {try {// step 1 清除壁纸的WindowTokenmIWindowManager.removeWindowToken(mToken, mDisplayId);} catch (RemoteException e) {}try {// step 2 回调mEngine的destroy函数if (mEngine != null) {mEngine.destroy();}} catch (RemoteException e) {}mEngine = null;
}
3.7 WallpaperConnection::onServiceConnected
public void WallpaperConnection::onServiceConnected(ComponentName name, IBinder service) {synchronized (mLock) {if (mWallpaper.connection == this) {mService = IWallpaperService.Stub.asInterface(service);// step 1, 调用attachServiceLocked函数attachServiceLocked(this, mWallpaper);// step 2, 将用户相关数据保存到XML文件中if (!mWallpaper.equals(mFallbackWallpaper)) {saveSettingsLocked(mWallpaper.userId);}FgThread.getHandler().removeCallbacks(mResetRunnable);if (mPerformance != null) {// step 3, 通知壁纸相关信息改变mPerformance.notifyWallpaperChanged(name.getPackageName());}}}
}
3.8 WallpaperConnection::attachServiceLocked
private void attachServiceLocked(WallpaperConnection conn, WallpaperData wallpaper) {conn.forEachDisplayConnector(connector-> connector.connectLocked(conn, wallpaper));
}
3.9 WallpaperConnection::DisplayConnector::connectLocked

connection.mService为IWallpaperService接口,如果为空直接返回, 接下来添加壁纸的token到WindowManagerService中, 这样壁纸窗口才能添加到WindowManagerService中显示出来.IWallpaperService的attach为WallpaperService的attach函数,回调attach函数来对WallpaperService的Engine等相关资源的初始化。

void WallpaperConnection::DisplayConnector::connectLocked(WallpaperConnection connection, WallpaperData wallpaper) {// step 1, IWallpaperService为空直接返回if (connection.mService == null) {Slog.w(TAG, "WallpaperService is not connected yet");return;}try {// step 2, 将壁纸相关的WindowToken添加到WMS中// 得有壁纸Token, WallPaperService才能往WMS中添加壁纸窗口mIWindowManager.addWindowToken(mToken, TYPE_WALLPAPER, mDisplayId);} catch (RemoteException e) {Slog.e(TAG, "Failed add wallpaper window token on display " + mDisplayId, e);return;}final DisplayData wpdData = getDisplayDataOrCreate(mDisplayId);try {// step 3, 回调WallPaperService的attach函数connection.mService.attach(connection, mToken, TYPE_WALLPAPER, false,wpdData.mWidth, wpdData.mHeight,wpdData.mPadding, mDisplayId);} catch (RemoteException e) {Slog.w(TAG, "Failed attaching wallpaper on display", e);if (wallpaper != null && !wallpaper.wallpaperUpdating&& connection.getConnectedEngineSize() == 0) {bindWallpaperComponentLocked(null /* componentName */, false /* force */,false /* fromUser */, wallpaper, null /* reply */);}}
}
4 WallPaperService代码分析
4.1 WallpaperService::attach

如果服务以及销毁,则返回,接下来是初始化相关.mSession变量的初始化, 用来向WindowManagerService添加壁纸窗口,surfeace相关初始化后,初始化完成, 更新surface相关信息,最后调用updateSurface函数

void attach(IWallpaperEngineWrapper wrapper) {// step 1 ,如果已经销毁,返回if (mDestroyed) {return;}mInitializing = true;// step 2 ,获取Session对象, 和WMS直接通信mSession = WindowManagerGlobal.getWindowSession();mWindow.setSession(mSession);// step 3 , surface相关初始化onCreate(mSurfaceHolder);mInitializing = false;mReportedVisible = false;updateSurface(false, false, false);
}
WallpaperService::updateSurface

创建事件接收者对象mInputEventReceiver ,调用mSession的addToDisplay将壁纸窗口mWinodw添加到WindowManagerservice中,mSession.relayout将为mWindow分配surface,以及窗口大小告知WallpaperService。mIWallpaperEngine.reportShown()来告知WallpaperManagerService壁纸已经开始显示了。

void updateSurface(boolean forceRelayout, boolean forceReport, boolean redrawNeeded) {if (mDestroyed) {Log.w(TAG, "Ignoring updateSurface: destroyed");}if (forceRelayout || creating || surfaceCreating || formatChanged || sizeChanged|| typeChanged || flagsChanged || redrawNeeded|| !mIWallpaperEngine.mShownReported) {try {if (!mCreated) {mInputChannel = new InputChannel();// step 1, 将mWindow添加到WindowManagerService中if (mSession.addToDisplay(mWindow, mWindow.mSeq, mLayout, View.VISIBLE,mDisplay.getDisplayId(), mWinFrame, mContentInsets, mStableInsets,mOutsets, mDisplayCutout, mInputChannel,mInsetsState) < 0) {Log.w(TAG, "Failed to add window while updating wallpaper surface.");return;}mCreated = true;// step 2, 创建按键接收者mInputEventReceiver = new WallpaperInputEventReceiver(mInputChannel, Looper.myLooper());}// step 3, 给壁纸窗口布局和分配surfacefinal int relayoutResult = mSession.relayout(mWindow, mWindow.mSeq, mLayout, mWidth, mHeight,View.VISIBLE, 0, -1, mWinFrame, mOverscanInsets, mContentInsets,mVisibleInsets, mStableInsets, mOutsets, mBackdropFrame,mDisplayCutout, mMergedConfiguration, mSurfaceControl,mInsetsState);} finally {mIsCreating = false;mSurfaceCreated = true;if (redrawNeeded) {mSession.finishDrawing(mWindow);}// step 4, 通知WallPaperManagerService已经显示了mIWallpaperEngine.reportShown();}} catch (RemoteException ex) {}}
}

壁纸相关总结

  • 1 壁纸服务相关的配置需要在配置文件中声明
  • 2 实现壁纸服务相关的核心是WallpaperService中的Engine类和surface相关操作
  • 3 静态壁纸和动态壁纸都继承自WallpaperService
  • 4 壁纸窗口WindowToken是通过WindowPaperManagerService来向WMS中来添加和移除操作,和输入法窗口相似
  • 5 Android中的壁纸实现采用系统服务和四大组件中的服务两层框架来实现, 只需要我们实现自定义的WallpaperService, 就能够通过系统服务来控制整个壁纸的显示,隐藏,切换等, 隐藏服务之间的接口调用细节,能够很轻松的来开发壁纸服务

Android WallpaperManager 壁纸分析相关推荐

  1. android9壁纸存放路径,Android基础入门教程——10.9 WallpaperManager(壁纸管理器)

    Android基础入门教程--10.9 WallpaperManager(壁纸管理器) 标签(空格分隔): Android基础入门教程 本节引言: 本节给大家带来的是WallpaperManager( ...

  2. 【Android笔记104】Android之壁纸管理器(WallpaperManager)的使用

    这篇文章,主要介绍Android之壁纸管理器(WallpaperManager)的使用. 目录 一.壁纸管理器(WallpaperManager) 1.1.壁纸管理器介绍

  3. Android基础入门教程——10.9 WallpaperManager(壁纸管理器)

    Android基础入门教程--10.9 WallpaperManager(壁纸管理器) 标签(空格分隔): Android基础入门教程 本节引言: 本节给大家带来的是WallpaperManager( ...

  4. android之壁纸相关

    android之壁纸机制 1.涉及核心类: 1>ImageWallpaper.java(IW):继承WallpaperService主要负责静态壁纸的draw处理; 2>Wallpaper ...

  5. android 手机壁纸源码,Android设置手机壁纸-源码(WallPaper)

    Android设置手机壁纸-源码(WallPaper) /** * Andorid设置手机屏幕的壁纸 * * @description: * @author ldm * @date 2016-5-4 ...

  6. android壁纸设置,android设置壁纸 的方法

    前几天看到豌豆荚上面风景壁纸的APP 下载得挺火,闲来无事心痒痒的想写一个壁纸程序.由于是Android开发新手,忘记了一个很重要的开发步骤,就是对权限的设置.开始以为自己写的壁纸设置程序有问题,经过 ...

  7. Android 源码分析

    查看源码版本号: build\core\version_defaults.mk //搜索该文件中的 PLATFORM_VERSION值 frameworks 目录 (核心框架--java及C++语言) ...

  8. Android设置壁纸的几种方案

    Android设置壁纸有许多方法,主要思路有两种: 1:通过WallpaperManager设置 2:通过系统程序设置 下文将分开说明: <1>通过WallpaperManager设置 该 ...

  9. Android动态壁纸解析

    转载自 Yalin Jin的文章:http://www.kinglloy.com/ MENU Android动态壁纸解析 24 JULY 2017 阅读之前 建议下载使用Style动态壁纸应用 文章后 ...

  10. 10.9 安卓WallpaperManager(壁纸管理器)

    10.9 WallpaperManager(壁纸管理器) 分类 Android 基础入门教程 本节引言: 本节给大家带来的是WallpaperManager(壁纸管理器),如其名,就是手机壁纸相关的一 ...

最新文章

  1. 基于Hyper-V3.0搭建XenDesktop7之九 部署虚拟应用之模板准备
  2. java打包后发布找不到jsp_eclipse中web项目部署以后jsp的java文件找不到问题(Tomcat配置serverlocations)...
  3. boost::mpi模块实现传输数据类型的骨架和内容的通信器的测试
  4. java生成数据插入hbase_hbase实战之javaAPI插入数据
  5. Q76:仿射变换(Affine Transformation)
  6. php foreach、while性能比较
  7. 不知道STAR法则还敢去面试?备受500强HR推崇的STAR法则详解
  8. 【git】小甲鱼Git教程《极客Python之Git实用教程》笔记二
  9. 【ASM】ASMLIB 系列
  10. 用C++做数据分析 - 唐代诗人的朋友圈
  11. 打开PowerPoint提示:PowerPoint上次起送时失败。以安全模式启动PowperPoint将帮助您纠正或发现启动中的问题
  12. sklearn神经网络回归示例
  13. 给力!低代码开发平台广州流辰信息科技助您增辉创价值!
  14. python存csv中文乱码问题
  15. Docker私服搭建
  16. Android 动态加载多版本SDK之DexClassLoader实践
  17. 一文看懂麒麟9000:153亿晶体管,刷新5G速度,还有更强游戏体验,并且会“继续前行”...
  18. malloc函数java_malloc函数具体解释
  19. Rhino 减少结构线的一个方法—重建曲面
  20. PHP Framework YII的里的gii设置。

热门文章

  1. 光储并网VSG系统Matlab/simulink仿真模型 系统前级直流部分包括光伏阵列、变换器、储能系统和双向dcdc变换器,后级交流子系统包括逆变器LC滤波器,交流负载
  2. 鼓形齿的计算机械设计标准,鼓形齿设计主要尺寸计算
  3. 用百宝云在线表单制作一个简单的投票系统
  4. 四大逆向工程软件简介
  5. php 三菱plc,三菱FX系列PLC编程语言概述及之间的转换关系
  6. 软件实施工程师到底是做什么的?
  7. Java太阳系行星运动模型
  8. ArcGIS图解建模工具与DEM提取山脊、山谷线(水文分析、表面分析)
  9. cpc客户端网络不通
  10. 计算机安装软件没有管理员权限设置,实用技巧:如何在win10中安装没有管理员权限的软件...