如何正确实现runOnUiThread Android

Posted

技术标签:

【中文标题】如何正确实现runOnUiThread Android【英文标题】:How to Properly Implement runOnUiThread Android 【发布时间】:2017-09-22 20:11:33 【问题描述】:

我需要在从 web 服务获取一些字符串的工作线程中使用 runOnUiThread 更新 UI 中的 TextView。我在这里查看了其他一些帖子并尝试实施提供的解决方案,但是当我启动我的应用程序时它崩溃了。我对这种方法不太熟悉,所以我需要一些指导来解决这个问题。

04-25 12:19:59.917 4108-4108/? I/art: 不延迟启用 -Xcheck:jni (已经启用) 04-25 12:19:59.917 4108-4108/? W/art:使用默认值的 X86 的意外 CPU 变体:x86 04-25 12:20:00.147 4108-4108/com.example.gmars.parseltonguev2 W/系统:类加载器引用未知路径:/data/app/com.example.gmars.parseltonguev2-2/lib/x86 04-25 12:20:00.154 4108-4108/com.example.gmars.parseltonguev2 I/InstantRun: 启动即时运行服务器: 是主进程 04-25 12:20:00.218 4108-4108/com.example.gmars.parseltonguev2 D/androidRuntime: 关闭虚拟机 04-25 12:20:00.218 4108-4108/com.example.gmars.parseltonguev2 E/AndroidRuntime: 致命异常: main 进程:com.example.gmars.parseltonguev2,PID:4108 java.lang.RuntimeException:无法实例化活动 ComponentInfocom.example.gmars.parseltonguev2/com.example.gmars.parseltonguev2.MainActivity:java.lang.NullPointerException:尝试调用虚拟方法 'android.view.Window$Callback android.view.Window.getCallback()' 在空对象引用上 在 android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2548) 在 android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2707) 在 android.app.ActivityThread.-wrap12(ActivityThread.java) 在 android.app.ActivityThread$H.handleMessage(ActivityThread.java:1460) 在 android.os.Handler.dispatchMessage(Handler.java:102) 在 android.os.Looper.loop(Looper.java:154) 在 android.app.ActivityThread.main(ActivityThread.java:6077) 在 java.lang.reflect.Method.invoke(本机方法) 在 com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:865) 在 com.android.internal.os.ZygoteInit.main(ZygoteInit.java:755) 原因:java.lang.NullPointerException:尝试在空对象引用上调用虚拟方法 'android.view.Window$Callback android.view.Window.getCallback()' 在 android.support.v7.app.AppCompatDelegateImplBase.(AppCompatDelegateImplBase.java:120) 在 android.support.v7.app.AppCompatDelegateImplV9.(AppCompatDelegateImplV9.java:155) 在 android.support.v7.app.AppCompatDelegateImplV11.(AppCompatDelegateImplV11.java:31) 在 android.support.v7.app.AppCompatDelegateImplV14.(AppCompatDelegateImplV14.java:55) 在 android.support.v7.app.AppCompatDelegateImplV23.(AppCompatDelegateImplV23.java:33) 在 android.support.v7.app.AppCompatDelegateImplN.(AppCompatDelegateImplN.java:33) 在 android.support.v7.app.AppCompatDelegate.create(AppCompatDelegate.java:201) 在 android.support.v7.app.AppCompatDelegate.create(AppCompatDelegate.java:185) 在 android.support.v7.app.AppCompatActivity.getDelegate(AppCompatActivity.java:519) 在 android.support.v7.app.AppCompatActivity.findViewById(AppCompatActivity.java:190) 在 com.example.gmars.parseltonguev2.MainActivity.(MainActivity.java:30) 在 java.lang.Class.newInstance(本机方法) 在 android.app.Instrumentation.newActivity(Instrumentation.java:1078) 在 android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2538) 在 android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2707) 在 android.app.ActivityThread.-wrap12(ActivityThread.java) 在 android.app.ActivityThread$H.handleMessage(ActivityThread.java:1460) 在 android.os.Handler.dispatchMessage(Handler.java:102) 在 android.os.Looper.loop(Looper.java:154) 在 android.app.ActivityThread.main(ActivityThread.java:6077) 在 java.lang.reflect.Method.invoke(本机方法) 在 com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:865) 在 com.android.internal.os.ZygoteInit.main(ZygoteInit.java:755)

Activity updateUI;

