有个场景就是一个页面里有多个VideoView播放视频,然后每个视频都有一个音量值,但是VideoView并不支持直接设置音量,而是要通过调节系统音量来实现,那么这样的话,就不能实现为每个视频独立调节音量了我们知道MediaPlayer+SurfaceView也能实现视频的播放,并且MediaPlayer是可以直接通过setVolume来调节视频音量的,但是因为这里已经用了VideoView实现了播放,再改的话就太浪费时间和人力了。想到VideoView是继承SurfaceView的,那会不会VideoView的视频播放控制是由MediaPlayer实现的呢,查看VideoView源码:
try {
mMediaPlayer = new MediaPlayer();
// TODO: create SubtitleController in MediaPlayer, but we need
// a context for the subtitle renderers
final Context context = getContext();
final SubtitleController controller = new SubtitleController(
context, mMediaPlayer.getMediaTimeProvider(), mMediaPlayer);
controller.registerRenderer(new WebVttRenderer(context));
controller.registerRenderer(new TtmlRenderer(context));
controller.registerRenderer(new Cea708CaptionRenderer(context));
controller.registerRenderer(new ClosedCaptionRenderer(context));
mMediaPlayer.setSubtitleAnchor(controller, this);
if (mAudioSession != 0) {
mMediaPlayer.setAudioSessionId(mAudioSession);
} else {
mAudioSession = mMediaPlayer.getAudioSessionId();
}
mMediaPlayer.setOnPreparedListener(mPreparedListener);
mMediaPlayer.setOnVideoSizeChangedListener(mSizeChangedListener);
mMediaPlayer.setOnCompletionListener(mCompletionListener);
mMediaPlayer.setOnErrorListener(mErrorListener);
mMediaPlayer.setOnInfoListener(mInfoListener);
mMediaPlayer.setOnBufferingUpdateListener(mBufferingUpdateListener);
mCurrentBufferPercentage = 0;
mMediaPlayer.setDataSource(mContext, mUri, mHeaders);
mMediaPlayer.setDisplay(mSurfaceHolder);
mMediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
mMediaPlayer.setScreenOnWhilePlaying(true);
mMediaPlayer.prepareAsync();
果然如此,VideoView仅仅只是封装了MediaPlayer和SurfaceView。那么既然这样的话就好办了,只需要获取到VideoView里面的MediaPlayer就可以单独修改这个视频的声音大小了,但是发现MediaPlayer是私有的并且VideoView并没有留出方法给外层调用,那么只能通过反射了。
/**
* @param volume 音量大小
* @param object VideoView实例
* */
public void setVolume(float volume,Object object) {
try {
Class<?> forName = Class.forName("android.widget.VideoView");
Field field = forName.getDeclaredField("mMediaPlayer");
field.setAccessible(true);
MediaPlayer mMediaPlayer = (MediaPlayer) field.get(object);
mMediaPlayer.setVolume(volume, volume);
} catch (Exception e) {
}
}
据此思想,当遇到系统Api限制的问题时,我们不妨另辟蹊径,通过反射解除限制,从而解决问题,比如下面这个例子:
某些情况下我们想在ViewPager滑动的时候才创建页面,并不希望ViewPager给我们缓存页面,所以我们先找到ViewPager里面设置缓存个数相关的参数和方法。
private static final int DEFAULT_OFFSCREEN_PAGES = 1;
private int mOffscreenPageLimit = DEFAULT_OFFSCREEN_PAGES;
public void setOffscreenPageLimit(int limit) {
if (limit < DEFAULT_OFFSCREEN_PAGES) {
Log.w(TAG, "Requested offscreen page limit " + limit + " too small; defaulting to " +
DEFAULT_OFFSCREEN_PAGES);
limit = DEFAULT_OFFSCREEN_PAGES;
}
if (limit != mOffscreenPageLimit) {
mOffscreenPageLimit = limit;
populate();
}
}
可以看到ViewPager限制了传入缓存页数不能小于1,所以无论如何ViewPager都会给我们缓存一个以上的页面。没关系,同样的道理,我们可以修改这个限制,这里我们直接通过反射修改mOffscreenPageLimit的值来实现。
try {
Class<?> forName = Class.forName("android.support.v4.view.ViewPager");
Field defauleField = forName.getDeclaredField("mOffscreenPageLimit");
defauleField.setAccessible(true);
defauleField.set(0,viewPager);
} catch (Exception e) {
}
通过这两个例子主要是想告诉大家不要被系统Api所限制,当发现SDK里没提供你所需要的方法时候,别放弃,细心看看源码,也许会柳暗花明又一村,代码是死的,人是活的,为达目的,可以不择手段,正常路径走不通,那么可以通过一些非正常的方式来达到目的。