Android Wallpaper之设置壁纸流程

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.添加设置壁纸的权限

2.获取WallpaperManager对象

3.设置壁纸,四个参数分别对应:

a.InputStream:图片对于的输入流

b.visibleCropHint:图片裁剪相关,一般默认为null

c.allowBack:是否允许回退

d.which:壁纸分为锁屏壁纸和桌面壁纸,所以需要设置FLGA:锁屏壁纸--WallpaperManager.FLAG_LOCK,桌面壁纸:WallpaperManager.FLAG_SYSTEM

壁纸是如何显示的?

下面就对如上图中的每个过程做一个简单的分析:

1.setStreamtry {

//sGlobals.mService即WallpaperManagerService

ParcelFileDescriptor 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 @Override

public 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#onEventWallpaperObserver是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方法,在这里更新锁屏壁纸的。

@Override

public 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 it

notifyLockWallpaperChanged();

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赋值的地方:

@Override

public 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:@Override

public void onWallpaperChanged() {

// Called on Binder thread.

mH.removeCallbacks(this);

mH.post(this);

}LockscreenWallpaper实现了Runnable接口的,所以看下它的run方法: @Override

public void run() {

// Called in response to onWallpaperChanged on the main thread.

mLoader = new AsyncTask() {

@Override

protected LoaderResult doInBackground(Void... params) {

return loadBitmap(currentUser, selectedUser);

}

@Override

protected 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 bindWallpaperComponentLockedif (sysWallpaperChanged) {

// If this was the system wallpaper, rebind...

bindWallpaperComponentLocked(mImageWallpaper, true,

false, wallpaper, null);

notifyColorsWhich |= FLAG_SYSTEM;

}mImageWallpaper = ComponentName.unflattenFromString( 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就是ImageWallpaper

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)));

}ImageWallpaper继承了Service,既然是bindService,那么主要看下conn,也就是WallpaperConnection。它的onServiceConnected方法: @Override

public 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 继承了IWallpaperService.Stub。 @Override

public 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里

@Override

public 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里 @Override

public 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 */);

}

surfaceHolder是在父类里初始化的,surfaceHolder = new BaseSurfaceHolder();6.4 updateSurfaceSize 在这里主要是继续调用loadWallpaper方法去解析壁纸并最终绘制到surfaceHolder上。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 rotation

unloadWallpaper(false /* forgetSize */);

}

}到这里,把壁纸的设置的简单过程基本上就讲完了,作为笔记做一个记录。

如有错误的地方,欢迎指正。

🎀 相关推荐

25个够开头的词语大全(够开头的成语有哪些成语)
🎯 best365体育正不正规

25个够开头的词语大全(够开头的成语有哪些成语)

📅 09-15 👀 3574
测眼型软件
🎯 棋牌365大厅

测眼型软件

📅 06-27 👀 620
越发造句
🎯 beat365娱乐网址

越发造句

📅 09-11 👀 6314