搜索

首页  >  问答  >  正文

thread - Android 主线程更新UI问题

本人使用了OKGO的框架, 下载文件,在下载之前创建一个 等待框,在更新进度的回调中,更新等待框的百分比, 可是爆了如下错误!

通过LOG 发现 UI线程ID 不一样

是什么问题? 求大神解决,以下是源码~!

public void setDownloadUrl(String url) {
        if (TextUtils.isEmpty(url))
            return;

        if (dialog == null) {
            dialog = new ProgressDialog(mainViewImpl.getContext());
            dialog.setCancelable(false);
            dialog.setCanceledOnTouchOutside(false);
            dialog.setMessage("正在下载文件...");
            dialog.setMax(100);
            dialog.setTitle("软件更新");
        }
        dialog.show();

        L.i("(外)线程ID: " + Thread.currentThread().getId());

        OkGo.get(url)//
                .tag(this)//
                .execute(new FileCallback() {  //文件下载时,可以指定下载的文件目录和文件名
                    @Override
                    public void onSuccess(File file, Call call, Response response) {
                        // file 即为文件数据,文件保存在指定目录
                        L.i("成功: ");
                        if (dialog != null && dialog.isShowing())
                            dialog.dismiss();
                        startUpdate(file);
                    }

                    @Override
                    public void downloadProgress(long currentSize, long totalSize, float progress, long networkSpeed) {
                        //这里回调下载进度(该回调在主线程,可以直接更新ui)
                        L.i("(内)线程ID: " + Thread.currentThread().getId());
                        dialog.setMessage("正在下载文件......" + (int) (progress * 100) + "%");
                    }

                    @Override
                    public void onError(Call call, Response response, Exception e) {
                        super.onError(call, response, e);
                        L.i("失败: ");
                        if (dialog != null && dialog.isShowing())
                            dialog.dismiss();
                    }
                });
    }
天蓬老师天蓬老师2771 天前1295

全部回复(3)我来回复

  • PHP中文网

    PHP中文网2017-04-18 09:15:26

    你使用的是MVP架构,在Presenter中不应该操作View中的控件,而你在Presenter中操作了Dialog这个控件。
    所以正确的做法应该是下面的:

    Interface View{
    
        void showDialog();
        void updateDialog(int count);
        void dismissDialog();
        void showError();
        void dismissError();
        
        }
        
        class Presenter{
          View  view;
          public Presenter(View view){
          this.view=view;
          }
        
          public void setDownloadUrl(String url){
                OkGo.get(url)//
                        .tag(this)//
                        .execute(new FileCallback() {  //文件下载时,可以指定下载的文件目录和文件名
                            @Override
                            public void onSuccess(File file, Call call, Response response) {
                                // file 即为文件数据,文件保存在指定目录
                                view.dismissDialog();
                            }
        
                            @Override
                            public void downloadProgress(long currentSize, long totalSize, float progress, long networkSpeed) {
                                view.update((int)progress);
                            }
        
                            @Override
                            public void onError(Call call, Response response, Exception e) {
                                super.onError(call, response, e);
                                view.dismissDialog();
                                view.showError();
                            }
                        });
                        
             }
             
         }               
                        
            
    

    回复
    0
  • ringa_lee

    ringa_lee2017-04-18 09:15:26

    你已经打印出downloadProgress()方法执行的环境不是UI线程,那么你把downloadProgress()方法执行的环境不是UI线程,那么你把

    dialog.setMessage("正在下载文件......" + (int) (progress * 100) + "%");

    的执行放在UI线程就可以了.

    runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    dialog.setMessage("正在下载文件......" + (int) (progress * 100) + "%");
                }
            });

    ||-------------------------补充 -------------------------||

    我刚刚也打印了log,在ui线程和子线程中:

    03-03 15:20:54.592 30842-30842/com.didikee.commondependence E/test: Out-->ThreadName: main  id: 1
    03-03 15:20:54.592 30842-30891/com.didikee.commondependence E/test: Inner-->ThreadName: main  id: 807

    主线程的id是1,子线程的id是807,他们的名称都是main,而线程的名称是可以指定的:

    public Thread(String name) {
            //这是指定线程名称的构造函数
            init(null, null, name, 0);
        }

    而线程的id却是内部生成的,不可以手动指定,也就是说线程的名称展示成什么并没有说服力,id才是唯一,两个id不一样就不是同一线程,与主线程id不一样那不一样的那个就不是主线程,题主不知道纠结什么,可以去看Thread

    tid = nextThreadID();
    ....
    private static synchronized long nextThreadID() {
            return ++threadSeqNumber;
        }

    的执行放在UI线程就可以了.🎜 rrreee 🎜||-------------------------补充 -------------------------||🎜 🎜我刚刚也打印了log,在ui线程和子线程中:🎜 rrreee 🎜主线程的id是1,子线程的id是807,他们的名称都是main,而线程的名称是可以指定的:🎜 rrreee 🎜而线程的id却是内部生成的,不可以手动指定,也就是说线程的名称展示成什么并没有说服力,id才是唯一,两个id不一样就不是同一线程,与主线程id不一样那不一样的那个就不是主线程,题主不知道纠结什么,可以去看Thread类的源码:🎜 rrreee

    回复
    0
  • ringa_lee

    ringa_lee2017-04-18 09:15:26

    下载任务执行在UIThread将会阻塞界面,为了不影响应用体验,我们都会在异步线程当中执行下载任务,而异步线程中执行的回调自然是在异步线程了。
    Android中最常用的线程通讯机制是Handler:

        Handler mHandler = new Handler(Looper.getMyLooper());
        handler.post(new Runnable(){
            @Override
            void run(){
            //在这里更新ui就好了
            }
        })

    回复
    0
  • 取消回复