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

转载请标明出处:http://blog.csdn.net/sk719887916/article/details/40049137,作者:skay

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

   一 准备异步加载工具

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

[java]  view plain
  1. /** 
  2.      * Represents an asynchronous loaderVideoInfos task used to authenticate the 
  3.      * user. 
  4.      */  
  5.     public class VideoLoaderTask extends  
  6.             AsyncTask<TvModle, String, List<TvTaiModel>> {  
  7.   
  8.         @SuppressWarnings("unchecked")  
  9.           
  10.         @Override  
  11.         protected List<TvTaiModel> doInBackground(TvModle... params) {  
  12.             // TODO Auto-generated method stub  
  13.             return lists = DataVideoManager.getData(params[0]);  
  14.         }  
  15.   
  16.         @Override  
  17.         protected void onPostExecute(final List<TvTaiModel> resList) {  
  18.               
  19.             mAuthTask = null;  
  20.             showProgress(false);  
  21.             if (resList != null && resList.size() > 0) {  
  22.                 // Log.e(TAG ,success +"--");  
  23.                 adapter = new VideoWallAdapter(VideoInfoActivity.this0,  
  24.                         resList, mPhotoWall);  
  25.                   
  26.                 mPhotoWall.setAdapter(adapter);  
  27.                   
  28.                 adapter.notifyDataSetChanged();  
  29.             } else {  
  30.                 Toast.makeText(VideoInfoActivity.this"失败", Toast.LENGTH_SHORT).show();  
  31.             }  
  32.         }  
  33.   
  34.         @Override  
  35.         protected void onCancelled() {  
  36.             mAuthTask = null;  
  37.             showProgress(false);  
  38.   
  39.         }  
    此类设计到安卓AsyncTask的用法,需要大家了解此Api,具体原理是利用Thead+ handler机制实现,实际开发中我们更新UI也可以用安卓自带的UI线程runOnUiThread 代码可以如下,具体执行动作在run()实现,不管是用哪种 的方式 更新UI,必须注意的是主线程不能执行网络耗时操作任务,容易出现ANR,(安卓4.0rom以后 主线程直接不能访问网络)。UI也必须由主线程来更新,子线程无UI操作权限。

  1) 利用UI线程

[java]  view plain
  1. this.runOnUiThread(new Runnable() {  
  2.               
  3.             @Override  
  4.             public void run() {  
  5.                 // TODO Auto-generated method stub  
  6.                   
  7.             }  
  8.         });  
 2 ) 利用handler发送Message

[java]  view plain
  1. mHandler = new Handler() {    
  2.    @Override    
  3.    public void handleMessage(Message msg) {    
  4.          
  5.       super.handleMessage(msg);    
  6.    }    
  7.   };    
3) 利用view的post方法


[java]  view plain
  1. <span style="color: rgb(85, 85, 85); font-family: 'microsoft yahei'; font-size: 14.7368421554565px; line-height: 35px;">     View.post(</span>new Runnable() {  
  2.               
  3.             @Override  
  4.             public void run() {  
  5.                 // TODO Auto-generated method stub  
  6.                   
  7.             }  
  8.         }  
)
4 )AsyncTask

  AsyncTask.start()  一般用此类获取网络数据,但是本线程不能重复调用,不然会出异常,只有在task没有运行的情况下才能调用start()方法,多个线程同是调用其start() 也会出现线程安全问题。


2  因为网络任务还耗时的因此给Task加一个过渡loading

  

