文档章节

Android 实现简易下载管理器 (暂停、断点续传、多线程下载)

shzwork
 shzwork
发布于 07/14 16:35
字数 3662
阅读 12
收藏 0

什么都先别说,先看预览图!

预览图中是限制了同时最大下载数为 2 的.

其实下载管理器的实现是挺简单的,我们需要弄清楚几点就行了

1.所有任务的Bean应该存在哪里,用什么存? 
2.如何判断任务是否已存在? 
3.如何判断任务是新的任务或是从等待中恢复的任务? 
4.应该如何把下载列表传递给Adapter? 
5.如何将下载的进度传递出去? 
6.如何有效率地刷新显示的列表? (ListView 或 RecycleView)

服务基础
首先我们需要明确一点,下载我们应该使用服务来进行,这样我们才能进行后台下载。 
所以我们就开始创建我们的Service:

public class OCDownloadService extends Service{

    ... ...

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        //当服务被Bind的时候我们就返回一个带有服务对象的类给Bind服务的Activity
        return new GetServiceClass();
    }

    /**
     * 传递服务对象的类
     */
    public class GetServiceClass extends Binder{

        public OCDownloadService getService(){
            return OCDownloadService.this;
        }

    }
    ... ...
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
然后我们在AndroidManifest.xml里面注册一下:

<service android:name=".OCDownloader.OCDownloadService"/>
1
下载请求的检查与处理
然后我们就开始进入正题 ! 
首先第一点,我们使用HashMap来当作储存下载任务信息的总表,这样的好处是我们可以在查找任务的时候通过 Key 来查询,而不需要通过遍历 List 的方法来获取任务信息。而且我们传递的时候可以直接使用它的一份Copy就行了,不需要把自己传出去。

下面我们来看代码:

(关于Service的生命周期啥的我就不再重复说了。我这里使用的是本地广播来传输下载信息的更新。剩下的在代码注释中有详细的解释)

public class OCDownloadService extends Service{

    static final int MAX_DOWNLOADING_TASK = 2; //最大同时下载数
    private LocalBroadcastManager broadcastManager;
    private HashMap<String,DLBean> allTaskList;
    private OCThreadExecutor threadExecutor;

    private boolean keepAlive = false;
    private int runningThread = 0;

    @Override
    public void onCreate() {
        super.onCreate();

        //创建任务线程池
        if (threadExecutor == null){
            threadExecutor = new OCThreadExecutor(MAX_DOWNLOADING_TASK,"downloading");
        }

        //创建总表对象
        if (allTaskList == null){
            allTaskList = new HashMap<>();
        }

        //创建本地广播器
        if (broadcastManager == null){
            broadcastManager = LocalBroadcastManager.getInstance(this);
        }
    }

    /**
     * 下载的请求就是从这里传进来的,我们在这里进行下载任务的前期处理
     */
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {

        //检测传过来的请求是否完整。我们只需要 下载网址、文件名、下载路径 即可。
        if (intent != null && intent.getAction() != null && intent.getAction().equals("NewTask")){
            String url = intent.getExtras().getString("url");
            String title = intent.getExtras().getString("title");
            String path = intent.getExtras().getString("path");

            //检测得到的数据是否有效
            if (TextUtils.isEmpty(url) || TextUtils.isEmpty(title) || TextUtils.isEmpty(path)){
                Toast.makeText(OCDownloadService.this,"Invail data",Toast.LENGTH_SHORT).show();
                return super.onStartCommand(intent, flags, startId);
            }else {

                //如果有效则执行检查步骤
                checkTask(new DLBean(title,url,path));
            }
        }
        return super.onStartCommand(intent, flags, startId);
    }

    /**
     * 检查新的下载任务
     * @param requestBean   下载对象的信息Bean
     */
    private synchronized void checkTask(@Nullable DLBean requestBean){
        if (requestBean != null){

            //先检查是否存在同名的文件
            if (new File(requestBean.getPath()+"/"+requestBean.getTitle()).exists()){
                Toast.makeText(OCDownloadService.this,"File is already downloaded",Toast.LENGTH_SHORT).show();
            }else {

                //再检查是否在总表中
                if (allTaskList.containsKey(requestBean.getUrl())){
                    DLBean bean = allTaskList.get(requestBean.getUrl());
                    //检测当前的状态
                    //如果是 暂停 或 失败 状态的则当作新任务开始下载
                    switch (bean.getStatus()){
                        case DOWNLOADING:
                            Toast.makeText(OCDownloadService.this,"Task is downloading",Toast.LENGTH_SHORT).show();
                            return;
                        case WAITTING:
                            Toast.makeText(OCDownloadService.this,"Task is in the queue",Toast.LENGTH_SHORT).show();
                            return;
                        case PAUSED:
                        case FAILED:
                            requestBean.setStatus(OCDownloadStatus.WAITTING);
                            startTask(requestBean);
                            break;
                    }
                }else {
                    //如果不存在,则添加到总表
                    requestBean.setStatus(OCDownloadStatus.WAITTING);
                    allTaskList.put(requestBean.getUrl(),requestBean);
                    startTask(requestBean);
                }

            }

        }

    }

