For those running in foreground, they are called 'Activity' (those applications we can see and use in our mobile)
For those running in background, they are called 'Service' (those we cannot see while running in background and keep consuming your mobile's RAM. Haha they are one of the evilest guys that make your mobile so slow)
First of all, take a look at Service's life cycle: onCreate()->onStartCommand(Intent, int, int)->onDestroy()
You can see that it is a bit different from that of Activity. Today we are going to work on onStartCommand(Intent, int, int) only.
Service is mostly used in combination with Notification, a typical one is like:
1. Service does something in background secretly
2. Service finished, it triggers a Notification to notify user
3. User clicks the Notification and open the Application (Activity) to view the result
So we are going to make use of the Notification Application and fine tune it into a Service Notification Application (For detail about the Notification Application, please refer to http://nevescheng.blogspot.hk/2013/05/notification-with-big-view.html)
Do some restructure, create one more NotificationUtil Class :
package com.example.sample_service_notification; import android.app.NotificationManager; import android.app.PendingIntent; import android.content.Context; import android.content.Intent; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.support.v4.app.NotificationCompat; import android.support.v4.app.TaskStackBuilder; public class NotificationUtil { public final static int NOTI_MODE_BASIC = 0; public final static int NOTI_MODE_BIG_TEXT = 1; public final static int NOTI_MODE_BIG_PIC = 2; public final static int NOTI_MODE_INBOX = 3; public static void setNotification(int mode, Context mContext, Class mClass){ int mId = 1; String contentTitle = "Service Notification Test:"; String contentText = "Service Content"; String contentInfo = "Service Info"; String contentText2 = "Service Content2"; // Using NotificationCompat to create the notification builder can use // the new features introduced after API level 4 without compatibility // problem with lower API level NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(mContext) .setSmallIcon(R.drawable.ic_launcher) .setContentTitle(contentTitle) .setContentText(contentText) .setContentInfo(contentInfo); // A notification's big view appears only when the notification is expanded, // which happens when the notification is at the top of the notification // drawer, or when the user expands the notification with a gesture. // Expanded notifications are available starting with Android 4.1. switch (mode){ case NOTI_MODE_BIG_TEXT: NotificationCompat.BigTextStyle bigTextStyle = new NotificationCompat.BigTextStyle(); bigTextStyle.bigText("Big Text Line 1\nBig Text Line 2\nBig Text Line 3"); // summary is displayed replacing the position of contentText // if summary is not set, contentInfo will not be displayed too bigTextStyle.setSummaryText(contentText2); // Moves the big view style object into the notification object. mBuilder.setStyle(bigTextStyle); break; case NOTI_MODE_BIG_PIC: NotificationCompat.BigPictureStyle bigPicStyle = new NotificationCompat.BigPictureStyle(); Bitmap bm = BitmapFactory.decodeResource(mContext.getResources(), R.drawable.interact); bigPicStyle.bigPicture(bm); bigPicStyle.setSummaryText(contentText2); // Moves the big view style object into the notification object. mBuilder.setStyle(bigPicStyle); break; case NOTI_MODE_INBOX: NotificationCompat.InboxStyle inboxStyle = new NotificationCompat.InboxStyle(); String[] events = new String[4]; events[0] = "Inbox Line 1"; events[1] = "Inbox Line 2"; events[2] = "Inbox Line 3"; events[3] = "Inbox Line 4"; // Moves events into the big view for (int i=0; i < events.length; i++) { inboxStyle.addLine(events[i]); } inboxStyle.setSummaryText(contentText2); // Moves the big view style object into the notification object. mBuilder.setStyle(inboxStyle); break; case NOTI_MODE_BASIC: break; } // Creates an explicit intent for an Activity in your app Intent resultIntent = new Intent(mContext, mClass); // The stack builder object will contain an artificial back stack // for the started Activity // This ensures that navigating backward from the Activity leads out of // your application to the Home screen. TaskStackBuilder stackBuilder = TaskStackBuilder.create(mContext); // Adds the back stack for the Intent (but not the Intent itself) stackBuilder.addParentStack(mClass); // Adds the Intent that starts the Activity to the top of the stack stackBuilder.addNextIntent(resultIntent); // A PendingIntent is used to specify the action which should be performed // once the user select the notification. PendingIntent resultPendingIntent = stackBuilder.getPendingIntent( 0, PendingIntent.FLAG_UPDATE_CURRENT); mBuilder.setContentIntent(resultPendingIntent); // Just add a fake action here, max 3 mBuilder.addAction(R.drawable.ic_action_search, "Search", resultPendingIntent); mBuilder.addAction(R.drawable.ic_action_search, "Add", resultPendingIntent); mBuilder.addAction(R.drawable.ic_action_search, "Save", resultPendingIntent); // Hide the notification after its selected mBuilder.setAutoCancel(true); NotificationManager mNotificationManager = (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE); // mId allows you to update the notification later on. mNotificationManager.notify(mId, mBuilder.build()); } }
The MainActivity Class after restructure:
package com.example.sample_service_notification; import android.os.Bundle; import android.app.Activity; import android.view.Menu; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; public class MainActivity extends Activity { private Button btn_basic; private Button btn_bigtext; private Button btn_bigpic; private Button btn_inbox; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); findViews(); setListieners(); } @Override public boolean onCreateOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu.activity_main, menu); return true; } private void findViews(){ btn_basic = (Button) this.findViewById(R.id.btn_basic); btn_bigtext = (Button) this.findViewById(R.id.btn_bigtext); btn_bigpic = (Button) this.findViewById(R.id.btn_bigpic); btn_inbox = (Button) this.findViewById(R.id.btn_inbox); } private void setListieners(){ btn_basic.setOnClickListener(btn_basic_click); btn_bigtext.setOnClickListener(btn_bigtext_click); btn_bigpic.setOnClickListener(btn_bigpic_click); btn_inbox.setOnClickListener(btn_inbox_click); } private OnClickListener btn_basic_click = new OnClickListener() { public void onClick(View v) { // TODO Auto-generated method stub NotificationUtil.setNotification(NotificationUtil.NOTI_MODE_BASIC, MainActivity.this, MainActivity.class); } }; private OnClickListener btn_bigtext_click = new OnClickListener() { public void onClick(View v) { // TODO Auto-generated method stub NotificationUtil.setNotification(NotificationUtil.NOTI_MODE_BIG_TEXT, MainActivity.this, MainActivity.class); } }; private OnClickListener btn_bigpic_click = new OnClickListener() { public void onClick(View v) { // TODO Auto-generated method stub NotificationUtil.setNotification(NotificationUtil.NOTI_MODE_BIG_PIC, MainActivity.this, MainActivity.class); } }; private OnClickListener btn_inbox_click = new OnClickListener() { public void onClick(View v) { // TODO Auto-generated method stub NotificationUtil.setNotification(NotificationUtil.NOTI_MODE_INBOX, MainActivity.this, MainActivity.class); } }; }
We will then make use of Receiver and Service to create a "Periodic Service" which is bind with the Activity
Create a new MainService Class:
package com.example.sample_service_notification; import android.app.Service; import android.content.Intent; import android.os.Binder; import android.os.IBinder; import android.util.Log; public class MainService extends Service{ private String TAG = "MainService"; public int repeatTimes = 0; // MyBinder is used to bind the Service to the Activity public class MyBinder extends Binder { MainService getService() { return MainService.this; } } private final IBinder mBinder = new MyBinder(); @Override public IBinder onBind(Intent arg0) { //TODO for communication return IBinder implementation return mBinder; } public void onCreate(){ super.onCreate(); Log.d(TAG,"onCreate"); } @Override public int onStartCommand(Intent intent, int flags, int startId){ //TODO do something useful Log.d(TAG,"onStartCommand"); repeatTimes = repeatTimes + 1; NotificationUtil.setNotification(NotificationUtil.NOTI_MODE_BASIC, this, MainActivity.class); return Service.START_STICKY; } public void onDestroy(){ super.onDestroy(); Log.d(TAG,"onDestroy"); } }
<service android:name=".MainService" android:icon="@drawable/ic_launcher" android:label="@string/app_name" > </service>
Create a new StartServiceReceiver Class:
package com.example.sample_service_notification; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; public class StartServiceReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { // Used to receive message from ScheduleReceiver and then call the MainService // Act as the bridge between ScheduleReceiver and MainService Intent service = new Intent(context, MainService.class); context.startService(service); } }
<receiver android:name="StartServiceReceiver" ></receiver>
Create a new ScheduleReceiver Class:
package com.example.sample_service_notification; import java.util.Calendar; import android.app.AlarmManager; import android.app.PendingIntent; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; public class ScheduleReceiver extends BroadcastReceiver { // Restart service every 60 seconds private static final long REPEAT_TIME = 1000 * 60; @Override public void onReceive(Context context, Intent intent) { AlarmManager service = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); Intent i = new Intent(context, StartServiceReceiver.class); PendingIntent pending = PendingIntent.getBroadcast(context, 0, i, PendingIntent.FLAG_CANCEL_CURRENT); Calendar cal = Calendar.getInstance(); // Start 30 seconds after boot completed cal.add(Calendar.SECOND, 30); // // Fetch every 60 seconds // Use InexactRepeating instead of setRepeating as // InexactRepeating allows Android to optimize the energy consumption service.setInexactRepeating(AlarmManager.RTC_WAKEUP, cal.getTimeInMillis(), REPEAT_TIME, pending); // service.setRepeating(AlarmManager.RTC_WAKEUP, cal.getTimeInMillis(), // REPEAT_TIME, pending); } }
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" /> …… <receiver android:name="ScheduleReceiver" > <intent-filter> <action android:name="android.intent.action.BOOT_COMPLETED" /> </intent-filter> </receiver>
Finally modify the MainActivity Class to bind with the MainService:
package com.example.sample_service_notification; import android.os.Bundle; import android.os.IBinder; import android.app.Activity; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; import android.view.Menu; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; import android.widget.Toast; public class MainActivity extends Activity { private Button btn_basic; private Button btn_bigtext; private Button btn_bigpic; private Button btn_inbox; private Button btn_check_service_repeat; private MainService myMainService; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); findViews(); setListieners(); } @Override public boolean onCreateOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu.activity_main, menu); return true; } private void findViews(){ btn_basic = (Button) this.findViewById(R.id.btn_basic); btn_bigtext = (Button) this.findViewById(R.id.btn_bigtext); btn_bigpic = (Button) this.findViewById(R.id.btn_bigpic); btn_inbox = (Button) this.findViewById(R.id.btn_inbox); btn_check_service_repeat = (Button) this.findViewById(R.id.btn_check_service_repeat); } private void setListieners(){ btn_basic.setOnClickListener(btn_basic_click); btn_bigtext.setOnClickListener(btn_bigtext_click); btn_bigpic.setOnClickListener(btn_bigpic_click); btn_inbox.setOnClickListener(btn_inbox_click); btn_check_service_repeat.setOnClickListener(btn_check_service_repeat_click); } private OnClickListener btn_basic_click = new OnClickListener() { public void onClick(View v) { // TODO Auto-generated method stub NotificationUtil.setNotification(NotificationUtil.NOTI_MODE_BASIC, MainActivity.this, MainActivity.class); } }; private OnClickListener btn_bigtext_click = new OnClickListener() { public void onClick(View v) { // TODO Auto-generated method stub NotificationUtil.setNotification(NotificationUtil.NOTI_MODE_BIG_TEXT, MainActivity.this, MainActivity.class); } }; private OnClickListener btn_bigpic_click = new OnClickListener() { public void onClick(View v) { // TODO Auto-generated method stub NotificationUtil.setNotification(NotificationUtil.NOTI_MODE_BIG_PIC, MainActivity.this, MainActivity.class); } }; private OnClickListener btn_inbox_click = new OnClickListener() { public void onClick(View v) { // TODO Auto-generated method stub NotificationUtil.setNotification(NotificationUtil.NOTI_MODE_INBOX, MainActivity.this, MainActivity.class); } }; private OnClickListener btn_check_service_repeat_click = new OnClickListener() { public void onClick(View v) { // TODO Auto-generated method stub Toast.makeText(MainActivity.this, String.valueOf(myMainService.repeatTimes), Toast.LENGTH_SHORT).show(); } }; @Override protected void onResume() { super.onResume(); doBindService(); } @Override protected void onPause() { super.onPause(); unbindService(mConnection); } private ServiceConnection mConnection = new ServiceConnection() { public void onServiceConnected(ComponentName className, IBinder binder) { myMainService = ((MainService.MyBinder) binder).getService(); Toast.makeText(MainActivity.this, "Connected", Toast.LENGTH_SHORT).show(); } public void onServiceDisconnected(ComponentName className) { myMainService = null; Toast.makeText(MainActivity.this, "Disconnected", Toast.LENGTH_SHORT).show(); } }; private void doBindService() { //The activity binds itself to the service to access its data. bindService(new Intent(this, MainService.class), mConnection, Context.BIND_AUTO_CREATE); } }
Resulting Manifest.xml:
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.sample_service_notification" android:versionCode="1" android:versionName="1.0" > <uses-sdk android:minSdkVersion="8" android:targetSdkVersion="15" /> <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" /> <application android:icon="@drawable/ic_launcher" android:label="@string/app_name" android:theme="@style/AppTheme" > <activity android:name=".MainActivity" android:label="@string/title_activity_main" > <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> <service android:name=".MainService" android:icon="@drawable/ic_launcher" android:label="@string/app_name" > </service> <receiver android:name="ScheduleReceiver" > <intent-filter> <action android:name="android.intent.action.BOOT_COMPLETED" /> </intent-filter> </receiver> <receiver android:name="StartServiceReceiver" ></receiver> </application> </manifest>
Resulting activity_main.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/LinearLayout1" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" > <Button android:id="@+id/btn_basic" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="Basic" /> <Button android:id="@+id/btn_bigtext" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="Big Text" /> <Button android:id="@+id/btn_bigpic" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="Big Pic" /> <Button android:id="@+id/btn_inbox" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="Inbox" /> <Button android:id="@+id/btn_check_service_repeat" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="Check Service Repeat" /> </LinearLayout>
Last but not least, after installing the app (boot-up service), it will only be active after the device/emulator is rebooted
Result:
Wait for a while and you will see there is a notification icon appears in the notification bar
Click the "Check Service Repeat" button to see how many times the Service has run
Drag down the notification bar, notice that the time captured is 3:41 AM
Close the application, wait for a while and the notification icon appears again, the time captured now is 3:43 AM
沒有留言:
張貼留言