在Android中子线程是不能更新ui的。

大纲

videoview原型:videoView extends SurfaceView implements
MediaController.MediaPlayerControl其中SurfaceView
为显示提供支持,MediaPlayerControl则为媒体控制提供了支持。如果要构建更为复杂和有特色个性的视频View,需要继承SurfaceView
和实现MediaPlayerControl接口。

用videoview播放视频是非常简单的一件事,下面我放全代码,实现以下:首先布局文件video_play_layout.xml中

 <VideoView android: android:layout_width="match_parent" android:layout_height="240dp" android:layout_centerVertical="true" />

VideoPlayActivity.java中

public class VideoPlayActivity extends AppCompatActivity { private VideoView videoView; private MediaController mc; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.video_play_layout); videoView = (VideoView) findViewById(R.id.videoPlayView); //设置视频控制器,组件可以控制视频的播放,暂停,快进,组件,不需要你实现 mc = new MediaController; videoView.setMediaController; String netPlayUrl="http://baobab.wdjcdn.com/145076769089714.mp4"; Uri uri = Uri.parse(netPlayUrl); videoView.setVideoURI;//设置视频的播放地址,网络地址。播放网络视频 //播放本地视频 videoView.setVideoPath(Environment.getExternalStorageDirectory().getPath()+"video.mp4"); videoView.requestFocus();//让VideiView获取焦点 videoView.start();//开始播放 }}

调用VideoView的如下两个方法来加载指定的视频setVidePath(String
path):加载路径文件代表的视频setVideoURI:加载uri所对应的视频⚠️:加载网络视频的时候记得要在AndroidManifest.xml中加入权限:<uses-permission
android:name=”android.permission.INTERNET” />

视频缓存策略:先加载,缓存到本地再播放,这适用于微信朋友圈这种小视频、若视频过大,肯定不能用这种。边播放边缓存:对余大多数,还是使用边播边缓存的。

  • 第一种方法:开两个线程,一个去正常让videoview请求播放,另一个线程去下载文件,等到再次点击播放,此时如果视频以及有缓存,就播放本地缓存。(但是这种相当于两份网络请求,时间会变慢)
  • 第二种方法:通过代理的策略实现一个中间层将我们的网络请求转移到本地实现的代理服务器上,这样我们真正请求的数据就会被代理拿到,这样代理一边向本地写入数据,一边根据我们需要的数据看是读网络数据还是读本地缓存数据再提供给我们,真正做到了数据的复用。对于videoview,github开源上有一个写好的库实现边播边缓存地址:AndroidVideoCacheAndroidVideoCache-视频边播放边缓存的代理策略

具体使用,是分简单,

compile ‘com.danikula:videocache:2.7.0’
//在app/build.gradle中加入依赖,引第三方

CustomViewApp.javapublic class CustomViewApp extends Application { //全局初始化一个本地代理服务器 private HttpProxyCacheServer proxy; public static HttpProxyCacheServer getProxy(Context context) { CustomViewApp app = (CustomViewApp) context.getApplicationContext(); return app.proxy == null ? (app.proxy = app.newProxy : app.proxy; } private HttpProxyCacheServer newProxy() { return new HttpProxyCacheServer; }}

VideoPlayActivity.java中: private VideoView videoView; private MediaController mc; private String proxyUrl; private HttpProxyCacheServer proxy; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.video_play_layout); videoView = (VideoView) findViewById(R.id.videoPlayView); //设置视频控制器,组件可以控制视频的播放,暂停,快进,组件,不需要你实现 mc = new MediaController; videoView.setMediaController; String netPlayUrl="http://baobab.wdjcdn.com/145076769089714.mp4"; proxy = CustomViewApp.getProxy(getApplicationContext; proxyUrl = proxy.getProxyUrl(netPlayUrl); videoView.setVideoPath;//播放的是代理服务器返回的url,已经进行了封装处理 videoView.requestFocus();//让VideiView获取焦点 videoView.start();//开始播放}....

AndroidManifest.xml中,因为application改变了,所以application标签中,android:name=”.CustomViewApp”
⚠️

类似朋友圈,播放前我们是需要一个封面图加一个按钮,单击按钮跳转到我们刚才写的activity中,

