Android Wallpaper之设置壁纸流程
What?
什么是壁纸?
android wallpaper包括锁屏壁纸和桌面壁纸,壁纸又区分静态和动态两种。我们每天使用手机第一眼看到的就是壁纸,好看的壁纸对于手机的颜值也有大大的提升(滑稽),就让我们对壁纸一探究竟吧。
本文基于Android 8.1源码,相关文件如下:
1./frameworks/base/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
2./frameworks/base/services/core/java/android/app/WallpaperManager.java
3./frameworks/base/core/java/android/service/wallpaper/WallpaperService.java
4./frameworks/base/core/java/android/service/wallpaper/IwallpaperService.aidl
5./frameworks/base/packages/apps/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenWallpaper.java
6./frameworks/base/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
7./frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
How?
1.作为开发者如何去设置壁纸?
2.壁纸是怎么显示的?
3.壁纸存储在什么位置?
如何设置壁纸?举个栗子。
咱们手机中一般有内置主题的应用,在这里可以下载使用非常多好看的壁纸,点击即可设置为锁屏或桌面壁纸。
设置方法很简单,look:
WallpaperManager wallpaperManager = WallpaperManager.getInstance(this);
try {
wallpaperManager.setStream(InputStream,null,true,WallpaperManager.FLAG_LOCK);
} catch (IOException e) {e.printStackTrace();
}
三步骤:
1.添加设置壁纸的权限
<uses-permission android:name="android.permission.SET_WALLPAPER"/>
2.获取WallpaperManager对象
3.设置壁纸,四个参数分别对应:
a.InputStream:图片对于的输入流
b.visibleCropHint:图片裁剪相关,一般默认为null
c.allowBack:是否允许回退
d.which:壁纸分为锁屏壁纸和桌面壁纸,所以需要设置FLGA:锁屏壁纸--WallpaperManager.FLAG_LOCK,桌面壁纸:WallpaperManager.FLAG_SYSTEM
壁纸是如何显示的?
下面就对如上图中的每个过程做一个简单的分析:
1.setStream
try {//sGlobals.mService即WallpaperManagerServiceParcelFileDescriptor fd = sGlobals.mService.setWallpaper(null,mContext.getOpPackageName(), visibleCropHint, allowBackup,result, which, completion, UserHandle.myUserId());if (fd != null) {FileOutputStream fos = null;try {//将壁纸copy一份并存储到对应目录,默认是/data/system/users/0/wallpaper(或wallpaper_lock),其中0是主用户的userId,支持多用户fos = new ParcelFileDescriptor.AutoCloseOutputStream(fd);copyStreamToWallpaperFile(bitmapData, fos);fos.close();completion.waitForCompletion();} finally {IoUtils.closeQuietly(fos);}}} catch (RemoteException e) {throw e.rethrowFromSystemServer();}
}
2.WallpaperManagerService.java#setWallpaper
@Overridepublic ParcelFileDescriptor setWallpaper(String name, String callingPackage,Rect cropHint, boolean allowBackup, Bundle extras, int which,IWallpaperManagerCallback completion, int userId) {//检查有没有设置壁纸的权限checkPermission(android.Manifest.permission.SET_WALLPAPER);//调用setStream方法的时候参数which必须是正确的if ((which & (FLAG_LOCK|FLAG_SYSTEM)) == 0) {final String msg = "Must specify a valid wallpaper category to set";Slog.e(TAG, msg);throw new IllegalArgumentException(msg);}/* If we're setting system but not lock, and lock is currently sharing the system* wallpaper, we need to migrate that image over to being lock-only before* the caller here writes new bitmap data.*///如果当前没有锁屏壁纸的话,并且是设置桌面壁纸即which == FLAG_SYSTEM,那么同时设置为锁屏壁纸if (which == FLAG_SYSTEM && mLockWallpaperMap.get(userId) == null) {if (DEBUG) {Slog.i(TAG, "Migrating system->lock to preserve");}migrateSystemToLockWallpaperLocked(userId);}ParcelFileDescriptor pfd = updateWallpaperBitmapLocked(name, wallpaper, extras);}
3.最主要的方法:WallpaperObserver#onEvent
WallpaperObserver是WallpaperManagerservice.java的内部类,它的主要职责是监听文件变化,也就是壁纸对应的文件更新,看下源码中关于它的注释:
/*** Observes the wallpaper for changes and notifies all IWallpaperServiceCallbacks* that the wallpaper has changed. The CREATE is triggered when there is no* wallpaper set and is created for the first time. The CLOSE_WRITE is triggered* every time the wallpaper is changed.*/
监听wallpaper变化并通知IWallpaperServiceCallbacks,前文提到的LockscreenWallpaper就是继承了
IWallpaperServiceCallbacks,并重写了它的onWallppaerChanged方法,在这里更新锁屏壁纸的。
@Overridepublic void onEvent(int event, String path) {//如果是锁屏壁纸更新if (moved && lockWallpaperChanged) {notifyLockWallpaperChanged();//android 8.0新增的一个变化,锁屏包括下拉快捷的主题会根据当前的壁纸来变化,避免壁纸和锁屏的图标颜色一致导致的显示不清问题,但是有一个缺陷就是://获取的是当前壁纸的主色调,而不是某个区域的主色调,这样就会导致虽然主色调是白色,比如时间的区域是黑色,这一点小米做的比较好,它是根据当前区域的壁纸的主色调来进行反色的。notifyWallpaperColorsChanged(wallpaper, FLAG_LOCK);return;}if (sysWallpaperChanged || lockWallpaperChanged) {notifyCallbacksLocked(wallpaper);}if (sysWallpaperChanged) {//桌面壁纸变化,那么bind ImageWallpaper,ImageWallpaper是负责显示静态桌面壁纸的// If this was the system wallpaper, rebind...bindWallpaperComponentLocked(mImageWallpaper, true,false, wallpaper, null);notifyColorsWhich |= FLAG_SYSTEM;}if (lockWallpaperChanged|| (wallpaper.whichPending & FLAG_LOCK) != 0) {if (DEBUG) {Slog.i(TAG, "Lock-relevant wallpaper changed");}// either a lock-only wallpaper commit or a system+lock event.// if it's system-plus-lock we need to wipe the lock bookkeeping;// we're falling back to displaying the system wallpaper there.//如果参数which是system+lock,也就是同时设置锁屏和桌面壁纸,那么remove锁屏壁纸,因为已经是同一张壁纸了if (!lockWallpaperChanged) {mLockWallpaperMap.remove(wallpaper.userId);}// and in any case, tell keyguard about itnotifyLockWallpaperChanged();notifyColorsWhich |= FLAG_LOCK;}}
先看锁屏壁纸更新这一部分:
void notifyLockWallpaperChanged() {final IWallpaperManagerCallback cb = mKeyguardListener;if (cb != null) {try {cb.onWallpaperChanged();} catch (RemoteException e) {// Oh well it went away; no big deal}}}
mKeyguardListener赋值的地方:
@Overridepublic boolean setLockWallpaperCallback(IWallpaperManagerCallback cb) {checkPermission(android.Manifest.permission.INTERNAL_SYSTEM_WINDOW);synchronized (mLock) {mKeyguardListener = cb;}return true;}
前面我们说过LockscreenWallpaper.java是继承了IWallpaperManagerCallback的,那么setLockWallpaperCallback调用的地方应该是在这里:
public LockscreenWallpaper(Context ctx, PhoneStatusBar bar, Handler h) {mService = IWallpaperManager.Stub.asInterface(ServiceManager.getService(Context.WALLPAPER_SERVICE));mWallpaperManager = (WallpaperManager) ctx.getSystemService(Context.WALLPAPER_SERVICE);try {//在这里给mKeyguardListener赋值的mService.setLockWallpaperCallback(this);} catch (RemoteException e) {Log.e(TAG, "System dead?" + e);}}
4.LockscreenWallpaper.java#onWallpaperChanged:
@Overridepublic void onWallpaperChanged() {// Called on Binder thread.mH.removeCallbacks(this);mH.post(this);}
LockscreenWallpaper实现了Runnable接口的,所以看下它的run方法:
@Overridepublic void run() {// Called in response to onWallpaperChanged on the main thread.mLoader = new AsyncTask<Void, Void, LoaderResult>() {@Overrideprotected LoaderResult doInBackground(Void... params) {return loadBitmap(currentUser, selectedUser);}@Overrideprotected void onPostExecute(LoaderResult result) {super.onPostExecute(result);if (isCancelled()) {return;}if (result.success) {mCached = true;mCache = result.bitmap;mUpdateMonitor.setHasLockscreenWallpaper(result.bitmap != null);//通知StatsuBar更新壁纸mBar.updateMediaMetaData(true /* metaDataChanged */, true /* allowEnterAnimation */);}mLoader = null;}}.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);}
异步获取壁纸,并通知StatusBar去更新壁纸。
5.StatusBar.java#updateMediaMetaData:
这里主要就是对锁屏壁纸所在的View做最基本的setImageBitmap。
6.再看桌面壁纸部分:
6.1 bindWallpaperComponentLocked
if (sysWallpaperChanged) {// If this was the system wallpaper, rebind...bindWallpaperComponentLocked(mImageWallpaper, true,false, wallpaper, null);notifyColorsWhich |= FLAG_SYSTEM;}
context.getResources().getString(R.string.image_wallpaper_component));
也就是一开始提到的:
/frameworks/base/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
boolean bindWallpaperComponentLocked(ComponentName componentName, boolean force,boolean fromUser, WallpaperData wallpaper, IRemoteCallback reply) {Intent intent = new Intent(WallpaperService.SERVICE_INTERFACE);WallpaperConnection newConn = new WallpaperConnection(wi, wallpaper);//componentName就是ImageWallpaperintent.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)));}
ImageWallpaper继承了Service,既然是bindService,那么主要看下conn,也就是WallpaperConnection。
@Overridepublic void onServiceConnected(ComponentName name, IBinder service) {synchronized (mLock) {if (mWallpaper.connection == this) {mService = IWallpaperService.Stub.asInterface(service);attachServiceLocked(this, mWallpaper);// XXX should probably do saveSettingsLocked() later// when we have an engine, but I'm not sure about// locking there and anyway we always need to be able to// recover if there is something wrong.saveSettingsLocked(mWallpaper.userId);FgThread.getHandler().removeCallbacks(mResetRunnable);}}}
6.2 继续看attachServcieLocked方法:
void attachServiceLocked(WallpaperConnection conn, WallpaperData wallpaper) {try {conn.mService.attach(conn, conn.mToken,TYPE_WALLPAPER, false,wallpaper.width, wallpaper.height, wallpaper.padding);} catch (RemoteException e) {Slog.w(TAG, "Failed attaching wallpaper; clearing", e);if (!wallpaper.wallpaperUpdating) {bindWallpaperComponentLocked(null, false, false, wallpaper, null);}}}
conn.mService.attach是调用了IWallpaperServiceWrapper 的attach方法,IWallpaperServiceWrapper 继承了
@Overridepublic void attach(IWallpaperConnection conn, IBinder windowToken,int windowType, boolean isPreview, int reqWidth, int reqHeight, Rect padding) {new IWallpaperEngineWrapper(mTarget, conn, windowToken,windowType, isPreview, reqWidth, reqHeight, padding);}
在看它的构造方法,发送了一个DO_ATTACH的消息:
Message msg = mCaller.obtainMessage(DO_ATTACH);
mCaller.sendMessage(msg);
case DO_ATTACH: {try {mConnection.attachEngine(this);} catch (RemoteException e) {Log.w(TAG, "Wallpaper host disappeared", e);return;}Engine engine = onCreateEngine();mEngine = engine;mActiveEngines.add(engine);engine.attach(this);return;}
onCreateEngine也是一个抽象的方法:
/*** Must be implemented to return a new instance of the wallpaper's engine.* Note that multiple instances may be active at the same time, such as* when the wallpaper is currently set as the active wallpaper and the user* is in the wallpaper picker viewing a preview of it as well.*/public abstract Engine onCreateEngine();
实现的地方仍然是在ImageWallpaper.java里
@Overridepublic Engine onCreateEngine() {mEngine = new DrawableEngine();return mEngine;}
DrawableEngine是自定义的继承Engine的内部类
最后调用engine.attach方法。
WallpaperService.java的attach方法:
void attach(IWallpaperEngineWrapper wrapper) {onCreate(mSurfaceHolder);mInitializing = false;mReportedVisible = false;updateSurface(false, false, false);}
6.3 onCreate(mSurfaceHolder)
它是一个抽象方法
/*** Called once to initialize the engine. After returning, the* engine's surface will be created by the framework.*/public void onCreate(SurfaceHolder surfaceHolder) {}
它是一个抽象方法,那么真正的实现是在它的子类,也就是ImageWallpaper.java里
@Overridepublic void onCreate(SurfaceHolder surfaceHolder) {if (DEBUG) {Log.d(TAG, "onCreate");}super.onCreate(surfaceHolder);mDefaultDisplay = getSystemService(WindowManager.class).getDefaultDisplay();setOffsetNotificationsEnabled(false);updateSurfaceSize(surfaceHolder, getDefaultDisplayInfo(), false /* forDraw */);}
6.4 updateSurfaceSize
6.5 drawFrame
对壁纸进行一些裁剪操作,根据是否支持硬件加速来决定绘制的方法:
//支持硬件加速if (mIsHwAccelerated) {if (!drawWallpaperWithOpenGL(sh, availw, availh, xPixels, yPixels)) {drawWallpaperWithCanvas(sh, availw, availh, xPixels, yPixels);}} else {drawWallpaperWithCanvas(sh, availw, availh, xPixels, yPixels);if (FIXED_SIZED_SURFACE) {// If the surface is fixed-size, we should only need to// draw it once and then we'll let the window manager// position it appropriately. As such, we no longer needed// the loaded bitmap. Yay!// hw-accelerated renderer retains bitmap for faster rotationunloadWallpaper(false /* forgetSize */);}}
到这里,把壁纸的设置的简单过程基本上就讲完了,作为笔记做一个记录。
如有错误的地方,欢迎指正。
Android Wallpaper之设置壁纸流程相关推荐
- Android Audio音量设置原理流程分析
Android Audio音量设置原理流程分析 简介 本篇文章主要介绍Android音量设置从App应用层到framework层执行流程,以及相关的细节和原理分析,建议在阅读此文章前去看博主的混音理论 ...
- Android 10 设置壁纸流程
---------------------------------------------------------------------------------------------------- ...
- Android 设置壁纸流程
W/InputManager-JNI: Input channel object '7d2a123 com.android.wallpaperpicker/com.android.wallpaperp ...
- android 恢复出厂设置代码流程(Good!)
android的恢复出厂设置 文章问多一般都是从完整的recover mode讲起,恢复出厂设置只是 recovery mode下一个小部分. recovery mode流程分析的文章很多,比较完整的 ...
- Android 恢复出厂设置上层流程
最近看恢复出厂的一个问题,以前也查过这方面的流程,所以这里整理一些AP+framework层的流程: 在setting-->备份与重置--->恢复出厂设置--->重置手机---> ...
- Android恢复出厂设置代码流程分析
工作中排查到了恢复出厂设置的bug, 有一些细节是需要注意的,于是把这块的代码流程看一下: 代码基于:Android9.0 应用层: 就发送MASTER_CLEAR的广播, 这里没有带参数的 priv ...
- android恢复出厂设置的流程
原址:http://blog.csdn.net/sergeycao/article/details/8151808 在MasterClearConfirm.java中显示恢复出厂提示和对应button ...
- ANDROID手表怎么设置壁纸,表盘背景随心换 果壳智能手表换壁纸教程
在智能手机和电脑上,我们一般都会换一张自己喜欢的图片作为壁纸,当我们想把一张喜欢的图片做成果壳GEAK Watch表盘时,应该怎么做呢?其实只要简单的几个步骤,你就能获得独一无二的专属表盘. 首先,打 ...
- Android Launcher 设置壁纸
版本:1.0 日期:2014.11.25 2014.11.26 版权:©kince 特别推荐:泡在网上的日子 一.概述 一般Launcher都带有壁纸设置的功能,Android提供了设置壁纸的API ...
最新文章
- webpack4 系列教程: 前言
- ADO.NET入门教程(一) 初识ADO.NET
- DRAGAN + SRCNN 的简单理解
- 微信小程序 事件点击后如何动态增删class类名(自用,没毛病)
- JVM解释器和编译器
- linux查看删除init内容,linux常用命令
- modelsim仿真不出波形_直接扩频通信(下)仿真
- Access数据库问题锦集
- Lc5-最长回文字串
- mysql - binlog主从复制
- python逐行比较两个csv_python内两个CSV文件数据比较。。。求大神解答!!
- jquery 事件冒泡的介绍
- 微信小程序demo汇总
- oracle solaris 10 是什么,Oracle Solaris 10 操作系统
- 精仿互站模板 友价源码商城T5内核二次开发运营版
- 前端通过后端返回文件流下载文件
- 【jmx学习】2、jmx之Html Adaptor
- 论文解读:Semantic Neural Machine Translation using AMR
- JAVA基础复习(一)
- 计算机病毒辽师大教案,四年级下信息技术教案计算机病毒知识二辽师大版.docx...
热门文章
- 从苏宁电器到卡巴斯基第25篇:难忘的三年硕士时光 III
- OneDrive账号被冻结如何进行解冻
- python ftp上传/下载文件,带进度条显示
- streamlit基本使用
- 设想2015年,云计算厂商将与线下渠道紧密合作
- 发现几个有趣的公众号!
- 微信小程序:去除自带顶部导航栏
- java监听适配器是什么_JAVA 监听器与适配器 小小的分析
- 考研:研究生考试(十五天学完)之《高等数学上/下册》研究生学霸重点知识点总结之考试内容各科占比及常考知识重点梳理(函数极限连续、一元/多元函数微分学/积分学、常微分函数、向量代数与空间几何、无穷级数)
- 2020扁皮筋排名_CSCC2020赛季竞赛规则