Android 10.0 动态壁纸 LiveWallpaper (一)

您所在的位置:网站首页 安卓滚屏怎么设置壁纸 Android 10.0 动态壁纸 LiveWallpaper (一)

Android 10.0 动态壁纸 LiveWallpaper (一)

2024-07-10 22:38| 来源: 网络整理| 查看: 265

学习笔记:

在 Android 中,壁纸分为动态与静态两种,但其实两者得本质都是一样。都以一个 Service 得形式在后台运行,在一个类型为 TYPE_WALLPAPER 的窗口上绘制内容。也可以这么去理解:静态壁纸是一种特殊的动态壁纸,它仅在窗口上渲染了一张图片,而不会对用户的操作做出反应。动态壁纸不能只应用于锁屏。

壁纸实现时涉及的几个主要的类:

WallpaperService 及其内部类 Engine:壁纸在 WallpaperService 这个服务中运⾏,当需要实现⾃⼰的壁纸时,继承和实现这个类,是⾸先需要做的。Engine是WallpaperService中的⼀个内部类,实现了壁纸服务窗⼝的创建以及 Surface 的维护,同时 Engine 内部类还提供了 onVisibilityChanged(),onCommand() 等回调⽅法,⽤于可见状态变化和⽤户触摸事件等。Engine 类因此也是壁纸实现的核⼼类,实现和重写其接⼝在开发中也相当重要。

WallpaperManagerService 和 WallpaperManager:WallpaperManagerService ⽤于管理壁纸的运⾏与切换,并通过 WallpaperManager 对外界提供操作壁纸的接⼝。

WindowMangerService:该类⽤于计算壁纸窗⼝的 Z 序,可见性以及为壁纸窗⼝应⽤动画。

壁纸服务的两种启动场景:

1、重启壁纸服务启动流程: SystemService 进程启动时,会启动各种系统服务。在该类的 startOtherServices() ⽅法中会⾸先拉起 WallpaperManagerService,通过该类,WallpaperService 后⾯才得以启动。

// SystemService.java if (context.getResources().getBoolean(R.bool.config_enableWallpaperService)) { t.traceBegin("StartWallpaperManagerService"); mSystemServiceManager.startService(WALLPAPER_SERVICE_CLASS); t.traceEnd(); } else { Slog.i(TAG, "Wallpaper service disabled by config"); }

WallpaperManagerService 启动之后回调用到 systemReady() ⽅法。

// WallpaperManagerService.java public static class Lifecycle extends SystemService { private IWallpaperManagerService mService; public Lifecycle(Context context) { super(context); } @Override public void onStart() { // 省略部分代码...... } @Override public void onBootPhase(int phase) { if (mService != null) { mService.onBootPhase(phase); } } @Override public void onUnlockUser(int userHandle) { // 省略部分代码...... } } @Override public void onBootPhase(int phase) { if (phase == SystemService.PHASE_ACTIVITY_MANAGER_READY) { systemReady(); } else if (phase == SystemService.PHASE_THIRD_PARTY_APPS_CAN_START) { switchUser(UserHandle.USER_SYSTEM, null); } } void systemReady() { if (DEBUG) Slog.v(TAG, "systemReady"); initialize(); // 省略部分代码...... try { ActivityManager.getService().registerUserSwitchObserver( new UserSwitchObserver() { @Override public void onUserSwitching(int newUserId, IRemoteCallback reply) { switchUser(newUserId, reply); } }, TAG); } catch (RemoteException e) { e.rethrowAsRuntimeException(); } }

会通过 initialize() 方法调用 loadSettingsLocked() ⽅法加载⽤户设置过的壁纸信息,然后监听⽤户是否切换⽤户 switchUser(),当切换⽤户时,switchWallpaper() 会调⽤ bindWallpaperComponentLocked() ⽅法拉起对应的壁纸服务。

2、⼿动切换时壁纸服务的启动流程

⼿动切换壁纸(这里说的是动态壁纸)时需要通过WallpaperManager.getInstance(activity).setWallpaperComponent() ⽅法完成,我们在这个接⼝中传⼊壁纸服务对应的 ComponentName,getIWallpaperManager 返回的是 WallpaperManagerService 的 Bp(binder proxy binder代理) 端,在 WallpaperManagerService 端,我们可以查看到 setWallpaperComponent 的具体实现:

注意:WallpaperManager.getIWallpaperManager() 并没有作为 SDK 的一部分提供给开发者。因此第三方应用无法进行动态壁纸的设置。

