Xamarin.Android廣播接收器與綁定服務


一、前言

學習了前面的活動與服務后,你會發現服務對於活動而言似乎就是透明的,相反活動對於服務也是透明的,所以我們還需要一中機制能夠將服務和活動之間架起一座橋梁,通過本節的學習,你將會學到廣播與綁定服務,這兩種方式恰恰是解決上面問題的關鍵。

 

二、簡單的廣播接收器

實現一個最簡單的廣播接收器需要繼承BroadcastReceiver類,並且還要實現OnReceive方法,我們可以在項目中新建一個MainReceiver類,然后寫入如下代碼:

1     public class MainReceiver : BroadcastReceiver
2     {
3         public override void OnReceive(Context context, Intent intent)
4         {
5 
6         }
7 }

 上面其實已經實現了一個簡單的廣播接收器,並且可以使用。我們還需要注冊廣播接收器,否則廣播接收器就無法接收廣播,所以我們需要在MainActivity.cs中注冊這個廣播接收器。當然為了能夠接近現實,我們需要在OnResume中注冊,在OnPause中注銷。

首先我們在OnResume中注冊

1         protected override void OnResume()
2         {
3             base.OnResume();
4             receiver = new MainReceiver();
5             RegisterReceiver(receiver, new IntentFilter("xamarin-cn.main.receiver"));
6         }

 接着我們在OnPause中注銷

1         protected override void OnPause()
2         {
3             base.OnPause();
4             UnregisterReceiver(receiver);
5         }

 全部代碼如下所示

 1     [Activity(Label = "BroadcastStudy", MainLauncher = true, Icon = "@drawable/icon")]
 2     public class MainActivity : Activity
 3     {
 4         private MainReceiver receiver;
 5 
 6         protected override void OnCreate(Bundle bundle)
 7         {
 8             base.OnCreate(bundle);
 9             SetContentView(Resource.Layout.Main);
10         }
11 
12         protected override void OnResume()
13         {
14             base.OnResume();
15             receiver = new MainReceiver();
16             RegisterReceiver(receiver, new IntentFilter("xamarin-cn.main.receiver"));
17         }
18 
19         protected override void OnPause()
20         {
21             base.OnPause();
22             UnregisterReceiver(receiver);
23         }
24 }
View Code

 注冊好了廣播接收器,我們還需要一個能夠發送廣播的地方,既然我們說了這節重點解決的是服務與活動的通信,那么我們就實現一個服務來發送廣播。為了能夠貼近現實,我們的服務中將會新建一個線程,讓這個線程發送一個廣播給這個廣播接收器。

 1     [Service]
 2     public class MainService : Service
 3     {
 4         public override StartCommandResult OnStartCommand(Intent intent, StartCommandFlags flags, int startId)
 5         {
 6             new Thread(() =>
 7             {
 8                 Thread.Sleep(1000);
 9                 var sintent = new Intent("xamarin-cn.main.receiver");
10                 sintent.PutExtra("_str", "來自服務");
11                 SendBroadcast(sintent);
12             }).Start();
13             return StartCommandResult.Sticky;
14         }
15 
16         public override IBinder OnBind(Intent intent)
17         {
18             return null;
19         }
20 }

這里我們通過意圖傳遞了一個參數,而在服務中發送廣播的方法是SendBroadcast。其實我們可以看到在創建意圖的時候傳入了一個字符串,而這個字符串必須與注冊廣播接收器時指定的字符串一致,否則對應的廣播接收器是無法接收到這個廣播的,下面我們修改廣播接收器的OnReceive方法,以便獲取傳遞過來的字符串並顯示。

1         public override void OnReceive(Context context, Intent intent)
2         {
3             string str = intent.GetStringExtra("_str");
4             new Handler().Post(() =>
5             {
6                 Toast.MakeText(Application.Context, str, ToastLength.Long).Show();
7             });
8         }

其中我們通過意圖的GetXXXX方法獲取傳遞過來的參數,然后創建了一個Handler對象並使用Toast發送了一個提示,這里使用Handler是為了與UI線程同步。因為前面講過只用UI線程才能夠訪問控件等等對象,而這里並沒有RunOnUiThread方法,所以我們需要使用Handler對象的Post方法來實現。

 

最后有了服務還不行,我們還需要開啟這個服務。當然我們依然還是要在OnResume中開啟,在OnPause中暫停。

 1         protected override void OnResume()
 2         {
 3             base.OnResume();
 4             receiver = new MainReceiver();
 5             RegisterReceiver(receiver, new IntentFilter("xamarin-cn.main.receiver"));
 6             StartService(new Intent(this, typeof(MainService)));
 7         }
 8 
 9         protected override void OnPause()
10         {
11             base.OnPause();
12             UnregisterReceiver(receiver);
13             StopService(new Intent(this, typeof(MainService)));
14         }

