RTFSC - Android5.1 壁纸设置流程简析

Android5.1 壁纸设置流程浅析

Ubuntu14.04  Android5.1  Source Insight3

源代码请参阅http://androidxref.com/

这里只是简单分析一下5.1里是如何设置壁纸的;这个流程和4.4有一些不同。但基本都是找个地方存放壁纸文件,需要的时候读取,设置的时候更新

这里只看设置的过程。权当参考。

机器使用launcher3,在桌面上长按,底部显示设置壁纸的入口。
进入设置壁纸界面,观察log可知,此界面属于Trebuchet。也是launcher3
点击设置壁纸按钮,发现整个标题栏都有响应。在以下文件中可以找到相关定义:

/*--- ↓ --- WallpaperPickerActivity.java *********/

        // Action bar
        // Show the custom action bar view
        final ActionBar actionBar = getActionBar();
        actionBar.setCustomView(R.layout.actionbar_set_wallpaper);
        actionBar.getCustomView().setOnClickListener(
                new View.OnClickListener() {
                    @Override
                    public void onClick(View v) {
                        if (mSelectedTile != null) {
                            WallpaperTileInfo info = (WallpaperTileInfo) mSelectedTile.getTag();
                            info.onSave(WallpaperPickerActivity.this);
                        } else {
                            // no tile was selected, so we just finish the activity and go back
                            setResult(Activity.RESULT_OK);
                            finish();
                        }
                    }
                });
        mSetWallpaperButton = findViewById(R.id.set_wallpaper_button);
/*--- ↑ --- (packages/apps/Trebuchet/WallpaperPicker/src/com/android/launcher3/) *********/

整个 actionBar都拿来响应点击。info是 WallpaperTileInfo抽象类的对象,有5个子类。
这样系统能根据图片来选择使用哪一个子类。再调用 onSave方法。以FileWallpaperInfo类为例。
它复写了 onSave方法

/*--- ↓ --- WallpaperPickerActivity.java *********/
        @Override
        public void onSave(WallpaperPickerActivity a) {
            a.setWallpaper(Uri.fromFile(mFile), true);
        }
/*--- ↑ --- (packages/apps/Trebuchet/WallpaperPicker/src/com/android/launcher3/) *********/

因为WallpaperPickerActivity 继承自 WallpaperCropActivity
setWallpaper方法在 WallpaperCropActivity中找到;传入了Uri和一个boolean值

/*↓ --- WallpaperCropActivity.java *********/
    protected void setWallpaper(Uri uri, final boolean finishActivityWhenDone) {
        int rotation = getRotationFromExif(this, uri);
        BitmapCropTask cropTask = new BitmapCropTask(
                this, uri, null, rotation, 0, 0, true, false, null);
        final Point bounds = cropTask.getImageBounds();
        Runnable onEndCrop = new Runnable() {
            public void run() {
                updateWallpaperDimensions(bounds.x, bounds.y);
                if (finishActivityWhenDone) {
                    setResult(Activity.RESULT_OK);
                    finish();
                }
            }
        };
        cropTask.setOnEndRunnable(onEndCrop);
        cropTask.setNoCrop(true);
        cropTask.execute();
    }
/*↑ --- (packages/apps/trebuchet/wallpaperpicker/src/com/android/launcher3) *********/    

以上代码可以看出,它启动了一个异步任务  BitmapCropTask extends AsyncTask ;把一些列参数都传入了cropTask
启动一个线程onEndCrop,等待任务结束
进入BitmapCropTask看,系统做了什么工作
这是setWallpaper使用的构造方法

/*↓ --- WallpaperCropActivity.java *********/
        public BitmapCropTask(Context c, Uri inUri,
                RectF cropBounds, int rotation, int outWidth, int outHeight,
                boolean setWallpaper, boolean saveCroppedBitmap, Runnable onEndRunnable) {
            mContext = c;
            mInUri = inUri;
            init(cropBounds, rotation,
                    outWidth, outHeight, setWallpaper, saveCroppedBitmap, onEndRunnable);
        }
