原文:Xamarin.Android開發實踐(八)html
前面咱們已經學了關於服務的不少知識,可是對於真實的開發那些遠遠不夠,經過這節咱們將學習其餘類型的服務,好比前臺服務、IntentService和消息服務。下面咱們開始進入正題。android
顧名思義,就是擁有前臺的優先等級。固然服務仍是不可見的。由於前面咱們介紹過 Android系統會在低內存的狀況下將一些長時間不用的應用關閉,若是仍是不夠,那麼就會經過關閉服務服務來達到目的,然而對於某些應用而言,這樣將會 影響用戶的正常使用。好比聽音樂,咱們基本上都會打開應用選擇歌曲後將應用置爲後臺。可是你會發現通知欄中會存在這個通知而且沒法移除,只有正確的退出這 個應用了纔會消失,而這節咱們就要實現這個功能。ide
首先咱們必需要用一個通知,經過這個通知咱們的服務纔可以變成前臺服務,這裏咱們新建一個名爲ForegroundService的服務,而後重寫OnStartCommand方法。post
1 public override StartCommandResult OnStartCommand(Intent intent, StartCommandFlags flags, int startId) 2 { 3 var notify = new Notification(Resource.Drawable.Icon, "前臺服務"); 4 var activityIntent = new Intent(this, typeof(MainActivity)); 5 var activityPintent = PendingIntent.GetActivity(this, 0, activityIntent, PendingIntentFlags.UpdateCurrent); 6 notify.SetLatestEventInfo(this, "標題", "內容", activityPintent); 7 StartForeground((int)NotificationFlags.ForegroundService, notify); 8 return StartCommandResult.Sticky; 9 }
不少代碼都是咱們在討論通知的時候都已經掌握的了,既然是前臺服務,天然最後發送這個方法是不一樣的,咱們須要使用服務的StartForeground來發送這個通知,同時第一個參數也要設置爲前臺服務,這樣咱們就能夠看到如圖的結果了(須要在MainActivity的OnCreate方法中開啓該服務)。學習
雖然已是一個前臺服務了,可是咱們只能經過服務不斷的更新這個通知,而沒法接收用戶的事件,下面咱們還要實現一個自定義界面的通知,上面有一個Text和兩個Button用戶點擊不一樣的按鈕後將由服務去更新通知,從而改變Text中的值。this
首先咱們在Resources/layout/下新建一個NotificationLayout視圖,並在其中寫入以下的xml標記。url
1 <?xml version="1.0" encoding="utf-8"?> 2 <RelativeLayout xmlns:p1="http://schemas.android.com/apk/res/android" 3 p1:minWidth="25px" 4 p1:minHeight="25px" 5 p1:layout_width="match_parent" 6 p1:layout_height="match_parent" 7 p1:id="@+id/relativeLayout1"> 8 <TextView 9 p1:text="0" 10 p1:textAppearance="?android:attr/textAppearanceLarge" 11 p1:layout_width="wrap_content" 12 p1:layout_height="match_parent" 13 p1:id="@+id/textView1" /> 14 <Button 15 p1:text="顯示1" 16 p1:layout_width="wrap_content" 17 p1:layout_height="match_parent" 18 p1:layout_toRightOf="@id/textView1" 19 p1:id="@+id/button1" /> 20 <Button 21 p1:text="顯示2" 22 p1:layout_width="wrap_content" 23 p1:layout_height="match_parent" 24 p1:layout_toRightOf="@id/button1" 25 p1:id="@+id/button2" /> 26 </RelativeLayout>
打開ForegroundService並在其中新建一個CreateNotify方法,並在其中寫入以下代碼。spa
1 public Notification CreateNotify(string text) 2 { 3 notify = new Notification(Resource.Drawable.Icon, "前臺服務"); 4 var sintent = new Intent(this, typeof(MainActivity)); 5 sintent.SetFlags(ActivityFlags.LaunchedFromHistory); 6 notify.ContentView = new RemoteViews(PackageName, Resource.Layout.NotificationLayout); 7 notify.ContentIntent = PendingIntent.GetActivity(this, 0, sintent, PendingIntentFlags.NoCreate); 8 9 var btn1Intent = new Intent(this, typeof(ForegroundService)); 10 btn1Intent.PutExtra("showBtn1", true); 11 var btn1Pintent = PendingIntent.GetService(this, 0, btn1Intent, PendingIntentFlags.UpdateCurrent); 12 notify.ContentView.SetOnClickPendingIntent(Resource.Id.button1, btn1Pintent); 13 14 var btn2Intent = new Intent(this, typeof(ForegroundService)); 15 btn2Intent.PutExtra("showBtn2", true); 16 var btn2Pintent = PendingIntent.GetService(this, 1, btn2Intent, PendingIntentFlags.UpdateCurrent); 17 notify.ContentView.SetOnClickPendingIntent(Resource.Id.button2, btn2Pintent); 18 19 notify.ContentView.SetTextViewText(Resource.Id.textView1, text); 20 return notify; 21 }
這裏須要說明下,一旦通知發送出去了咱們是沒法同ContentView的Set去修改控件的,只能從新發送這個同時去更新舊的通知,因此筆者才須要一個單獨的方法負責建立通知。上面的代碼咱們以前都已經學習過了,不理解的能夠看這篇文件《Xamarin.Android通知詳解》。筆者設置按鈕的點擊事件是打開服務自己,同時還經過Intent傳遞了一個參數,由於後面咱們須要經過這些參數去區分哪一個按鈕按下了,同時還要注意PendingIntent的GetService方法的第二個參數,咱們二者都是0那麼會形成按下按鈕1和按紐2都傳遞一樣的參數。下面咱們在OnStartCommand中實現響應。線程
1 public override StartCommandResult OnStartCommand(Intent intent, StartCommandFlags flags, int startId) 2 { 3 if (notify == null) 4 { 5 notify = CreateNotify("初始化"); 6 StartForeground((int)NotificationFlags.ForegroundService, notify); 7 } 8 bool isBtn1Click = intent.GetBooleanExtra("showBtn1", false); 9 bool isBtn2Click = intent.GetBooleanExtra("showBtn2", false); 10 if (isBtn1Click) 11 { 12 if (notify != null) 13 { 14 notify = CreateNotify("來自按鈕1"); 15 StartForeground((int)NotificationFlags.ForegroundService, notify); 16 } 17 } 18 else if (isBtn2Click) 19 { 20 if (notify != null) 21 { 22 notify = CreateNotify("來自按鈕2"); 23 StartForeground((int)NotificationFlags.ForegroundService, notify); 24 } 25 } 26 return StartCommandResult.Sticky; 27 }
能夠看到咱們經過GetBooleanExtra獲取了經過意圖傳遞的參數,固然筆者這裏的用法不一樣於前面的方式,我還傳入了第二個參數,這樣作的目的就是在乎圖中不存在該值的時候將會把第二參數返回,下面就是進行不一樣的判斷從而更新通知。3d
最終運行結果以下所示:
按下「顯示1」後
按下「顯示2」後
至此咱們就完成了前臺服務的學習。
不少時候咱們都須要利用服務進行耗時的操做,勢必須要建立新的線程去處理。可是普通的Service並不會主動建立而須要開發者自行在OnStartCommand中去建立,爲此就繁衍出了IntentService類,它會爲咱們建立好線程去執行咱們的代碼,從而避免一些代碼。可是咱們不能重寫OnStartCommand方法而應該是OnHandleIntent方法。好比下面的代碼。
1 [Service] 2 public class MainIntentService : IntentService 3 { 4 protected override void OnHandleIntent(Android.Content.Intent intent) 5 { 6 Thread.Sleep(1000); 7 Toast.MakeText(this, "來自新線程" , ToastLength.Long).Show(); 8 } 9 }
經過下面的截圖咱們能夠看到OnHandleIntent中執行的代碼是新建的一個線程
關於IntentService的使用很是簡單。
上一節關於綁定服務的學習中,活動必須確切的知道服務的類型才能使用,這樣就加大了他們之間的耦合度,而經過本節咱們將會學習如何經過消息機制將他們解耦,首先咱們須要理解Handler類,它將會負責處理髮送過來的消息,咱們須要繼承該類,並重寫HandleMessage方法,咱們新建一個MainHandler類並繼承該類,而後重寫。
1 public class MainHandler : Handler 2 { 3 public override void HandleMessage(Message msg) 4 { 5 Toast.MakeText(Application.Context, "接收到的消息的what爲" + msg.What.ToString() + " 內容爲" + msg.Data.GetString("_str"), ToastLength.Short).Show(); 6 } 7 }
這裏咱們僅僅只是簡單的輸出了消息的類型以及消息傳遞的參數,下面咱們還須要一個服務將這個消息傳遞給活動。
1 [Service] 2 public class MessengerService : Service 3 { 4 Messenger messenger; 5 6 public MessengerService() 7 { 8 messenger = new Messenger(new MainHandler()); 9 } 10 11 public override Android.OS.IBinder OnBind(Android.Content.Intent intent) 12 { 13 return messenger.Binder; 14 } 15 }
這裏咱們還須要Messenger去封裝MainHandler,由於MainHandler是沒法在OnBind中直接返回的,只有Messenger的Binder屬性能夠,天然活動那邊就須要接收這個接口,下面是IserviceConnection的實現。
1 public class MessengerServiceConnection : Java.Lang.Object , IServiceConnection 2 { 3 MainActivity mainActivity; 4 5 public MessengerServiceConnection(MainActivity ma) 6 { 7 mainActivity = ma; 8 } 9 10 public void OnServiceConnected(ComponentName name, Android.OS.IBinder service) 11 { 12 mainActivity.messenger = new Messenger(service); 13 } 14 15 public void OnServiceDisconnected(ComponentName name) 16 { 17 mainActivity.messenger.Dispose(); 18 mainActivity.messenger = null; 19 } 20 }
這裏的方式依然是使用以前咱們講述綁定服務時候的方法,只是在咱們接收接口的時候是用Messenger的去封裝的,這樣就統一了。咱們的活動只要有Messenger,而且對應的服務都知足這個接口那麼咱們的活動就能夠靈活的綁定任意服務,使用他們的功能了,最後是MainActivity的代碼(須要在Main.axml中拖拽兩個按鈕,以便發送消息給服務)。
1 [Activity(Label = "OtherService", MainLauncher = true, Icon = "@drawable/icon")] 2 public class MainActivity : Activity 3 { 4 public Messenger messenger; 5 6 protected override void OnCreate(Bundle bundle) 7 { 8 base.OnCreate(bundle); 9 SetContentView(Resource.Layout.Main); 10 BindService(new Intent(this, typeof(MessengerService)), new MessengerServiceConnection(this), Bind.AutoCreate); 11 Button btn1 = FindViewById<Button>(Resource.Id.button1); 12 btn1.Click += (e, s) => 13 { 14 Message msg = Message.Obtain(); 15 Bundle b = new Bundle(); 16 b.PutString("_str", "消息1"); 17 msg.Data = b; 18 msg.What = 1; 19 messenger.Send(msg); 20 }; 21 22 Button btn2 = FindViewById<Button>(Resource.Id.button2); 23 btn2.Click += (e, s) => 24 { 25 Message msg = Message.Obtain(); 26 Bundle b = new Bundle(); 27 b.PutString("_str", "消息2"); 28 msg.Data = b; 29 msg.What = 2; 30 messenger.Send(msg); 31 }; 32 } 33 }
惟一要說的就是發送消息,咱們須要實例化Messager(不是Messenger),設置它的what,若是咱們還須要傳遞更多的參數咱們能夠實例化一個Bundle,而後經過其PutXXX方法賦值,最後賦給Message的Data類型,最後要經過Messenger實例的Send方法發送這個消息,那麼MainHandler就能夠處理這個消息了。
下面是實際的運行圖。
點擊「發送消息1」按鈕後
點擊「發送消息2」按鈕後