最后我們運行之后的結果如下所示

 

三、服務向活動發送消息

上面的例子我們僅僅只是打通了服務與廣播接收器的通信,而我們今天的主題是服務與活動的雙向通信,但是為了能夠循序漸進學習,所以我們先學習了服務與廣播接收器怎么通信,而這節我們將學習廣播接收器如何與活動通信。

 

因為c#並沒有java的部分語言的特性,所以我們沒法直接通過匿名的方法創建一個繼承自BroadcastReceiver類的實例,所以我們需要先創建一個繼承自BroadcastReceiver的具體類,然后在其中定義活動需要響應的方法的委托(Action或者Func),這樣我們可以在實例化這個具體類的同時將活動中的方法賦給廣播接收器,這樣廣播接收器在OnReceive中就可以調用活動中的方法了,自然而言就打通了廣播接收器與活動的通信。當然還有其他的方法,希望讀者可以在留言中留下,以便更多的人進行學習。

首先修改MainReceiver類:

 1     public class MainReceiver : BroadcastReceiver
 2     {
 3         public Action<string> Alert;
 4 
 5         public override void OnReceive(Context context, Intent intent)
 6         {
 7             string str = intent.GetStringExtra("_str");
 8             if (Alert != null)
 9             {
10                 Alert(str);
11             }
12         }
13 }

在這里我們定義了一個委托(Action<string>  Alert)以便活動可以重寫,同時還修改了OnReceive中的代碼,從而使用活動的方法來顯示提示,有了接口之后,我們就可以回到活動中進行重寫了。因為廣播被實例化的步驟是在OnResume中,所以我們這里直接給出這個方法中的代碼(這里我們使用了一個TextView控件tv讀者可以需要自行添加下)。

 1         protected override void OnResume()
 2         {
 3             base.OnResume();
 4             receiver = new MainReceiver()
 5             {
 6                 Alert = (s) =>
 7                 {
 8                     RunOnUiThread(() =>
 9                     {
10                         tv.Text = s;
11                     });
12                 }
13             };
14             RegisterReceiver(receiver, new IntentFilter("xamarin-cn.main.receiver"));
15             StartService(new Intent(this, typeof(MainService)));
16         }

現在我們就打通了廣播接收器與活動的橋梁,如果有多個方法也是一樣的道理,我們現在運行程序可以發現一切正常,下面筆者還要介紹另一種使用接口的方法,首先我們需要一個接口去規定活動需要實現哪些方法,然后在初始化廣播接收器的同時將活動的實例賦廣播接收器的對應接口變量。下面我們將上面的例子改寫,先定義個含有Alert的接口。

1     public interface IMainInterface
2     {
3         void Alert(string s);
4 }