// ......
        private void init(RectF cropBounds, int rotation, int outWidth, int outHeight,
                boolean setWallpaper, boolean saveCroppedBitmap, Runnable onEndRunnable) {
            Log.d("rust","init after bitmapcroptask");
            mCropBounds = cropBounds;
            mRotation = rotation;    //    传入各项参数
            mOutWidth = outWidth;
            mOutHeight = outHeight;
            mSetWallpaper = setWallpaper;
            mSaveCroppedBitmap = saveCroppedBitmap;
            mOnEndRunnable = onEndRunnable;
        }
// ...... 在这里处理任务,启动 cropBitmap()
        @Override
        protected Boolean doInBackground(Void... params) {
            return cropBitmap();
        }
// ......
        public boolean cropBitmap() {
            boolean failure = false;

            WallpaperManager wallpaperManager = null;
            if (mSetWallpaper) {
                wallpaperManager = WallpaperManager.getInstance(mContext.getApplicationContext());
            }

            if (mSetWallpaper && mNoCrop) {
                try {
                    InputStream is = regenerateInputStream();  // 获得InputStream
                    if (is != null) {
                        wallpaperManager.setStream(is);    // 把is写进去,从这里进入到wallpaperManager
                        Utils.closeSilently(is);
                    }
                } catch (IOException e) {
                    Log.w(LOGTAG, "cannot write stream to wallpaper", e);
                    failure = true;
                }
                return !failure;
            } else { // 顺利的话不会进入这里
// ......
        // Helper to setup input stream
        private InputStream regenerateInputStream() {
            if (mInUri == null && mInResId == 0 && mInFilePath == null && mInImageBytes == null) {
                Log.w(LOGTAG, "cannot read original file, no input URI, resource ID, or " +
                        "image byte array given");
            } else {
                try {
                    String filepath = mInFilePath;
                    if(mInUri != null){
                        filepath = DrmHelper.getFilePath(mContext, mInUri);
                    }
                    if(DrmHelper.isDrmFile(filepath)){
                        byte[] bytes = DrmHelper.getDrmImageBytes(filepath);
                        return new ByteArrayInputStream(bytes);    //    返回一个ByteArray
                    }
/*↑ --- (packages/apps/trebuchet/wallpaperpicker/src/com/android/launcher3) *********/    

调用了wallpaperManager.setStream(is);进入 WallpaperManager.java 看看

/*↓ --- WallpaperManager.java *********/

    public void setStream(InputStream data) throws IOException {  //luncher3 里直接调用了这个方法
        if (sGlobals.mService == null) {                          //WallpaperCropActivity 调用
            Log.w(TAG, "WallpaperService not running");
            return;
        }
        try {
            ParcelFileDescriptor fd = sGlobals.mService.setWallpaper(null);  // 利用service得到fd
            if (fd == null) {    // 这里要去WallpaperManagerService里找到对应方法
                return;
            }
            FileOutputStream fos = null;
            try {
                fos = new ParcelFileDescriptor.AutoCloseOutputStream(fd);
                setWallpaper(data, fos);    // 写入壁纸数据
            } finally {
                if (fos != null) {
                    fos.close();
                }
            }
        } catch (RemoteException e) {
            // Ignore
        }
    }
// ...... 在这里写入壁纸,写入过程结束,Manager完成任务
    private void setWallpaper(InputStream data, FileOutputStream fos)
            throws IOException {
        byte[] buffer = new byte[32768];
        int amt;
        while ((amt=data.read(buffer)) > 0) {
            fos.write(buffer, 0, amt);
        }
    }
/*↑ --- (framework/base/core/java/android/app) *********/    

再来看Globals extends IWallpaperManagerCallback.Stub
包含成员变量 IWallpaperManager mService,通过

Globals(Looper looper) {
    IBinder b = ServiceManager.getService(Context.WALLPAPER_SERVICE);
        mService = IWallpaperManager.Stub.asInterface(b);
    }

调用 WallpaperManagerService.java 里的setWallpaper方法

Service的作用

进入WallpaperManagerService看看,是如何返回ParcelFileDescriptor值的

/*↓ --- WallpaperManagerService.java *********/
    public ParcelFileDescriptor setWallpaper(String name) {    //  供 WallpaperManager 调用
        checkPermission(android.Manifest.permission.SET_WALLPAPER);
        synchronized (mLock) {
            if (DEBUG) Slog.v(TAG, "setWallpaper");
            int userId = UserHandle.getCallingUserId();
            WallpaperData wallpaper = mWallpaperMap.get(userId);
            if (wallpaper == null) {
                throw new IllegalStateException("Wallpaper not yet initialized for user " + userId);
            }
            final long ident = Binder.clearCallingIdentity();
            try {
                ParcelFileDescriptor pfd = updateWallpaperBitmapLocked(name, wallpaper);
                if (pfd != null) {// 启动下面的方法
                    wallpaper.imageWallpaperPending = true;
                }
                return pfd;    // 返回ParcelFileDescriptor值给WallpaperManager
            } finally {
                Binder.restoreCallingIdentity(ident);
            }
        }
    }

    ParcelFileDescriptor updateWallpaperBitmapLocked(String name, WallpaperData wallpaper) {
        if (name == null) name = "";
        try {
            File dir = getWallpaperDir(wallpaper.userId);  // 找到壁纸
            if (!dir.exists()) {
                dir.mkdir();
                FileUtils.setPermissions(
                        dir.getPath(),
                        FileUtils.S_IRWXU|FileUtils.S_IRWXG|FileUtils.S_IXOTH,
                        -1, -1);
            }
            File file = new File(dir, WALLPAPER); //创建一个新的文件
            ParcelFileDescriptor fd = ParcelFileDescriptor.open(file,
                    MODE_CREATE|MODE_READ_WRITE|MODE_TRUNCATE);
            if (!SELinux.restorecon(file)) {
                return null;
            }
            wallpaper.name = name;
            return fd;    // 返回ParcelFileDescriptor值
        } catch (FileNotFoundException e) {
            Slog.w(TAG, "Error setting wallpaper", e);
        }
        return null;
    }

    private static File getWallpaperDir(int userId) {  // 取得壁纸文件
        return Environment.getUserSystemDirectory(userId);
    }
/*↑ --- (framework/base/services/core/java/com/android/server/wallpaper/) *********/

至此一个简单的流程就结束了
我们可反过来看
壁纸文件是存在 Environment.getUserSystemDirectory(userId) 这个路径下
WallpaperManagerService取到这个文件后,将其打开为ParcelFileDescriptor形式,交给WallpaperManager
WallpaperManager把launcher传来的数据写入这个文件中
小结一下:
选定壁纸,点击按钮,launcher把壁纸信息给WallpaperManager;
WallpaperManagerService把存放壁纸的文件打开,交给WallpaperManager
WallpaperManager把壁纸信息写入;一次设置壁纸的动作就完成了

这里传输数据使用到engine,一个挺有意思的东西。

时间: 2024-10-27 07:30:42

RTFSC - Android5.1 壁纸设置流程简析的相关文章

SylixOS中TPSFS格式化流程简析

1.TPSFS简介 TPSFS文件系统是一款掉电安全的文件系统,该系统是SylixOS内建文件系统(专利技术),该文件系统有如下特点: 采用B+树存储文件数据,读取与定位速度快,空间管理效率高: 对数据使用原子操作,掉电安全: 64位文件系统,支持EP级别文件长度: 大文件处理性能好: 支持文件记录锁,可支持大型数据库: 支持多块分配机制,提高了系统性能,使得分配器有了充足的优化空间: 支持子目录可扩展性,使得在一个目录下可以创建无数多个子目录. TPSFS文件系统结构如图 1.1所示. 图 1

android5.0系统设置搜索功能简析

一.目的: 简单介绍系统设置搜索功能实现,初步熟悉搜索数据库构建规则以及匹配逻辑. 二.相关问题解答 1.系统设置可对那些设置项进行构建搜索数据库? 答:系统设置对数据项的构建规则在com.android.settings.search.SearchIndexableResources类中进行定义,例如如下,将wifi设置,wifi高级设置设置项假如搜索数据匹配库. sResMap.put(WifiSettings.class.getName(), new SearchIndexableReso

Linux系统启动流程简析

在日常生活中,我们开机的操作一般为按下电源键,等待系统自己起来就好了.这开机的过程看似简单,但其中却包含着十分复杂的各种小过程.以Linux为例,其流程为下图所示: 一.POST 首先,先介绍下BIOS和POST的概念. BIOS:Basic Input Output System,即基本输入输出系统,它是一组固化到计算机内主板上一个ROM芯片上的程序,它保存着计算机最重要的基本输入输出的程序.开机后自检程序和系统自启动程序,它可从CMOS中读写系统设置的具体信息.其主要功能是为计算机提供最底层

springboot启动流程简析

Spring Boot可以轻松创建独立的,生产级的基于Spring的应用程序,而这只需要很少的一些Spring配置.本文将从SpringBoot的启动流程角度简要的分析SpringBoot启动过程中主要做了哪些事情. 说明: springboot 2.0.6.RELEASE SpringBoot启动简要流程图 附原始大图链接 启动流程概述 启动流程从角度来看,主要分两个步骤.第一个步骤是构造一个SpringApplication应用,第二个步骤是调用它的run方法,启动应用. 1 构造Sprin

SIFT特征原理简析(HELU版)

SIFT(Scale-Invariant Feature Transform)是一种具有尺度不变性和光照不变性的特征描述子,也同时是一套特征提取的理论,首次由D. G. Lowe于2004年以<Distinctive Image Features from Scale-Invariant Keypoints[J]>发表于IJCV中.开源算法库OpenCV中进行了实现.扩展和使用. 本文主要依据原始论文和网络上相关专业分析,对SIFT特征提取的算法流程进行简单分析.由于涉及到的知识概念较多,本人

Android -- MediaPlayer内部实现简析

Android -- MediaPlayer内部实现简析 在之前的博客中,已经介绍了使用MediaPlayer时要注意的内容.现在,这里就通过一个MediaPlayer代码实例,来进一步分析MediaPlayer内部是如何运作.实现的:当然这里的分析只截止到底层调用播放器之前,因为播放器这块实在是没搞懂. 我们使用的例子来源于之前MediaPlayer Playback译文中的官方实例: String url = "http://........"; // your URL here

最简单的基于FFMPEG的转码程序分析 +ffmpga代码简析(转 +总结)

模块:  libavcodec    - 编码解码器         libavdevice   - 输入输出设备的支持         libavfilter   - 视音频滤镜支持         libavformat   - 视音频等格式的解析         libavutil     - 工具库         libpostproc   - 后期效果处理         libswscale    - 图像颜色.尺寸转换 1. ffmpga代码简析 1.1 av_log() av_

0002 - Spring MVC 拦截器源码简析:拦截器加载与执行

1.概述 Spring MVC中的拦截器(Interceptor)类似于Servlet中的过滤器(Filter),它主要用于拦截用户请求并作相应的处理.例如通过拦截器可以进行权限验证.记录请求信息的日志.判断用户是否登录等. 2.简单示例 2.1.继承 HandlerInterceptorAdapter 抽象类实现一个拦截器.代码如下: public class DemoInterceptor extends HandlerInterceptorAdapter { @Override    pu

web应用构架LAMT及tomcat负载简析

Httpd    (mod_jk.so) workers.properties文件 uriworkermap.properties文件 <--AJP1.3--> Tomcat  --> jdk 大致流程:apache服务器通过mod_jk.so 模块处理jsp文件的动态请求.通过tomcat worker等待执行servlet/JSP的tomcat实例.使用 AJP1.3协议与tomcat通信.tomcat有借助jdk解析. 负载就是 多台tomcat.共同解析apache发送的jsp请