[java]  view plain
  1. /** 
  2.  * Shows the progress UI and hides the geimainUI form. 
  3.  */  
  4. @TargetApi(Build.VERSION_CODES.HONEYCOMB_MR2)  
  5. private void showProgress(final boolean show) {  
  6.     // On Honeycomb MR2 we have the ViewPropertyAnimator APIs, which allow  
  7.     // for very easy animations. If available, use these APIs to fade-in  
  8.     // the progress spinner.  
  9.     if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR2) {  
  10.         int shortAnimTime = getResources().getInteger(  
  11.                 android.R.integer.config_shortAnimTime);  
  12.   
  13.         mLoginStatusView.setVisibility(View.VISIBLE);  
  14.         mLoginStatusView.animate().setDuration(shortAnimTime)  
  15.                 .alpha(show ? 1 : 0)  
  16.                 .setListener(new AnimatorListenerAdapter() {  
  17.                     @Override  
  18.                     public void onAnimationEnd(Animator animation) {  
  19.                         mLoginStatusView.setVisibility(show ? View.VISIBLE  
  20.                                 : View.GONE);  
  21.                     }  
  22.                 });  
  23.   
  24.         mPhotoWall.setVisibility(View.VISIBLE);  
  25.         mPhotoWall.animate().setDuration(shortAnimTime).alpha(show ? 0 : 1)  
  26.                 .setListener(new AnimatorListenerAdapter() {  
  27.                     @Override  
  28.                     public void onAnimationEnd(Animator animation) {  
  29.                         mPhotoWall.setVisibility(show ? View.GONE  
  30.                                 : View.VISIBLE);  
  31.                     }  
  32.                 });  
  33.     } else {  
  34.         // The ViewPropertyAnimator APIs are not available, so simply show  
  35.         // and hide the relevant UI components.  
  36.         mLoginStatusView.setVisibility(show ? View.VISIBLE : View.GONE);  
  37.         mPhotoWall.setVisibility(show ? View.GONE : View.VISIBLE);  
  38.     }  
  39. }  
  3 获取到网络数据必定含有图片 为了防止OOM,新建ImageLoader 

   当然目前已经有开源的图片缓存框架,但是我们必须懂期原理,为了防止图片内存溢出,我们必须合理利用安卓内存,熟悉安卓回收机制的朋友也非常了解安卓强引用和弱引用,所谓的一级缓存和二级缓存,开发中常用的缓存技术,一般是采用合理分配内存和适当过渡承载实现,具体可以获取手机当前的内存大小,合理设置本次图片请求的最大的负载内存,超过了缓存大小 移除使用频率较低的图片键值,一般是去sd卡读取资源,然后再内存,最后才去请求网络数据。当然请求的图片实际尺寸过大会导致oom。因此必要时还需要根据当前屏幕上所要展示ImageView的大小去缩放bitmap,本人平时喜欢绘制bitmap带自自定义控件上,不喜欢用安卓原生的图片控件。其利弊以后我再慢慢道来。

   1 ), 具体缓存逻辑如下,

   移动智能终端多媒体爬虫技术 获取加载网页视频源_第1张图片