    /**
     * 将任务添加到下载队列中
     * @param requestBean   下载对象的信息Bean
     */
    private void startTask(DLBean requestBean){
        if (runningThread < MAX_DOWNLOADING_TASK){
            //如果当前还有空闲的位置则直接下载 , 否则就是在等待中
            requestBean.setStatus(OCDownloadStatus.DOWNLOADING);
            runningThread += 1;
            threadExecutor.submit(new FutureTask<>(new DownloadThread(requestBean)),requestBean.getUrl());
        }
        updateList();
    }

    /**
     * 得到一份总表的 ArrayList 的拷贝
     * @return  总表的拷贝
     */
    public ArrayList<DLBean> getTaskList(){
        return new ArrayList<>(allTaskList.values());
    }

    /**
     * 更新整个下载列表
     */
    private void updateList(){
        //我们等下再说这里
        ... ...
    }

    /**
     * 更新当前项目的进度
     * @param totalSize 下载文件的总大小
     * @param downloadedSize    当前下载的进度
     */
    private void updateItem(DLBean bean , long totalSize, long downloadedSize){
        //我们等下再说这里
        ... ...
    }

    /**
     * 执行的下载任务的Task
     */
    private class DownloadThread implements Callable<String>{
        //我们等下再说这里
        ... ...
    }

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
在大家看了一遍之后我再解释一遍流程:

1.收到新的任务请求 
2.判断任务的信息是否完整 
3.检查任务是否存在于总表,并检查状态 
4.如果任务不存在总表中 或 任务之前是暂停、失败状态则当作新任务,否则提示任务已存在 
5.如果当前已经是最大下载数,则任务标记为等待,不执行;否则开始下载

下载线程的实现
下面我们来看是如何下载的,这就会讲到断点续传的问题了,首先这个断点续传的功能得服务器支持才可以。然后我们在下载的时候生成一个临时文件,在下载完成之前我们将这个任务的所有数据存入这个文件中,直到下载完成,我们才将名字更改回正式的。网上有人将数据存入数据库中,我觉得这种方式虽然避免了临时文件的产生,但是这效率就…………

    /**
     * 执行的下载任务方法
     */
    private class DownloadThread implements Callable<String>{

        private DLBean bean;
        private File downloadFile;
        private String fileSize = null;

        public DownloadThread(DLBean bean) {
            this.bean = bean;
        }

        @Override
        public String call() throws Exception {

            //先检查是否有之前的临时文件
            downloadFile = new File(bean.getPath()+"/"+bean.getTitle()+".octmp");
            if (downloadFile.exists()){
                fileSize = "bytes=" + downloadFile.length() + "-";
            }

            //创建 OkHttp 对象相关
            OkHttpClient client = new OkHttpClient();

            //如果有临时文件,则在下载的头中添加下载区域
            Request request;
            if ( !TextUtils.isEmpty(fileSize) ){
                request = new Request.Builder().url(bean.getUrl()).header("Range",fileSize).build();
            }else {
                request = new Request.Builder().url(bean.getUrl()).build();
            }
            Call call = client.newCall(request);
            try {
                bytes2File(call);
            } catch (IOException e) {
                Log.e("OCException",""+e);
                if (e.getMessage().contains("interrupted")){
                    Log.e("OCException","Download task: "+bean.getUrl()+" Canceled");
                    downloadPaused();
                }else {
                    downloadFailed();
                }
                return null;
            }
            downloadCompleted();
            return null;
        }

        /**
         * 当产生下载进度时
         * @param downloadedSize    当前下载的数据大小
         */
        public void onDownload(long downloadedSize) {
            bean.setDownloadedSize(downloadedSize);
            Log.d("下载进度", "名字:"+bean.getTitle()+"  总长:"+bean.getTotalSize()+"  已下载:"+bean.getDownloadedSize() );
            updateItem(bean, bean.getTotalSize(), downloadedSize);
        }

