参考第一行代码,代码: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);
            }
        }

    }
}
Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