[java]  view plain
  1. public class ImageLoader {  
  2.   
  3.     /** 
  4.      * 图片缓存加载器 
  5.      */  
  6.     private static LruCache<String, Bitmap> mMemoryCache;  
  7.   
  8.     /** 
  9.      * ImageLoader。 
  10.      */  
  11.     private static ImageLoader mImageLoader;  
  12.   
  13.     @SuppressLint("NewApi")  
  14.     private ImageLoader() {  
  15.         // 获取应用程序最大可用内存  
  16.         int maxMemory = (int) Runtime.getRuntime().maxMemory();  
  17.         int cacheSize = maxMemory / 8;  
  18.         // 设置图片缓存大小为程序最大可用内存的1/8  
  19.         mMemoryCache = new LruCache<String, Bitmap>(cacheSize) {  
  20.             @SuppressLint("NewApi")  
  21.             @Override  
  22.             protected int sizeOf(String key, Bitmap bitmap) {  
  23.                 return bitmap.getByteCount();  
  24.             }  
  25.         };  
  26.     }  
  27.   
  28.     /** 
  29.      * 获取ImageLoader的实例。 
  30.      *  
  31.      * @return ImageLoader的实例。 
  32.      */  
  33.     public static ImageLoader getInstance() {  
  34.         if (mImageLoader == null) {  
  35.             mImageLoader = new ImageLoader();  
  36.         }  
  37.         return mImageLoader;  
  38.     }  
  39.   
  40.     /** 
  41.      * 将一张图片存储到LruCache中。 
  42.      *  
  43.      * @param key 
  44.      *            LruCache的键,这里传入图片的URL地址。 
  45.      * @param bitmap 
  46.      *            LruCache的键,这里传入从网络上下载的Bitmap对象。 
  47.      */  
  48.     @SuppressLint("NewApi")  
  49.     public void addBitmapToMemoryCache(String key, Bitmap bitmap) {  
  50.         if (getBitmapFromMemoryCache(key) == null) {  
  51.             mMemoryCache.put(key, bitmap);  
  52.         }  
  53.     }  
  54.   
  55.     /** 
  56.      * 从LruCache中获取一张图片,如果不存在就返回null。 
  57.      *  
  58.      * @param key 
  59.      *            LruCache的键,这里传入图片的URL地址。 
  60.      * @return 对应传入键的Bitmap对象,或者null。 
  61.      */  
  62.     @SuppressLint("NewApi")  
  63.     public Bitmap getBitmapFromMemoryCache(String key) {  
  64.         return mMemoryCache.get(key);  
  65.     }  
  66.   
  67.     public static int calculateInSampleSize(BitmapFactory.Options options,  
  68.             int reqWidth) {  
  69.         // 源图片的宽度  
  70.         final int width = options.outWidth;  
  71.         int inSampleSize = 1;  
  72.         if (width > reqWidth) {  
  73.             // 计算出实际宽度和目标宽度的比率  
  74.             final int widthRatio = Math.round((float) width / (float) reqWidth);  
  75.             inSampleSize = widthRatio;  
  76.         }  
  77.         return inSampleSize;  
  78.     }  
  79.   
  80.     public static Bitmap decodeSampledBitmapFromResource(String pathName,  
  81.             int reqWidth) {  
  82.         // 第一次解析将inJustDecodeBounds设置为true,来获取图片大小  
  83.         final BitmapFactory.Options options = new BitmapFactory.Options();  
  84.         options.inJustDecodeBounds = true;  
  85.         BitmapFactory.decodeFile(pathName, options);  
  86.         // 调用上面定义的方法计算inSampleSize值  
  87.         options.inSampleSize = calculateInSampleSize(options, reqWidth);  
  88.         // 使用获取到的inSampleSize值再次解析图片  
  89.         options.inJustDecodeBounds = false;  
  90.         return BitmapFactory.decodeFile(pathName, options);  
  91.     }  
  92.   
  93. }  

   4 新建请求图片的异步加载任务

   

[java]  view plain
  1. /** 
  2.      * 异步下载图片的任务。 
  3.      *  
  4.      * @author guolin 
  5.      */  
  6.     class BitmapWorkerTask extends AsyncTask<String, Void, Bitmap> {  
  7.   
  8.         /** 
  9.          * 图片的URL地址 
  10.          */  
  11.         private String imageUrl;  
  12.   
  13.         @Override  
  14.         protected Bitmap doInBackground(String... params) {  
  15.             imageUrl = params[0];  
  16.             // 在后台开始下载图片  
  17.             Bitmap bitmap = downloadBitmap(params[0]);  
  18.             if (bitmap != null) {  
  19.                 // 图片下载完成后缓存到LrcCache中  
  20.                 addBitmapToMemoryCache(params[0], bitmap);  
  21.             }  
  22.             return bitmap;  
  23.         }  
  24.   
  25.         @Override  
  26.         protected void onPostExecute(Bitmap bitmap) {  
  27.             super.onPostExecute(bitmap);  
  28.             // 根据Tag找到相应的ImageView控件,将下载好的图片显示出来。  
  29.             ImageView imageView = (ImageView) mPhotoWall.findViewWithTag(imageUrl);  
  30.             if (imageView != null && bitmap != null) {  
  31.                 imageView.setImageBitmap(bitmap);  
  32.             }  
  33.             taskCollection.remove(this);  
  34.         }  
  35.   
  36.         /** 
  37.          * 建立HTTP请求,并获取Bitmap对象。 
  38.          *  
  39.          * @param imageUrl 
  40.          *            图片的URL地址 
  41.          * @return 解析后的Bitmap对象 
  42.          */  
  43.         private Bitmap downloadBitmap(String imageUrl) {  
  44.             Bitmap bitmap = null;  
  45.             HttpURLConnection con = null;  
  46.             try {  
  47.                 URL url = new URL(imageUrl);  
  48.                 con = (HttpURLConnection) url.openConnection();  
  49.                 con.setConnectTimeout(5 * 1000);  
  50.                 con.setReadTimeout(10 * 1000);  
  51.                 bitmap = BitmapFactory.decodeStream(con.getInputStream());  
  52.             } catch (Exception e) {  
  53.                 e.printStackTrace();  
  54.             } finally {  
  55.                 if (con != null) {  
  56.                     con.disconnect();  
  57.                 }  
  58.             }  
  59.             return bitmap;  
  60.         }  
  61.   
  62.     }  
   二 新建视图 activity

      我们可以利用第五篇中实现的UI界面加以利用 点击去请求音悦台MV视频数据。

