问题
A seemingly simple requirement - to update UI from an IntentService
. In the shot below, when the Start Service
button is clicked, I need to show a ProgressBar
above the only TextView
and after a delay, remove the ProgressBar
.
Found a lot of answers on SO, but somehow unable to still crack it. I understand from this that LocalBroadcastManager
is a good way to go. I have also tried following this, (an approach with Handler
s), but it fails to show the ProgressBar
too. Finally, based on this answer, here's what I have ended up with. The output I've managed so far is just the Toast
s appearing in succession, after all the logging is done.
Greatly appreciate if you could point out where I am going wrong, been struggling for quite some time now. Many thanks in advance!
MainActivity
updated
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
updated
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>
回答1:
First, you are tying up the main application thread for ~2.5 seconds. This will freeze your UI during this period of time. Do not do this.
Second, you are calling updateUI()
once before the ~2.5 seconds, and once after. Since you are tying up the main application thread during that time, this will have the same visual effect as calling updateUI()
twice in succession after the delay. updateUI()
toggles the ProgressBar
visibility, so two calls will cancel each other out, leaving you in the same state as you started.
I need to show a ProgressBar above the only TextView and after a delay, remove the ProgressBar.
Showing a ProgressBar
for 2.5 seconds, irrespective of any actual work being done, is rather arbitrary.
That being said, call updateUI()
once, then use pb.postDelayed()
to schedule a Runnable
to run 2500 milliseconds later, where the run()
method in the Runnable
calls updateUI()
the second time. This avoids you blocking the main application thread, so it allows the first updateUI()
call to take effect, while still giving you the 2.5-second duration.
回答2:
Thanks so much, @CommonsWare! Finally got it to work. It needed two receivers for the two different actions, which I discovered here.
For the benefit of those looking for the final working code...
- MainActivity
- NanisIntentService
Result screens:- after clicking and after processing
Pardon any copy-paste ghosts in the code, it is after all a PoC, albeit functional.
来源:https://stackoverflow.com/questions/36071091/unable-to-update-ui-from-intentservice