        /**
         * 下载完成后的操作
         */
        private void downloadCompleted(){
            //当前下载数减一
            runningThread -= 1;
            //将临时文件名更改回正式文件名
            downloadFile.renameTo(new File(bean.getPath()+"/"+bean.getTitle()));
            //从总表中移除这项下载信息
            allTaskList.remove(bean.getUrl());
            //更新列表
            updateList();
            if (allTaskList.size() > 0){
                //执行剩余的等待任务
                checkTask(startNextTask());
            }
            threadExecutor.removeTag(bean.getUrl());
        }

        /**
         * 下载失败后的操作
         */
        private void downloadFailed(){
            runningThread -= 1;
            bean.setStatus(OCDownloadStatus.FAILED);
            if (allTaskList.size() > 0){
                //执行剩余的等待任务
                checkTask(startNextTask());
            }
            updateList();
            threadExecutor.removeTag(bean.getUrl());
        }

        /**
         * 下载暂停后的操作
         */
        private void downloadPaused(){
            runningThread -= 1;
            bean.setStatus(OCDownloadStatus.PAUSED);
            if (allTaskList.size() > 0){
                //执行剩余的等待任务
                checkTask(startNextTask());
            }
            updateList();
            threadExecutor.removeTag(bean.getUrl());
        }

        /**
         * 查找一个等待中的任务
         * @return  查找到的任务信息Bean , 没有则返回 Null
         */
        private DLBean startNextTask(){
            for (DLBean dlBean : allTaskList.values()) {
                if (dlBean.getStatus() == OCDownloadStatus.WAITTING) {
                    //在找到等待中的任务之后,我们先把它的状态设置成 暂停 ,再进行创建
                    dlBean.setStatus(OCDownloadStatus.PAUSED);
                    return dlBean;
                }
            }
            return null;
        }

        /**
         * 将下载的数据存到本地文件
         * @param call  OkHttp的Call对象
         * @throws IOException  下载的异常
         */
        private void bytes2File(Call call) throws IOException{

            //设置输出流. 
            OutputStream outPutStream;

            //检测是否支持断点续传
            Response response = call.execute();
            ResponseBody responseBody = response.body();
            String responeRange = response.headers().get("Content-Range");
            if (responeRange == null || !responeRange.contains(Long.toString(downloadFile.length()))){

                //最后的标记为 true 表示下载的数据可以从上一次的位置写入,否则会清空文件数据.
                outPutStream = new FileOutputStream(downloadFile,false);
            }else {
                outPutStream = new FileOutputStream(downloadFile,true);
            }

            InputStream inputStream = responseBody.byteStream();

            //如果有下载过的历史文件,则把下载总大小设为 总数据大小+文件大小 . 否则就是总数据大小
            if ( TextUtils.isEmpty(fileSize) ){
                bean.setTotalSize(responseBody.contentLength());
            }else {
                bean.setTotalSize(responseBody.contentLength() + downloadFile.length());
            }

            int length;
            //设置缓存大小
            byte[] buffer = new byte[1024];

            //开始写入文件
            while ((length = inputStream.read(buffer)) != -1){
                outPutStream.write(buffer,0,length);
                onDownload(downloadFile.length());
            }

            //清空缓冲区
            outPutStream.flush();
            outPutStream.close();
            inputStream.close();
        }

    }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
代码实现的步骤:

1.检测是否存在本地文件并由此设置请求头内的请求长度范围 
2.访问网址并获取到返回的头,检测是否支持断点续传,由此设置是否重新开始写入数据 
3.获取输入流,开始写入数据 
4.如果抛出了异常,并且异常不为中断,则为下载失败,否则不作响应 
5.下载失败、下载完成,都会自动寻找仍在队列中的等待任务进行下载

广播更新消息
在Service这里面我们什么都不用管,就是把数据广播出去就行了

    /**
     * 更新整个下载列表
     */
    private void updateList(){
        broadcastManager.sendBroadcast(new Intent("update_all"));
    }

    /**
     * 更新当前项目的进度
     * @param totalSize 下载文件的总大小
     * @param downloadedSize    当前下载的进度
     */
    private void updateItem(DLBean bean , long totalSize, long downloadedSize){
        int progressBarLength = (int) (((float)  downloadedSize / totalSize) * 100);
        Intent intent = new Intent("update_singel");
        intent.putExtra("progressBarLength",progressBarLength);
        intent.putExtra("downloadedSize",String.format("%.2f", downloadedSize/(1024.0*1024.0)));
        intent.putExtra("totalSize",String.format("%.2f", totalSize/(1024.0*1024.0)));
        intent.putExtra("item",bean);
        broadcastManager.sendBroadcast(intent);
    }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
下载管理Activity 实现
Service做好了之后,我们接下来就是要做查看任务的Activity了! 
这个Activity用于展示下载任务、暂停继续终止任务。

我们先看整个Activity的基础部分,我们之后再说接收器部分的实现。RecyclerView的Adapter点击事件回调 和 服务连接这类的我就不再赘述了。这些都不是我们关心的重点,需要注意的就是服务和广播要注意解除绑定和解除注册。

public class OCDownloadManagerActivity extends AppCompatActivity implements OCDownloadAdapter.OnRecycleViewClickCallBack{

