转载请标明出处: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. Android Studio下多渠道打包

    Android Studio下实现多渠道打包 直接上步骤 步骤 1. 清单文件添加属性(以友盟统计为例) 在application标签下添加meta-data属性 <application -- ...

  2. EBS技术开发之VPD策略

    VPD (虚拟专用数据库的简称),主要作用是根据运行环境的上下文,隐式的添加条 件. 好处是在数据库层解决细粒度的角色权限访问,避免在中间层写大量代码:坏处 是数据屏蔽的逻辑太隐蔽了,对于分析查找问题 ...

  3. XMPP系列(六)---创建群组

    最近公司项目需要,要做一个自己的IMSDK,顺便先把之前没有记录的群聊功能记录一下. 先上资料,查看XMPP群聊相关的资料,可以去这里看协议:XEP-0045 . 创建群组 XMPP 框架里有一个类X ...

  4. iOS7 CookBook精彩瞬间(一)property、selector细节、__unused

    1.我们常常使用nonatomic,很多人只知道它的效率较高,却不知道其含义,其含义是非线程安全的,也就是说多线程修改时不加锁,可能出现多个线程先后修改而成为脏数据的情况. 2.unsafe_unre ...

  5. Shell命令:echo 命令详解

    http://blog.chinaunix.net/uid-27124799-id-3383327.html # echo命令介绍 功能说明:显示文字. 语 法:echo [-ne][字符串] / e ...

  6. 转义字符\(在hive+shell以及java中注意事项):正则表达式的转义字符为双斜线,split函数解析也是正则

    转义字符 将后边字符转义,使特殊功能字符作为普通字符处理,或者普通字符转化为特殊功能字符. 各个语言中都用应用,如java.python.sql.hive.shell等等. 如sql中 "\ ...

  7. iOS下JS与原生OC互相调用(总结)

    这是去年总结的一篇文章,也一并先放到这个目录下好了. iOS开发免不了要与UIWebView打交道,然后就要涉及到JS与原生OC交互,今天总结一下JS与原生OC交互的两种方式. JS调用原生OC篇 方 ...

  8. linux ubuntu系统下MySQL的安装及设置

    debian下安装MySQL:1.构建源或使用光盘镜像,当然你插入光盘也没问题2.有源时本地文件的源配置:修改/etc/apt/sources.list文件, 示例:deb http://192.16 ...

  9. Android简易实战教程--第三话《自己实现打电话》

    需要一个文本输入框输入号码,需要一个按钮打电话.本质:点击按钮,调用系统打电话功能. xml布局文件代码:: <LinearLayout xmlns:android="http://s ...

  10. Unable to instantiate Action, MenuAction, defined for 'QueryMenuAll' in namespace '/'MenuAction

    我刚好也遇到这样的情况,发现是自己的配置文件里写错了,spring里的id属性值要对应struts里class属性值.