参考第一行代码,代码:github
写的很乱,下载完毕后的通知也没有实现。由于没有写回调处理downloadWithResult的返回值,所以返回值用不上。
之前用asyncTask也写了一份,碰到notify通知频繁导致app很卡的问题,试着用handler来解决。
最后发现是数值设置有误,导致notification每秒刷新上百次(理论上)。
通过百度学习了以下东西:
- activity和service通过Messenger互相获取对方的handler,并发送消息。参考利用Handler实现Activity和Service之间通信
- callable可以获取返回值。但本例中如果在主线程等待返回值会阻塞主线程,应该和asyncTask的代码一样通过回调来解决
- 线程被interupt不一定马上停止,即使停止状态也不是isinterupted,应该使用isalive查看线程是否运行
package com.slq.r1.activity;
import android.Manifest;
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.Messenger;
import android.os.RemoteException;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.NotificationCompat;
import com.slq.r1.R;
import com.slq.r1.service.DownloaderService2;
public class DownloaderActivityWithHandler extends AppCompatActivity implements View.OnClickListener {
final int DownloaderServiceNotificationId = 1;
private final String TAG = "DownloaderActivityWithH";
public NotificationManager manager;
NotificationCompat.Builder mBuilder;
String channelId = "id1";
String DOWNLOADFILEURL = "https://dl.google.com/android/studio/plugins/android-gradle/preview/offline-android-gradle-plugin-preview.zip";
Button binddownloadservice, unbinddownloadservice, startdownload, stopdownload, pausedownload;
TextView downloadprogresstext, downloadurltext;
String[] rights = new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE};
int RIGHTREWUESTCODE = 1;
Messenger serviceMessenger;
Bundle bundle;
private ServiceConnection connection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
serviceMessenger = new Messenger(service);
Messenger messenger = new Messenger(handler);
// 创建消息
Message msg = new Message();
msg.what = -1;
msg.replyTo = messenger;
// 使用service中的messenger发送Activity中的messenger
try {
serviceMessenger.send(msg);
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_downloader_with_handler);
init();
}
@Override
protected void onDestroy() {
super.onDestroy();
Intent it = new Intent(DownloaderActivityWithHandler.this, DownloaderService2.class);
stopService(it);
try {
unbindService(connection);
} catch (IllegalArgumentException e) {
e.printStackTrace();
}
}
private void init() {
binddownloadservice = findViewById(R.id.binddownloadservice2);
binddownloadservice.setOnClickListener(this);
unbinddownloadservice = findViewById(R.id.unbinddownloadservice2);
unbinddownloadservice.setOnClickListener(this);
startdownload = findViewById(R.id.startdownload2);
startdownload.setOnClickListener(this);
stopdownload = findViewById(R.id.stopdownload2);
stopdownload.setOnClickListener(this);
pausedownload = findViewById(R.id.pausedownload2);
pausedownload.setOnClickListener(this);
downloadprogresstext = findViewById(R.id.downloadprogresstext2);
downloadurltext = findViewById(R.id.downloadurltext2);
downloadurltext.setText(DOWNLOADFILEURL);
manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
getNotificationPart1();
bundle = new Bundle();
bundle.putString("url", downloadurltext.getText().toString());
}
private void getNotificationPart1() {
Intent it = new Intent(this, DownloaderActivityWithHandler.class);
PendingIntent pi = PendingIntent.getActivity(this, 0, it, 0);
NotificationChannel chan1 = new NotificationChannel(channelId, "name1", NotificationManager.IMPORTANCE_MAX);
manager.createNotificationChannel(chan1);
mBuilder = new NotificationCompat.Builder(getApplicationContext(), channelId);
mBuilder.setSmallIcon(R.mipmap.ic_launcher)
.setContentIntent(pi)
.setNotificationSilent();
}
private Notification getNotificationPart2(String title, int... params) {
mBuilder.setContentTitle(title) //设置标题
.setContentText("已下载" + params[0] + "/" + params[1])
.setProgress(params[1], params[0], false);
return mBuilder.build();
}
public Handler handler = new Handler() {
@Override
public void handleMessage(@NonNull Message msg) {
switch (msg.what) {
case 1:
Bundle data = msg.getData();
int[] progresses = data.getIntArray("progress");
manager.notify(DownloaderServiceNotificationId, getNotificationPart2("下载进度", progresses[0], progresses[1]));
break;
case 2: //stop
manager.cancel(DownloaderServiceNotificationId);
break;
case 3:
manager.cancel(DownloaderServiceNotificationId);
break;
default:
break;
}
}
};
@Override
public void onClick(View v) {
Message msg = new Message();
switch (v.getId()) {
case R.id.startdownload2:
msg.what = 0;
msg.setData(bundle);
sendMsg(msg);
break;
case R.id.stopdownload2:
msg.what = 1;
sendMsg(msg);
break;
case R.id.pausedownload2:
msg.what = 2;
sendMsg(msg);
break;
case R.id.binddownloadservice2:
Intent it = new Intent(DownloaderActivityWithHandler.this, DownloaderService2.class);
startService(it);
bindService(it, connection, BIND_AUTO_CREATE);
break;
case R.id.unbinddownloadservice2:
unbindService(connection);
break;
default:
break;
}
}
private void sendMsg(Message msg) {
try {
serviceMessenger.send(msg);
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (requestCode == RIGHTREWUESTCODE) {
if (grantResults.length > 1 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
Message msg = new Message();
msg.what = 0;
msg.setData(bundle);
sendMsg(msg);
}
}
}
}
package com.slq.r1.service;
import android.app.Service;
import android.content.Intent;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.Messenger;
import android.os.RemoteException;
import android.util.Log;
import android.widget.Toast;
import androidx.annotation.NonNull;
import com.slq.r1.utils.DownloaderTask;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.RandomAccessFile;
import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
public class DownloaderService2 extends Service {
private static final String TAG = "DownloaderService2";
DownloaderTask downloaderTask;
Messenger activityMessenger;
Thread downloadThread;
String url;
final public static int SUCCESS = 1, PAUSE = 2, DELETE = 3, FAIL = 0, DOING = 5;
int state;
String directory = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).getPath();
File file;
@Override
public void onCreate() {
super.onCreate();
}
public Handler handler = new Handler() {
@Override
public void handleMessage(@NonNull Message msg) {
Message msg1 = new Message();
switch (msg.what) {
case -1:
activityMessenger = msg.replyTo;
break;
case 10: //使用Runnable
url = msg.getData().getString("url");
if (downloadThread == null || !downloadThread.isAlive()) {
downloadThread = new Thread(new Runnable() {
@Override
public void run() {
downloadWithResult(url);
}
});
downloadThread.start();
}
break;
case 0: //使用Callable
url = msg.getData().getString("url");
if (downloadThread == null || !downloadThread.isAlive()) {
Callable caller = new Callable<Integer>() {
@Override
public Integer call() throws Exception {
return downloadWithResult(url);
}
};
FutureTask f = new FutureTask(caller);
downloadThread = new Thread(f);
try {
downloadThread.start();
//Log.e(TAG, "f.get(): "+f.get() );
//放开注释f.get()会阻塞主线程,应该用回调来解决
} catch (Exception e) {
e.printStackTrace();
}
}
break;
case 1: //stop
if (downloadThread != null) {
downloadThread.interrupt();
file.delete();
Toast.makeText(getApplicationContext(), "你已经stop下载", Toast.LENGTH_SHORT).show();
msg1.what = 2;
sendMsg(msg1);
}
break;
case 2: //pause
if (downloadThread != null) {
downloadThread.interrupt();
Toast.makeText(getApplicationContext(), "你已经暂停下载", Toast.LENGTH_SHORT).show();
}
break;
default:
break;
}
}
};
@Override
public IBinder onBind(Intent intent) {
// 创建Messenger对象包含handler引用
Messenger messenger = new Messenger(handler);
// 返回Messenger的binder
return messenger.getBinder();
}
protected Integer downloadWithResult(String... strings) {
if (strings[0] != "") url = strings[0];
state = DOING;
String filename = url.substring(url.lastIndexOf("/") + 1);
InputStream is = null;
RandomAccessFile rw = null;
Response response = null;
try {
file = new File(directory, filename);
Log.e(TAG, "download path:" + directory + "/" + filename);
if (!file.exists()) file.createNewFile();
long current = file.length();
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
.addHeader("range", "bytes=" + current + "-")
.url(url).build();
response = client.newCall(request).execute();
if (response != null) {
long fileSize = response.body().contentLength() + current;
if (current == fileSize) {
return SUCCESS;
}
is = response.body().byteStream();
rw = new RandomAccessFile(file, "rw");
rw.seek(current);
byte[] b = new byte[1024];
int len, co = 0;
while ((len = is.read(b)) != -1) {
if (state != DOING) {
return state;
}
rw.write(b, 0, len);
current += len;
if (++co % 100 == 0) {
publishProgress(current, fileSize);
}
}
return SUCCESS;
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (is != null) is.close();
if (rw != null) rw.close();
if (response != null) response.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return FAIL;
}
void publishProgress(long... values) {
try {
Message msg1 = new Message();
msg1.what = 1;
Bundle bundle = new Bundle();
bundle.putIntArray("progress", new int[]{(int) values[0], (int) values[1]});
msg1.setData(bundle);
activityMessenger.send(msg1);
} catch (RemoteException e) {
e.printStackTrace();
}
}
private void sendMsg(Message msg) {
try {
activityMessenger.send(msg);
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
回调参考DownloaderTask部分onPostExecute里downloaderListener的相关操作,downloaderListener是自己定义的接口。代码如下:
public class DownloaderTask extends AsyncTask<String, Long, Integer> {
private static final String TAG = "DownloaderTask";
private String url;
String directory = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).getPath();
File file;
final public static int SUCCESS = 1, PAUSE = 2, DELETE = 3, FAIL = 0, DOING = 5;
int state;
DownloaderListener downloaderListener;
public void setState(int state) {
this.state = state;
}
public void setDownloaderListener(DownloaderListener downloaderListener) {
this.downloaderListener = downloaderListener;
}
@Override
protected void onPreExecute() {
super.onPreExecute();
}
@Override
protected void onPostExecute(Integer integer) {
super.onPostExecute(integer);
Log.e(TAG, "下载结果:" + integer);
switch (integer) {
case SUCCESS:
downloaderListener.onSucess();
break;
case FAIL:
downloaderListener.onFail();
break;
case PAUSE:
downloaderListener.onPause();
break;
case DELETE:
file.delete();
downloaderListener.onStop();
break;
default:
break;
}
}
@Override
protected void onProgressUpdate(Long... values) {
super.onProgressUpdate(values);
downloaderListener.onProgress(values[0], values[1]);
}
@Override
protected void onCancelled() {
super.onCancelled();
}
@Override
protected Integer doInBackground(String... strings) {
if(strings[0]!="")url = strings[0];
// if (state == DOING) {
// return null;
// } else {
// state = DOING;
// }
state = DOING;
String filename = url.substring(url.lastIndexOf("/") + 1);
InputStream is = null;
RandomAccessFile rw = null;
Response response = null;
try {
file = new File(directory, filename);
Log.e(TAG, "download path:" + directory + "/" + filename);
if (!file.exists()) file.createNewFile();
long current = file.length();
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
.addHeader("range", "bytes=" + current + "-")
.url(url).build();
response = client.newCall(request).execute();
if (response != null) {
long fileSize = response.body().contentLength() + current;
if (current == fileSize) {
return SUCCESS;
}
is = response.body().byteStream();
rw = new RandomAccessFile(file, "rw");
rw.seek(current);
byte[] b = new byte[1024];
int len, co = 0;
while ((len = is.read(b)) != -1) {
if (state != DOING) {
return state;
}
rw.write(b, 0, len);
current += len;
if (++co % 100 == 0) publishProgress(current, fileSize);
}
return SUCCESS;
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (is != null) is.close();
if (rw != null) rw.close();
if (response != null) response.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return FAIL;
}
}
public class DownloaderService extends Service {
private static final String TAG = "DownloaderService";
private DownloaderBinder binder = new DownloaderBinder();
DownloaderTask downloaderTask;
DownloaderListener downloaderListener = new MyDownloadListener();
final int DownloaderServiceNotificationId = 1;
NotificationManager manager;
NotificationCompat.Builder mBuilder;
String channelId = "id1";
public class MyDownloadListener implements DownloaderListener {
@Override
public void onSucess() {
downloaderTask = null;
//stopForeground(true); //todo
Notification notification = getNotificationPart2("下载完毕", 1, 1);
//int importance = manager.getNotificationChannel(channelId).getImportance();
//Log.e(TAG, "importance: " + importance);
manager.notify(DownloaderServiceNotificationId, notification);
Toast.makeText(getApplicationContext(), "下载完毕", Toast.LENGTH_SHORT).show();
}
@Override
public void onPause() {
downloaderTask = null;
Toast.makeText(getApplicationContext(), "你已经暂停下载", Toast.LENGTH_SHORT).show();
}
@Override
public void onStop() {
downloaderTask = null;
manager.cancel(DownloaderServiceNotificationId);
//stopForeground(true); //todo
Toast.makeText(getApplicationContext(), "你停止了下载", Toast.LENGTH_SHORT).show();
}
@Override
public void onFail() {
downloaderTask = null;
Notification notification = getNotificationPart2("下载失败", 0, 0);
//int importance = manager.getNotificationChannel(channelId).getImportance();
manager.notify(DownloaderServiceNotificationId, notification);
//Toast.makeText(getApplicationContext(), "下载失败!!!", Toast.LENGTH_SHORT).show();
}
@Override
public void onProgress(long... values) {
Notification notification = getNotificationPart2("下载进度", (int) values[0], (int) values[1]);
manager.notify(DownloaderServiceNotificationId, notification);
//Log.e(TAG, "当前下载进度: " + values[0] + "/" + values[1]);
}
}
private void getNotificationPart1() {
Intent it = new Intent(this, DownloaderActivity.class);
PendingIntent pi = PendingIntent.getActivity(this, 0, it, 0);
NotificationChannel chan1 = new NotificationChannel(channelId, "name1", NotificationManager.IMPORTANCE_MAX);
manager.createNotificationChannel(chan1);
mBuilder = new NotificationCompat.Builder(getApplicationContext(), channelId);
mBuilder.setSmallIcon(R.mipmap.ic_launcher) //设置小图标
.setContentIntent(pi)
//.setSound(null)
//.setDefaults(NotificationCompat.FLAG_ONLY_ALERT_ONCE)
.setNotificationSilent();
}
private Notification getNotificationPart2(String title, int... params) {
mBuilder.setContentTitle(title) //设置标题
.setContentText("已下载" + params[0] + "/" + params[1])
.setProgress(params[1], params[0], false);
return mBuilder.build();
}
@Override
public void onCreate() {
super.onCreate();
manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
getNotificationPart1();
}
public DownloaderService() {
}
@Override
public IBinder onBind(Intent intent) {
return binder;
}
public class DownloaderBinder extends Binder {
//如果去掉if,连续点两次会开启两个task
public void startDownload(String url) {
//如果没有task,或者task已经被暂停或停止,重新new task
if (downloaderTask == null) {
downloaderTask = new DownloaderTask();
downloaderTask.setDownloaderListener(downloaderListener);
downloaderTask.execute(url);
//startForeground(DownloaderServiceNotificationId,getNotificationPart2("starting",1,1)); //todo
}
}
public void pauseDownload() {
if (downloaderTask != null) {
downloaderTask.setState(DownloaderTask.PAUSE);
}
}
//如果先暂停再停止会无效,需要对downloaderTask == null的情况进行处理
public void stopDownload() {
if (downloaderTask != null) {
downloaderTask.setState(DownloaderTask.DELETE);
}
}
}
}