surfaceview生命周期(surface 寿命)

http://www.itjxue.com  2023-01-29 09:51  来源:未知  点击次数: 

Android surfaceview实现显示画面但不立即播放功能!

package com.zsch.forestinventory.activity.left_activity;

import android.media.MediaPlayer;

import android.net.Uri;

import android.os.Bundle;

import android.os.Environment;

import android.view.MotionEvent;

import android.view.SurfaceHolder;

import android.view.SurfaceView;

import android.view.View;

import android.view.animation.Animation;

import android.view.animation.RotateAnimation;

import android.widget.ImageButton;

import android.widget.ImageView;

import android.widget.MediaController;

import android.widget.Toast;

import com.bumptech.glide.Glide;

import com.zsch.androidlib.activity.BaseActivity;

import com.zsch.androidlib.ui.ZoomableImageView;

import com.zsch.androidlib.view.LVCircularRing;

import com.zsch.forestinventory.R;

import java.io.IOException;

/**

* 查看单张照片

*/

public class ShowVideoActivity extends BaseActivity implements View.OnClickListener, MediaController.MediaPlayerControl, MediaPlayer.OnBufferingUpdateListener, SurfaceHolder.Callback {

private ImageButton ibBack;

private Uri uri;

private MediaPlayer mediaPlayer;

private MediaController controller; //视频播放控制器

private int bufferPercentage = 0;

@Override

protected void initVariables() {

uri = getIntent().getData();

if (uri == null)

finish();

}

@Override

protected void initView(Bundle savedInstanceState) {

setContentView(R.layout.activity_show_video);

mediaPlayer = new MediaPlayer();

controller = new MediaController(this);

//媒体控制器将创建一个具有默认设置的控件,并把它们放到一个窗口里漂浮在你的应用程序上。具体来说,这些控件会漂浮在通过setAnchorView()指定的视图上

//设置这个控制器绑定(anchor/锚)到一个视图上。例如可以是一个VideoView对象,或者是你的activity的主视图。

controller.setAnchorView(findViewById(R.id.root_ll));

if (savedInstanceState == null) {

// Bundle类型的数据与Map类型的数据相似,都是以key-value的形式存储数据的。实际上,savedInstanceState也就是保存Activity的状态的

//onsaveInstanceState方法是用来保存Activity的状态的。当一个Activity在生命周期结束前,会调用该方法保存状态

// 用来保存状态信息的Bundle会同时传给两个method,即onRestoreInstanceState() and onCreate().

uri = getIntent().getData();

} else {

uri = savedInstanceState.getParcelable("uri");

}

if (uri == null) {

Toast.makeText(this, "无视频展示", Toast.LENGTH_SHORT).show();

finish();

return;

}

SurfaceView videoSuf = (SurfaceView) findViewById(R.id.controll_surfaceView);

ibBack=(ImageButton) findViewById(R.id.ibBack);

ibBack.setOnClickListener(this);

videoSuf.setZOrderOnTop(false);//true时 会置顶到window的最上层,那么会遮挡其它view ...

videoSuf.getHolder().setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);

videoSuf.getHolder().addCallback(this); //只要继承SurfaceView类并实现SurfaceHolder.Callback接口就可以实现一个自定义的SurfaceView了, 然后有surfaceCreated,surfaceChanged,surfaceDestroyed 等三种方法

// videoSuf.getHolder().setKeepScreenOn(true); //屏幕总是打开

//videoSuf.setFixedSize(320, 220);//显示的分辨率,不设置为视频默认

}

@Override

protected void loadData() {

}

@Override

protected void onResume() {

super.onResume();

// try {

// mediaPlayer.setDataSource(uri.getPath()); //播放存储设备的资源文件

// mediaPlayer.setOnBufferingUpdateListener(this); //监听事件,网络流媒体的缓冲监听。这个方法与上个接口中的方法int getBufferPercentage()进行结合使用

// //mediaPlayer.prepare();

// controller.setMediaPlayer(this);

// controller.setEnabled(true); //设置按钮可点击 false 设置为不可点击

// }catch (IOException e){

// e.printStackTrace();

// }

}

