今天在写 Android 的时候,用到了 ProgressDialog 这个进度提示框,我把 progress 提前 new 出来,并在子线程开始之前把它 show 出来。这里我的子线程是 new 了一个 runnable 来实现的,在子线程做完耗时工作的最后,调用 progress 的 dismiss 方法。但是很奇怪,直到子线程结束,进度框都没有显示出来。开始我以为是子线程的耗时太短,根本来不及显示就隐藏掉了。我又让子线程里每循环一次睡上 0.1 秒,但是还是不能显示。我又注释了隐藏进度条的逻辑,这个时候才发现子线程执行完毕之后进度条才显示出来,所以之前其实是子线程执行完毕后才显示进度条,但是又立刻被关闭掉了,以至于我根本观察不到。但这样我还是不知道是什么导致了进度条在子线程执行完了才显示。下面是我开始用 runnable 的代码:
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
| final ProgressDialog progressDialog = new ProgressDialog(ProToolsActovoty.this); progressDialog.setCancelable(false); progressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL); progressDialog.setTitle("备份 SMS 信息"); progressDialog.setMessage("正在备份,请稍等。。。"); progressDialog.show(); new Runnable() { @Override public void run() { List<SmsMessage> allSmsMessage = SMSEngine.getAllSmsMessage( context, new SMSEngine.ProgressChangedListener() { @Override public void setMax(int max) { progressDialog.setMax(max); } @Override public void setProgress(int progressValue) { progressDialog.setProgress(progressValue); } }); try { String outerSdPath = SystemUtils.getOuterSDPath(); if (TextUtils.isEmpty(outerSdPath)) { outerSdPath = getExternalFilesDir(null).getPath(); } outerSdPath += "/sms-backup.xml"; backupSmsByDOM(allSmsMessage, outerSdPath); progressDialog.dismiss(); toast("备份成功!备份路径为:" + outerSdPath, Toast.LENGTH_LONG); } catch (IOException e) { e.printStackTrace(); } } }.run();
|
我也是知道 Thread 和 runnable 在某些条件下是可以通用的,不过脑海中总是隐约记得要优先使用 runnable 而不是 Thread 。所以 runnable 不对头,只好去试试 Thread 了。不过这一试还确实试对了,使用 Thread 来开启子线程的结果也是我所预期的。在子线程开始之前显示进度条,子线程结束之后隐藏,还是 Thread 大法好呀。但仅仅知道怎么做是正确的还不够,必须得知道为什么要这么做呢?只有了解了 runnable 和 Thread 的区别,才能真的算是会使用。
然而当我开始试图分清楚 runnable 和 Thread 的区别的时候,我发现我一开始就错了,因为 runnable 只是一个接口啊,即使我通过 runnable 创建了一个实例,它仍然只是个接口,并非一个真正意义上的子线程。为了验证我的想法,新建一个小例子:
1 2 3 4 5 6 7 8 9 10 11 12
| public class RunnableTest { public static void main(String[] args) { new Runnable(){ @Override public void run() { System.out.println(Thread.currentThread().getId()); } }.run();
System.out.println(Thread.currentThread().getId()); } }
|
我用之前的办法创建了一个 runnable 的实例,并分别在这个实例中和主线程中打印了当前的线程 ID ,结果:
1
1
两个线程 ID 完全一致,所以 runnable 并没有在子线程中执行,只不过是相当于在主线程中调用了 runnable 的 run 方法而已。可即便如此,作为一个方法,它也无法阻止进度条的显示吧。这里我暂时还不能得出结论,先当做一个疑问留下来,如果有读者了解也欢迎留言一起探讨。
正确是的使用 runnable 接口的方法是在一个新的 Thread 中传入 runnable 接口,像这样:
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
| new Thread(new Runnable() { @Override public void run() { List<SmsMessage> allSmsMessage = SMSEngine.getAllSmsMessage( context, new SMSEngine.ProgressChangedListener() { @Override public void setMax(int max) { progressDialog.setMax(max); }
@Override public void setProgress(int progressValue) { progressDialog.setProgress(progressValue); } }); try { String outerSdPath = SystemUtils.getOuterSDPath(); if (TextUtils.isEmpty(outerSdPath)) { outerSdPath = getExternalFilesDir(null).getPath(); } outerSdPath += "/sms-backup.xml"; backupSmsByDOM(allSmsMessage, outerSdPath); progressDialog.dismiss(); toast("备份成功!备份路径为:" + outerSdPath, Toast.LENGTH_LONG); } catch (IOException e) { e.printStackTrace(); } } }).start();
|
尽管程序已经能够正确运行,但是我还是不知道我之前的错误是如何发生的,也不明白其中的原理,不过现在不明白,不代表将来也还是不明白,最近我会多多研究多线程以及 ProgressDialog 的深入解析,争取搞清楚其中的门道。