在android 逐帧动画中,使用getFrameAtTime函数对视频文件取帧,速度好慢啊,有没有大

Android MediaMetadataRetriever.getFrameAtTime()提供的函数是不是系统下层没给实现,无论怎么改变时间_百度知道
Android MediaMetadataRetriever.getFrameAtTime()提供的函数是不是系统下层没给实现,无论怎么改变时间
下面是测试代码./zhidao/pic/item/eac4bd31bfc8f8b82b.jpg" esrc="http.hiphotos./zhidao/wh%3D600%2C800/sign=8cb57aba730e0cf3a0a246fd3a76de27/eac4bd31bfc8f8b82b://e://e,获得的都是第一帧.jpg" target="_blank" title="点击查看大图" class="ikqb_img_alink"><img class="ikqb_img" src="http.baidu://e。<a href="/zhidao/wh%3D450%2C600/sign=672ce8def31fbe091c0bcb105e502005/eac4bd31bfc8f8b82b
提问者采纳
还不到当前时间,我估计你是传比如1000这样的值我记得我以前犯过这个错,知道为什么会只显示第一帧了吧,当然是第一帧了, getFrameAtTime() 是标准时间,也就是从1970年至今的毫秒数
了解你的意思,我试试。行不通哦,之前查了文档,貌似是说该方法在底层直接给return null了。其实这个方法的这个时间,指的是播放视频的总时长中具体时间的具体的位置。
提问者评价
谢谢,看样子没什么人去仔细研究这个东东,很多人只拿来做缩略图了。
其他类似问题
为您推荐:
android的相关知识
等待您来回答
下载知道APP
随时随地咨询
出门在外也不愁(SOS)关于android获取视频缩略图的问题,大神进来 - 开源中国社区
当前访客身份:游客 [
当前位置:
MediaMetadataRetriever mmr = new MediaMetadataRetriever();
mmr.setDataSource(path);
String title = new File(path).getName();
Bitmap bitMap =
String mataDataStr = mmr.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION);
int seconds = 0;
if(mataDataStr!=null){
seconds = Integer.valueOf(mataDataStr);
if (seconds & 1) {
bitMap = mmr.getFrameAtTime(1 * 1000 * 1000, MediaMetadataRetriever.OPTION_CLOSEST_SYNC);
} catch (Exception e) {
e.printStackTrace();
先上代码。播放器侧边有一个listview列表,可以进入USB文件夹查看视频文件,listview中会加载视频文件的缩略图。 当前正在播放某个文件,假设进入其他某个文件夹中有一个视频文件被损坏的,或者是其他类型的文件重命名成视频文件的。。获取缩略图的时候就报错了,导致播放器也停止了正在播放的视频文件。。 所以我想知道,有没有其他方法获取视频缩略图?经测试MediaMetadataRetriever&,MediaStore.Video.Thumbnails.getThumbnail等系统自带的方法获取,都会导致mediaplay停止播放。。。
而且因为软件支持的视频格式比较多,不知道如何通过文件头信息判断是不是真实的视频文件
&"mp4", "wmv", "mpeg",
"m4v", "3gp", "3gpp", "3g2", "3gpp2", "asf", "mkv", "avi", "mpg",
"flv", "vob", "rmvb", "mov", "ts", "m2ts"&
共有0个答案
更多开发者职位上
有什么技术问题吗?
类似的话题Android 播放视频并获取指定时间的帧画面
Android 播放视频并获取指定时间的帧画面
[摘要:比来做的项目请求既能播放视频(近似于视频播放器),又能每隔1s摆布猎取一帧视频绘里,然后对图片举行处置惩罚,观察了一周,也被熬煎了一周,总算找到了大抵相符请求的方式。起首对]
最近做的项目要求既能播放视频(类似于视频播放器),又能每隔1s左右获取一帧视频画面,然后对图片进行处理,调查了一周,也被折磨了一周,总算找到了大致符合要求的方法。首先对调查过程中涉及到的方法进行简单介绍,再重点介绍最终所采用的方法,话不多说,进入正题。
一.MediaMetadataRetriever
播放视频并取得画面的一帧,大家最先想到应该都是这个,我同样也最先对它进行了测试,这里使用MediaPlayer进行播放,视频播放界面使用SurfaceView来实现。
public class PlayerMainActivity extends Activity implements OnClickListener,
SurfaceHolder.Callback, Runnable {
private static final String TAG = "Movie";
private MediaPlayer mediaP
private SurfaceView surfaceV
private SurfaceHolder surfaceH
private Button play_private int currentPosition = 0;
private Bitmap bitmap = null;
private String dataPath = Environment.getExternalStorageDirectory()
+ "/Video/Test_movie.AVI";
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
surfaceView = (SurfaceView) findViewById(R.id.surfaceView);
play_btn = (Button) findViewById(R.id.play_btn);
play_btn.setOnClickListener(this);
screen_cut_btn.setOnClickListener(this);
surfaceHolder = surfaceView.getHolder();
surfaceHolder.addCallback(this);
public void run() {
mediaPlayer = new MediaPlayer();
mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
mediaPlayer.setDisplay(surfaceHolder);
mediaPlayer.setDataSource(dataPath);
mediaPlayer.prepare();
MediaMetadataRetriever mediaMetadataRetriever = new MediaMetadataRetriever();
mediaMetadataRetriever.setDataSource(dataPath);
int millis = mediaPlayer.getDuration();
Log.i(TAG, "millis: " + millis/1000);
for (int i = ; i & 20*; i+=500*1000) {
bitmap = mediaMetadataRetriever.getFrameAtTime(i, MediaMetadataRetriever.OPTION_CLOSEST);
String path = Environment.getExternalStorageDirectory() + "/bitmap/"
+ i + ".png";
FileOutputStream fileOutputStream = null;
fileOutputStream = new FileOutputStream(path);
press(CompressFormat.PNG, 100, fileOutputStream);
Log.i(TAG, "i: " + i/);
} catch (Exception e) {
Log.i(TAG, "Error: " + i/);
e.printStackTrace();
if (fileOutputStream != null) {
fileOutputStream.close();
bitmap.recycle();
} catch (Exception e) {
e.printStackTrace();
public void surfaceCreated(SurfaceHolder holder) {
Thread t = new Thread(this);
t.start();
public void surfaceChanged(SurfaceHolder holder, int format, int width,
int height) {
public void surfaceDestroyed(SurfaceHolder holder) {
public void onClick(View view) {
switch (view.getId()) {
case R.id.play_btn:
if (mediaPlayer.isPlaying()) {
mediaPlayer.pause();
play_btn.setText(getResources().getText(R.string.play));
mediaPlayer.start();
play_btn.setText(getResources().getText(R.string.pause));
break;default:
protected void onDestroy() {
super.onDestroy();
if (mediaPlayer.isPlaying()) {
mediaPlayer.stop();
mediaPlayer.release();
获取一帧的关键代码为:
Bitmap bitmap = mediaMetadataRetriever.getFrameAtTime(timeMs * 1000, MediaMetadataRetriever.OPTION_CLOSEST);
public Bitmap getFrameAtTime(long timeUs, int option)&
第一个参数是传入时间,只能是us(微秒)
第二个参数:
OPTION_CLOSEST&&& 在给定的时间,检索最近一个帧,这个帧不一定是关键帧。
OPTION_CLOSEST_SYNC&& 在给定的时间,检索最近一个同步与数据源相关联的的帧(关键帧)。
OPTION_NEXT_SYNC 在给定时间之后检索一个同步与数据源相关联的关键帧。
OPTION_PREVIOUS_SYNC& 顾名思义,同上
这里为了提取我们想要的帧,不使用关键帧,所以用 OPTION_CLOSEST .
最终的测试结果并不理想,连续取20帧画面,其中真正有效的只有7张,其余都是重复的,原因为即使是使用参数OPTION_CLOSEST,程序仍然会去取指定时间临近的关键帧,如10s-15s总是取同一帧,因此这种方法不可用。
提高视频的质量或许有效,未尝试。
补充MediaMetadataRetriever的其他知识
// 取得视频的总长度(单位为毫秒)
String time = mediaMetadataRetriever. extractMetadata( MediaMetadataRetriever. METADATA_KEY_DURATION);
MediaMetadataRetriever主要用来取缩略图。
二.ThumbnailUtils
同样主要是用来获取视频的缩略图,不可靠,因此并未深入研究。
从以上两种方法可以看出,Android API 所提供的获取视频帧的方法大都只能获取视频的缩略图,没办法获取视频每一帧的图片。因此,调查方向应当转向对视频进行解码,然后获取任意一帧。
三.MediaCodec
硬件解码,尝试从inputBuffers、outputBuffers中获取帧画面,失败,bitmap中的数据大小始终为0 KB。
public class MoviePlayerActivity extends Activity implements OnTouchListener, OnClickListener, SurfaceHolder.Callback {
private static final String TAG = "Image";
private String file_
private Button movie_
private boolean playButtonV
private boolean playP
private SurfaceView surfaceV
private SurfaceHolder surfaceH
private PlayerThread playerThread = null;
private ByteBuffer mPixelB
protected void onCreate(Bundle savedInstanceState) {
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
requestWindowFeature(Window.FEATURE_NO_TITLE);  
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
super.onCreate(savedInstanceState);
setContentView(R.layout.movie_player_activity);
movie_play = (Button) findViewById(R.id.movie_videoview_play);
movie_play.setOnClickListener(this);
movie_play.setText("Play");
Intent intent = getIntent();
file_path = intent.getStringExtra("file_path");  //此Activity是从其他页面跳转来的,file_path为要播放的视频地址
surfaceView = (SurfaceView) findViewById(R.id.surfaceView);
surfaceHolder = surfaceView.getHolder();
surfaceHolder.addCallback(this);
mPixelBuf = ByteBuffer.allocateDirect(640*480*4);
mPixelBuf.order(ByteOrder.LITTLE_ENDIAN);
public void surfaceCreated(SurfaceHolder holder) {
if (playerThread == null) {
playerThread = new PlayerThread(holder.getSurface());
playerThread.start();
public void surfaceChanged(SurfaceHolder holder, int format, int width,
int height) {
public void surfaceDestroyed(SurfaceHolder holder) {
if (playerThread != null) {
playerThread.interrupt();
public boolean onTouch(View v, MotionEvent event) {
if (!playButtonVisible) {
movie_play.setVisibility(View.VISIBLE);
movie_play.setEnabled(true);
movie_play.setVisibility(View.INVISIBLE);
playButtonVisible = !playButtonV
return false;
public void onClick(View view) {
switch (view.getId()) {
case R.id.movie_videoview_play:
if (!playPause) {
movie_play.setText("Pause");
movie_play.setText("Play");
playPause = !playP
private void writeFrameToSDCard(byte[] bytes, int i, int sampleSize) {
if (i%10 == 0) {
Bitmap bmp = BitmapFactory.decodeByteArray(bytes, 0, sampleSize);
mPixelBuf.rewind();
bmp.copyPixelsFromBuffer(mPixelBuf);
String path = Environment.getExternalStorageDirectory() + "/bitmap/" + i + ".png";
FileOutputStream fileOutputStream = null;
fileOutputStream = new FileOutputStream(path);
press(CompressFormat.PNG, 90, fileOutputStream);
bmp.recycle();
Log.i(TAG, "i: " + i);
} catch (Exception e) {
Log.i(TAG, "Error: " + i);
e.printStackTrace();
if (fileOutputStream != null) {
fileOutputStream.close();
} catch (Exception e) {
e.printStackTrace();
private class PlayerThread extends Thread {
private MediaE
private MediaCodec mediaC
public PlayerThread(Surface surface) {
this.surface =
public void run() {
extractor = new MediaExtractor();
extractor.setDataSource(file_path);
} catch (IOException e1) {
Log.i(TAG, "Error");
e1.printStackTrace();
for (int i = 0; i & extractor.getTrackCount(); i++) {
MediaFormat format = extractor.getTrackFormat(i);
String mime = format.getString(MediaFormat.KEY_MIME);
if (mime.startsWith("video/")) {
extractor.selectTrack(i);
mediaCodec = MediaCodec.createDecoderByType(mime);
mediaCodec.configure(format, surface, null, 0);
if (mediaCodec == null) {
Log.e(TAG, "Can't find video info!");
mediaCodec.start();
ByteBuffer[] inputBuffers = mediaCodec.getInputBuffers();
ByteBuffer[] outputBuffers = mediaCodec.getOutputBuffers();
BufferInfo info = new BufferInfo();
boolean isEOS = false;
long startMs = System.currentTimeMillis();
int i = 0;
while (!Thread.interrupted()) {
if (!isEOS) {
int inIndex = mediaCodec.dequeueInputBuffer(10000);
if (inIndex &= 0) {
ByteBuffer buffer = inputBuffers[inIndex];
int sampleSize = extractor.readSampleData(buffer, 0);
if (sampleSize & 0) {
// We shouldn't stop the playback at this point, just pass the EOS
// flag to mediaCodec, we will get it again from the dequeueOutputBuffer
Log.d(TAG, "InputBuffer BUFFER_FLAG_END_OF_STREAM");
mediaCodec.queueInputBuffer(inIndex, 0, 0, 0, MediaCodec.BUFFER_FLAG_END_OF_STREAM);
isEOS = true;
mediaCodec.queueInputBuffer(inIndex, 0, sampleSize, extractor.getSampleTime(), 0);
extractor.advance();
int outIndex = mediaCodec.dequeueOutputBuffer(info, 100000);
switch (outIndex) {
case _OUTPUT_BUFFERS_CHANGED:
Log.d(TAG, "INFO_OUTPUT_BUFFERS_CHANGED");
outputBuffers = mediaCodec.getOutputBuffers();
case _OUTPUT_FORMAT_CHANGED:
Log.d(TAG,"New format " + mediaCodec.getOutputFormat());
case _TRY_AGAIN_LATER:
Log.d(TAG, "dequeueOutputBuffer timed out!");
ByteBuffer buffer = outputBuffers[outIndex];
Log.v(TAG,"We can't use this buffer but render it due to the API limit, " + buffer);
// We use a very simple clock to keep the video FPS, or the video
// playback will be too fast
while (info.presentationTimeUs / 1000 & System.currentTimeMillis() - startMs) {
sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
mediaCodec.releaseOutputBuffer(outIndex, true);
/* saves frame to SDcard */
mPixelBuf.rewind();
GLES20.glReadPixels(0, 0, 640, 480, GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE, mPixelBuf);
ByteBuffer outByteBuffer = outputBuffers[outIndex];
outByteBuffer.position(info.offset);
outByteBuffer.limit(info.offset + info.size);  //info的两个参数值始终为0,所保存的.png也都是0KB。
outByteBuffer.limit(2);
byte[] dst = new byte[outByteBuffer.capacity()];
outByteBuffer.get(dst);
writeFrameToSDCard(dst, i, dst.length);
} catch (Exception e) {
Log.d(TAG, "Error while creating bitmap with: " + e.getMessage());
// All decoded frames have been rendered, we can stop playing now
if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
Log.d(TAG,
"OutputBuffer BUFFER_FLAG_END_OF_STREAM");
mediaCodec.stop();
mediaCodec.release();
extractor.release();
所保存的图片大小始终为0的原因大概和下面的方法五类似。
想要深入研究此方法可以参考:
/questions//mediacodec-get-all-frames-from-video
/questions//how-to-get-bitmap-frames-from-video-using-mediacodec
四.JCodec
http://jcodec.org/
jcodec-samples-0.1.7.apk
解码视频文件耗时较长,性能较差,平均1.5s取一帧图片,达不到要求。
五.使用VideoView播放视频,使用getDrawingCache获取View视图
VideoView是Android提供的播放视频组件,上手很容易。这里使用getDrawingCache获取控件的View。
但是DrawingCache只能截取非视频部分的画面,播放视频的那个小窗口一直是黑色的。原因为Activity画面走的是framebuffer,视频是硬解码推送过来的,所有读取/dev/graphics/fb0& 视频播放的那一块就是黑色的,硬件解码不会推送到buffer的,而是直接推送到硬件输出了。
public class MoviePlayerActivity extends Activity implements OnTouchListener, OnClickListener, Runnable {
private static final String TAG = "Image";
private String file_
private VideoView videoV
private Button movie_
private boolean playButtonV
private boolean playP
protected void onCreate(Bundle savedInstanceState) {
// full screen
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
// no title
requestWindowFeature(Window.FEATURE_NO_TITLE);
// landscape or horizontal screen
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
super.onCreate(savedInstanceState);
setContentView(R.layout.movie_player_activity);
movie_play = (Button) findViewById(R.id.movie_videoview_play);
movie_play.setOnClickListener(this);
movie_play.setText("Play");
Intent intent = getIntent();
file_path = intent.getStringExtra("file_path");
videoView = (VideoView) findViewById(R.id.movie_palyer_videoview);
videoView.setMediaController(null);
// videoView.setMediaController(new MediaController(this));
videoView.setVideoPath(file_path);
videoView.start();
videoView.requestFocus();
Thread screenShootThread = new Thread(this);
screenShootThread.start();
videoView.setOnTouchListener(this);
public void run() {            //播放视频时后台自动截图,注意参数为videoView
for (int i = 10000 * 1000; i & 20 * 1000 * 1000; i += 500 * 1000) {
int nowTime = videoView.getCurrentPosition();
screenShot(videoView, i);
} catch (Exception e1) {
Log.i(TAG, "Error: screenShot. ");
e1.printStackTrace();
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
public boolean onTouch(View v, MotionEvent event) {
if (!playButtonVisible) {
movie_play.setVisibility(View.VISIBLE);
movie_play.setEnabled(true);
movie_play.setVisibility(View.INVISIBLE);
playButtonVisible = !playButtonV
return false;
public void onClick(View view) {
switch (view.getId()) {
case R.id.movie_videoview_play:
if (!playPause) {
movie_play.setText("Pause");
movie_play.setText("Play");
playPause = !playP
int nowTime = videoView.getCurrentPosition();
Log.i(TAG, "nowTime: " + nowTime);
screenShot(videoView, nowTime);    //点击按钮截图,注意参数为videoView
} catch (Exception e) {
e.printStackTrace();
public void screenShot(View view, int nowTime) throws Exception {
view.measure(MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED), MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
view.layout(0, 0, view.getMeasuredWidth(), view.getMeasuredHeight());
view.setDrawingCacheEnabled(true);
view.buildDrawingCache();
Bitmap bitmap = Bitmap.createBitmap(view.getDrawingCache());
String path = Environment.getExternalStorageDirectory() + "/bitmap/" + nowTime + ".png";
FileOutputStream fileOutputStream = null;
fileOutputStream = new FileOutputStream(path);
pressFormat.PNG, 90, fileOutputStream);
Log.i(TAG, "i: " + nowTime);
} catch (Exception e) {
Log.i(TAG, "Error: " + nowTime);
e.printStackTrace();
} finally {
if (fileOutputStream != null) {
fileOutputStream.close();
bitmap.recycle();
view.setDrawingCacheEnabled(false);
DrawingCache 获取其他控件如Button等View时正常。
六.VideoView播放视频,MediaMetadataRetriever获取帧画面(就是你了!)
能够正常获取帧画面,并且画面之间不重复,即不是只取关键帧,而是去取相应时间点的帧画面。若不保存为图片(.png/.jpg),耗时最多为0.4s,基本达到要求。
参考:http://yashirocc./.html
public class MoviePlayerActivity extends Activity implements OnTouchListener, OnClickListener, Runnable {
private static final String TAG = "ImageLight";
private String file_
private VideoView videoV
private Button movie_
private boolean playButtonV
private boolean playP
protected void onCreate(Bundle savedInstanceState) {
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
// full screen
requestWindowFeature(Window.FEATURE_NO_TITLE);
// no title
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
// landscape or horizontal screen
super.onCreate(savedInstanceState);
setContentView(R.layout.movie_player_activity);
movie_play = (Button) findViewById(R.id.movie_videoview_play);
movie_play.setOnClickListener(this);
movie_play.setText("Play");
Intent intent = getIntent();
file_path = intent.getStringExtra("file_path");
videoView = (VideoView) findViewById(R.id.movie_palyer_videoview);
videoView.setMediaController(null);
// videoView.setMediaController(new MediaController(this));
videoView.setVideoPath(file_path);
videoView.start();
videoView.requestFocus();
Thread screenShootThread = new Thread(this);
screenShootThread.start();
videoView.setOnTouchListener(this);
public void run() {
MediaMetadataRetriever metadataRetriever = new MediaMetadataRetriever();
metadataRetriever.setDataSource(file_path);
for (int i = 40000 * 1000; i & 50 * 1000 * 1000; i += 500 * 1000) {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
Bitmap bitmap = metadataRetriever.
getFrameAtTime(videoView.getCurrentPosition()*1000, MediaMetadataRetriever.OPTION_CLOSEST);
Log.i(TAG, "bitmap---i: " + i/1000);
String path = Environment.getExternalStorageDirectory() + "/bitmap/" + i + ".png";
FileOutputStream fileOutputStream = null;
fileOutputStream = new FileOutputStream(path);
pressFormat.PNG, 90, fileOutputStream);
Log.i(TAG, "i: " + i/1000);
} catch (Exception e) {
Log.i(TAG, "Error: " + i/1000);
e.printStackTrace();
} finally {
if (fileOutputStream != null) {
fileOutputStream.close();
} catch (IOException e) {
e.printStackTrace();
bitmap.recycle();
public boolean onTouch(View v, MotionEvent event) {
if (!playButtonVisible) {
movie_play.setVisibility(View.VISIBLE);
movie_play.setEnabled(true);
movie_play.setVisibility(View.INVISIBLE);
playButtonVisible = !playButtonV
return false;
public void onClick(View view) {
switch (view.getId()) {
case R.id.movie_videoview_play:
if (!playPause) {
movie_play.setText("Pause");
movie_play.setText("Play");
playPause = !playP
感谢关注 Ithao123Android频道,是专门为互联网人打造的学习交流平台,全面满足互联网人工作与学习需求,更多互联网资讯尽在 IThao123!
Laravel是一套简洁、优雅的PHP Web开发框架(PHP Web Framework)。它可以让你从面条一样杂乱的代码中解脱出来;它可以帮你构建一个完美的网络APP,而且每行代码都可以简洁、富于表达力。
Hadoop是一个由Apache基金会所开发的分布式系统基础架构。
用户可以在不了解分布式底层细节的情况下,开发分布式程序。充分利用集群的威力进行高速运算和存储。
Hadoop实现了一个分布式文件系统(Hadoop Distributed File System),简称HDFS。HDFS有高容错性的特点,并且设计用来部署在低廉的(low-cost)硬件上;而且它提供高吞吐量(high throughput)来访问应用程序的数据,适合那些有着超大数据集(large data set)的应用程序。HDFS放宽了(relax)POSIX的要求,可以以流的形式访问(streaming access)文件系统中的数据。
Hadoop的框架最核心的设计就是:HDFS和MapReduce。HDFS为海量的数据提供了存储,则MapReduce为海量的数据提供了计算。
产品设计是互联网产品经理的核心能力,一个好的产品经理一定在产品设计方面有扎实的功底,本专题将从互联网产品设计的几个方面谈谈产品设计
随着国内互联网的发展,产品经理岗位需求大幅增加,在国内,从事产品工作的大部分岗位为产品经理,其实现实中,很多从事产品工作的岗位是不能称为产品经理,主要原因是对产品经理的职责不明确,那产品经理的职责有哪些,本专题将详细介绍产品经理的主要职责
IThao123周刊

我要回帖

更多关于 android 逐帧动画 的文章

 

随机推荐