In many cases, you have to update Service data to UI so as that user can recognize Service status (i.e: download file process), so we must put data through Intent and use BroadcastReceiver to "listen" Service status and display it to UI. With this project, I hope that you can understand this data tranfer process and as well as have more experiences in Android application programming!
1. Project Description
- There are 3 Fragments (pages) in ViewPager: DownLoadFragment, DateTimeFragment and a BlankFragment to "test".
- Includes 2 Services run in background: Download file Service (show it's status to DownloadFragment) and getting DateTime Service (show curent date/time to UI in DateTimeFragment).
2. Main Activity
Set ViewPager adapter in programmatically code (PagerActivity.java):
package info.devexchanges.services.ui; import android.os.Bundle; import android.support.v4.app.FragmentActivity; import android.support.v4.view.ViewPager; import info.devexchanges.services.R; import info.devexchanges.services.adapter.ViewPagerAdapter; public class PagerActivity extends FragmentActivity { private ViewPager pager; private ViewPagerAdapter adapter; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_pager); pager = (ViewPager) findViewById(R.id.view_pager); //init view pager adapter adapter = new ViewPagerAdapter(getSupportFragmentManager()); pager.setAdapter(adapter); } }
3. Download Fragment and download file Service
For "listen" a Service status, provide a BroadcastReceiver object in Fragment and initialize it in onCreate() method. Therefore, create a new Intent with current Service:
@Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); broadcastReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { updateProgressBar(intent); } }; intent = new Intent(getActivity(), DownloadService.class); }Register/unregister broadcast receiver in onStart()/onStop() of DownloadFragment LocalBroadcastManager instance:
@Override public void onStart() { super.onStart(); if (isServiceRunning(DownloadService.class)) { btnDownload.setVisibility(View.GONE); } LocalBroadcastManager.getInstance(getActivity()).registerReceiver((broadcastReceiver), new IntentFilter(DownloadService.DOWNLOAD_ACTION) ); } @Override public void onStop() { super.onStop(); LocalBroadcastManager.getInstance(getActivity()).unregisterReceiver(broadcastReceiver); }Ofcourse, in this Fragment, we'll start download file after click Download Button:
/** * Handling button Download event * @return */ private View.OnClickListener onStartDownloadListener() { return new View.OnClickListener() { @Override public void onClick(View v) { getActivity().startService(intent); btnDownload.setVisibility(View.GONE); //init BroadcastManager instance when service start LocalBroadcastManager.getInstance(getActivity()).registerReceiver((broadcastReceiver), new IntentFilter(DownloadService.DOWNLOAD_ACTION) ); } }; }Another important method is updateProgressBar() to update percentage of download file process to ProgressBar and TextView:
/** * Update the percentage of download process to TextView and Progress Bar * @param intent */ private void updateProgressBar(Intent intent) { String progress = intent.getStringExtra(DownloadService.PERCENTAGE_STAMP); if (progress != null ) { progressBar.setProgress(Integer.parseInt(progress)); percentage.setText(progress + "%"); //stop download service when task completed if (progress.equals("100")) { getActivity().stopService(intent); } } }Adding some neccessary methods, we have full DownloadFragment.java code:
package info.devexchanges.services.ui; import android.app.ActivityManager; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.os.Bundle; import android.support.v4.app.Fragment; import android.support.v4.content.LocalBroadcastManager; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.ProgressBar; import android.widget.TextView; import info.devexchanges.services.DownloadService; import info.devexchanges.services.R; public class DownLoadFragment extends Fragment { private ProgressBar progressBar; private TextView percentage; private View btnDownload; private Intent intent; private BroadcastReceiver broadcastReceiver; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); broadcastReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { updateProgressBar(intent); } }; intent = new Intent(getActivity(), DownloadService.class); } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { return inflater.inflate(R.layout.fragment_down_load, container, false); } @Override public void onViewCreated(View view, Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); percentage = (TextView) view.findViewById(R.id.percent); progressBar = (ProgressBar) view.findViewById(R.id.progress_bar); btnDownload = (View) view.findViewById(R.id.btn_start); btnDownload.setOnClickListener(onStartDownloadListener()); } @Override public void onStart() { super.onStart(); if (isServiceRunning(DownloadService.class)) { btnDownload.setVisibility(View.GONE); } LocalBroadcastManager.getInstance(getActivity()).registerReceiver((broadcastReceiver), new IntentFilter(DownloadService.DOWNLOAD_ACTION) ); } @Override public void onStop() { super.onStop(); LocalBroadcastManager.getInstance(getActivity()).unregisterReceiver(broadcastReceiver); } /** * Update the percentage of download process to TextView and Progress Bar * @param intent */ private void updateProgressBar(Intent intent) { String progress = intent.getStringExtra(DownloadService.PERCENTAGE_STAMP); if (progress != null ) { progressBar.setProgress(Integer.parseInt(progress)); percentage.setText(progress + "%"); //stop download service when task completed if (progress.equals("100")) { getActivity().stopService(intent); } } } /** * Handling button Download event * @return */ private View.OnClickListener onStartDownloadListener() { return new View.OnClickListener() { @Override public void onClick(View v) { getActivity().startService(intent); btnDownload.setVisibility(View.GONE); //init BroadcastManager instance when service start LocalBroadcastManager.getInstance(getActivity()).registerReceiver((broadcastReceiver), new IntentFilter(DownloadService.DOWNLOAD_ACTION) ); } }; } /** * Check if Service is running * @param serviceClass * @return true if one or more service is running in background thread */ private boolean isServiceRunning(Class serviceClass) { ActivityManager manager = (ActivityManager) getActivity().getSystemService(Context.ACTIVITY_SERVICE); for (ActivityManager.RunningServiceInfo service : manager.getRunningServices(Integer.MAX_VALUE)) { if (serviceClass.getName().equals(service.service.getClassName())) { return true; } } return false; } }In DownloadService.java, we provide an AsyncTask to download file trom URL, when it's running, in doInBackground(), calculating downloaded percentages, and write data to file at Internal Storage. In onProgressUpdate(), sending this data to BroadcastReceiver:
/** * Updating progress bar */ protected void onProgressUpdate(String... progress) { // setting progress percentage //put info through intent intent = new Intent(DOWNLOAD_ACTION); intent.putExtra(PERCENTAGE_STAMP, progress[0]); //sending intent through BroadcastManager broadcastManager.sendBroadcast(intent); }And this is full code of AsyncTask:
Creating BroadcastReceiver instance in onCreate() method of Service and start AsyncTask in onStartCommand():
@Override public void onCreate() { super.onCreate(); //init Intent broadcastManager = LocalBroadcastManager.getInstance(this); } @Override public int onStartCommand(Intent intent, int flags, int startId) { new DownloadFileFromURLTask().execute(DOWNLOAD_LINK); return START_STICKY; }Full code of DownloadService.java:
This screen when app running:
4. DateTime Fragment and get current date/time Service
@Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); broadcastReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { updateDataToUI(intent); } }; intent = new Intent(getActivity(), DateTimeService.class); } @Override public void onStart() { super.onStart(); getActivity().startService(intent); LocalBroadcastManager.getInstance(getActivity()).registerReceiver((broadcastReceiver), new IntentFilter(DateTimeService.DATE_TIME_ACTION) ); } @Override public void onStop() { super.onStop(); getActivity().stopService(intent); LocalBroadcastManager.getInstance(getActivity()).unregisterReceiver(broadcastReceiver); }in updateDataToUI() method, we'll update Service data (current Date and Time) to TextViews. Full code of DateTimeFragment.java:
package info.devexchanges.services.ui; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.os.Bundle; import android.support.v4.app.Fragment; import android.support.v4.content.LocalBroadcastManager; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.TextView; import info.devexchanges.services.DateTimeService; import info.devexchanges.services.R; public class DateTimeFragment extends Fragment { private TextView date; private TextView time; private Intent intent; private BroadcastReceiver broadcastReceiver; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); broadcastReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { updateDataToUI(intent); } }; intent = new Intent(getActivity(), DateTimeService.class); } @Override public void onStart() { super.onStart(); getActivity().startService(intent); LocalBroadcastManager.getInstance(getActivity()).registerReceiver((broadcastReceiver), new IntentFilter(DateTimeService.DATE_TIME_ACTION) ); } @Override public void onStop() { super.onStop(); getActivity().stopService(intent); LocalBroadcastManager.getInstance(getActivity()).unregisterReceiver(broadcastReceiver); } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { return inflater.inflate(R.layout.fragment_date_time, container, false); } @Override public void onViewCreated(View view, Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); date = (TextView) view.findViewById(R.id.date); time = (TextView) view.findViewById(R.id.time); } /** * Update date and time to textviews */ private void updateDataToUI(Intent intent) { date.setText(intent.getStringExtra(DateTimeService.DATE_STAMP)); time.setText(intent.getStringExtra(DateTimeService.TIME_STAMP)); } }In DateTimeService,java, getting current system date/time and doing this work after every 1 second execute a Runnable method with a Handler. Put data through Intent and LocalBroadcastManager instance :
private Runnable sendUpdatesToUI = new Runnable() { public void run() { getDateTimes(); handler.postDelayed(this, 1000); // 1 seconds } }; public void getDateTimes() { calendar = Calendar.getInstance(); int seconds = calendar.get(Calendar.SECOND); int minutes = calendar.get(Calendar.MINUTE); int hours = calendar.get(Calendar.HOUR); dateFormat = new SimpleDateFormat("dd-MM-yyyy"); String formattedDate = "Current Date: " + dateFormat.format(calendar.getTime()); String time = "Current Time: " + hours + ":" + minutes + ":" + seconds; //put info through intent intent = new Intent(DATE_TIME_ACTION); intent.putExtra(TIME_STAMP, time); intent.putExtra(DATE_STAMP, formattedDate); broadcastManager.sendBroadcast(intent); }Like above Service, initial BroadcastReceiver and start it through onCreate/onStartCommand. Full code:
package info.devexchanges.services; import android.app.Service; import android.content.Intent; import android.os.Handler; import android.os.IBinder; import android.support.v4.content.LocalBroadcastManager; import java.text.SimpleDateFormat; import java.util.Calendar; /** * Created by Hong Thai */ public class DateTimeService extends Service { private Calendar calendar; private SimpleDateFormat dateFormat; private Intent intent; private final Handler handler = new Handler(); private LocalBroadcastManager broadcastManager; public final static String TIME_STAMP = "Time"; public final static String DATE_STAMP = "Date"; public final static String DATE_TIME_ACTION = "info.devexchanges.dateservice"; @Override public IBinder onBind(Intent intent) { return null; } @Override public void onCreate() { super.onCreate(); broadcastManager = LocalBroadcastManager.getInstance(this); } @Override public int onStartCommand(Intent intent, int flags, int startId) { handler.removeCallbacks(sendUpdatesToUI); handler.postDelayed(sendUpdatesToUI, 1000); // 1 second return START_STICKY; } @Override public void onDestroy() { super.onDestroy(); handler.removeCallbacks(sendUpdatesToUI); } private Runnable sendUpdatesToUI = new Runnable() { public void run() { getDateTimes(); handler.postDelayed(this, 1000); // 1 seconds } }; public void getDateTimes() { calendar = Calendar.getInstance(); int seconds = calendar.get(Calendar.SECOND); int minutes = calendar.get(Calendar.MINUTE); int hours = calendar.get(Calendar.HOUR); dateFormat = new SimpleDateFormat("dd-MM-yyyy"); String formattedDate = "Current Date: " + dateFormat.format(calendar.getTime()); String time = "Current Time: " + hours + ":" + minutes + ":" + seconds; //put info through intent intent = new Intent(DATE_TIME_ACTION); intent.putExtra(TIME_STAMP, time); intent.putExtra(DATE_STAMP, formattedDate); broadcastManager.sendBroadcast(intent); } }Our result when running:
5. Some necessary files
A customize adapter based on FragmentPagerAdapter for ViewPager (ViewPagerAdapter.java):
package info.devexchanges.services.adapter; import android.support.v4.app.Fragment; import android.support.v4.app.FragmentManager; import android.support.v4.app.FragmentPagerAdapter; import info.devexchanges.services.ui.BlankFragment; import info.devexchanges.services.ui.DateTimeFragment; import info.devexchanges.services.ui.DownLoadFragment; /** * Created by Hong Thai. */ public class ViewPagerAdapter extends FragmentPagerAdapter{ public ViewPagerAdapter(FragmentManager fm) { super(fm); } @Override public Fragment getItem(int i) { if (i == 1) { return new DateTimeFragment(); } else if (i == 0){ return new DownLoadFragment(); } else { return new BlankFragment(); } } public CharSequence getPageTitle(int position) { if (position == 0) { return "Download"; } else if (position == 1) { return "Date Time"; } else { return "Blank Fragment"; } } @Override public int getCount() { return 3; } }
Providing WRITE_EXTERNAL_STORAGE and INTERNET permissions, register our services in AndroidManifest: