10e*)
…
echo “IJK_NDK_REL=
I
J
K
N
D
K
R
E
L
"
c
a
s
e
"
IJK_NDK_REL" case "
IJKNDKREL"case"IJK_NDK_REL” in
11*|12*|13*|14*)
if test -d
A
N
D
R
O
I
D
N
D
K
/
t
o
o
l
c
h
a
i
n
s
/
a
r
m
−
l
i
n
u
x
−
a
n
d
r
o
i
d
e
a
b
i
−
4.9
t
h
e
n
e
c
h
o
"
N
D
K
r
{ANDROID_NDK}/toolchains/arm-linux-androideabi-4.9 then echo "NDKr
ANDROIDNDK/toolchains/arm−linux−androideabi−4.9thenecho"NDKrIJK_NDK_REL detected"
else
echo “You need the NDKr10e or later”
exit 1
fi
;;
*)
echo “You need the NDKr10e or later”
exit 1
;;
esac
;;
esac
可以看出来这里只支持10e,11,12,13,14,所以ndk版本低了不行,高了也不行,没办法,我们得去重新去官网下载低一点的版本,如r14b。
4.配置SDK和NDK路径
找到/home/(用户名)/目录,使用快捷键Ctrl + H显示隐藏文件,找到.bashrc文件打开,配置自己的SDK和NDK路径,例如:
export ANDROID_NDK=/home/leon/Android/andriod-ndk-r14b
export ANDROID_SDK=/home/leon/Android/Sdk
export PATH=
A
N
D
R
O
I
D
N
D
K
:
ANDROID_NDK:
ANDROIDNDK:ANDROID_SDK:$PATH
配置完成后,重启命令行,输入ndk-build命令,如果不报命令行找不到,说明NDK环境变量配置成功。
编译IjkPlayer
Android环境搭建好后,就可以参考官方文档着手编译ijkplayer了。
sudo apt-get update
sudo apt-get install git //安装git
sudo apt-get install yasm //安装yasm
sudo dpkg-reconfigure dash //在弹出提示框选择“否”来使用bash
//下载ijkplayer到ijkplayer-android目录
git clone https://github.com/Bilibili/ijkplayer.git ijkplayer-android
cd ijkplayer-android
//使用默认配置
cd config
rm module.sh
ln -s module-lite.sh module.sh
cd …
cd android/contrib
./compile-ffmpeg.sh clean //清理
cd ~/ijkplayer-android //返回源码根目录
./init-android.sh //主要是去下载ffmpeg
cd android/contrib
./compile-ffmpeg.sh clean
./compile-ffmpeg.sh all //编译ffmpeg,all是全部编译,需要等待一段时间
cd … //回到ijkplayer-android/android
./compile-ijk.sh all //编译ijkplayer
编译完成后,在android/ijkplayer目录下各个库模块当中找到生成的so库:
既然so库已经生成,就可以使用Andorid Studio再次打开ijkplayer中的安卓示例项目(android/ijkplayer),运行后就可以播放示例视频了。这个带有so库的示例项目我已上传到Github,地址为github.com/uncleleonfa…,欢迎下载。
PLDroidPlayer
PLDroidPlayer 是七牛推出的一款适用于 Android 平台的播放器 SDK,采用全自研的跨平台播放内核,拥有丰富的功能和优异的性能,可高度定制化和二次开发。示例项目地址为:github.com/pili-engine…。
PLDroidPlayer的集成要比ijkPlayer简单很多,不用自己编译so库,不用自己创建SurfaceView和TextureView来播放视频。可参考官方开发指南集成即可。
测试开发
为了保证测试的变量只是播放器引擎本身(这里暂时将播放器引擎简单的理解为各个播放器的MediaPlayer),我们定义一个公共的UI界面即VideoView来播放视频流,然后通过代理模式去代理不同的播放器引擎。这样VideoView在播放视频时,可以通过代理使用不同的播放引擎(MediaPlayer)来播放。我们这里主要测试播放器播放视频首开的时间,播放器播放视频过程中Cpu,内存的占用情况。测试项目地址为:github.com/uncleleonfa…,测试项目运行效果:
IMediaPlayer
定义统一的MediaPlayer接口。
public interface IMediaPlayer {
void prepareAsync() throws IllegalStateException;
void start() throws IllegalStateException;
void stop() throws IllegalStateException;
void pause() throws IllegalStateException;
void release();
void reset();
…
}
IMeidaPlayerProxy
定义MeidaPlayer的代理接口,所有MediaPlayer的代理必须实现newInstance接口创建MediaPlayer。
interface IMediaPlayerProxy {
IMediaPlayer newInstance();
}
IjkPlayer的MediaPlayer的代理
在使用IjkPlayer之前需要添加依赖,并且将编译好的so库添加到项目中的jniLibs下。
//添加ijkplayer依赖
dependencies {
compile ‘tv.danmaku.ijk.media:ijkplayer-java:0.8.4’
compile ‘tv.danmaku.ijk.media:ijkplayer-armv7a:0.8.4’
compile ‘tv.danmaku.ijk.media:ijkplayer-x86:0.8.4’
}
//IjkMediaPlayer代理,实现IMediaPlayer接口
public class IjkMediaPlayerProxy implements IMediaPlayerProxy, IMediaPlayer {
//声明一个IjkMediaPlayer对象
private IjkMediaPlayer mIjkMediaPlayer;
@Override
public IMediaPlayer newInstance() {
//创建IjkMeidaPlayer对象
mIjkMediaPlayer = new IjkMediaPlayer();
return this;
}
@Override
public void prepareAsync() throws IllegalStateException {
mIjkMediaPlayer.prepareAsync();
}
@Override
public void start() throws IllegalStateException {
mIjkMediaPlayer.start();
}
@Override
public void stop() throws IllegalStateException {
mIjkMediaPlayer.stop();
}
…
}
PLDroidPlayer的MediaPlayer代理
在使用PLMediaPlayer之前参考官方文档集成PLDroidPlayer
//PLMediaPlayer代理,实现IMediaPlayer接口
public class PLMediaPlayerProxy implements IMediaPlayerProxy, IMediaPlayer {
//定义PLMediaPlayer对象
private PLMediaPlayer mMediaPlayer;
//AVOptions为MediaPlayer的选项配置,例如可以配置开启硬解码
private AVOptions mAvOptions;
@Override
public IMediaPlayer newInstance() {
//创建PLDroidPlayer的PLMediaPlayer对象
mMediaPlayer = new PLMediaPlayer(mContext, mAvOptions);
return this;
}
@Override
public void prepareAsync() throws IllegalStateException {
mMediaPlayer.prepareAsync();
}
@Override
public void start() throws IllegalStateException {
mMediaPlayer.start();
}
@Override
public void stop() throws IllegalStateException {
mMediaPlayer.stop();
}
…
}
VideoView
VideoView仿照原生VideoView的实现,这里主要修改的是MediaPlayer的逻辑,方便配置使用不同播放器的MediaPlayer。
public class VideoView extends SurfaceView implements IMediaPlayer.OnPreparedListener,
IMediaPlayer.OnErrorListener,
IMediaPlayer.OnCompletionListener,
IMediaPlayer.OnInfoListener,
IMediaPlayer.OnVideoSizeChangeListener{
//定义MediaPlayer代理
private IMediaPlayerProxy mMediaPlayerProxy;
//定义VideoView使用的MediaPlayer
private IMediaPlayer mMediaPlayer;
//设置MediaPlayer的代理
public void setMediaPlayerProxy(IMediaPlayerProxy mediaPlayerProxy) {
mMediaPlayerProxy = mediaPlayerProxy;
}
//打开视频
private void openVideo() {
if (mVideoPath == null) {
return;
}
release();
//使用代理创建对应的MediaPlayer对象
mMediaPlayer = mMediaPlayerProxy.newInstance();
mMediaPlayer.setScreenOnWhilePlaying(true);
mMediaPlayer.setDisplay(mSurfaceHolder);
mMediaPlayer.setLogEnabled(BuildConfig.DEBUG);
mMediaPlayer.setOnPreparedListener(this);
mMediaPlayer.setOnInfoListener(this);
mMediaPlayer.setOnCompletionListener(this);
mMediaPlayer.setOnErrorListener(this);
mMediaPlayer.setOnVideoSizeChangeListener(this);
try {
mMediaPlayer.setDataSource(mVideoPath);
…
mMediaPlayer.prepareAsync();
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void onPrepared(IMediaPlayer iMediaPlayer) {
iMediaPlayer.start();//开始播放
}
}
LogUtils
LogUtils用于采样cpu和内存数据,里面使用ScheduledThreadPoolExecutor每隔1s采样一次数据。
//开始采样
public void start() {
scheduler.scheduleWithFixedDelay(new SampleTask(), 0L, 1000L, TimeUnit.MILLISECONDS);
}
//停止采样
public void stop() {
scheduler.shutdown();
}
//采样任务
private class SampleTask implements Runnable {
@Override
public void run() {
float cpu = sampleCPU(); //采样CPU使用
float mem = sampleMemory(); //采样内存使用
}
}
LogView
LogView是打印Log的自定义控件,它由一个TextView和ScrollView组成,TextView在ScrollView内部,用来显示log,ScrollView用来滚动。
public class LogView extends RelativeLayout {
public LogView(@NonNull Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
LayoutInflater.from(context).inflate(R.layout.view_log, this);
final TextView textView = findViewById(R.id.tv);
final ScrollView scrollView = findViewById(R.id.scroll_view);
final StringBuilder stringBuilder = new StringBuilder();
//监听LogUtils的log
LogUtils.getInstance().setOnUpdateLogListener(new LogUtils.OnUpdateLogListener() {
@Override
public void onUpdate(final long timestamp, final String msg) {
//在主线程刷新界面
post(new Runnable() {
@Override
public void run() {
String dateString = mSimpleDateFormat.format(new Date(timestamp));
String log = dateString + ": " + msg + “\n”;
//添加一行log
stringBuilder.append(log);
//设置log显示
textView.setText(stringBuilder.toString());
//滚动ScrollView到底部
scrollView.fullScroll(View.FOCUS_DOWN);
}
});
}
});
}
}
测试
测试视频流是:
//点播MP4视频
String path = “http://hc.yinyuetai.com/uploads/videos/common/2B40015FD4683805AAD2D7D35A80F606.mp4?sc=364e86c8a7f42de3&br=783&rd=Android”;
//HLS直播流
String path = “http://ivi.bupt.edu.cn/hls/cctv1hd.m3u8”;
在VieoView中,MediaPlayer开始准备播放之前,初始化LogUtils,埋点记录MediaPlayer的准备时间。
try {
//设置视频源
mMediaPlayer.setDataSource(mVideoPath);
//初始化LogUtils
LogUtils.getInstance().init(getContext());
//记录开始准备时间
LogUtils.getInstance().onStartPrepare();
mMediaPlayer.prepareAsync();
} catch (IOException e) {
e.printStackTrace();
}
当MediaPlayer准备好后,会回调onPrepared,再次记录准备结束时间,这样,准备结束时间减去准备开始时间就是MediaPlayer准备耗时,即我们的首开时间。
//准备好后的回调
@Override
public void onPrepared(IMediaPlayer iMediaPlayer) {
//记录准备结束时间
LogUtils.getInstance().onEndPrepare();
//开始播放
iMediaPlayer.start();
//开始每隔1s采样,播放结束后停止采样,主要用于点播采样
LogUtils.getInstance().start();
//开始每隔1s采样,采样5min,5min之后,自行停止,主要用于直播采样
//LogUtils.getInstance().startForDuration(5);
}
//播放结束
@Override
public void onCompletion(IMediaPlayer iMediaPlayer) {
//播放结束,停止采样
LogUtils.getInstance().stop();
}
测试IjkPlayer
创建一个IjkPlayerActivity使用IjkMediaPlayer来播放视频。
public class IjkPlayerActivity extends AppCompatActivity{
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_ijkplayer);
//初始化IjkPlayer
IjkMediaPlayer.loadLibrariesOnce(null);
IjkMediaPlayer.native_profileBegin(“libijkplayer.so”);
VideoView videoView = findViewById(R.id.video_view);
//设置IjkMediaPlayer代理
videoView.setMediaPlayerProxy(new IjkMediaPlayerProxy());
String path = “视频url”
//设置视频url
videoView.setVideoPath(path);
}
@Override
protected void onStop() {
super.onStop();
//通知IjkMediaPlayer结束
IjkMediaPlayer.native_profileEnd();
}
}
另外,在VideoView初始化MediaPlayer时,可以调用enableMediaCodec()来开启IjkPlayer的硬解码:
private void openVideo() {
…
mMediaPlayer.enableMediaCodec();
}
测试PLDroidPlayer
创建一个PLDroidPlayerActivity使用PLMediaPlayer来播放视频。
public class PLDroidPlayerActivity extends AppCompatActivity{
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_pldroid);
VideoView mVideoView = findViewById(R.id.video_view);
//配置AVoptions来开启硬编码,默认为软编码
AVOptions avOptions = new AVOptions();
avOptions.setInteger(AVOptions.KEY_MEDIACODEC, AVOptions.MEDIA_CODEC_HW_DECODE);
mVideoView.setMediaPlayerProxy(new PLMediaPlayerProxy(this, avOptions));
String path = “视频url”;
mVideoView.setVideoPath(path);
}
}
测试结果
Round 1
- 机型:Moto G (2代)
- 系统版本:5.1
- 数据源:String path = “ivi.bupt.edu.cn/hls/cctv1hd…”;
- 软硬编码:软解码
- 采样时长:5min
- IjkPlayer版本:0.8.4
最后
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长,自己不成体系的自学效果低效漫长且无助。
因此我收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点!不论你是刚入门Android开发的新手,还是希望在技术上不断提升的资深开发者,这些资料都将为你打开新的学习之门
如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
学提升又不知道该从何学起的朋友,同时减轻大家的负担。**
[外链图片转存中…(img-g2SnFUnG-1715488481879)]
[外链图片转存中…(img-yvZzKxxq-1715488481880)]
[外链图片转存中…(img-QYDCHnhz-1715488481882)]
[外链图片转存中…(img-HVnj7oZX-1715488481884)]
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点!不论你是刚入门Android开发的新手,还是希望在技术上不断提升的资深开发者,这些资料都将为你打开新的学习之门
如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!