編輯:關於Android編程
android開發過程中,下載是必備的功能,下載安裝包,或者下載圖片,假設用戶下載過程中人為中斷網絡,或者網絡不穩定中斷下載任務,好的用戶體驗是從斷開的地方繼續下載,而不是又從頭開始下載,因為比方說用戶是拿4g來下載的,你一個游戲安裝包100多M,用戶下載了90M,突然手機沒電了,充好電,又從頭下載,那豈不是浪費用戶的流量。所以斷點續傳是非常必要的一個功能。其實斷點續傳也可以使用多線程來實現的,本篇先不寫的這麼麻煩了,就單線程去下載一個任務了,如果中斷了,下次再點擊下載的時候,從斷點繼續下載。好,開始我們的實驗。本實驗是下載一個安裝包。比如我們下載360手機衛士。給出Demo代碼。
1、activity_resume_download.xml 下載頁面
2、ResumeDownloadActivity.java
package com.figo.study.activity;
import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import android.view.View;
import android.widget.ProgressBar;
import android.widget.Toast;
import com.figo.study.R;
import com.figo.study.mgr.DownloadMgr;
import com.figo.study.utils.FileUtils;
import java.io.File;
public class ResumeDownloadActivity extends Activity implements View.OnClickListener {
String tag = "ResumeDownloadActivity";
ProgressBar mProgressBar;
String downloadUrl = "http://msoftdl.360.cn/mobilesafe/shouji360/360safe/500192/360MobileSafe.apk";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_resume_download);
findViewById(R.id.btn_download).setOnClickListener(this);
findViewById(R.id.btn_cancel).setOnClickListener(this);
mProgressBar = (ProgressBar) findViewById(R.id.progressBar);
}
private final Handler msgHandler = new Handler() {
public void handleMessage(Message msg) {
switch (msg.what) {
case 0:
Toast.makeText(getApplicationContext(), msg.getData().get("msg").toString(), Toast.LENGTH_SHORT).show();
break;
default:
break;
}
}
};
@Override
public void onClick(View view) {
switch (view.getId()) {
case R.id.btn_download:
String directory = FileUtils.getStorageDirectory();
String fileName = directory + File.separator + getFileName(downloadUrl);
DownloadMgr.getInstance().addTask(downloadUrl, fileName, new DownloadMgr.Callback() {
@Override
public void onProgress(long progress, long total) {
super.onProgress(progress, total);
mProgressBar.setProgress((int) (100 * progress / total));
}
@Override
public void onStart() {
super.onStart();
sendMsg("start");
}
@Override
public void onSuccess() {
super.onSuccess();
Log.i(tag, "success");
sendMsg("success");
}
@Override
public void onFailed(boolean cancelled, String msg) {
super.onFailed(cancelled, msg);
Log.e(tag, msg);
//Looper.getMainLooper().prepare();//這麼干雖然可以在子線程,彈出toast,但是子線程執行到這裡,後面的代碼將不再執行
// Toast.makeText(ResumeDownloadActivity.this, "download start", Toast.LENGTH_SHORT).show();
//Looper.getMainLooper().loop();
//進程間通信還是用Handler比較靠譜
sendMsg(msg);
}
});
break;
case R.id.btn_cancel:
DownloadMgr.getInstance().cancelTask(downloadUrl);
break;
}
}
private String getFileName(String downloadUrl) {
return downloadUrl.substring(downloadUrl.lastIndexOf("/"));
}
private void sendMsg(String msg) {
Message msgNew = new Message();
msgNew.what = 0;
Bundle bundle = new Bundle();
bundle.putString("msg", msg);
msgNew.setData(bundle);
msgHandler.sendMessage(msgNew);
}
}
package com.figo.study.mgr;
import android.content.Context;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import com.figo.study.activity.MyApplication;
import com.figo.study.utils.CommonUtil;
import com.figo.study.utils.IOUtil;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.InterruptedIOException;
import java.net.HttpURLConnection;
import java.net.SocketTimeoutException;
import java.net.URL;
import java.util.HashMap;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
/**
* Created by figo on 16/7/25.
*/
public class DownloadMgr {
private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1;
Executor mExecutor = Executors.newFixedThreadPool(MAXIMUM_POOL_SIZE);
static DownloadMgr mDownloadMgr;
static Object obj = new Object();
HashMap<String, DownloadTask> mTasks = new HashMap<String, DownloadTask>();
public static void init() {
getInstance();
}
public static DownloadMgr getInstance() {
synchronized (obj) {
if (mDownloadMgr == null) {
mDownloadMgr = new DownloadMgr();
}
}
return mDownloadMgr;
}
public void addTask(String downloadUrl, String filePath, Callback callback) {
if (!mTasks.containsKey(downloadUrl)) {
mTasks.put(downloadUrl, new DownloadTask(downloadUrl, filePath, callback));
}
mTasks.get(downloadUrl).startDownload();
}
public void removeTask(String downloadUrl, String filePath, Callback callback) {
if (mTasks.containsKey(downloadUrl)) {
mTasks.get(downloadUrl).cancel();
}
mTasks.remove(downloadUrl);
}
public class DownloadTask implements Runnable {
private String downloadUrl;
private String filePath;
Callback callback;
public DownloadTask(String downloadUrl, String filePath, Callback callback) {
this.downloadUrl = downloadUrl;
this.filePath = filePath;
this.callback = callback;
}
public void startDownload() {
mExecutor.execute(this);
}
@Override
public void run() {
runResumable(downloadUrl, filePath, callback);
}
synchronized boolean cancel() {
if (thread == null)
return false;
thread.interrupt();
return true;
}
}
Thread thread;
public void runResumable(String downloadUrl, String filePath, Callback callback) {
Thread.currentThread().setPriority(Thread.MIN_PRIORITY);
thread = Thread.currentThread();
final Context ctx = MyApplication.getInstance();
String msg = "";
boolean interrupted = false;
HttpURLConnection conn = null;
long resumePosition = 0;
final File file = new File(filePath);
try {
//20160720 add
final File parent = file.getParentFile();
if (!parent.exists()) {
parent.mkdirs();
}
if (!file.exists()) {
file.createNewFile();
}
callback.onStart();
//簡單一點就用md5來校驗
// if (file.exists() && StringUtil.equalsIgnoreCase(md5, Md5.md5(file))) {
// suc = true;
// return;
// }
//非wifi環境不下載
if (!isWifiActive(ctx)) {
msg = "請在wifi環境下下載";
callback.onFailed(true, msg);
return;
}
resumePosition = file.exists() ? file.length() : 0;
// Create connection object
conn = (HttpURLConnection) new URL(downloadUrl).openConnection();
conn.setConnectTimeout(60000);
conn.setReadTimeout(60000);
conn.setDoInput(true);
conn.setUseCaches(false);
// Make the request
conn.setRequestMethod("GET");
conn.setRequestProperty("User-Agent", "Java/Android");
conn.setRequestProperty("Connection", "close");
conn.setRequestProperty("Http-version", "HTTP/1.1");
conn.setRequestProperty("Cache-Control", "no-transform");
if (resumePosition > 0) {
//斷點續傳的關鍵設置Range
conn.setRequestProperty("Range", "bytes=" + resumePosition + "-");
}
conn.connect();
final int responseCode = conn.getResponseCode();
if (responseCode == 416) {
msg = "已經下載!";
callback.onFailed(true, msg);
return;
}
if (responseCode != 200 && responseCode != 206) {
msg = "網絡繁忙,請稍後再試!";
callback.onFailed(true, msg);
return;
}
long fileLength = conn.getContentLength();
InputStream is = new BufferedInputStream(conn.getInputStream());
FileOutputStream fos = new FileOutputStream(file, resumePosition > 0);
try {
int read = 0;
long progress = resumePosition;
byte[] buffer = new byte[4096 * 2];
while ((read = is.read(buffer)) > 0 && !(interrupted = Thread.interrupted())) {
try {
fos.write(buffer, 0, read);
} catch (Exception e) {
msg = "磁盤空間已滿,無法下載";
throw e;
}
// progress
progress += read;
callback.onProgress(progress, fileLength);
}
} finally {
IOUtil.closeQuietly(fos);
IOUtil.closeQuietly(is);
}
//20160720 resumable download
if (file.exists()) {
//也可以通過md5來校驗
// if (StringUtil.equalsIgnoreCase(md5, Md5.md5(file))) {
// suc = true;
// return;
// }
//檢驗數據是否完整
if (file.length() == fileLength + resumePosition) {
callback.onSuccess();
return;
}
}
} catch (Exception e) {
interrupted = interrupted || Thread.interrupted() || (e instanceof InterruptedIOException && !(e instanceof SocketTimeoutException));
msg = "網絡異常,下載失敗";
if (interrupted) {
msg = "下載被中斷!";
}
callback.onFailed(true, msg);
} finally {
disconnect(conn);
}
}
static void disconnect(HttpURLConnection conn) {
try {
if (conn == null)
return;
conn.disconnect();
} catch (Throwable e) {
e.printStackTrace();
}
}
public static abstract class Callback {
public void onStart() {
}
public void onProgress(long progress, long total) {
}
public void onSuccess() {
}
public void onFailed(boolean cancelled, String msg) {
}
}
public boolean isWifiActive(Context ctx) {
try {
ConnectivityManager mgr = (ConnectivityManager) ctx.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo info = mgr.getActiveNetworkInfo();
return (info != null) ? info.getType() == ConnectivityManager.TYPE_WIFI : false;
} catch (Exception e) {
return false;
}
}
public void cancelAllTask() {
try {
if (mTasks != null) {
for (String taskKey : mTasks.keySet()) {
mTasks.get(taskKey).cancel();
}
}
} catch (Exception e) {
CommonUtil.printStackTrace(e);
}
}
public void cancelTask(String key) {
try {
if (mTasks != null) {
mTasks.get(key).cancel();
}
} catch (Exception e) {
CommonUtil.printStackTrace(e);
}
}
public static boolean checkNetAvailable(Context ctx) {
try {
ConnectivityManager mgr = (ConnectivityManager) ctx.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo info = mgr.getActiveNetworkInfo();
return (info != null) ? true : false;
} catch (Exception e) {
return true;
}
}
}
詳細分析Android中onTouch事件傳遞機制
onTach介紹ontach是Android系統中整個事件機制的基礎。Android中的其他事件,如onClick、onLongClick等都是以onTach為基礎的。o
Android開發:使用Windows cmd窗口抓取Android手機log
寫在前面的廢話一般Android開發者都會使用Eclipse,Android studio觀察log輸出,其實後台是使用adb來打印log的,這裡介紹的是如何讓log輸出
Android 教你親手打造酷炫的彈幕效果
公司的新產品上線需要添加的彈幕功能,於是花了一天時間寫了一個Demo。效果實現如下:一開始的思路是:1、首先實現一個自定義的Layout,在其中獲得需要展示的彈幕數組,每
Android自定義賬戶類型和同步適配器模式 Custom Account Type & SyncAdapter
自定義賬戶類型 Custom Account Type當有多個APP共用一個賬號系統的時候,在用戶的Android設備上創建一個自定義賬戶用以處理登錄認證會方便很多,比如