// WallpaperManagerService.java private void setWallpaperComponent(ComponentName name, int userId) { userId = ActivityManager.handleIncomingUser(getCallingPid(), getCallingUid(), userId, false /* all */, true /* full */, "changing live wallpaper", null /* pkg */); /* ⾸先调⽤该⽅法的时候回去校验权限,该权限定义在frameworks/base/core/res/AndroidManifest.xml, 查看protectionLevel,只有是特权应⽤或者系统签名的应⽤才能获取到这个系统权限,所以普通的应⽤是没有办法进⾏壁纸设置的 */ // 设置动态壁纸需要调用者拥有一个签名级的系统权限 checkPermission(android.Manifest.permission.SET_WALLPAPER_COMPONENT); int which = FLAG_SYSTEM; boolean shouldNotifyColors = false; WallpaperData wallpaper; synchronized (mLock) { Slog.v(TAG, "setWallpaperComponent name=" + name); /* 此处会先通过当前的⽤户ID获取到与该⽤户相关的壁纸信息,WallpaperManagerService⽀持多⽤户机制, ⽤户的信息在mWallpaperMap中存储,每⼀个⽤户对应⼀个WallpaperData, WallpaperData存储壁纸相关信息,并在随后更新其内容使之保存新壁纸的信息。 */ wallpaper = mWallpaperMap.get(userId); if (wallpaper == null) { throw new IllegalStateException("Wallpaper not yet initialized for user " + userId); } // 省略部分代码...... // New live wallpaper is also a lock wallpaper if nothing is set if (mLockWallpaperMap.get(userId) == null) { which |= FLAG_LOCK; } try { wallpaper.imageWallpaperPending = false; boolean same = changingToSame(name, wallpaper); // 在这⾥真正会去拉起对应的 WallPaperService if (bindWallpaperComponentLocked(name, false, true, wallpaper, null)) { // 省略部分代码...... } } finally { Binder.restoreCallingIdentity(ident); } } // 省略部分代码...... }

上述两种场景都是通过 bindWallpaperComponentLocked() 方法来拉起相关服务的。下面看看 bindWallpaperComponentLocked() 方法:

// WallpaperManagerService.java private boolean bindWallpaperComponentLocked(ComponentName componentName, boolean force, boolean fromUser, WallpaperData wallpaper, IRemoteCallback reply) { // 省略部分代码...... try { // 当componentName为null时表示使用默认壁纸。 // 这里会将componentName参数改为默认壁纸的componentName if (componentName == null) { // mDefaultWallpaperComponent是从com.android.internal.R.string.default_wallpaper_component // 中获取默认壁纸的componentName。这个值的设置位于res/values/config.xml中, // 开发者可以通过修改这个值设置默认壁纸 componentName = mDefaultWallpaperComponent; // 倘若在上述的资源文件中没有指定一个默认壁纸,即default_wallpaper_component的 // 值被设置为@null),则使用ImageWallpaper代替默认壁纸。ImageWallpaper就是前文所述的静态壁纸, // 路径在:R.string.image_wallpaper_component if (componentName == null) { componentName = mImageWallpaper; } } // 接下来WallpaperMangerService会尝试从PackageManager中尝试获取ComponentName所 // 指定的Service的描述信息,获取此信息的目的在于确认该Service是一个符合要求的壁纸服务 int serviceUserId = wallpaper.userId; ServiceInfo si = mIPackageManager.getServiceInfo(componentName, PackageManager.GET_META_DATA | PackageManager.GET_PERMISSIONS, serviceUserId); if (si == null) { return false; } // 第一个检查,要求这个Service必须声明其访问权限为 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; } WallpaperInfo wi = null; // 第二个检查,要求这个Service必须可以用来处理android.service.wallpaper.WallpaperService这个Action // 其检查方式是从PackageManager中查询所有可以处理 android.service.wallpaper.WallpaperService的服务, // 然后检查即将启动的服务是否在PackageManager的查询结果之中 Intent intent = new Intent(WallpaperService.SERVICE_INTERFACE); if (componentName != null && !componentName.equals(mImageWallpaper)) { // Make sure the selected service is actually a wallpaper service. // 获取所有可以处理android.service.wallpaper.WallpaperService的服务信息 List ris = mIPackageManager.queryIntentServices(intent, intent.resolveTypeIfNeeded(mContext.getContentResolver()), PackageManager.GET_META_DATA, serviceUserId).getList(); // 第二个检查,这个检查来校验服务是否声明了android.service.wallpaper.WallpaperService这个action。 // 如果这个服务没有声明这个action的话那么,ris中就不会含有这个component信息。 for (int i=0; i


【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3