    RecyclerView downloadList;
    OCDownloadAdapter downloadAdapter;
    OCDownloadService downloadService;
    LocalBroadcastManager broadcastManager;
    UpdateHandler updateHandler;
    ServiceConnection serviceConnection;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_download_manager);

        //RecycleView 的 Adapter 创建与点击事件的绑定
        downloadAdapter = new OCDownloadAdapter();
        downloadAdapter.setRecycleViewClickCallBack(this);

        //RecyclerView 的创建与相关操作
        downloadList = (RecyclerView)findViewById(R.id.download_list);
        downloadList.setLayoutManager(new LinearLayoutManager(this,LinearLayoutManager.VERTICAL,false));
        downloadList.setHasFixedSize(true);
        downloadList.setAdapter(downloadAdapter);

        //广播过滤器的创建
        IntentFilter intentFilter = new IntentFilter();
        intentFilter.addAction("update_all");       //更新整个列表的 Action
        intentFilter.addAction("update_singel");    //更新单独条目的 Action

        //广播接收器 与 本地广播 的创建和注册
        updateHandler = new UpdateHandler();
        broadcastManager = LocalBroadcastManager.getInstance(this);
        broadcastManager.registerReceiver(updateHandler,intentFilter);

        //创建服务连接
        serviceConnection = new ServiceConnection() {
            @Override
            public void onServiceConnected(ComponentName name, IBinder service) {
                //当服务连接上的时候
                downloadService = ((OCDownloadService.GetServiceClass)service).getService();
                downloadAdapter.updateAllItem(downloadService.getTaskList());
            }

            @Override
            public void onServiceDisconnected(ComponentName name) {
                //当服务断开连接的时候
                if (broadcastManager != null && updateHandler != null){
                    broadcastManager.unregisterReceiver(updateHandler);
                }
            }
        };

        //连接服务并进行绑定
        startService(new Intent(this,OCDownloadService.class));
        bindService(new Intent(this,OCDownloadService.class),serviceConnection,BIND_AUTO_CREATE);    

    }

    /**
     * RecyclerView 的单击事件
     * @param bean  点击条目中的 下载信息Bean
     */
    @Override
    public void onRecycleViewClick(DLBean bean) {
        if (downloadService != null){
            downloadService.clickTask(bean.getUrl(),false);
        }
    }

    /**
     * RecyclerView 的长按事件
     * @param bean  点击条目中的 下载信息Bean
     */
    @Override
    public void onRecycleViewLongClick(DLBean bean) {
        if (downloadService != null){
            downloadService.clickTask(bean.getUrl(),true);
        }
    }

    /**
     * 本地广播接收器  负责更新UI
     */
    class UpdateHandler extends BroadcastReceiver{
        ... ...
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();

        //解绑接收器
        broadcastManager.unregisterReceiver(updateHandler);

        //解绑服务
        unbindService(serviceConnection);
    }    

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
广播更新UI
接下来我们来实现广播接收器部分,也就是列表的刷新。

为什么要分开单独更新与整体更新呢?因为在下载的过程中的进度更新是非常非常频繁的,如果我们以这么高的频率来刷新UI,无疑会产生很大的负担。如果列表中只有几项的时候也许还行,但如果有1000+条的时候就很不容乐观了 (1年前刚开始接触这个东西的时候,是QQ中的一个好友@eprendre 告诉了我这个思路的。 如果各位dalao还有更好的方法麻烦在评论区留下您的见解)

    /**
     * 本地广播接收器  负责更新UI
     */
    class UpdateHandler extends BroadcastReceiver{

        @Override
        public void onReceive(Context context, Intent intent) {
            switch (intent.getAction()){
                case "update_all":
                    //更新所有项目

                    downloadAdapter.updateAllItem(downloadService.getTaskList());
                    break;
                case "update_singel":
                    //仅仅更新当前项

                    DLBean bean = intent.getExtras().getParcelable("item");
                    String downloadedSize = intent.getExtras().getString("downloadedSize");
                    String totalSize = intent.getExtras().getString("totalSize");
                    int progressLength = intent.getExtras().getInt("progressBarLength");
                    //如果获取到的 Bean 有效
                    if (bean != null){
                        View itemView = downloadList.getChildAt(downloadAdapter.getItemPosition(bean));
                        //如果得到的View有效
                        if (itemView != null){
                            TextView textProgress = (TextView)itemView.findViewById(R.id.textView_download_length);
                            ProgressBar progressBar = (ProgressBar)itemView.findViewById(R.id.progressBar_download);

                            //更新文字进度
                            textProgress.setText(downloadedSize+"MB / "+totalSize+"MB");

                            //更新进度条进度
                            progressBar.setProgress(progressLength);
                            TextView status = (TextView)itemView.findViewById(R.id.textView_download_status);

                            //更新任务状态
                            switch (bean.getStatus()){
                                case DOWNLOADING:
                                    status.setText("Downloading");
                                    break;
                                case WAITTING:
                                    status.setText("Waitting");
                                    break;
                                case FAILED:
                                    status.setText("Failed");
                                    break;
                                case PAUSED:
                                    status.setText("Paused");
                                    break;
                            }
                        }
                    }
                    break;
            }
        }

    }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