[java]  view plain
  1. @Override  
  2. protected void onCreate(Bundle savedInstanceState) {  
  3.     super.onCreate(savedInstanceState);  
  4.     setContentView(R.layout.activity_video_info);  
  5.     mContext = getBaseContext();  
  6.       
  7.     mImageThumbSize = getResources().getDimensionPixelSize(  
  8.             R.dimen.image_thumbnail_size);  
  9.     mImageThumbSpacing = getResources().getDimensionPixelSize(  
  10.             R.dimen.image_thumbnail_spacing);  
  11.     mLoginStatusView = this.findViewById(R.id.login_status);  
  12.     mLoginStatusMessageView = (TextView) this  
  13.             .findViewById(R.id.login_status_message);  
  14.     mPhotoWall = (GridView) findViewById(R.id.video_info);  
  15.   
  16.     mPhotoWall.setOnItemClickListener(this);  
  17.     if (getIntent().getExtras() != null) {  
  18.   
  19.         modle = (TvModle) getIntent().getExtras().getSerializable(  
  20.                 "TvModle");  
  21.         if (modle != null) {  
  22.             mUrl = modle.getUrl();  
  23.         }  
  24.   
  25.     }  
  26.     mPhotoWall.getViewTreeObserver().addOnGlobalLayoutListener(  
  27.             new ViewTreeObserver.OnGlobalLayoutListener() {  
  28.   
  29.                 @Override  
  30.                 public void onGlobalLayout() {  
  31.                     final int numColumns = (int) Math.floor(mPhotoWall  
  32.                             .getWidth()  
  33.                             / (mImageThumbSize + mImageThumbSpacing));  
  34.                     if (numColumns > 0) {  
  35.                         int columnWidth = (mPhotoWall.getWidth() / numColumns)  
  36.                                 - mImageThumbSpacing;  
  37.                         //mAdapter.setItemHeight(columnWidth);  
  38.                         mPhotoWall.getViewTreeObserver()  
  39.                                 .removeGlobalOnLayoutListener(this);  
  40.                     }  
  41.                 }  
  42.             });  
  43.     /*mUrl = Constant.QQ_TAI_URL;*/  
  44.     attemptLoader();  
  45. }  
  46.   
  47. @Override  
  48. protected void onDestroy() {  
  49.     super.onDestroy();  
  50.     adapter.cancelAllTasks();  
  51. }  
  52.   
  53. /** 
  54.  * Attempts to sign in or register the account specified by the login form. 
  55.  * If there are form errors (invalid email, missing fields, etc.), the 
  56.  * errors are presented and no actual login attempt is made. 
  57.  */  
  58. public void attemptLoader() {  
  59.     if (mAuthTask != null) {  
  60.         return;  
  61.     }  
  62.   
  63.     boolean cancel = false;  
  64.   
  65.     if (cancel) {  
  66.   
  67.     } else {  
  68.         // Show a progress spinner, and kick off a background task to  
  69.         // perform the user login attempt.  
  70.         showProgress(true);  
  71.         mAuthTask = new VideoLoaderTask();  
  72.         mAuthTask.execute(modle);  
  73.     }  
  74. }  

   

三 增加控制逻辑

     创建gridView适配器 VideoWallAdapter

