安卓TV开发(八) 移动智能终端多媒体爬虫技术 获取加载网页视频源
转载请标明出处:http://blog.csdn.net/sk719887916/article/details/40049137,作者:skay
从上一篇学习中,学习了多媒体技术中的怎么去用josup加载一个网页并解析html标签的用法,今天就接着前篇 【安卓TV开发(七) 移动智能终端多媒体之在线解析网页视频源】 的学习。同时也了解下避免安卓内存溢出解决方式和安卓常用的几种UI更新的方式。
一 准备异步加载工具
1 新建 VideoLoaderTask 用来获取视频列表
/**
* Represents an asynchronous loaderVideoInfos task used to authenticate the
* user.
*/
public class VideoLoaderTask extends
AsyncTask<TvModle, String, List<TvTaiModel>> {
@SuppressWarnings("unchecked")
@Override
protected List<TvTaiModel> doInBackground(TvModle... params) {
// TODO Auto-generated method stub
return lists = DataVideoManager.getData(params[0]);
}
@Override
protected void onPostExecute(final List<TvTaiModel> resList) {
mAuthTask = null;
showProgress(false);
if (resList != null && resList.size() > 0) {
// Log.e(TAG ,success +"--");
adapter = new VideoWallAdapter(VideoInfoActivity.this, 0,
resList, mPhotoWall);
mPhotoWall.setAdapter(adapter);
adapter.notifyDataSetChanged();
} else {
Toast.makeText(VideoInfoActivity.this, "失败", Toast.LENGTH_SHORT).show();
}
}
@Override
protected void onCancelled() {
mAuthTask = null;
showProgress(false);
}
此类设计到安卓AsyncTask的用法,需要大家了解此Api,具体原理是利用Thead+ handler机制实现,实际开发中我们更新UI也可以用安卓自带的UI线程runOnUiThread 代码可以如下,具体执行动作在run()实现,不管是用哪种的方式更新UI,必须注意的是主线程不能执行网络耗时操作任务,容易出现ANR,(安卓4.0rom以后
主线程直接不能访问网络)。UI也必须由主线程来更新,子线程无UI操作权限。
1) 利用UI线程
this.runOnUiThread(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
}
});
2 ) 利用handler发送Message
mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
}
};
3) 利用view的post方法
<span style="color: rgb(85, 85, 85); font-family: 'microsoft yahei'; font-size: 14.7368421554565px; line-height: 35px;"> View.post(</span>new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
}
}
)
4 )AsyncTask
AsyncTask.start() 一般用此类获取网络数据,但是本线程不能重复调用,不然会出异常,只有在task没有运行的情况下才能调用start()方法,多个线程同是调用其start() 也会出现线程安全问题。
2 因为网络任务还耗时的因此给Task加一个过渡loading
/**
* Shows the progress UI and hides the geimainUI form.
*/
@TargetApi(Build.VERSION_CODES.HONEYCOMB_MR2)
private void showProgress(final boolean show) {
// On Honeycomb MR2 we have the ViewPropertyAnimator APIs, which allow
// for very easy animations. If available, use these APIs to fade-in
// the progress spinner.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR2) {
int shortAnimTime = getResources().getInteger(
android.R.integer.config_shortAnimTime);
mLoginStatusView.setVisibility(View.VISIBLE);
mLoginStatusView.animate().setDuration(shortAnimTime)
.alpha(show ? 1 : 0)
.setListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
mLoginStatusView.setVisibility(show ? View.VISIBLE
: View.GONE);
}
});
mPhotoWall.setVisibility(View.VISIBLE);
mPhotoWall.animate().setDuration(shortAnimTime).alpha(show ? 0 : 1)
.setListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
mPhotoWall.setVisibility(show ? View.GONE
: View.VISIBLE);
}
});
} else {
// The ViewPropertyAnimator APIs are not available, so simply show
// and hide the relevant UI components.
mLoginStatusView.setVisibility(show ? View.VISIBLE : View.GONE);
mPhotoWall.setVisibility(show ? View.GONE : View.VISIBLE);
}
}
3 获取到网络数据必定含有图片 为了防止OOM,新建ImageLoader
当然目前已经有开源的图片缓存框架,但是我们必须懂期原理,为了防止图片内存溢出,我们必须合理利用安卓内存,熟悉安卓回收机制的朋友也非常了解安卓强引用和弱引用,所谓的一级缓存和二级缓存,开发中常用的缓存技术,一般是采用合理分配内存和适当过渡承载实现,具体可以获取手机当前的内存大小,合理设置本次图片请求的最大的负载内存,超过了缓存大小 移除使用频率较低的图片键值,一般是去sd卡读取资源,然后再内存,最后才去请求网络数据。当然请求的图片实际尺寸过大会导致oom。因此必要时还需要根据当前屏幕上所要展示ImageView的大小去缩放bitmap,本人平时喜欢绘制bitmap带自自定义控件上,不喜欢用安卓原生的图片控件。其利弊以后我再慢慢道来。
1 ), 具体缓存逻辑如下,
public class ImageLoader {
/**
* 图片缓存加载器
*/
private static LruCache<String, Bitmap> mMemoryCache;
/**
* ImageLoader。
*/
private static ImageLoader mImageLoader;
@SuppressLint("NewApi")
private ImageLoader() {
// 获取应用程序最大可用内存
int maxMemory = (int) Runtime.getRuntime().maxMemory();
int cacheSize = maxMemory / 8;
// 设置图片缓存大小为程序最大可用内存的1/8
mMemoryCache = new LruCache<String, Bitmap>(cacheSize) {
@SuppressLint("NewApi")
@Override
protected int sizeOf(String key, Bitmap bitmap) {
return bitmap.getByteCount();
}
};
}
/**
* 获取ImageLoader的实例。
*
* @return ImageLoader的实例。
*/
public static ImageLoader getInstance() {
if (mImageLoader == null) {
mImageLoader = new ImageLoader();
}
return mImageLoader;
}
/**
* 将一张图片存储到LruCache中。
*
* @param key
* LruCache的键,这里传入图片的URL地址。
* @param bitmap
* LruCache的键,这里传入从网络上下载的Bitmap对象。
*/
@SuppressLint("NewApi")
public void addBitmapToMemoryCache(String key, Bitmap bitmap) {
if (getBitmapFromMemoryCache(key) == null) {
mMemoryCache.put(key, bitmap);
}
}
/**
* 从LruCache中获取一张图片,如果不存在就返回null。
*
* @param key
* LruCache的键,这里传入图片的URL地址。
* @return 对应传入键的Bitmap对象,或者null。
*/
@SuppressLint("NewApi")
public Bitmap getBitmapFromMemoryCache(String key) {
return mMemoryCache.get(key);
}
public static int calculateInSampleSize(BitmapFactory.Options options,
int reqWidth) {
// 源图片的宽度
final int width = options.outWidth;
int inSampleSize = 1;
if (width > reqWidth) {
// 计算出实际宽度和目标宽度的比率
final int widthRatio = Math.round((float) width / (float) reqWidth);
inSampleSize = widthRatio;
}
return inSampleSize;
}
public static Bitmap decodeSampledBitmapFromResource(String pathName,
int reqWidth) {
// 第一次解析将inJustDecodeBounds设置为true,来获取图片大小
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeFile(pathName, options);
// 调用上面定义的方法计算inSampleSize值
options.inSampleSize = calculateInSampleSize(options, reqWidth);
// 使用获取到的inSampleSize值再次解析图片
options.inJustDecodeBounds = false;
return BitmapFactory.decodeFile(pathName, options);
}
}
4 新建请求图片的异步加载任务
/**
* 异步下载图片的任务。
*
* @author guolin
*/
class BitmapWorkerTask extends AsyncTask<String, Void, Bitmap> {
/**
* 图片的URL地址
*/
private String imageUrl;
@Override
protected Bitmap doInBackground(String... params) {
imageUrl = params[0];
// 在后台开始下载图片
Bitmap bitmap = downloadBitmap(params[0]);
if (bitmap != null) {
// 图片下载完成后缓存到LrcCache中
addBitmapToMemoryCache(params[0], bitmap);
}
return bitmap;
}
@Override
protected void onPostExecute(Bitmap bitmap) {
super.onPostExecute(bitmap);
// 根据Tag找到相应的ImageView控件,将下载好的图片显示出来。
ImageView imageView = (ImageView) mPhotoWall.findViewWithTag(imageUrl);
if (imageView != null && bitmap != null) {
imageView.setImageBitmap(bitmap);
}
taskCollection.remove(this);
}
/**
* 建立HTTP请求,并获取Bitmap对象。
*
* @param imageUrl
* 图片的URL地址
* @return 解析后的Bitmap对象
*/
private Bitmap downloadBitmap(String imageUrl) {
Bitmap bitmap = null;
HttpURLConnection con = null;
try {
URL url = new URL(imageUrl);
con = (HttpURLConnection) url.openConnection();
con.setConnectTimeout(5 * 1000);
con.setReadTimeout(10 * 1000);
bitmap = BitmapFactory.decodeStream(con.getInputStream());
} catch (Exception e) {
e.printStackTrace();
} finally {
if (con != null) {
con.disconnect();
}
}
return bitmap;
}
}
二 新建视图 activity
我们可以利用第五篇中实现的UI界面加以利用 点击去请求音悦台MV视频数据。
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_video_info);
mContext = getBaseContext();
mImageThumbSize = getResources().getDimensionPixelSize(
R.dimen.image_thumbnail_size);
mImageThumbSpacing = getResources().getDimensionPixelSize(
R.dimen.image_thumbnail_spacing);
mLoginStatusView = this.findViewById(R.id.login_status);
mLoginStatusMessageView = (TextView) this
.findViewById(R.id.login_status_message);
mPhotoWall = (GridView) findViewById(R.id.video_info);
mPhotoWall.setOnItemClickListener(this);
if (getIntent().getExtras() != null) {
modle = (TvModle) getIntent().getExtras().getSerializable(
"TvModle");
if (modle != null) {
mUrl = modle.getUrl();
}
}
mPhotoWall.getViewTreeObserver().addOnGlobalLayoutListener(
new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
final int numColumns = (int) Math.floor(mPhotoWall
.getWidth()
/ (mImageThumbSize + mImageThumbSpacing));
if (numColumns > 0) {
int columnWidth = (mPhotoWall.getWidth() / numColumns)
- mImageThumbSpacing;
//mAdapter.setItemHeight(columnWidth);
mPhotoWall.getViewTreeObserver()
.removeGlobalOnLayoutListener(this);
}
}
});
/*mUrl = Constant.QQ_TAI_URL;*/
attemptLoader();
}
@Override
protected void onDestroy() {
super.onDestroy();
adapter.cancelAllTasks();
}
/**
* Attempts to sign in or register the account specified by the login form.
* If there are form errors (invalid email, missing fields, etc.), the
* errors are presented and no actual login attempt is made.
*/
public void attemptLoader() {
if (mAuthTask != null) {
return;
}
boolean cancel = false;
if (cancel) {
} else {
// Show a progress spinner, and kick off a background task to
// perform the user login attempt.
showProgress(true);
mAuthTask = new VideoLoaderTask();
mAuthTask.execute(modle);
}
}
三 增加控制逻辑
创建gridView适配器 VideoWallAdapter
public class VideoWallAdapter extends ArrayAdapter<TvTaiModel> implements OnScrollListener {
/**
* 记录所有正在下载或等待下载的任务。
*/
private Set<BitmapWorkerTask> taskCollection;
/**
* 图片缓存技术的核心类,用于缓存所有下载好的图片,在程序内存达到设定值时会将最少最近使用的图片移除掉。
*/
private LruCache<String, Bitmap> mMemoryCache;
/**
* GridView的实例
*/
private GridView mPhotoWall;
/**
* 第一张可见图片的下标
*/
private int mFirstVisibleItem;
/**
* 一屏有多少张图片可见
*/
private int mVisibleItemCount;
/**
* 记录是否刚打开程序,用于解决进入程序不滚动屏幕,不会下载图片的问题。
*/
private boolean isFirstEnter = true;
private List< TvTaiModel> lists = null;
@SuppressLint("NewApi")
public VideoWallAdapter(Context context, int textViewResourceId, List<TvTaiModel> taiModels,
GridView photoWall) {
super(context, textViewResourceId, taiModels);
//super(context, textViewResourceId);
lists = taiModels;
mPhotoWall = photoWall;
taskCollection = new HashSet<BitmapWorkerTask>();
// 获取应用程序最大可用内存
int maxMemory = (int) Runtime.getRuntime().maxMemory();
int cacheSize = maxMemory / 8;
// 设置图片缓存大小为程序最大可用内存的1/8
mMemoryCache = new LruCache<String, Bitmap>(cacheSize) {
@Override
protected int sizeOf(String key, Bitmap bitmap) {
return bitmap.getByteCount();
}
};
mPhotoWall.setOnScrollListener(this);
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
final String url = lists.get(position).getImg();
final String name = lists.get(position).getTitle();
Log.e("VideoWallAdapter", url);
View view;
if (convertView == null) {
view = LayoutInflater.from(getContext()).inflate(R.layout.vedio_item, null);
} else {
view = convertView;
}
final ImageView photo = (ImageView) view.findViewById(R.id.photo);
final TextView title = (TextView) view.findViewById(R.id.title);
// 给ImageView设置一个Tag,保证异步加载图片时不会乱序
photo.setTag(url);
setImageView(url, photo);
title.setText(name);
return view;
}
此时的我们的基础工具类已完成, 接下来 给我们的FocusView所在的acitity注册处理点击事件,用于获取当前TV的视频Url
@Override
public void onItemClick(FocusView mFocusView, View focusView,
FocusItemModle<TvModle> focusItem, int Postion, int row, int col,
long id) {
Intent intent = new Intent();
Bundle bundle = new Bundle();
if (focusItem != null && focusItem.getModle() != null) {
bundle.putSerializable("TvModle", focusItem.getModle());
}
intent.putExtras(bundle);
intent.setClass(FocusUIActivity.this, values[0]);
startActivity(intent);
}
最后运行 效果如下
这样就把音悦台的MV资源给解析出来并展现到我们自己的APP上,下一篇会继续结合本篇的逻辑,实现点击具体视频获取真实地址 播放网络视频功能,欢迎大家阅读,如需转载请标明出处 http://blog.csdn.net/sk719887916/article/details/40049137,欢迎交流分享。
安卓TV开发(八) 移动智能终端多媒体爬虫技术 获取加载网页视频源的更多相关文章
- 安卓TV开发(七) 移动智能终端多媒体之在线解析网页视频源
载请标明出处:http://blog.csdn.net/sk719887916/article/details/40049137,作者:skay 结束了所有UI绘制的学习,智能设备常用的应用音视频类, ...
- 安卓TV开发(十) 智能电视开发之在线视频直播
转载注明出处:http://blog.csdn.net/sk719887916/article/details/46582987 从<安卓TV开发(八) 移动智能终端多媒体之在线加载网页视频源& ...
- 安卓TV开发(五) 移动智能终端UI之实现主流TV焦点可控UI
载请标明出处:http://blog.csdn.net/sk719887916,作者:skay 由于其他网站收录,导致你无法查看本系列原创文章请点击此处 安卓TV开发(四)实现主流智能T ...
- 安卓TV开发(六) 移动智能终端UI之实现类似GridView的焦点控制FocusView框架
转载请标明出处:http://blog.csdn.net/sk719887916/article/details/40045089,作者:skay 前言 安卓TV开发(五) 移动智能终端UI之实现主流 ...
- 安卓Tv开发(一)移动智能电视之焦点控制(触控事件)
前言:移动智能设备的发展,推动了安卓另一个领域,包括智能电视和智能家居,以及可穿戴设备的大量使用,但是这些设备上的开发并不是和传统手机开发一样,特别是焦点控制和用户操作体验风格上有很大的区别,本系列博 ...
- 安卓TV开发(四) 实现主流智能TV视频播放器UI
前言:移动智能设备的发展,推动了安卓另一个领域,包括智能电视和智能家居,以及可穿戴设备的大量使用,但是这些设备上的开发并不是和传统手机开发一样,特别是焦点控制和用户操作体验上有很大的区别,本系列博文主 ...
- 安卓TV开发(三) 移动智能设备之实现主流TV电视盒子焦点可控UI
前言:移动智能设备的发展,推动了安卓另一个领域,包括智能电视和智能家居,以及可穿戴设备的大量使用,但是这些设备上的开发并不是和传统手机开发一样,特别是焦点控制和用户操作体验上有很大的区别,本系列博文主 ...
- 安卓Tv开发(二)移动智能电视之焦点控制(按键事件)
原文:http://blog.csdn.net/sk719887916/article/details/44781475 skay 前言:移动智能设备的发展,推动了安卓另一个领域,包括智能电视和智能家 ...
- 安卓TV开发(概述) 智能电视之视觉设计和体验分析
转载说明出处 :http://blog.csdn.net/sk719887916, 作者:skay 前言:移动智能设备的发展,推动了安卓另一个领域,包括智能电视和智能家居,以及可穿戴设备的大 ...
随机推荐
- Java异常处理-----Throwable类
Throwable类 1.toString() 输出该异常的类名. 2.getMessage() 输出异常的信息,需要通过构造方法传入异常信息(例如病态信息). 3.printStackTrace() ...
- AR模块常用函数
--AR模块常用函数 FUNCTION get_fnd_user_name ( p_user_id IN NUMBER ) return VARCHAR2 IS CURSOR c_user_name ...
- cuda网格的限制
限制于计算能力有关. 详情 http://docs.nvidia.com/cuda/cuda-c-programming-guide/index.html#compute-capabilities 只 ...
- mysql 远程连接配置
近期买了阿里云服务器,服务器 安装了mysql,需要远程操作mysql数据库,但是远程不配置的话,连接不上去的.需要配置 .具体的配置如下: 先看看my.cnf是否绑定了本机,如果绑定了地址就解绑吧. ...
- Tomcat的系统安全管理
Tomcat是一个Web容器,我们开发的Web项目运行在Tomcat平台,这就好比将一个应用嵌入到一个平台上面运行,要使嵌入的程序能正常运行,首先平台要能安全正常运行.并且要最大程度做到平台不受嵌入的 ...
- 集合框架之Map接口
Map是将键映射到值的对象.一个映射不能包含重复的键:每个键最多只能映射到一个值. Map 接口提供三种collection视图,允许以键集.值集或键-值映射关系集的形式查看某个映射的内容.映射顺序定 ...
- 删除表中重复行SQL
delete from table_name a where rowid < (select max(rowid) from table_name b where a.col1 = b.col1 ...
- MPI二维笛卡尔坐标划分【1】
本文简单演示,如何对现有进程进行二维划分,如何获得进程的X和Y坐标. 只有一段程序: #include <mpi.h> #include <stdio.h> #include ...
- UE4 C++与蓝图交互
一.C++调用蓝图的函数 本来应该有一个很好的示例,但一下却忘了适用于哪些地方,只有简单写一个步骤了. 1.新建一个C++类继承Actor,名称随意,我这里继承了一个SplineMesh,一样de,并 ...
- Spark技术内幕:Master的故障恢复
Spark技术内幕:Master基于ZooKeeper的High Availability(HA)源码实现 详细阐述了使用ZK实现的Master的HA,那么Master是如何快速故障恢复的呢? 处于 ...