Bitmap bitmap = null; MediaMetadataRetriever retriever = new MediaMetadataRetriever(); try { //根据网络视频的url获取第一帧--亲测可用。但是这个方法获取本地视频的第一帧,不可用,还没找到方法解决。 if (Build.VERSION.SDK_INT >= 14) { retriever.setDataSource(videoUrl, new HashMap<String, String>; } else { retriever.setDataSource; } //获得第一帧图片 bitmap = retriever.getFrameAtTime(); } catch (IllegalArgumentException e) { e.printStackTrace(); } finally { retriever.release(); } return bitmap;

videoview正常播放完毕会退出,如果做到循环播放,像朋友圈小视频一样?也是非常简单的。

 //循环播放 videoView.setOnCompletionListener(new MediaPlayer.OnCompletionListener() { @Override public void onCompletion(MediaPlayer mediaPlayer) { proxyUrl = proxy.getProxyUrl; videoView.setVideoPath; videoView.start;

去掉 private MediaController mc; 屏蔽关于它的操作

 @Override public boolean onTouchEvent(MotionEvent event) { if (event.getAction()== MotionEvent.ACTION_UP){ finish(); } return true; }

在默认情况下当萤幕从竖屏变到横屏时会触发onConfigurationChanged事件,画面会重新载入

  • a.
    在manifest中设置该Activity的configChanges为android:configChanges=“screenSize|keyboardHidden|orientation”
  • b.重载onConfigurationChanged事件

  • a.不设置Activity的android:configChanges时,切屏会重新调用各个生命周期,切横屏时会执行一次,切竖屏时会执行两次
  • b.设置Activity的android:configChanges=“orientation”时,切屏还是会重新调用各个生命周期,切横、竖屏时只会执行一次
  • c.设置Activity的android:configChanges=“orientation|keyboardHidden”时,切屏不会重新调用各个生命周期,只会执行onConfigurationChanged方法
  • d.是由于google在android3.2中添加了screensize改变的通知,在转屏的时候,不仅是orientation发生了改变,screensize同样也发生了改变,所以要设置screenSize参数:
    只竖屏显示的话(android:screenOrientation=”portrait”)
    只横屏显示的话(android:screenOrientation=”landscape”)

视频最初加载的时候,如果什么处理都不做,网速又卡一点的话,会有几秒的黑屏,所以我们总要在加载的时候做一点处理,增加用户体验吧:可以直接放一个progressbar,或是生动一点,我是自定义了一个view,一个封面图加一个progressbar,加载的时候显示,加载完最初的一段就可以取消了。

图片 11

 videoView.setOnInfoListener(new MediaPlayer.OnInfoListener() { @Override public boolean onInfo(MediaPlayer mp, int what, int extra) { if (what==MediaPlayer.MEDIA_INFO_BUFFERING_START){ bufferingCoverView.setVisibility(View.VISIBLE); } else { bufferingCoverView.setVisibility(View.GONE); } return true; } }); //在视频预处理完成后调用 videoView.setOnPreparedListener(new MediaPlayer.OnPreparedListener() { @Override public void onPrepared(MediaPlayer mp) { bufferingCoverView.setVisibility(View.GONE); } });

把之前写类似朋友圈播放视频时遇到的问题总结出来,哈哈哈,给个赞鼓励一下吧~~比心❤️

来一个属于程序猿的打招呼方式:
public class ConfigManager { private int mPwdLength; private boolean mIsAutoLogin; private boolean mIsFinishGuide; private Context mContext; private ConfigManager() { mContext = BaseApplication.getContext(); mIsAutoLogin = getBoolean(IS_AUTO_LOGIN); mIsFinishGuide = getBoolean(IS_FINISH_GUIDE); mPwdLength = SpfUtils.getInt(mContext, LENGTH, 0); } private static ConfigManager singleInstance = new ConfigManager(); public static ConfigManager getInstance() { return singleInstance; } private boolean getBoolean(String key) { return SpfUtils.getBoolean(mContext, key,false); } private boolean getBoolean(String key, boolean defaultValue) { return SpfUtils.getBoolean(mContext, key, defaultValue); } public int getPwdLength() { return mPwdLength; } public void setPwdLength(int pwdLength) { mPwdLength = pwdLength; SpfUtils.putInt(mContext, LENGTH, pwdLength); } public boolean isAutoLogin() { return mIsAutoLogin; } public void setAutoLogin(boolean autoLogin) { mIsAutoLogin = autoLogin; SpfUtils.putBoolean(mContext, IS_AUTO_LOGIN, autoLogin); } public boolean isFinishGuide() { return mIsFinishGuide; } public void setFinishGuide(boolean finishGuide) { mIsFinishGuide = finishGuide; SpfUtils.putBoolean(mContext, IS_FINISH_GUIDE, finishGuide); }}

看到这么密集的代码,估计有人想报警了(看个博客都不能安生,就不能让我愉快的阅读,远离代码几分钟?心理暗暗的骂道。)大兄弟,不要急!贴代码就是为了证明我不是在吹B,你再忍一忍!牛B的你肯定已经看出来了,这里只是对日常我们操作SP的一个封装。经常使用SP的童鞋肯定有写错key的经历,别环顾左右了,说的就是你。还有另外的一个好处是,只管调方法取你想要的值,完全不用关心是不是有哪个冒失鬼修改了里面的值。对,就是这么任性。这里写的都是一些设置里的配置参数,没有考虑并发的情况

所以我们要通过其他方式来动态改变ui视图,

吹完了优点总得出来溜一溜吧:

来一段熟悉的代码

 if (ConfigManager.getInstance().isFinishGuide { startActivity(new Intent(this, MainActivity.class)); finish(); }

设置?

ConfigManager.getInstance().setAutoLogin(isChecked);

AndroidStudio那么强大的代码提示功能怎么能闲着这个没有什么酷炫到爆的功能,但是能让工作更便利,你觉得呢?

activity提供的一个轻量级更新ui的方法,在Fragment需要使用的时候要用getActivity.runOnUiThread开启线程这种方法最简单,方便更新一些不需要判断的通知,比如在聊天项目中动态获取未读消息数量。

 runOnUiThread(new Runnable() { @Override public void run() { sendMessage("[自动回复]你好,我是机器人"); } });

使用这个方法可以设置比如按钮倒计时的控制,也是比较常见的一种更新ui的方法。

创建一个主线程用于接收子线程不断发送的消息,通过msg.what判断传递的消息类型。根据类型进行相关ui的更新操作。

发表评论

电子邮件地址不会被公开。 必填项已用*标注