@Override
public void onCreate(Bundle savedInstanceState) 
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    updateUI = this;

    itemList = new ArrayList<>();

    new GetItemAvailability().execute();



@Override
    public Void doInBackground(Void... arg0) 
        HttpHandler handler = new HttpHandler();

        String JSON_DATA = handler.makeServiceCall(url);

        Log.e(TAG, "Response from url: " + JSON_DATA);

        if (JSON_DATA != null) 
            JSON_DATA = JSON_DATA.substring(JSON_DATA.indexOf(''),JSON_DATA.lastIndexOf('')+1);
            Log.e(TAG, "Substring of response: " + JSON_DATA);
            try 
                JSONObject reader = new JSONObject(JSON_DATA);

                JSONObject availability = reader.getJSONObject("availability");

                final String availableQuantity = availability.getString("availableQuantity");
                final String defaultBranch = availability.getString("defaultBranch");
                final String defaultInventoryBranch = availability.getString("defaultInventoryBranch");
                final String inventoryBranch = availability.getString("inventoryBranch");
                final String footage = availability.getString("footage");
                final String anticipatedStockDate = availability.getString("anticipatedStockDate");
                final String discontinuedItemFlag = availability.getString("discontinuedItemFlag");
                final String mdc = availability.getString("mdc");
                final String mdcquantity = availability.getString("mdcquantity");

                HashMap<String, String> item = new HashMap<>();

                item.put("availableQuantity", availableQuantity);
                item.put("defaultBranch", defaultBranch);
                item.put("defaultInventoryBranch", defaultInventoryBranch);
                item.put("inventoryBranch", inventoryBranch);
                item.put("footage", footage);
                item.put("anticipatedStockDate", anticipatedStockDate);
                item.put("discontinuedItemFlag", discontinuedItemFlag);
                item.put("mdc", mdc);
                item.put("mdcquantity", mdcquantity);

                itemList.add(item);

                updateUI.runOnUiThread(new Runnable() 
                    @Override
                    public void run() 
                        tvAvailableQuantity.setText(availableQuantity);
                        tvDefaultBranch.setText(defaultBranch);
                        tvDefaultInventoryBranch.setText(defaultInventoryBranch);
                        tvInventoryBranch.setText(inventoryBranch);
                        tvFootage.setText(footage);
                        tvAnticipatedStockDate.setText(anticipatedStockDate);
                        tvDiscontinuedItemFlag.setText(discontinuedItemFlag);
                        tvMdc.setText(mdc);
                        tvMdcquantity.setText(mdcquantity);
                    
                );

            

【问题讨论】:

必须初始化UI变量,例如TextView tvAvailableQuantity = (TestView) findViewById(id) 你能编辑你的问题并添加崩溃的堆栈跟踪吗?可能与runOnUiThread() 无关,它会为我们提供更多信息。 我已经在 onCreate 方法之前使用我的其他变量完成了该操作。我只发布了有关 runOnUiThread 的代码的 sn-ps。谢谢。 请添加堆栈跟踪... 我添加了堆栈跟踪。 【参考方案1】:

您的 runOnUiThread() 实现是正确的。您将它传递给在 UI 线程上运行的可运行文件。

但是,在您的情况下,如果您将 AsyncTask 的结果类型更改为字符串,我认为这甚至没有必要,您可以简单地从 doInBackground() 方法返回 JSON_DATA 并将其余代码移动到 @987654323 @ 无论如何都在 UI 线程上运行。

【讨论】:

谢谢,我在 onCreate 之前初始化了我的 TextViews。但是,使用 runOnUiThread 或您的方法哪个更好? 通常情况下,如果需要,您可以使用 runOnUiThread,例如当你在不同的线程中做某事时向用户展示一些东西。在您的情况下,您的网络访问(首先需要单独的线程)已经结束,所以在这种情况下,我想说我的建议在这里更可取 - 它也更容易阅读,更短一点,你不需要创建 Runnable 对象。

以上是关于如何正确实现runOnUiThread Android的主要内容,如果未能解决你的问题,请参考以下文章

如何等待 Android runOnUiThread 完成?

我们如何在 Android 中使用 runOnUiThread?

使用 runOnUiThread 获取当前位置

runOnUiThread()方法

notifyDataSetChanged()使列表刷新并滚动回到顶部

Android 更新UI的两种方法——handler和runOnUiThread()