您的位置:首页 > 其它

移动智能终端多媒体爬虫技术 获取加载网页视频源

2016-06-19 07:47 459 查看
转载请标明出处:/article/1366058.html,作者:skay

从上一篇学习中,学习了多媒体技术中的怎么去用josup加载一个网页并解析html标签的用法,今天就接着前篇 【安卓TV开发(七)
移动智能终端多媒体之在线解析网页视频源】 的学习。同时也了解下避免安卓内存溢出解决方式和安卓常用的几种UI更新的方式。

一 准备异步加载工具

1 新建 VideoLoaderTask 用来获取视频列表

[java] view
plain

/**

* 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线程

[java] view
plain

this.runOnUiThread(new Runnable() {

@Override

public void run() {

// TODO Auto-generated method stub

}

});

2 ) 利用handler发送Message

[java] view
plain

mHandler = new Handler() {

@Override

public void handleMessage(Message msg) {

super.handleMessage(msg);

}

};

3) 利用view的post方法

[java] view
plain

<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

[java] view
plain

/**

* 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 ), 具体缓存逻辑如下,



[java] view
plain

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 新建请求图片的异步加载任务

[java] view
plain

/**

* 异步下载图片的任务。

*

* @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视频数据。

[java] view
plain

@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

[java] view
plain

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

[java] view
plain

@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上,下一篇会继续结合本篇的逻辑,实现点击具体视频获取真实地址 播放网络视频功能,欢迎大家阅读,如需转载请标明出处 /article/1366058.html,欢迎交流分享。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: