1.Thread使用:
继承Thread类:
public class MyThread extends Thread {
@Override
public void run() {
//线程中处理的逻辑
}
}
调用:
new MyThread().start();
实现Runnable:
public class MyThread implements Runnable {
@Override
public void run() {
//线程中处理的逻辑
}
}
调用:
MyThread myThread=new MyThread()
new Thread(myThread).start();
异步消息处理
public class ThreadTestActivity extends Activity {
private TextView text;
private Handler handler=new Handler(){
@Override
public void handleMessage(Message msg) {
switch (msg.what){
case 0:
//子线程不允许操作UI.,在这里操作UI 防止线程堵塞
text.setText("这里操作UI,防止线程堵塞");
break;
case 1:
break;
default:
break;
}
}
};
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button button=findViewById(R.id.start_download);
text=(TextView)findViewById(R.id.text_view);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
new Thread(new Runnable() {
@Override
public void run() {
Message message=new Message();
message.what=0;
handler.sendMessage(message);
}
}).start();
}
});
}
}
1.Message存储少量数据,用于进程间交换数据。除了what字段,还有arg1 arg2 以及obj.
2.Handler处理Message发送过来的信息,发送消息一般是Handler的sedMessage()方法。最终会传递到Handler的Handler的handleMessage()方法中。
3.MessageQueue是存放Handler的sendMessage()发送过来的消息,这些消息存在一个队列中,等待被处理。每个线程中只有一个MessageQueue对象。
4.Loop:MessageQueue的管家,调用Loop的loop()方法后,就会无线循环。没发现MessageQueue中存在消息队列,就会从中取出,传递到Handler的handleMessage()中。每个线程中只有已给Loop对象
runOnUiThread()方法就是这个原理实现的
2.AsyncTask使用:
继承AsyncTask,实现一个抽象方法 doInBackground()
public class MyAsyncTask extends AsyncTask<String,Integer,Integer> {
@Override
protected Integer doInBackground(String... strings) {
return null;
}
}
Params:执行AsyncTask时需要传入的参数,可用于在后台任务
Progress:界面上显示当前进度,只用这里指定泛型作为进度单位
Result:当任务执行完,需要对结果进行返回,可以指定泛型作为返回值类型
一些常用方法:
onPreExecute()
这个会在执行后台任务之前被调用,常用语一些初始化操作
doInBackground(Params…)
这个方法的代码都会在子线程中执行,常用的耗时操作都在这里执行,可以通过reture返回执行结果。AsyncTask的第三个参数指定为Void,则可以不返回任务执行结果。如果需要操作UI,比如反馈当前任务进度。可以调用publishProgress(Progress…)
onProgressUpdata(Progress..)
调用 publishProgress()方法后,onProgressUpdata()方法会很快被调用,这个方法携带过来的参数就是在后台任务传递过来的,可以利用参数中的数值进行UI操作
onPostExecute(Result…)
后台执行完毕通过return返回结果时,就会调用此方法,doInBackground()返回的结果会作为参数传递到此方法中,比如提醒任务执行结果
例子:DownloadTask.java代码
public class DownloadTask extends AsyncTask<String, Integer, Integer> {
public static final int TYPE_SUCCESS = 0;
public static final int TYPE_FAILED = 1;
public static final int TYPE_PAUSED = 2;
public static final int TYPE_CANCELED = 3;
private DownloadListener listener;
private boolean isCanceled = false;
private boolean isPaused = false;
private int lastProgress;
public DownloadTask(DownloadListener listener) {
this.listener = listener;
}
@Override
protected Integer doInBackground(String... params) {
InputStream is = null;
RandomAccessFile savedFile = null;
File file = null;
try {
long downloadedLength = 0; // 记录已下载的文件长度
String downloadUrl = params[0];
String fileName = downloadUrl.substring(downloadUrl.lastIndexOf("/"));
String directory = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).getPath();
file = new File(directory + fileName);
if (file.exists()) {
downloadedLength = file.length();
}
long contentLength = getContentLength(downloadUrl);
Log.d("TAG", "contentLength: "+contentLength);
if (contentLength == 0) {
return TYPE_FAILED;
} else if (contentLength == downloadedLength) {
// 已下载字节和文件总字节相等,说明已经下载完成了
return TYPE_SUCCESS;
}
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
// 断点下载,指定从哪个字节开始下载
.addHeader("RANGE", "bytes=" + downloadedLength + "-")
.url(downloadUrl)
.build();
Response response = client.newCall(request).execute();
if (response != null) {
is = response.body().byteStream();
savedFile = new RandomAccessFile(file, "rw");
savedFile.seek(downloadedLength); // 跳过已下载的字节
byte[] b = new byte[1024];
int total = 0;
int len;
while ((len = is.read(b)) != -1) {
if (isCanceled) {
return TYPE_CANCELED;
} else if(isPaused) {
return TYPE_PAUSED;
} else {
total += len;
savedFile.write(b, 0, len);
// 计算已下载的百分比
int progress = (int) ((total + downloadedLength) * 100 / contentLength);
publishProgress(progress);
}
}
response.body().close();
return TYPE_SUCCESS;
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (is != null) {
is.close();
}
if (savedFile != null) {
savedFile.close();
}
if (isCanceled && file != null) {
file.delete();
}
} catch (Exception e) {
e.printStackTrace();
}
}
return TYPE_FAILED;
}
@Override
protected void onProgressUpdate(Integer... values) {
int progress = values[0];
//更新下载进度
if (progress > lastProgress) {
listener.onProgress(progress);
lastProgress = progress;
}
}
@Override
protected void onPostExecute(Integer status) {
switch (status) {
case TYPE_SUCCESS:
listener.onSuccess();
break;
case TYPE_FAILED:
listener.onFailed();
break;
case TYPE_PAUSED:
listener.onPaused();
break;
case TYPE_CANCELED:
listener.onCanceled();
default:
break;
}
}
public void pauseDownload() {
isPaused = true;
}
public void cancelDownload() {
isCanceled = true;
}
/**
* 获取文件总字节数
* @param downloadUrl
* @return contentLength
* @throws IOException
*/
private long getContentLength(String downloadUrl) throws IOException {
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
.url(downloadUrl)
.build();
Response response = client.newCall(request).execute();
if (response != null && response.isSuccessful()) {
long contentLength = response.body().contentLength();
response.close();
return contentLength;
}
return 0;
}
}
DownloadListener.java代码
public interface DownloadListener {
void onProgress(int progress);
void onPaused();
void onCanceled();
void onSuccess();
void onFailed();
}
DownloadService.java代码
public class DownloadService extends Service {
private String downloadUrl;
private DownloadTask downloadTask;
private DownloadListener downloadListener=new DownloadListener() {
@Override
public void onProgress(int progress) {
getNotificationManage().notify(1,getNotification("download...",progress));
}
@Override
public void onPaused() {
downloadTask=null;
Toast.makeText(DownloadService.this,"Paused Download",Toast.LENGTH_LONG).show();
}
@Override
public void onCanceled() {
downloadTask=null;
stopForeground(true);
Toast.makeText(DownloadService.this,"Canceled Download",Toast.LENGTH_LONG).show();
}
@Override
public void onSuccess() {
downloadTask=null;
//前台服务关闭,并创建一个下载成功通知
stopForeground(true);
getNotificationManage().notify(1,getNotification("下载完成",-1));
Toast.makeText(DownloadService.this,"Download success",Toast.LENGTH_LONG).show();
}
@Override
public void onFailed() {
downloadTask=null;
//下载失败关闭前台服务通知,并创建一个下载失败通知
stopForeground(true);
getNotificationManage().notify(1,getNotification("Download Failed",-1));
Toast.makeText(DownloadService.this,"Download failed",Toast.LENGTH_LONG).show();
}
};
private DownloadBind mBind=new DownloadBind();
class DownloadBind extends Binder{
public void startDownload(String url){
if (downloadTask==null){
DownloadTask downloadTask=new DownloadTask(downloadListener);
downloadUrl=url;
downloadTask.execute(downloadUrl);
startForeground(1,getNotification("download...",0));
Toast.makeText(DownloadService.this,"download...",Toast.LENGTH_LONG).show();
}
}
public void pauseDownload(){
if (downloadTask!=null){
downloadTask.pauseDownload();
}
}
public void cancelDownload(){
if (downloadTask!=null){
downloadTask.cancelDownload();
}else {
if (downloadUrl!=null){
//取消下载,删除文件,关闭通知
String fileName=downloadUrl.substring(downloadUrl.lastIndexOf("/"));
String fileDir= Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).getPath();
File file=new File(fileDir+fileName);
if (file.exists()){
file.delete();
}
getNotificationManage().cancel(1);
stopForeground(true);
Toast.makeText(DownloadService.this,"Cancel download",Toast.LENGTH_LONG).show();
}
}
}
}
@Override
public IBinder onBind(Intent intent) {
return mBind;
}
public NotificationManager getNotificationManage(){
return (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
}
private Notification getNotification(String title,int progress){
Intent intent=new Intent(this,MainActivity.class);
PendingIntent pi=PendingIntent.getActivity(this,0,intent,0);
NotificationCompat.Builder builder=new NotificationCompat.Builder(this);
builder.setContentTitle(title).setSmallIcon(R.mipmap.ic_launcher).setLargeIcon(BitmapFactory.decodeResource(getResources(),R.mipmap.ic_launcher));
builder.setContentIntent(pi);
if (progress>0){
builder.setContentTitle(progress+"%");
builder.setProgress(100,progress,false);
}
return builder.build();
}
}
DownloadService服务中实现DownloadListener中的5个方法,onProgress()方法中创建通知栏,在下拉状态栏中实时更新下载进度。onSuccess()方法中关闭前台任务,创建一个新通知告诉用户下载完成了。
DownloadBind中提供三个方法 分别分别用于启动,暂停,和取消下载任务。startDownload()方法中实例了DownloadTask,并调用了execute()方法开启下载。这个下载服务就作为已给前台服务运行了startForeground()方法在系统状态栏创建持续运行通知
MainActivity.java代码
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
private Button startBtn, pauseBtn, cancelBtn;
private DownloadService.DownloadBind downloadBind;
private ServiceConnection connection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
downloadBind = (DownloadService.DownloadBind) iBinder;
}
@Override
public void onServiceDisconnected(ComponentName componentName) {
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
startBtn = (Button) findViewById(R.id.start_download);
pauseBtn = (Button) findViewById(R.id.pause_download);
cancelBtn = (Button) findViewById(R.id.cancel_download);
startBtn.setOnClickListener(this);
pauseBtn.setOnClickListener(this);
cancelBtn.setOnClickListener(this);
Intent intent = new Intent(MainActivity.this, DownloadService.class);
startService(intent);//启动服务
bindService(intent, connection, BIND_AUTO_CREATE);//绑定bind
if (ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(MainActivity.this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, 1);
}
}
@Override
public void onClick(View view) {
if (downloadBind == null) {
return;
}
switch (view.getId()) {
case R.id.start_download:
String url = "https://raw.githubusercontent.com/guolindev/eclipse/master/eclipse-inst-win64.exe";
String url2="http://sw.bos.baidu.com/sw-search-sp/software/71bdb07e88935/Firefox-55.0.2.6435-setup.exe";
downloadBind.startDownload(url2);
break;
case R.id.pause_download:
downloadBind.pauseDownload();
break;
case R.id.cancel_download:
downloadBind.cancelDownload();
break;
default:
break;
}
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
switch (requestCode) {
case 1:
if (grantResults.length > 0 && grantResults[0] != PackageManager.PERMISSION_GRANTED) {
Toast.makeText(this, "拒绝权限将无法使用程序", Toast.LENGTH_SHORT).show();
finish();
}
break;
default:
}
}
@Override
protected void onDestroy() {
super.onDestroy();
unbindService(connection);
}
}
layout布局文件就不贴出来了,感觉已经写了很长了。
在onCreatView()中启动服务,和bindService()绑定service.
ServiceConnection 中实例了DownloadBind。有了DownloadBind的实例就可以调用其中的StartDownload(),pauseDownload(),cancelDownload()三个方法了。分别对应到不同按钮的点击事件中。
另外则是一个权限的动态申请了。