1.服务(Service)
实现程序后台运行的解决方案,适合执行不需要用户交互但需要长期运行的任务。运行在主线程中,所以执行耗时任务的时候,需要在服务内部手动创建子线程。
2.android多线程编程
只能在主线程中更新UI,但有时候必须在子线程执行一些耗时任务,然后根据任务结果来更新相应的UI控件。这种情况下,就需要android提供的异步消息处理机制了。
//点击按钮,子线程中处理耗时任务,完成后发送消息; 主线程接收消息,更新UI
private Button login_btn;
private static final int UPDATE_TXT=1;
private Handler handler=new Handler(){
@Override
public void handleMessage(Message msg) {
switch (msg.what){
case UPDATE_TXT:
Bundle bundle=msg.getData();
login_btn.setText(bundle.get("data").toString());
break;
}
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
login_btn= (Button) findViewById(R.id.login_btn);
findViewById(R.id.changetxt_btn).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
new Thread(new Runnable() {
@Override
public void run() {
Message message=new Message();
message.what=UPDATE_TXT;
Bundle bundle=new Bundle();
bundle.putString("data","登陆啦啦");
message.setData(bundle);
handler.sendMessage(message);
}
}).start();
}
});
}
}
3.解析异步消息处理机制
- Message:线程之间传递消息,可携带少量信息。
- Handler:用于发送和处理消息。 主线程中创建一个Handler对象。
- MessageQueue:消息队列,存放搜游通过handler发送的消息,等待被处理。每个线程中只有一个MessageQueue一个对象。
- Looper:调用Looper的loop()方法后,进入无限循环,当发现MessageQueue中存在一条消息,就去除并传递到handler中的handleMessage()方法中。每个线程中只有一个Looper对象。
4.使用AsyncTask
借助AsyncTask,可从子线程切换到主线程。AsyncTask是一个抽象类,使用时创建子类继承。可为AsyncTask指定3个泛型参数,用途如下:
- params:在执行 AsyncTask是需要传入的参数,可用于后台任务中使用;
- progress: 后台执行任务,若果需要在界面上显示当前的进度,则使用这里指定的泛型作为进度单位;
- result: 任务执行完成后,如果需要对结果进行返回,则使用这了指定的泛型作为返回值。
public class DownloadTask extends AsyncTask {
@Override
protected void onPreExecute() {
//show progressDialog
}
@Override
protected Boolean doInBackground(Void... voids) { //执行耗时任务
return null;
}
@Override
protected void onProgressUpdate(Integer... values) {
// progressDialog.setMessage(values[0]+"%"); //进行UI操作
}
@Override
protected void onPostExecute(Boolean aBoolean) { //任务的收尾
super.onPostExecute(aBoolean); //close progressDialog
if (aBoolean){
//success
}else {
//failure
}
}
}
5. 服务启动和停止 绑定和解绑
参考此篇文章总结
6.服务的最佳实践--完整版的下载示例
private DownloadService.DownloadBinder downloadBinder;
private ServiceConnection connection=new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
downloadBinder= (DownloadService.DownloadBinder) iBinder;
}
@Override
public void onServiceDisconnected(ComponentName componentName) {
Log.i("failure",componentName.toString()+"//");
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.activity_login);
Intent intent=new Intent(this,DownloadService.class);
startService(intent);
bindService(intent,connection,BIND_AUTO_CREATE);
Log.i("failure",bindService(intent,connection,BIND_AUTO_CREATE)+"");
if (ContextCompat.checkSelfPermission(LoginActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE)!= PackageManager.PERMISSION_GRANTED){
ActivityCompat.requestPermissions(LoginActivity.this,new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},1);
}
findViewById(R.id.start_btn).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
if (downloadBinder==null){
return;
}
String url="http://192.168.1.101:8080/myvoice/lalala.mp3";
downloadBinder.startDownload(url);
}
});
findViewById(R.id.pause_btn).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
downloadBinder.pauseDownload();
}
});
findViewById(R.id.cancel_btn).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
downloadBinder.cancelDownload();
}
});
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
switch (requestCode){
case 1:
Toast.makeText(LoginActivity.this,"premission failure",Toast.LENGTH_SHORT).show();
break;
}
}
@Override
protected void onDestroy() {
super.onDestroy();
unbindService(connection);
}
public class DownloadService extends Service {
private DownloadTask downloadTask;
private String downloadUrl;
private DonwloadListener donwloadListener=new DonwloadListener() {
@Override
public void onProgress(int progress) {
getNotificationManager().notify(1,getNotification("下载中……",progress));
}
@Override
public void onSuccess() {
//下载成功,前台服务关闭,创建一个下载成功的通知
downloadTask=null;
stopForeground(true);
getNotificationManager().notify(1,getNotification("下载成功",-1));
Toast.makeText(DownloadService.this,"download success",Toast.LENGTH_SHORT).show();
}
@Override
public void onFailure() {
downloadTask=null;
stopForeground(true);
getNotificationManager().notify(1,getNotification("下载失败",-1));
Toast.makeText(DownloadService.this,"download failure",Toast.LENGTH_SHORT).show();
}
@Override
public void onPaused() {
downloadTask=null;
Toast.makeText(DownloadService.this,"download 暂停",Toast.LENGTH_SHORT).show();
}
@Override
public void onCanceled() {
downloadTask=null;
stopForeground(true);
Toast.makeText(DownloadService.this,"download failure",Toast.LENGTH_SHORT).show();
}
};
private DownloadBinder downloadBinder=new DownloadBinder();
public class DownloadBinder extends Binder{
public void startDownload(String url){
if (downloadTask==null){
downloadUrl=url;
downloadTask=new DownloadTask(donwloadListener);
downloadTask.execute(downloadUrl);
startForeground(1,getNotification("开始下载……",0));
Toast.makeText(DownloadService.this,"download start……",Toast.LENGTH_SHORT).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 directory= Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).getPath();
File file=new File(directory+fileName);
if (file.exists()){
file.delete();
}
getNotificationManager().cancel(1);
stopForeground(true);
Toast.makeText(DownloadService.this,"download cancel",Toast.LENGTH_SHORT).show();
}
}
}
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
return downloadBinder;
}
private NotificationManager getNotificationManager(){
return (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
}
private Notification getNotification(String title,int progress){
Intent intent=new Intent(this, LoginActivity.class);
PendingIntent pi=PendingIntent.getActivity(this,0,intent,0);
NotificationCompat.Builder builder=new NotificationCompat.Builder(this);
builder.setSmallIcon(R.mipmap.ic_launcher);
builder.setLargeIcon(BitmapFactory.decodeResource(getResources(),R.mipmap.ic_launcher));
builder.setContentIntent(pi);
builder.setContentTitle(title);
if (progress>0){
builder.setContentText(progress+"%");
builder.setProgress(100,progress,false);
}
return builder.build();
}
}
public class DownloadTask extends AsyncTask {
private static final int TYPE_SUCCESS=0;
private static final int TYPE_FAILURE=1;
private static final int TYPE_PAUSE=2;
private static final int TYPE_CANCEL=3;
private DonwloadListener donwloadListener;
private boolean isCancled=false;
private boolean isPaused=false;
private int lastProgress;
public DownloadTask(DonwloadListener donwloadListener) {
this.donwloadListener = donwloadListener;
}
@Override
protected Integer doInBackground(String... strings) {
InputStream is=null;
RandomAccessFile savedFile=null;
File file=null;
try {
long downloadLength = 0;
String downloadUrl = strings[0];
String fileName = downloadUrl.substring(downloadUrl.lastIndexOf("/"));
String directory = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).getPath();
file = new File(directory + fileName);
if (file.exists()) {
downloadLength = file.length();
}
long contentLength = getContentLength(downloadUrl);
if (contentLength == 0) {
return TYPE_FAILURE;
} else if (contentLength == downloadLength) {
return TYPE_SUCCESS;
}
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
.addHeader("RANG", "bytes=" + downloadLength + "-")
.url(downloadUrl)
.build();
Response response = client.newCall(request).execute();
if (request != null) {
is = response.body().byteStream();
savedFile = new RandomAccessFile(file, "rw");
savedFile.seek(downloadLength);
byte[] b=new byte[1024];
int total=0;
int len;
while ((len=is.read())!=-1){
if (isCancled){
return TYPE_CANCEL;
}else if (isPaused){
return TYPE_PAUSE;
}else {
total+=len;
savedFile.write(b,0,len);
//计算已下载的百分比
int progress= (int) ((total+downloadLength)*100/contentLength);
publishProgress(progress);
}
}
response.body().close();
return TYPE_SUCCESS;
}
}catch (Exception o){
o.printStackTrace();
}finally {
try {
if (is!=null){
is.close();
}
if (savedFile!=null){
savedFile.close();
}
if (isCancled&&file!=null){
file.delete();
}
}catch (Exception e){
e.printStackTrace();
}
}
return TYPE_FAILURE;
}
private long getContentLength(String downloadUrl) throws IOException {
OkHttpClient okHttpClient=new OkHttpClient();
Request request=new Request.Builder()
.url(downloadUrl)
.build();
Response response=okHttpClient.newCall(request).execute();
if (response!=null&&response.isSuccessful()){
long contentLength=response.body().contentLength();
response.body().close();
return contentLength;
}
return 0;
}
@Override
protected void onPostExecute(Integer integer) {
switch (integer){
case TYPE_SUCCESS:
donwloadListener.onSuccess();
break;
case TYPE_FAILURE:
donwloadListener.onFailure();
break;
case TYPE_PAUSE:
donwloadListener.onPaused();
break;
case TYPE_CANCEL:
donwloadListener.onCanceled();
break;
}
}
@Override
protected void onProgressUpdate(Integer... values) {
//进行UI操作
int progress=values[0];
if (progress>lastProgress){
donwloadListener.onProgress(progress);
lastProgress=progress;
}
}
public void pauseDownload(){
isPaused=true;
}
public void cancelDownload(){
isCancled=true;
}
}