然后讓活動實現該接口

    public class MainActivity : Activity, IMainInterface
    {
        private MainReceiver receiver;
        private TextView tv;

        public void Alert(string s)
        {
            RunOnUiThread(() =>
            {
                tv.Text = s;
            });
        }

接着我們修改廣播接收器,公開一個該接收的屬性,一遍在廣播接收器被初始化的時候可以復制。

 1     public class MainReceiver : BroadcastReceiver
 2     {
 3         public IMainInterface mainInterface;
 4 
 5         public override void OnReceive(Context context, Intent intent)
 6         {
 7             string str = intent.GetStringExtra("_str");
 8             if (mainInterface != null)
 9             {
10                 mainInterface.Alert(str);
11             }
12         }
13 }

回到MainActivity中修改OnResume方法。

 1         protected override void OnResume()
 2         {
 3             base.OnResume();
 4             receiver = new MainReceiver()
 5             {
 6                 mainInterface = this
 7             };
 8             RegisterReceiver(receiver, new IntentFilter("xamarin-cn.main.receiver"));
 9             StartService(new Intent(this, typeof(MainService)));
10         }

最后效果一樣的,讀者可以根據實際的情況選擇。畢竟他們各自都有或多或少的缺點。

 

四、綁定服務

其實綁定服務就是將服務中的功能公開給活動,只有這樣活動才能調用服務中的方法。而這一過程需要經過一個綁定。首先我們需要一個繼承自Binder的類,這樣才能將服務通過接口傳遞給活動。以下為繼承自Binder的類,其中我們需要在初始化時將服務傳入,然后公開一個方法將服務的實例返回。

 1     public class MainBinder : Binder
 2     {
 3         MainService mainService;
 4 
 5         public MainBinder(MainService ms)
 6         {
 7             mainService = ms;
 8         }
 9 
10         public MainService GetService()
11         {
12             return mainService;
13         }
14 }

接下來我們打開MainService文件,實現OnBind方法,並將上面類返回。

 1     [Service]
 2     public class MainService : Service
 3     {
 4         public override StartCommandResult OnStartCommand(Intent intent, StartCommandFlags flags, int startId)
 5         {
 6             return StartCommandResult.Sticky;
 7         }
 8 
 9         public override IBinder OnBind(Intent intent)
10         {
11             return new MainBinder(this);
12         }
13     }

到此為止,服務這邊已經做好了准備。既然是綁定自然不能通過簡單的StartService方法開啟,因為我們還需要OnBind返回的接口,否則活動無法與服務溝通。這就需要在活動中通過BindService方法進行綁定,但是該方法還需要一個實現了IserviceConnection接口的類,因為通過BindService方法進行綁定的操作是異步的,也就意味着不會阻塞當前調用該方法的線程,而是在服務成功開啟並並且OnBind方法返回接口后會回調IserviceConnection中的方法,我們可以看下該接口的方法。

1     public interface IServiceConnection : IJavaObject, IDisposable
2     {
3         void OnServiceConnected(ComponentName name, IBinder service);
4         void OnServiceDisconnected(ComponentName name);
5 }

關於接口的方法,大致的解釋如下:

OnServiceConnected:當服務中的OnBind方法返回接口后將回調該方法,並且通過service參數將OnBind返回的值傳遞給這個方法。

OnServiceDisconnected:當服務被關閉或者主動斷開連接后回調該方法,如果我們利用這個方法重新恢復連接,或者發出異常並關閉對應的活動。

 

下面我們實現該接口

 1     public class MainServiceConnection : Java.Lang.Object , IServiceConnection
 2     {
 3         public void OnServiceConnected(ComponentName name, Android.OS.IBinder service)
 4         {
 5 
 6         }
 7 
 8         public void OnServiceDisconnected(ComponentName name)
 9         {
10 
11         }
12 }

 

這里我們沒有實現任何代碼,該類與活動還沒有關聯起來,所以我們需要在活動中新建一個公開的變量去保存服務的接口。

1     [Activity(Label = "BroadcastStudy", MainLauncher = true, Icon = "@drawable/icon")]
2     public class MainActivity : Activity
3     {
4         private TextView tv;
5         public MainBinder mainBinder;

接着我們就可以實現MainServiceConnection類了。

 1     public class MainServiceConnection : Java.Lang.Object , IServiceConnection
 2     {
 3         MainActivity mainActivity;
 4         public MainServiceConnection(MainActivity ma)
 5         {
 6             mainActivity = ma;
 7         }
 8 
 9         public void OnServiceConnected(ComponentName name, Android.OS.IBinder service)
10         {
11             mainActivity.mainBinder = (MainBinder)service;
12         }
13 
14         public void OnServiceDisconnected(ComponentName name)
15         {
16             mainActivity.mainBinder = null;
17         }
18 }

最后我們在活動中就可以進行綁定了。

 1     [Activity(Label = "BroadcastStudy", MainLauncher = true, Icon = "@drawable/icon")]
 2     public class MainActivity : Activity
 3     {
 4         private IServiceConnection serviceConnection;
 5         private TextView tv;
 6         public MainBinder mainBinder;
 7 
 8 
 9         protected override void OnCreate(Bundle bundle)
10         {
11             base.OnCreate(bundle);
12             SetContentView(Resource.Layout.Main);
13             tv = FindViewById<TextView>(Resource.Id.textView1);
14         }
15 
16         protected override void OnResume()
17         {
18             base.OnResume();
19             serviceConnection = new MainServiceConnection(this);
20             BindService(new Intent(this, typeof(MainService)), serviceConnection, Bind.AutoCreate);
21         }
22 
23         protected override void OnPause()
24         {
25             base.OnPause();
26             UnbindService(serviceConnection);
27         }
28 }
View Code

通過上面的步驟我們還不能看到實際的效果,下面我們需要在服務中實現一個簡單的方法,只是返回一段字符串。

1         public string GetString()
2         {
3             return "來自服務";
4         }

然后在Main.axml中拖放一個按鈕,並在活動中進行綁定。

 1         protected override void OnCreate(Bundle bundle)
 2         {
 3             base.OnCreate(bundle);
 4             SetContentView(Resource.Layout.Main);
 5             Button btn = FindViewById<Button>(Resource.Id.button1);
 6             btn.Click += (e, s) =>
 7             {
 8                 if (mainBinder != null)
 9                 {
10                     string str = mainBinder.GetService().GetString();
11                     Toast.MakeText(this, str, ToastLength.Long).Show();
12                 }
13             };
14         }

這樣我們就完成了活動調用服務中的方法,但是現實開發中。如果是耗時的任務。都是活動調用服務公開的方法后立即返回,然后服務在完成之后通過廣播將處理的結果返回給活動,整個過程都是異步的。

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM