【问题标题】:Unable to update UI from IntentService无法从 IntentService 更新 UI
【发布时间】:2016-03-17 20:30:14
【问题描述】:

一个看似简单的要求 - 从 IntentService 更新 UI。在下面的截图中,当单击Start Service 按钮时,我需要在唯一的TextView 上方显示ProgressBar,并在延迟后删除ProgressBar

在 SO 上找到了很多答案,但不知何故仍然无法破解。我从this 了解到LocalBroadcastManager 是一个不错的选择。我也尝试过关注this,(Handlers 的一种方法),但它也无法显示ProgressBar。最后,基于this 的回答,这就是我的结果。到目前为止,我管理的输出只是在所有日志记录完成后连续出现的Toasts。

非常感谢您能指出我哪里出错了,我已经挣扎了很长一段时间了。非常感谢!

MainActivity 已更新

public class MainActivity extends AppCompatActivity
{
    private static final String TAG = "MainActivity";
    private ProgressBar pb;
    private MyBroadcastReceiver myBroadcastReceiver;

    @Override
    protected void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        pb = (ProgressBar) findViewById(R.id.pb);
        myBroadcastReceiver = new MyBroadcastReceiver();

        LocalBroadcastManager.getInstance(this).registerReceiver(myBroadcastReceiver, new IntentFilter("ACTION"));
    }

    private void updateUI(boolean show)
    {
        if (show)
            pb.setVisibility(View.VISIBLE);
        else
            pb.setVisibility(View.GONE);
//        Toast.makeText(this, "UI Updated...", Toast.LENGTH_LONG).show();
    }

    public void startIt(View view)
    {
        Intent intent = new Intent(this, NanisIntentService.class);
        startService(intent);
    }

    public class MyBroadcastReceiver extends BroadcastReceiver
    {
        @Override
        public void onReceive(final Context context, Intent intent)
        {
            String action = intent.getAction();
            Log.e(TAG, "In onReceive(): " + action);
            if (action.equals("ACTION"))
            {
                updateUI(true);
            } // of if (action = "ACTION")
            else if (action.equals("NOITCA"))
            {
                updateUI(false);
            } // of else of if (action = "ACTION")
        } // of onReceive()
    } // of class MyBroadcastReceiver
}

IntentService 已更新

public class NanisIntentService extends IntentService
{
    private static final String TAG = "NanisIntentService";

    public NanisIntentService()
    {
        super("NanisIntentService");
    }

    @Override
    protected void onHandleIntent(Intent intent)
    {
        Log.e(TAG, "In onHandleIntent(): Intent is being serviced");
        LocalBroadcastManager.getInstance(testing.com.myintentservice.NanisIntentService.this).sendBroadcast(new Intent().setAction("ACTION"));
        int i = 0;
        while (i <= 50)
        {
            try
            {
                Thread.sleep(50);
                i++;
                Log.e("", "" + i);
            } catch (InterruptedException e)
            {
                e.printStackTrace();
            }
        }
    }

    @Override
    public void onDestroy()
    {
        super.onDestroy();
        LocalBroadcastManager.getInstance(testing.com.myintentservice.NanisIntentService.this).sendBroadcast(new Intent().setAction("NOITCA"));
        Log.e(TAG, "In onDestroy(): The service has been destroyed");
    }
}

    @Override
    public void onStart(Intent intent, int startId)
    {
        super.onStart(intent, startId);
        LocalBroadcastManager.getInstance(testing.com.myintentservice.NanisIntentService.this).sendBroadcast(new Intent().setAction("ACTION"));
    }

    @Override
    public void onDestroy()
    {
        super.onDestroy();
        LocalBroadcastManager.getInstance(testing.com.myintentservice.NanisIntentService.this).sendBroadcast(new Intent().setAction("NOITCA"));
        Log.e(TAG, "In onDestroy(): The service has been destroyed");
    }
}

    @Override
    public void onDestroy()
    {
        super.onDestroy();
        LocalBroadcastManager.getInstance(testing.com.myintentservice.NanisIntentService.this).sendBroadcast(new Intent().setAction("NOITCA"));
        Log.e(TAG, "In onDestroy(): The service has been destroyed");
    }
}

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context="testing.com.myintentservice.MainActivity">

    <ProgressBar
        android:id="@+id/pb"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_above="@+id/tv"
        android:layout_centerHorizontal="true"
        android:indeterminate="true"
        android:visibility="gone"/>

    <TextView
        android:id="@+id/tv"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        android:text="Hello IntentService!"
        android:textColor="#1298CE"
        android:textSize="32sp"/>

    <Button
        android:id="@+id/bt"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:layout_centerHorizontal="true"
        android:text="Start Service"
        android:onClick="startIt"/>