@Override

protected void onPause() {

super.onPause();

if (mediaPlayer.isPlaying()){

mediaPlayer.stop();

}

}

@Override

protected void onDestroy() {

super.onDestroy();

if (null != mediaPlayer){

mediaPlayer.release();

mediaPlayer = null;

}

}

@Override

public void onClick(View view) {

switch (view.getId()) {

case R.id.ibBack:

onBackPressed();

break;

}

}

@Override

public void onBackPressed() {

// if (mediaPlayer.isPlaying()){

// mediaPlayer.stop();

// }

// if (null != mediaPlayer){

// mediaPlayer.release();

// mediaPlayer = null;

// }

finish(); // finish();//在Activity中执行this.finish()方法之后,执行如下过程: onPause(),onStop(),onDestory(),

}

@Override

public boolean onTouchEvent(MotionEvent event) { //实现这个方法来处理触摸屏幕引发的事件。

controller.show();

return super.onTouchEvent(event);

}

@Override

public void onPointerCaptureChanged(boolean hasCapture) {

}

//MediaPlayerControl

@Override

public void onBufferingUpdate(MediaPlayer mediaPlayer, int i) {

bufferPercentage = i;

}

@Override

public void surfaceCreated(SurfaceHolder surfaceHolder) { //这步很重要 surfaceCreated创建时使用 //必须在surface创建后才能初始化MediaPlayer,否则不会显示图像

try {

mediaPlayer.setDataSource(uri.getPath()); //播放存储设备的资源文件

mediaPlayer.setOnBufferingUpdateListener(this); //监听事件,网络流媒体的缓冲监听。这个方法与上个接口中的方法int getBufferPercentage()进行结合使用

//mediaPlayer.prepare();

controller.setMediaPlayer(this);

controller.setEnabled(true); //设置按钮可点击 false 设置为不可点击

}catch (IOException e){

e.printStackTrace();

}

//设置显示视频显示在SurfaceView上

mediaPlayer.setDisplay(surfaceHolder);

// prepare方法是将资源同步缓存到内存中,一般加载本地较小的资源可以用这个,如果是较大的资源或者网络资源建议使用prepareAsync方法,异步加载.

// 但如果想让资源启动,即start()起来,因为在异步中,如果不设置监听直接start的话,是拿不到这个资源,如果让线程睡眠一段时间,则可以取得资源,

mediaPlayer.prepareAsync(); //准备 为异步加载

controller.show(); //显示控制器

}

@Override

public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {

}

@Override

public void surfaceDestroyed(SurfaceHolder holder) {

}

//这里的 start()和pause() 指的是 media的拍摄过程中的 步骤

@Override

public void start() {

if (null != mediaPlayer){

mediaPlayer.start();

}

}

@Override

public void pause() {

if (null != mediaPlayer){

mediaPlayer.pause();

}

}

@Override

public int getDuration() {

return mediaPlayer.getDuration();

}

@Override

public int getCurrentPosition() { //获得 视频播放的位置 为了暂停后 继续播放

return mediaPlayer.getCurrentPosition();

}

@Override

public void seekTo(int i) {

mediaPlayer.seekTo(i);

}

@Override

public boolean isPlaying() {

if (mediaPlayer.isPlaying()){

return true;

}

return false;

}

@Override

public int getBufferPercentage() {

return bufferPercentage;

}

@Override

public boolean canPause() {

return true;

}

@Override

public boolean canSeekBackward() {

return true;

}

@Override

public boolean canSeekForward() {

return true;

}

@Override

public int getAudioSessionId() {

return 0;

}

}

android surfaceview 怎么用

@Override

public void surfaceDestroyed(SurfaceHolder holder) {

}

}

