Android-动态壁纸的实现

前言:

  最近遇到了几个很恶心的问题,第一个是手机录制视频发现资源不够用,第二个是手机播放视频发现卡顿。找了半天两个问题的root cause都是手机动态壁纸。我们手机动态壁纸的实现是无论当前壁纸是否处于前台,video instance都是一直活跃的。不得不说,这个设计真的很过分,一是浪费资源,二是会引起很多video的问题。所以今天就了解下动态壁纸的具体实现过程,看看是否可以优化这个设计……

Android壁纸的实现和管理概述:

Android壁纸的实现和管理分为三层:

  • WallpaperService与Engine

  壁纸运行在一个Android服务之中,这个服务的名字叫做WallpaperService。当用户选择了一个壁纸之后,此壁纸所对应的WallpaperService便会启动并开始进行壁纸的绘制工作。Engine是WallpaperService中的一个内部类,实现了壁纸窗口的创建以及Surface的维护工作。这一层次的内容主要体现了壁纸的实现原理。

  • WallpaperManagerService

  这个系统服务用于管理壁纸的运行与切换,并通过WallpaperManager类向外界提供操作壁纸的接口。这一层次主要体现了Android对壁纸的管理方式。

  • WindowManagerService

  用于计算壁纸窗口的Z序、可见性以及为壁纸应用窗口动画。这一层次主要体现了Android对壁纸窗口的管理方式。

 上层调用component_deinit的过程:

MediaCodec: stop  or release
ACodec::initiateShutdown 在此函数中会判断keepComponentAllocated是否为false,如果是false就发送kWhatReleaseCodecInstance
status_t err = mCodec->mOMXNode->freeNode();
Omx::freeNode // 此处调用mMaster->destroyComponentInstance
QComOMXPlugin::destroyComponentInstance // (*mFreeHandle)(reinterpret_cast<OMX_HANDLETYPE *>(component));
// mFreeHandle = (FreeHandleFunc)dlsym(mLibHandle, "OMX_FreeHandle");
OMX_FreeHandle // qc_omx_component_deinit
qc_omx_component_deinit   // eRet = pThis->component_deinit(hComp);

Android动态壁纸的具体实现:

  安卓壁纸的实现都是在WallpaperService中完成的,动态壁纸只是对壁纸功能的扩展,所以动态壁纸的实现首先会创建一个服务并集成WallpaperService。WallpaperService中有一个内部类Engine,Engine实现了实现了壁纸窗口的创建以及Surface的维护工作。就是说我们可以获取到一个SurfaceHolder,拿到这个东西就好办了,我们可以在上面画自己想要的东西或者把视频输出到上面去。下面直接看代码:

// packages/apps/ThemeManager/src/com/android/thememanager/service/VideoWallpaperService.java
public class VideoWallpaperService extends WallpaperService {
    private static final String TAG = "VideoWallpaper";

    public Engine onCreateEngine() { // 创建VideoEngine,VideoWallpaperService实现的内容都在VideoEngine中
        return new VideoEngine();
    }

    private class VideoEngine extends WallpaperService.Engine {

        private MediaPlayer mediaPlayer = new MediaPlayer(); // 创建播放器MediaPlayer
        private String path;
        private boolean isAudioOn;

        private AudioManager mAudioManager;
        // 创建AudioFocusChangeListener
        private AudioManager.OnAudioFocusChangeListener mOnAudioFocusChangeListener = new AudioManager.OnAudioFocusChangeListener() {
            @Override
            public void onAudioFocusChange(int focusChange) {
                // 略
            }
        };

        private BroadcastReceiver mUpdateVideoReceiver = new BroadcastReceiver() {

            @Override
            public void onReceive(Context context, Intent intent) {
                String action = intent.getAction();
                    // 略
                    initEngine(); // 初始化Engine
                }
            }
        };

        @Override
        public void onCreate(SurfaceHolder surfaceHolder) {
            // 略
        }

        @Override
        public void onDestroy() {
            super.onDestroy();
            mediaPlayer.release(); // 释放mediaPlayer
            unregisterReceiver(mUpdateVideoReceiver);
        }

        @Override
        public void onSurfaceCreated(SurfaceHolder holder) {
            super.onSurfaceCreated(holder);
            initEngine();
        }

        @Override
        public void onVisibilityChanged(boolean visible) { // 当壁纸有前后台的切换
            super.onVisibilityChanged(visible);
            try {
                if (visible) {
                    mediaPlayer.start(); // 如果切换为前台,开始播放
                } else {
                    mediaPlayer.pause(); // 如果切换为后台,暂停播放
                    // 调用pause只会pause source和render不会释放decoder.
                }
            } catch (IllegalStateException e) {
                Log.e(TAG, "onVisibilityChanged video exception ", e);
            }
        }

        private void initEngine() { // 在此函数中实现了对视频文件的处理,及对player的操作
            Log.d(TAG, "initEngine");
            File videoFile = null;
            InputStream inputStream = null;
            FileOutputStream outputStream = null;
            try {
                Context context = VideoWallpaperService.this;
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
                    context = context.createDeviceProtectedStorageContext();
                }
                File filesDir = context.getFilesDir();
                String videoPath = filesDir.getAbsolutePath() + "/video";
                File videoFileDir = new File(videoPath);
                final String fileName = FileUtils.getFileName(path);
                videoFile = new File(videoFileDir, fileName);
                if (!videoFile.exists()) {
                    // Clean all video
                    FileUtils.rm(videoPath);
                    videoFileDir.mkdir();
                    // Write current video
                    inputStream = new FileInputStream(new File(path));
                    outputStream = new FileOutputStream(videoFile);
                    // 16k buffer
                    byte buffer[] = new byte[16384];
                    int length = 0;
                    while ((length = inputStream.read(buffer)) != -1) {
                        outputStream.write(buffer, 0, length);
                    }
                }
            } catch (Exception e) {
                Log.e(TAG, "load video file error : ", e);
            } finally {
                Utils.closeQuietly(outputStream);
                Utils.closeQuietly(inputStream);
            }

            try {
                if (videoFile != null && videoFile.exists()) {
                    mediaPlayer.reset();
                    Surface surface = getSurfaceHolder().getSurface();
                    mediaPlayer.setSurface(surface);
                    mediaPlayer.setDataSource(new FileInputStream(videoFile).getFD());
                    setAudioState(isAudioOn);
                    mediaPlayer.setLooping(true);
                    mediaPlayer.prepare();
                    mediaPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
                        @Override
                        public void onPrepared(MediaPlayer mp) {
                            mp.start();
                        }
                    });
                    mediaPlayer.setOnErrorListener(new MediaPlayer.OnErrorListener() {
                        @Override
                        public boolean onError(MediaPlayer mp, int what, int extra) {
                            mp.reset();
                            return false;
                        }
                    });
                }
            } catch (Exception e) {
                Log.e(TAG, "configVideoEngine err ", e);
            }
        }

        private void setAudioState(boolean isOn) { // 设置音频
            try {
                float volume = isOn ? 0.5f : 0;
                mediaPlayer.setVolume(volume, volume); 
            } catch (IllegalStateException e) {
                Log.e(TAG, "setAudioState err", e);
            }
        }
    }

 

  • 0
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值