//Api类
public class Api { //http://api.svipmovie.com/front/columns/getVideoList.do?catalogId=402834815584e463015584e539330016 public static final String benUrl="http://api.svipmovie.com/front/columns/"; }
//Api接口
public interface ApiSevert { // getVideoList.do?catalogId=402834815584e463015584e539330016 @GET("getVideoList.do?catalogId=402834815584e463015584e539330016") Observable<Bean> getNoPrea(); }
//model层
public interface MainActivityModelListener { void getData(OnFinish finish); }
public class MainActivityModel implements MainActivityModelListener { @Override public void getData(final OnFinish finish) { OkHttpClient okHttpClient=new OkHttpClient.Builder() //网络拦截器 // .addInterceptor(new LoggingInterceptor()) .build(); Retrofit retrofit=new Retrofit.Builder() .baseUrl(Api.benUrl) .addCallAdapterFactory(RxJavaCallAdapterFactory.create()) .addConverterFactory(GsonConverterFactory.create()) // .client(okHttpClient) .build(); Observable<Bean> getNoPrea = retrofit.create(ApiSevert.class).getNoPrea(); getNoPrea.subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(new Subscriber<Bean>() { @Override public void onCompleted() { } @Override public void onError(Throwable e) { } @Override public void onNext(Bean bean) { finish.finish(bean); } }); } }
//view层
public interface MainActivityViewListener { void ShowData(Bean bean); }
//presenter层,Bean层自己实现
public interface IPresenter { void loadList(); }
public class ListPresenter implements IPresenter { private final MainActivityModelListener molder; private MainActivityViewListener gv; public ListPresenter(MainActivityViewListener gv) { this.gv=gv; molder=new MainActivityModel(); } @Override public void loadList() { molder.getData(new OnFinish() { @Override public void finish(Bean bean) { gv.ShowData(bean); } }); } }
//适配器
public class MyAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder> implements View.OnClickListener,View.OnLongClickListener{ //点击事件 private OnItemClickListener mOnItemClickListener = null; //长按点击事件 private OnRecyclerItemLongListener mOnItemLong = null; private Context context; private List<Bean.RetBean.ListBean>list; public MyAdapter(Context context,List<Bean.RetBean.ListBean> list) { this.context = context; this.list = list; } public void setData(Bean bean) { if (this.list == null) { this.list = new ArrayList<>(); } this.list.addAll(bean.getRet().getList()); notifyDataSetChanged(); } @Override public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { View view = LayoutInflater.from(context).inflate(R.layout.item, parent, false); ViewHolder holder=new ViewHolder(view); view.setOnClickListener(this); view.setOnLongClickListener(this); return holder; } @Override public void onBindViewHolder(ViewHolder holder, int position) { ViewGroup.LayoutParams params = holder.img.getLayoutParams();//得到item的LayoutParams布局参数 holder.img.setLayoutParams(params);//把params设置给itemView布局 Uri uri = Uri.parse(list.get(position).getPic()); holder.img.setImageURI(uri); holder.tv.setText(list.get(position).getTitle()); holder.tv2.setText(list.get(position).getDuration()); holder.itemView.setTag(position); } @Override public int getItemCount() { return list == null ? 0 : list.size(); } //长按点击事件 public interface OnRecyclerItemLongListener{ void onItemLongClick(View view,int position); } public void setOnItemLongClickListener(OnRecyclerItemLongListener listener){ this.mOnItemLong = listener; } @Override public boolean onLongClick(View view) { if(mOnItemLong != null){ mOnItemLong.onItemLongClick(view,1); } return true; } //点击事件接口 //define interface public static interface OnItemClickListener { void onItemClick(View view , int position); } public void setOnItemClickListener(OnItemClickListener listener) { this.mOnItemClickListener = listener; } @Override public void onClick(View view) { if (mOnItemClickListener != null) { //注意这里使用getTag方法获取position mOnItemClickListener.onItemClick(view,(int)view.getTag()); } } public class ViewHolder extends RecyclerView.ViewHolder{ public SimpleDraweeView img; public TextView tv,tv2; public ViewHolder(View itemView) { super(itemView); img=itemView.findViewById(R.id.img); tv=itemView.findViewById(R.id.tv1); tv2=itemView.findViewById(R.id.tv2); } } }
//适配器布局
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" xmlns:fresco="http://schemas.android.com/apk/res-auto" android:orientation="vertical" android:layout_height="wrap_content"> <com.facebook.drawee.view.SimpleDraweeView android:scaleType="fitXY" android:padding="10dp" android:id="@+id/img" android:src="@mipmap/ic_launcher_round" android:layout_width="120dp" android:layout_height="120dp" fresco:viewAspectRatio="1.33"/> <!--<!– other attributes –>--> <LinearLayout android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent"> <TextView android:id="@+id/tv1" android:padding="10dp" android:text="成都" android:textSize="20sp" android:layout_width="wrap_content" android:layout_height="wrap_content" /> <TextView android:id="@+id/tv2" android:padding="10dp" android:textSize="20sp" android:text="二货_无法长大" android:layout_width="wrap_content" android:layout_height="wrap_content" /> </LinearLayout> </LinearLayout>
//主Activity
public class MainActivity extends FragmentActivity implements MainActivityViewListener{ private ListPresenter presenter; private List<Bean.RetBean.ListBean>list; private RecyclerView thirdRecyclerview; private MyAdapter myAdapter; //下载 private int max; private TextView total; private ProgressBar mProgressBar; private DownloadUtil mDownloadUtil; private static final String TAG = MainActivity.class.getSimpleName(); private String urlString; //长度 private Handler handler = new Handler(); int pro=0; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); Fresco.initialize(this); setContentView(R.layout.activity_main); initView(); urlString = "http://phonemovie.ks3-cn-beijing.ksyun.com/image/2017/10/16/1508141676941055287.jpg"; String localPath = Environment.getExternalStorageDirectory() .getAbsolutePath() + "/local"; mDownloadUtil = new DownloadUtil(2, localPath, "ab.jpg", urlString, this); mDownloadUtil.setOnDownloadListener(new DownloadUtil.OnDownloadListener() { @Override public void downloadStart(int fileSize) { // TODO Auto-generated method stub Log.w(TAG, "fileSize::" + fileSize); max = fileSize; mProgressBar.setMax(fileSize); } @Override public void downloadProgress(int downloadedSize) { Log.w(TAG, "Compelete::" + downloadedSize); mProgressBar.setProgress(downloadedSize); total.setText((int) downloadedSize * 100 / max + "%"); } @Override public void downloadEnd() { Log.w(TAG, "ENd"); } }); } private void initView() { thirdRecyclerview = findViewById(R.id.third_recyclerview); thirdRecyclerview.setLayoutManager(new LinearLayoutManager(this)); myAdapter=new MyAdapter(MainActivity.this,list); presenter = new ListPresenter(this); thirdRecyclerview.setAdapter(myAdapter); presenter.loadList(); myAdapter.setOnItemClickListener(new MyAdapter.OnItemClickListener() { @Override public void onItemClick(View view, int position) { mDownloadUtil.start(); handler.post(runnable); //开始执行 Toast.makeText(MainActivity.this,"开始",Toast.LENGTH_LONG).show(); } }); myAdapter.setOnItemLongClickListener(new MyAdapter.OnRecyclerItemLongListener() { @Override public void onItemLongClick(View view, int position) { mDownloadUtil.pause(); Toast.makeText(MainActivity.this,"暂停",Toast.LENGTH_LONG).show(); } }); //下载 total= (TextView) findViewById(R.id.textView); mProgressBar= (ProgressBar) findViewById(R.id.progressBar); } //启动子线程进度完之后跳转 Runnable runnable=new Runnable() { @Override public void run() { mProgressBar.setVisibility(View.VISIBLE); pro=mProgressBar.getProgress()+100; mProgressBar.setProgress(pro); //如果进度小于100,,则延迟1000毫秒后重复执行runnable if(pro<100){ handler.postDelayed(runnable, 100); }else{ mProgressBar.setVisibility(View.GONE); startActivity(new Intent(MainActivity.this, Main2Activity.class)); handler.removeCallbacks(runnable); mProgressBar.setProgress(0); } } }; @Override public void ShowData(Bean bean) { myAdapter.setData(bean); } }
//主布局
<LinearLayout android:orientation="vertical" xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context="com.bawei.mythetests.MainActivity"> <android.support.v7.widget.RecyclerView android:layout_width="match_parent" android:layout_weight="1" android:layout_height="match_parent" android:id="@+id/third_recyclerview" /> <TextView android:id="@+id/textView" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentLeft="true" android:layout_alignParentStart="true" android:layout_alignParentTop="true" android:layout_marginLeft="23dp" android:layout_marginStart="23dp" android:layout_marginTop="18dp" android:text="TextView" /> <ProgressBar android:id="@+id/progressBar" style="?android:attr/progressBarStyleHorizontal" android:layout_width="fill_parent" android:layout_height="7.5dp" android:max="100" android:progress="100" android:layout_marginRight="8dp" android:layout_below="@+id/textView" android:visibility="visible"/> </LinearLayout>
//接口
public interface OnFinish { void finish(Bean bean); }
//Main2类
public class Main2Activity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main2); VideoView vv=(VideoView) findViewById(R.id.videoView1); //设置播放路径 ///data/media/0/local/abc.MP4 ///data/iToolsVMShare/minion_09.mp4 vv.setVideoPath("data/media/0/local/abc.MP4"); MediaController control=new MediaController(this); control.setMediaPlayer(vv); vv.setMediaController(control); //开始播放 vv.start(); } }
//Main2布局
<VideoView android:id="@+id/videoView1" android:layout_width="match_parent" android:layout_height="200dp" />
//断点续传
public class DownlaodSqlTool { private DownLoadHelper dbHelper; public DownlaodSqlTool(Context context){ dbHelper=new DownLoadHelper(context); } /** * 创建下载的具体信息 */ public void insertInfos(List<DownloadInfo> infos) { SQLiteDatabase database = dbHelper.getWritableDatabase(); for (DownloadInfo info : infos) { String sql = "insert into download_info(thread_id,start_pos, end_pos,compelete_size,url) values (?,?,?,?,?)"; Object[] bindArgs = { info.getThreadId(), info.getStartPos(), info.getEndPos(), info.getCompeleteSize(), info.getUrl() }; database.execSQL(sql, bindArgs); } } /** * 得到下载具体信息 */ public List<DownloadInfo> getInfos(String urlstr) { List<DownloadInfo> list = new ArrayList<DownloadInfo>(); SQLiteDatabase database = dbHelper.getWritableDatabase(); String sql = "select thread_id, start_pos, end_pos,compelete_size,url from download_info where url=?"; Cursor cursor = database.rawQuery(sql, new String[] { urlstr }); while (cursor.moveToNext()) { DownloadInfo info = new DownloadInfo(cursor.getInt(0), cursor.getInt(1), cursor.getInt(2), cursor.getInt(3), cursor.getString(4)); list.add(info); } return list; } /** * 更新数据库中的下载信息 */ public void updataInfos(int threadId, int compeleteSize, String urlstr) { SQLiteDatabase database = dbHelper.getWritableDatabase(); String sql = "update download_info set compelete_size=? where thread_id=? and url=?"; Object[] bindArgs = { compeleteSize, threadId, urlstr }; database.execSQL(sql, bindArgs); } /** * 关闭数据库 */ public void closeDb() { dbHelper.close(); } /** * 下载完成后删除数据库中的数据 */ public void delete(String url) { SQLiteDatabase database = dbHelper.getWritableDatabase(); database.delete("download_info", "url=?", new String[] { url }); } }
//断点续传
public class DownLoadHelper extends SQLiteOpenHelper { private static final String SQL_NAME = "download.db"; private static final int DOWNLOAD_VERSION=1; public DownLoadHelper(Context context) { super(context, SQL_NAME, null, DOWNLOAD_VERSION); } @Override public void onCreate(SQLiteDatabase db) { db.execSQL("create table download_info(_id integer PRIMARY KEY AUTOINCREMENT, thread_id integer, " + "start_pos integer, end_pos integer, compelete_size integer,url char)"); } @Override public void onUpgrade(SQLiteDatabase sqLiteDatabase, int i, int i1) { } }
//断点续传
public class DownloadHttpTool { /** * * 利用Http协议进行多线程下载具体实践类 */ private static final String TAG = DownloadHttpTool.class.getSimpleName(); private int threadCount;//线程数量 private String urlstr;//URL地址 private Context mContext; private Handler mHandler; private List<DownloadInfo> downloadInfos;//保存下载信息的类 private String localPath;//目录 private String fileName;//文件名 private int fileSize; private DownlaodSqlTool sqlTool;//文件信息保存的数据库操作类 private enum Download_State { Downloading, Pause, Ready;//利用枚举表示下载的三种状态 } private Download_State state = Download_State.Ready;//当前下载状态 private int globalCompelete = 0;//所有线程下载的总数 public DownloadHttpTool(int threadCount, String urlString, String localPath, String fileName, Context context, Handler handler) { super(); this.threadCount = threadCount; this.urlstr = urlString; this.localPath = localPath; this.mContext = context; this.mHandler = handler; this.fileName = fileName; sqlTool = new DownlaodSqlTool(mContext); } //在开始下载之前需要调用ready方法进行配置 public void ready() { Log.w(TAG, "ready"); globalCompelete = 0; downloadInfos = sqlTool.getInfos(urlstr); if (downloadInfos.size() == 0) { initFirst(); } else { File file = new File(localPath + "/" + fileName); if (!file.exists()) { sqlTool.delete(urlstr); initFirst(); } else { fileSize = downloadInfos.get(downloadInfos.size() - 1) .getEndPos(); for (DownloadInfo info : downloadInfos) { globalCompelete += info.getCompeleteSize(); } Log.w(TAG, "globalCompelete:::" + globalCompelete); } } } public void start() { Log.w(TAG, "start"); if (downloadInfos != null) { if (state == Download_State.Downloading) { return; } state = Download_State.Downloading; for (DownloadInfo info : downloadInfos) { Log.v(TAG, "startThread"); new DownloadThread(info.getThreadId(), info.getStartPos(), info.getEndPos(), info.getCompeleteSize(), info.getUrl()).start(); } } } public void pause() { state = Download_State.Pause; sqlTool.closeDb(); } public void delete(){ compelete(); File file = new File(localPath + "/" + fileName); file.delete(); } public void compelete() { sqlTool.delete(urlstr); sqlTool.closeDb(); } public int getFileSize() { return fileSize; } public int getCompeleteSize() { return globalCompelete; } //第一次下载初始化 private void initFirst() { Log.w(TAG, "initFirst"); try { URL url = new URL(urlstr); HttpURLConnection connection = (HttpURLConnection) url .openConnection(); connection.setConnectTimeout(5000); connection.setRequestMethod("GET"); fileSize = connection.getContentLength(); Log.w(TAG, "fileSize::" + fileSize); File fileParent = new File(localPath); if (!fileParent.exists()) { fileParent.mkdir(); } File file = new File(fileParent, fileName); if (!file.exists()) { file.createNewFile(); } // 本地访问文件 RandomAccessFile accessFile = new RandomAccessFile(file, "rwd"); accessFile.setLength(fileSize); accessFile.close(); connection.disconnect(); } catch (Exception e) { e.printStackTrace(); } int range = fileSize / threadCount; downloadInfos = new ArrayList<DownloadInfo>(); for (int i = 0; i < threadCount - 1; i++) { DownloadInfo info = new DownloadInfo(i, i * range, (i + 1) * range - 1, 0, urlstr); downloadInfos.add(info); } DownloadInfo info = new DownloadInfo(threadCount - 1, (threadCount - 1) * range, fileSize - 1, 0, urlstr); downloadInfos.add(info); sqlTool.insertInfos(downloadInfos); } //自定义下载线程 private class DownloadThread extends Thread { private int threadId; private int startPos; private int endPos; private int compeleteSize; private String urlstr; private int totalThreadSize; public DownloadThread(int threadId, int startPos, int endPos, int compeleteSize, String urlstr) { this.threadId = threadId; this.startPos = startPos; this.endPos = endPos; totalThreadSize = endPos - startPos + 1; this.urlstr = urlstr; this.compeleteSize = compeleteSize; } @Override public void run() { HttpURLConnection connection = null; RandomAccessFile randomAccessFile = null; InputStream is = null; try { randomAccessFile = new RandomAccessFile(localPath + "/" + fileName, "rwd"); randomAccessFile.seek(startPos + compeleteSize); URL url = new URL(urlstr); connection = (HttpURLConnection) url.openConnection(); connection.setConnectTimeout(5000); connection.setRequestMethod("GET"); connection.setRequestProperty("Range", "bytes=" + (startPos + compeleteSize) + "-" + endPos); is = connection.getInputStream(); byte[] buffer = new byte[1024]; int length = -1; while ((length = is.read(buffer)) != -1) { randomAccessFile.write(buffer, 0, length); compeleteSize += length; Message message = Message.obtain(); message.what = threadId; message.obj = urlstr; message.arg1 = length; mHandler.sendMessage(message); sqlTool.updataInfos(threadId, compeleteSize, urlstr); Log.w(TAG, "Threadid::" + threadId + " compelete::" + compeleteSize + " total::" + totalThreadSize); if (compeleteSize >= totalThreadSize) { break; } if (state != Download_State.Downloading) { break; } } } catch (Exception e) { e.printStackTrace(); } finally { try { if (is != null) { is.close(); } randomAccessFile.close(); connection.disconnect(); } catch (Exception e) { e.printStackTrace(); } } } } }
//断点续传
public class DownloadInfo { /** * 保存每个下载线程下载信息类 * */ private int threadId;// 下载器id private int startPos;// 开始点 private int endPos;// 结束点 private int compeleteSize;// 完成度 private String url;// 下载文件的URL地址 public DownloadInfo(int threadId, int startPos, int endPos, int compeleteSize, String url) { this.threadId = threadId; this.startPos = startPos; this.endPos = endPos; this.compeleteSize = compeleteSize; this.url = url; } public DownloadInfo() { } public String getUrl() { return url; } public void setUrl(String url) { this.url = url; } public int getThreadId() { return threadId; } public void setThreadId(int threadId) { this.threadId = threadId; } public int getStartPos() { return startPos; } public void setStartPos(int startPos) { this.startPos = startPos; } public int getEndPos() { return endPos; } public void setEndPos(int endPos) { this.endPos = endPos; } public int getCompeleteSize() { return compeleteSize; } public void setCompeleteSize(int compeleteSize) { this.compeleteSize = compeleteSize; } @Override public String toString() { return "DownloadInfo [threadId=" + threadId + ", startPos=" + startPos + ", endPos=" + endPos + ", compeleteSize=" + compeleteSize + "]"; } }
//断点续传
public class DownloadUtil { private DownloadHttpTool mDownloadHttpTool; private OnDownloadListener onDownloadListener; private int fileSize; private int downloadedSize = 0; @SuppressLint("HandlerLeak") private Handler mHandler = new Handler() { @Override public void handleMessage(Message msg) { // TODO Auto-generated method stub super.handleMessage(msg); int length = msg.arg1; synchronized (this) {//加锁保证已下载的正确性 downloadedSize += length; } if (onDownloadListener != null) { onDownloadListener.downloadProgress(downloadedSize); } if (downloadedSize >= fileSize) { mDownloadHttpTool.compelete(); if (onDownloadListener != null) { onDownloadListener.downloadEnd(); } } } }; public DownloadUtil(int threadCount, String filePath, String filename, String urlString, Context context) { mDownloadHttpTool = new DownloadHttpTool(threadCount, urlString, filePath, filename, context, mHandler); } //下载之前首先异步线程调用ready方法获得文件大小信息,之后调用开始方法 public void start() { new AsyncTask<Void,Void,Void>() { @Override protected Void doInBackground(Void... arg0) { // TODO Auto-generated method stub mDownloadHttpTool.ready(); return null; } @Override protected void onPostExecute(Void result) { // TODO Auto-generated method stub super.onPostExecute(result); fileSize = mDownloadHttpTool.getFileSize(); downloadedSize = mDownloadHttpTool.getCompeleteSize(); Log.w("Tag", "downloadedSize::" + downloadedSize); if (onDownloadListener != null) { onDownloadListener.downloadStart(fileSize); } mDownloadHttpTool.start(); } }.execute(); } public void pause() { mDownloadHttpTool.pause(); } public void delete(){ mDownloadHttpTool.delete(); } public void reset(){ mDownloadHttpTool.delete(); start(); } public void setOnDownloadListener(OnDownloadListener onDownloadListener) { this.onDownloadListener = onDownloadListener; } //下载回调接口 public interface OnDownloadListener { public void downloadStart(int fileSize); public void downloadProgress(int downloadedSize);//记录当前所有线程下总和 public void downloadEnd(); } }
//权限
<uses-sdk android:minSdkVersion="8" android:targetSdkVersion="18" /> <uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS" /> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> <uses-permission android:name="android.permission.INTERNET"/>
//依赖
compile 'com.android.support:appcompat-v7:26.+' compile 'com.android.support.constraint:constraint-layout:1.0.2' compile 'com.squareup.retrofit2:retrofit:2.0.1' compile 'com.squareup.retrofit2:converter-gson:2.0.1' compile 'com.squareup.retrofit2:adapter-rxjava:2.0.1' compile 'io.reactivex:rxandroid:1.1.0' compile 'com.squareup.okhttp3:logging-interceptor:3.4.1' compile 'com.android.support:recyclerview-v7:26.+' compile 'com.facebook.fresco:fresco:0.9.0+' compile 'com.jakewharton:butterknife:8.8.1' testCompile 'junit:junit:4.12' annotationProcessor 'com.jakewharton:butterknife-compiler:8.8.1'