上面代码我们在SurfaceView的构造方法中执行了init初始化方法,在这个方法里,我们先获取SurfaceView里的 SurfaceHolder对象,然后通过它设置Surface的生命周期回调方法,使用DemoSurfaceView类本身作为回调方法代理类。 surfaceCreated方法,是当SurfaceView被显示时会调用的方法,所以你需要再这边开启绘制的线 程,surfaceDestroyed方法是当SurfaceView被隐藏会销毁时调用的方法,在这里你可以关闭绘制的线程。上面的例子运行后什么也不 显示,因为还没定义一个执行绘制的线程。下面我们修改下代码,使用一个线程绘制一个逐渐变大的圆圈:

[java] view plain copy print?

package com.android777.demo.uicontroller.graphics;

import android.content.Context;

import android.graphics.Canvas;

import android.graphics.Color;

import android.graphics.Paint;

import android.view.SurfaceHolder;

import android.view.SurfaceHolder.Callback;

import android.view.SurfaceView;

public class DemoSurfaceView extends SurfaceView implements Callback{

LoopThread thread;

public DemoSurfaceView(Context context) {

super(context);

init(); //初始化,设置生命周期回调方法

}

private void init(){

SurfaceHolder holder = getHolder();

holder.addCallback(this); //设置Surface生命周期回调

thread = new LoopThread(holder, getContext());

}

@Override

public void surfaceChanged(SurfaceHolder holder, int format, int width,

int height) {

如何正确的在fragment中显示surfaceview

由于在ViewPage中PageAdapter来管理所有的Fragment。在加载一个Fragment的时候,会自动缓存左右几个(默认是一个)页面,此时也会调用到正常的生命周期函数,onCreate,onCrateView,onResume.可是这样就干扰了我们统计页面打开次数。例如:一个ViewPager中存在三个页面的时候,当默认是第一个页面,并且被打开。那么在我们的后台就会收集到用户打开两个页面的信息。这个对我们的统计就是一个很大的干扰。所以就不能再将统计代码放在onResume中,要放在一个其他的函数内,但问题的关键是如何确认当前的Fragment可见?这样我才能把统计函数移植过去。同时Fragment虽然有onResume和onPause的,但是这两个方法是Activity的方法,调用时机也是与Activity相同,和ViewPager搭配使用这个方法就很鸡肋了,根本不是你想要的效果,这里介绍一种方法。

在问这个问题的时候把焦点都放在生命周期的触发上了。发现Fragment下专门有一个setUserVisibleHint函数来处理这个事情:

android surfaceview 会不会阻塞ui线程

系统不会为每个组件单独创建线程,在同一个进程里的UI组件都会在UI线程里实例化,系统对每一个组件的调用都从UI线程分发出去。

结果就是,响应系统回调的方法(比如响应用户动作的onKeyDown()和各种生命周期回调)永远都是在UI线程里运行。

当App做一些比较重(intensive)的工作的时候,除非你合理地实现,否则单线程模型的performance会很poor。

特别的是,如果所有的工作都在UI线程,做一些比较耗时的工作比如访问网络或者数据库查询,都会阻塞UI线程,导致事件停止分发(包括绘制事件)。对于用户来说,应用看起来像是卡住了,更坏的情况是,如果UI线程blocked的时间太长(大约超过5秒),用户就会看到ANR(application not responding)的对话框。

另外,Andoid UI toolkit并不是线程安全的,所以你不能从非UI线程来操纵UI组件。你必须把所有的UI操作放在UI线程里,所以Android的单线程模型有两条原则:

1.不要阻塞UI线程。

2.不要在UI线程之外访问Android UI toolkit(主要是这两个包中的组件:android.widget andandroid.view)。

SurfaceView/VideoView

我们经常用SurfaceView和VideoView来播放视频,但是这两个东东,经常都会出问题。

①SurfaceView不会添加到View树上,并且显示在所有View之上

②在按Home键的时候,会让Surface销毁,并且在重新进入APP的时候,让Surface重建,在Surface重建的时候,SurfaceView那一块是透明的,显示的会是Activity的背景

③如果是列表中的子view中播放视频,在上下滑动的时候,会导致Surface绘制不及时,会有残留

④多个VideoView同时播放的时候,在SurfaceFlinger支持不好的手机上,会出现下一个SurfaceView的某一帧会显示在上一个SurfaceView上

①VideoView是直接继承SurfaceView

②VideoView中的openVideo可能会ANR

③VideoView中的release,stopPlayBack都会导致ANR,因为这些方法都是同步执行的,并且通过IPC服务交给MediaServer去释放资源

解决方法:

使用TextureView替换SurfaceView实现VideoView,因为TextureView是直接继承View的,并且在ListView中滑动的时候,也不会在滑动的时候,有帧残留

当我们有一个列表,每个子项就是一个视频,并且自动播放。surfaceView使用的就是Android自带的状态机来控制播放,所以就会一段一段的将视频先读到缓冲区,再播放。由于MediaPlayer中的release,reset,stopPlayBack都是同步的。而且当视频卡片在滑出屏幕之后,需要把视频暂停,在不可见的时候不进行播放。节省系统资源,并且节省用户流量。而如果同时出现多个视频的时候,会频繁调用到上述生命周期方法,导致很容易出现ANR。

解决方法:

问题1.在视频划出ListView的时候,停止播放视频。

解决方案:在ListView中调用setRecycleListener,设置View回收的监听,因为ListView的重用性,会在View回收到scrap区的时候,通过这个Listener进行一些处理,所以在这里根据View.getTag,找到视频View的引用,调用stopPlayBack停止

问题2.频繁调用release等方法导致ANR

解决方案:在视频调用的时候,建立一个释放视频资源的守护线程。在Android中,直接可以用HandlerThread,因为这样可以尽可能的让资源的消耗达到最少,HandlerThread在没有新事件到来的时候,都是处于wait状态,直到有新事件的到来,才会被notify,处理新事件。它里面也是通过一个Thread,在这个Thread中新建一个Looper,在Looper中没有事件的话,则wait,一旦通过Handler发送新事件的话,则会被notify。所以会在子线程中加入一个队列,当需要release的MediaPlayer,直接丢到子线程去进行资源释放。

但是这样会导致一个问题,就是Android维护的MediaPlayer的状态机中的状态可能会乱,这时候就会抛出IllegalStateException,目前对于这种异常,我们选择了捕获它。

另外,由于MediaPlayer.setSurface需要传递一个Surface,然后再在这个Surface上进行绘制,如果频繁new Surface传入的话,就会导致GrafficBuffer分配Surface失败,从而MediaPlayer会回调onError中,显示视频不能播放。所以尽可能一个视频View用一个Surface。

SurfaceView的简单使用

public class MainActivity extends AppCompatActivity implements View.OnClickListener {

protected Button mBufferBtn;

protected Button mVideoBtn;

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

super.setContentView(R.layout.activity_main);

initView();

}

@Override

public void onClick(View view) {

Intent intent = new Intent();

if (view.getId() == R.id.button) {

intent.setClass(this, BufferActivity.class);

} else if (view.getId() == R.id.button) {

intent.setClass(this, VideoActivity.class);

}

startActivity(intent);

}

private void initView() {

mBufferBtn = (Button) findViewById(R.id.button2);

mBufferBtn.setOnClickListener(MainActivity.this);

mVideoBtn = (Button) findViewById(R.id.button2);

mVideoBtn.setOnClickListener(MainActivity.this);

}

}

public class BufferActivity extends AppCompatActivity implements SurfaceHolder.Callback {

protected SurfaceView mSurfaceView;

private SurfaceHolder mHolder;

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

super.setContentView(R.layout.activity_buffer);

initView();

initSurfaceHolder();

}

// 初始化Surface的管理者

private void initSurfaceHolder() {

mHolder = mSurfaceView.getHolder();

// 添加管理生命周期的接口回调

mHolder.addCallback(this);

}

private void initView() {

mSurfaceView = (SurfaceView) findViewById(R.id.surface_view);

}

// 缓冲区创建

@Override

public void surfaceCreated(SurfaceHolder holder) {

Log.d("1507", "surfaceCreated");

new DrawThread().start();

}

// 缓冲区内容改变(子线程渲染UI的过程)

@Override

public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {

Log.d("1507", "surfaceChanged");

}

// 缓冲区销毁

@Override

public void surfaceDestroyed(SurfaceHolder holder) {

Log.d("1507", "surfaceDestroyed");

}

// 绘制UI的子线程

private class DrawThread extends Thread {

@Override

public void run() {

super.run();

// 创建画笔

Paint paint = new Paint();

paint.setColor(Color.GREEN);// 画笔颜色

paint.setStrokeWidth(10);// 画笔粗细。注意:Java中设置的尺寸单位都是px

paint.setStyle(Paint.Style.FILL_AND_STROKE);// 设置实心

paint.setAntiAlias(true);// 设置是否抗锯齿

// 获取SurfaceView的盖度

int height = mSurfaceView.getHeight();

Canvas canvas = null;

for (int i = 0; i height; i+= 5) {

// 获取Surface中的画布

canvas = mHolder.lockCanvas();// 锁定画布

// 使用画笔在画布上绘制指定形状

canvas.drawCircle(100, i + 50, 50, paint);// 圆心x坐标,圆心y坐标,半径,画笔

// 缓冲区的画布绘制完毕,需要解锁并提交给窗口展示

mHolder.unlockCanvasAndPost(canvas);

//? ? ? ? ? ? ? ? try {

//? ? ? ? ? ? ? ? ? ? Thread.sleep(100);

//? ? ? ? ? ? ? ? } catch (InterruptedException e) {

//? ? ? ? ? ? ? ? ? ? e.printStackTrace();

//? ? ? ? ? ? ? ? }

}

}

}

}