[java]  view plain
  1. public class VideoWallAdapter extends ArrayAdapter<TvTaiModel> implements OnScrollListener {  
  2.   
  3.     /** 
  4.      * 记录所有正在下载或等待下载的任务。 
  5.      */  
  6.     private Set<BitmapWorkerTask> taskCollection;  
  7.   
  8.     /** 
  9.      * 图片缓存技术的核心类,用于缓存所有下载好的图片,在程序内存达到设定值时会将最少最近使用的图片移除掉。 
  10.      */  
  11.     private LruCache<String, Bitmap> mMemoryCache;  
  12.   
  13.     /** 
  14.      * GridView的实例 
  15.      */  
  16.     private GridView mPhotoWall;  
  17.   
  18.     /** 
  19.      * 第一张可见图片的下标 
  20.      */  
  21.     private int mFirstVisibleItem;  
  22.   
  23.     /** 
  24.      * 一屏有多少张图片可见 
  25.      */  
  26.     private int mVisibleItemCount;  
  27.   
  28.     /** 
  29.      * 记录是否刚打开程序,用于解决进入程序不滚动屏幕,不会下载图片的问题。 
  30.      */  
  31.     private boolean isFirstEnter = true;  
  32.       
  33.     private List< TvTaiModel> lists = null;  
  34.   
  35.     @SuppressLint("NewApi")  
  36.     public VideoWallAdapter(Context context, int textViewResourceId, List<TvTaiModel> taiModels,  
  37.             GridView photoWall) {  
  38.         super(context, textViewResourceId, taiModels);  
  39.         //super(context, textViewResourceId);  
  40.         lists = taiModels;  
  41.         mPhotoWall = photoWall;  
  42.         taskCollection = new HashSet<BitmapWorkerTask>();  
  43.         // 获取应用程序最大可用内存  
  44.         int maxMemory = (int) Runtime.getRuntime().maxMemory();  
  45.         int cacheSize = maxMemory / 8;  
  46.         // 设置图片缓存大小为程序最大可用内存的1/8  
  47.         mMemoryCache = new LruCache<String, Bitmap>(cacheSize) {  
  48.             @Override  
  49.             protected int sizeOf(String key, Bitmap bitmap) {  
  50.                 return bitmap.getByteCount();  
  51.             }  
  52.         };  
  53.         mPhotoWall.setOnScrollListener(this);  
  54.     }  
  55.   
  56.     @Override  
  57.     public View getView(int position, View convertView, ViewGroup parent) {  
  58.           
  59.         final String url = lists.get(position).getImg();  
  60.         final String name = lists.get(position).getTitle();  
  61.           
  62.         Log.e("VideoWallAdapter", url);  
  63.         View view;  
  64.         if (convertView == null) {  
  65.             view = LayoutInflater.from(getContext()).inflate(R.layout.vedio_item, null);  
  66.         } else {  
  67.             view = convertView;  
  68.         }  
  69.         final ImageView photo = (ImageView) view.findViewById(R.id.photo);  
  70.         final TextView title  = (TextView) view.findViewById(R.id.title);  
  71.         // 给ImageView设置一个Tag,保证异步加载图片时不会乱序  
  72.         photo.setTag(url);  
  73.         setImageView(url, photo);  
  74.         title.setText(name);  
  75.         return view;  
  76.     }  

    此时的我们的基础工具类已完成, 接下来 给我们的FocusView所在的acitity注册处理点击事件,用于获取当前TV的视频Url

  

[java]  view plain
  1. @Override  
  2.     public void onItemClick(FocusView mFocusView, View focusView,  
  3.             FocusItemModle<TvModle> focusItem, int Postion, int row, int col,  
  4.             long id) {  
  5.         Intent intent =  new Intent();  
  6.         Bundle bundle = new Bundle();  
  7.         if (focusItem != null && focusItem.getModle() != null) {  
  8.             bundle.putSerializable("TvModle", focusItem.getModle());  
  9.         }  
  10.         intent.putExtras(bundle);  
  11.         intent.setClass(FocusUIActivity.this, values[0]);  
  12.         startActivity(intent);  
  13.     }  

  最后运行 效果如下

移动智能终端多媒体爬虫技术 获取加载网页视频源_第2张图片


        这样就把音悦台的MV资源给解析出来并展现到我们自己的APP上,下一篇会继续结合本篇的逻辑,实现点击具体视频获取真实地址 播放网络视频功能,欢迎大家阅读,如需转载请标明出处 http://blog.csdn.net/sk719887916/article/details/40049137,欢迎交流分享。

你可能感兴趣的:(获取网络视频缓存)