thread - Android 主线程更新UI问题
天蓬老师
天蓬老师 2017-04-18 09:13:26
0
3
1289

本人使用了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();
                    }
                });
    }
天蓬老师
天蓬老师

欢迎选择我的课程,让我们一起见证您的进步~~

全部回覆(3)
洪涛

你使用的是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();
                        }
                    });
                    
         }
         
     }               
                    
        
左手右手慢动作

你已經印出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;
    }
左手右手慢动作

下載任務執行在UIThread將會阻塞介面,為了不影響應用程式體驗,我們都會在非同步執行緒當中執行下載任務,而非同步執行緒中執行的回呼自然是在非同步執行緒了。
Android中最常用的執行緒通訊機制是Handler:

    Handler mHandler = new Handler(Looper.getMyLooper());
    handler.post(new Runnable(){
        @Override
        void run(){
        //在这里更新ui就好了
        }
    })
熱門教學
更多>
最新下載
更多>
網站特效
網站源碼
網站素材
前端模板