转载请标明出处: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开发(八) 移动智能终端多媒体爬虫技术 获取加载网页视频源的更多相关文章

  1. 安卓TV开发(七) 移动智能终端多媒体之在线解析网页视频源

    载请标明出处:http://blog.csdn.net/sk719887916/article/details/40049137,作者:skay 结束了所有UI绘制的学习,智能设备常用的应用音视频类, ...

  2. 安卓TV开发(十) 智能电视开发之在线视频直播

    转载注明出处:http://blog.csdn.net/sk719887916/article/details/46582987 从<安卓TV开发(八) 移动智能终端多媒体之在线加载网页视频源& ...

  3. 安卓TV开发(五) 移动智能终端UI之实现主流TV焦点可控UI

      载请标明出处:http://blog.csdn.net/sk719887916,作者:skay    由于其他网站收录,导致你无法查看本系列原创文章请点击此处 安卓TV开发(四)实现主流智能T ...

  4. 安卓TV开发(六) 移动智能终端UI之实现类似GridView的焦点控制FocusView框架

    转载请标明出处:http://blog.csdn.net/sk719887916/article/details/40045089,作者:skay 前言 安卓TV开发(五) 移动智能终端UI之实现主流 ...

  5. 安卓Tv开发(一)移动智能电视之焦点控制(触控事件)

    前言:移动智能设备的发展,推动了安卓另一个领域,包括智能电视和智能家居,以及可穿戴设备的大量使用,但是这些设备上的开发并不是和传统手机开发一样,特别是焦点控制和用户操作体验风格上有很大的区别,本系列博 ...

  6. 安卓TV开发(四) 实现主流智能TV视频播放器UI

    前言:移动智能设备的发展,推动了安卓另一个领域,包括智能电视和智能家居,以及可穿戴设备的大量使用,但是这些设备上的开发并不是和传统手机开发一样,特别是焦点控制和用户操作体验上有很大的区别,本系列博文主 ...

  7. 安卓TV开发(三) 移动智能设备之实现主流TV电视盒子焦点可控UI

    前言:移动智能设备的发展,推动了安卓另一个领域,包括智能电视和智能家居,以及可穿戴设备的大量使用,但是这些设备上的开发并不是和传统手机开发一样,特别是焦点控制和用户操作体验上有很大的区别,本系列博文主 ...

  8. 安卓Tv开发(二)移动智能电视之焦点控制(按键事件)

    原文:http://blog.csdn.net/sk719887916/article/details/44781475 skay 前言:移动智能设备的发展,推动了安卓另一个领域,包括智能电视和智能家 ...

  9. 安卓TV开发(概述) 智能电视之视觉设计和体验分析

         转载说明出处 :http://blog.csdn.net/sk719887916, 作者:skay 前言:移动智能设备的发展,推动了安卓另一个领域,包括智能电视和智能家居,以及可穿戴设备的大 ...

随机推荐

  1. 1git命令的使用,查看git仓库状态,添加文件到git跟踪,git提交,查看git分支,查看git仓库日志信息,切换git分支,解决git分支合并后出现冲突的问题

    1新建一个存储git的文件夹,命令是: toto@toto-K45VD:~$ mkdir gitfolder 2初始化一个git仓库,命令是: toto@toto-K45VD:~$cd gitfold ...

  2. java的断言(assert)

    概述 在C和C++语言中都有assert关键,表示断言.在Java中,同样也有assert关键字,表示断言,用法和含义都差不多.在Java中,assert关键字是从JAVA SE 1.4 引入的,为了 ...

  3. 无网络环境下安装Dynamics CRM

    在安装CRM时会需要很多的组件支持,没有这些组件是没法安装的,一般我们都是选择机器联网后在线安装,但也有特殊情况确实不能联网的,可参考这篇文章 https://blogs.msdn.microsoft ...

  4. Swift基础之init方法,实例方法,类方法(静态方法)的使用(多标签Demo)

    Xcode更新过后,有些方法都进行了改变,Demo中有变化的都进行了简单的标记,具体以后遇见再说 创建一个UIView类,用init方法创建两种类型,显示多标签,创建静态方法进行调用,创建类方法进行调 ...

  5. 文件操作:fseek函数和ftell函数

    1.fseek函数: int fseek(FILE * _File, long _Offset, int _Origin); 函数设置文件指针stream的位置.如果执行成功,stream将指向以fr ...

  6. 浅谈C语言 extern 指针与数组

    /* * d.c * * Created on: Nov 15, 2011 * Author: root */ #include "apue.h" int a[] = {3,2}; ...

  7. (八十)MapKit放置系统默认大头针和自定义大头针

    有关MapView的显示和定位在上一节已经说明,这一节说明如何在地图上放置大头针,以及设置点击大头针的视图. [系统默认大头针] mapView上放置大头针的方法是调用其addAnnotation:方 ...

  8. 高通QSD MSM APQ区别

    高通msm是Mobile Station Modem 的缩写,即移动基带工作站,是指带有基带芯片的移动处理器,实际就是基带内置的手机处理器(soc)系列. qsd是qualcomm snapdrago ...

  9. Chipmunk僵尸物理对象的出现和解决(六)

    既然出现了这个问题下面就是如何找到原因. 因为该问题不是每次都出现,偶尔反弹棒碰到五角星时才会多出一个僵尸棒,现象比较随机,较难悉知具体原因. 有时多次触碰又没有出现问题,有时短时间内每次触碰都出现问 ...

  10. Java实现简易的文本编辑器

    需求分析: 获得文本名称 实现尾部追加功能 实现覆盖式添加数据 删除数据 获取光标位置 在特定光标位置处添加数据 查找特定字符串在主串中第一次出现的位置 统计文本文件内出现的数字,汉字,英文字母,特殊 ...