public class VideoActivity extends AppCompatActivity implements View.OnClickListener {

protected MyVideoSurfaceView mSurfaceView;

protected Button mPlayBtn;

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

super.setContentView(R.layout.activity_video);

initView();

}

// 运行、可见

@Override

protected void onStart() {

super.onStart();

}

// 可交互

@Override

protected void onResume() {

super.onResume();

}

private void play() {

String videoPath = Environment.getExternalStorageDirectory().getPath() +

"/VID_20171117_144736.3gp";// 外部存储根路径

mSurfaceView.playVideo(videoPath);

}

private void initView() {

mSurfaceView = (MyVideoSurfaceView) findViewById(R.id.surface_view);

mPlayBtn = (Button) findViewById(R.id.play_btn);

mPlayBtn.setOnClickListener(VideoActivity.this);

}

@Override

public void onClick(View view) {

if (view.getId() == R.id.play_btn) {

play();

}

}

}

public class MyVideoSurfaceView extends SurfaceView implements SurfaceHolder.Callback {

private SurfaceHolder mHolder;

private MediaPlayer mMediaPlayer;

public MyVideoSurfaceView(Context context, AttributeSet attrs) {

super(context, attrs);

init();

}

private void init() {

// 获取Surface换朝哪个区的持有者

mHolder = getHolder();

mHolder.addCallback(this);

}

// 设置播放源

public void playVideo(String path) {

if (mMediaPlayer == null) {

mMediaPlayer = new MediaPlayer();

}

try {

// 设置播放源

mMediaPlayer.setDataSource(path);

// 设置多媒体的显示部分:使用SurfaceHolder渲染画面

mMediaPlayer.setDisplay(mHolder);

mMediaPlayer.prepare();

mMediaPlayer.start();

} catch (IOException e) {

e.printStackTrace();

}

}

@Override

public void surfaceCreated(SurfaceHolder holder) {

}

@Override

public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {

}

@Override

public void surfaceDestroyed(SurfaceHolder holder) {

mMediaPlayer.release();

mMediaPlayer = null;

}

}

(责任编辑:IT教学网)

更多

推荐Flash教程文章