</RelativeLayout>

AndroidManifest

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN"/>

                <category android:name="android.intent.category.LAUNCHER"/>
            </intent-filter>
        </activity>
        <service android:name="testing.com.myintentservice.NanisIntentService"/>
    </application>

【问题讨论】:

    标签: android android-intentservice localbroadcastmanager


    【解决方案1】:

    首先,您将占用主应用程序线程约 2.5 秒。这将在这段时间内冻结您的 UI。 不要这样做

    其次,您在 ~2.5 秒之前和之后调用 updateUI() 一次。由于这段时间你正在占用主应用程序线程,这将与延迟后连续两次调用updateUI()具有相同的视觉效果。 updateUI() 切换 ProgressBar 可见性,因此两个呼叫将相互取消,使您处于与开始时相同的状态。

    我需要在唯一的 TextView 上方显示一个 ProgressBar,并在延迟后移除 ProgressBar。

    显示ProgressBar 2.5 秒,无论实际执行的工作如何,都是相当随意的。

    话虽如此,调用updateUI() 一次,然后使用pb.postDelayed() 安排Runnable 在2500 毫秒后运行,其中Runnable 中的run() 方法第二次调用updateUI()。这可以避免您阻塞主应用程序线程,因此它允许第一个 updateUI() 调用生效,同时仍为您提供 2.5 秒的持续时间。

    【讨论】:

    • 感谢@CommonsWare 的快速响应!抱歉,我没有提到这是一个实验,一个PoC - 所以不是2.5秒的延迟,而是一个不确定的互联网下载,在此期间我需要显示ProgressBar。你的建议仍然有效吗?如果可能的话,如果您能详细说明解决方案,将不胜感激。干杯!
    • @NarayanaJ:“你的建议仍然有效吗?”——假设ProgressBar 的持续时间是基于服务的,那么您不妨一开始就这样设置。对“显示”和“隐藏”使用两个不同的LocalBroadcastManager 事件。在短期内,以服务所做的“假工作”的形式将延迟放入服务中。然后,您可以用真实的工作替换假工作,并且您的ProgressBar逻辑不必更改。
    • 请检查我原始问题中的更新代码。我所做的是: - 将“假工作”移至 IntentService - 为广播设置 2 个操作,一个显示,一个隐藏 ProgressBar 我也不确定 Runnable 调用是否(你最初建议)仍然应该制作,也不应该精确地制作它。 ProgressBar 仍然没有显示。请回复。
    • @NarayanaJ:你的第二条LocalBroadcastManager 消息直到onDestroy() 才出现,我看不出你在哪里停止了服务。此外,您再次占用了主应用程序线程,因为您将所有逻辑都放在onStart() 而不是onHandleIntent()
    • 已经意识到错误,你的观点完美地证实了这一点。请在原始问题中查看我更新的IntentServiceProgressBar 现在确实出现了,但永远不会停止。在another answer by you 中,无需停止IntentService。那么,我应该如何以及在哪里停止它?谢谢!
    【解决方案2】:

    非常感谢@CommonsWare!终于让它工作了。它需要 两个 接收器来处理两个不同的动作,我发现了here

    为了那些寻找最终工作代码的人的利益......

    结果屏幕:- 点击后处理后

    请原谅代码中的任何复制粘贴幽灵,它毕竟是一个 PoC,尽管是功能性的。

    【讨论】:

    • @user3705478,想知道它是如何从馅饼中消失的。当我有空闲时间时,将用代码更新这篇文章。到那时,请忍耐。
    猜你喜欢
    • 1970-01-01
    • 2017-08-02
    • 2011-12-13
    • 1970-01-01
    • 2014-07-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多