这里说一点就是 OKHttp 的下载进度监听,我之前曾按照

http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2015/0904/3416.html

这里说的方法做过一次,能成功监听到下载。但是我现在用OKHTTP3,这个方法好像并不奏效……估计是版本的问题吧。
--------------------- 
作者:妇校长啊 
来源:CSDN 
原文:https://blog.csdn.net/ocwvar/article/details/51199500 
版权声明:本文为博主原创文章,转载请附上博文链接!

本文转载自:https://blog.csdn.net/ocwvar/article/details/51199500

shzwork

shzwork

粉丝 15
博文 864
码字总数 18401
作品 0
厦门
私信 提问
一个来自Afinal断点下载BUG的解决方案

作为国内第一个Android开发框架Afinal,相信有很多开发者都知道的。虽然随着Android版本的迭代,其中有一些方法有了更好的解决办法但从来没有人怀疑Afinal的价值。 最近在做一个断点下载的功...

kymjs张涛
2014/12/14
4.8K
14
Android实现多线程断点续传

前言 我们常常在开发过程中会遇到下载的功能实现,当我们下载中断时,又不希望下次从头开始继续下载,我们就需要用到断点续传了。 断点续传原理 断点续传是指当下载中断后,再次下载时可以从...

Big_Blue
2018/03/16
0
0
一个关于android下开发下载断点续传的问题与研究

Android下应用程序要实现断点续传,可以把系统的下载服务搬过来改一下即可(本身已经有一个pause状态应对网络中断,利用它就可以实现暂停跟恢复) 但是………… 我发现很多android站自身建站...

hawkyoung
2011/12/28
1K
1
Android 快速开发框架--ThinkAndroid

ThinkAndroid简介 ThinkAndroid是一个免费的开源的、简易的、遵循Apache2开源协议发布的Android开发框架,其开发宗旨是简单、快速的进行Android应用程序的开发,包含Androidmvc、简易sqlite ...

white-cat
2013/05/05
67.3K
6
Android快速开发框架:ThinkAndroid

ThinkAndroid是包含Android mvc和简易sqliteorm以及ioc模块,它封装了Android httpclitent中的http模块,具有快速构建文件缓存功能,无需考虑什么格式的文件,都可以非常轻松的实现缓存,它实...

丨小丶牧灬
2015/07/30
504
1

没有更多内容

加载失败,请刷新页面

加载更多

利用CSS禁止手机长按出现气泡: 复制、选择等功能

可以用 * ,也可作用于一个div div{  -webkit-touch-callout:none;  /*系统默认菜单被禁用*/  -webkit-user-select:none; /*webkit浏览器*/  -khtml-user-select:none; /*早期浏览...

蓝小驴
52分钟前
7
0
前端的一些雕虫小技,从100%和滚动条说起

1、100%和滚动条 当我们在css中把html和body同时设为100%时,会出现滚动条 html, body { width: 100%; height: 100%; } 原因是html和b...

wphmoon
今天
8
0
电力区块链应用案例【2019】

随着区块链技术的日益普及,出现了大量创业企业尝试使用区块链技术来解决能源与电力行业中存在的问题。在本文中,我们将介绍其中的三个能源区块链项目。 能源行业以价格不透明著称:消费者很...

汇智网教程
今天
12
0
聊聊rocketmq的adjustThreadPoolNumsThreshold

序 本文主要研究一下rocketmq的adjustThreadPoolNumsThreshold DefaultMQPushConsumer rocketmq-client-4.5.2-sources.jar!/org/apache/rocketmq/client/consumer/DefaultMQPushConsumer.ja......

go4it
今天
10
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部