无法从 IntentService 更新 UI

Unable to update UI from IntentService

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

在SO上找到了很多答案,但不知为何还是破解不了。我从 this that LocalBroadcastManager is a good way to go. I have also tried following this, (an approach with Handlers), but it fails to show the ProgressBar too. Finally, based on 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>

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

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

I need to show a ProgressBar above the only TextView and after a delay, remove the ProgressBar.

显示 ProgressBar 2.5 秒,而不考虑正在完成的任何实际工作,这是相当随意的。

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

非常感谢@CommonsWare!终于让它工作了。我发现 here.

需要 两个 两个不同的 动作 接收器

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

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

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