Android學習筆記(九)一個例子弄清Service與Activity通信


  上一篇博文主要整理了Service的創建、綁定過程,本篇主要整理一下Service與Activity的通信方式。包括在啟動一個Service時向它傳遞數據、怎樣改變運行中的Service中得數據和偵聽Service內數據的改變。

  本篇將寫一個demo來說明以下三個問題:

  1、怎樣在啟動一個Service時向它傳遞數據

  關鍵點:Intent傳值,onStartCommand()接收。

  2、怎樣向運行的Service中同步數據

  關鍵點:通過onBind()獲取Service實例,然后再調用Binder中的相關方法。

  3、怎樣偵聽Service中數據變化

  關鍵點:通過回調函數達到目的。

 

一、准備Service

  先貼出Service的詳細代碼,然后再慢慢分析

 1 public class MyService extends Service {
 2     private String data = "默認消息";
 3     private boolean serviceRunning = false;
 4     
 5     // 必須實現的方法,用於返回Binder對象
 6     @Override
 7     public IBinder onBind(Intent intent) {
 8         System.out.println("--onBind()--");
 9         return new MyBinder();
10     }
11 
12     public class MyBinder extends Binder {
13         MyService getService() {
14             return MyService.this;
15         }
16 
17         public void setData(String data) {
18             MyService.this.data = data;
19         }
20     }
21 
22     // 創建Service時調用該方法,只調用一次
23     @Override
24     public void onCreate() {
25         super.onCreate();
26         System.out.println("--onCreate()--");
27         serviceRunning = true;
28         new Thread() {
29             @Override
30             public void run() {
31                 int n = 0;
32                 while (serviceRunning) {
33                     n++;
34                     String str = n + data;
35                     System.out.println(str);
36                     if (dataCallback != null) {
37                         dataCallback.dataChanged(str);
38                     }
39                     try {
40                         sleep(1000);
41                     } catch (InterruptedException e) {
42                         e.printStackTrace();
43                     }
44                 }
45             };
46         }.start();
47     }
48 
49     // 每次啟動Servcie時都會調用該方法
50     @Override
51     public int onStartCommand(Intent intent, int flags, int startId) {
52         System.out.println("--onStartCommand()--");
53         data = intent.getStringExtra("data");
54         return super.onStartCommand(intent, flags, startId);
55     }
56 
57     // 解綁Servcie調用該方法
58     @Override
59     public boolean onUnbind(Intent intent) {
60         System.out.println("--onUnbind()--");
61         return super.onUnbind(intent);
62     }
63 
64     // 退出或者銷毀時調用該方法
65     @Override
66     public void onDestroy() {
67         serviceRunning = false;
68         System.out.println("--onDestroy()--");
69         super.onDestroy();
70     }
71 
72     DataCallback dataCallback = null;
73 
74     public DataCallback getDataCallback() {
75         return dataCallback;
76     }
77 
78     public void setDataCallback(DataCallback dataCallback) {
79         this.dataCallback = dataCallback;
80     }
81 
82     // 通過回調機制,將Service內部的變化傳遞到外部
83     public interface DataCallback {
84         void dataChanged(String str);
85     }
86 
87 }

  代碼分析:我們都知道,通過startService啟動一個Service時,Service會調用生命周期函數onStartCommand(),在代碼中創建一個Service,在onStartCommand()方法中獲取從Activity傳遞過來的數據,並在Service的onCreate()方法中開啟一個新的線程,使其循環調用回調函數,以達到通知外界信息改變的目的。並在Service中通過Binder類,將Service與Activity鏈接起來,以實現信息同步。

二、准備布局文件

  布局文件比較簡單,直接貼出,就不分析了,activity_main.xml如下:

 1 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
 2     xmlns:tools="http://schemas.android.com/tools"
 3     android:layout_width="match_parent"
 4     android:layout_height="match_parent"
 5     android:orientation="vertical">
 6 
 7     <TextView
 8         android:id="@+id/tv_out"
 9         android:layout_width="fill_parent"
10         android:layout_height="wrap_content"
11         android:text="顯示區域" />
12     
13     <EditText
14         android:id="@+id/et_data"
15         android:layout_width="match_parent"
16         android:layout_height="wrap_content"
17         android:ems="10" >
18 
19         <requestFocus />
20     </EditText>
21     
22     <Button
23         android:id="@+id/btn_start_service"
24         android:layout_width="fill_parent"
25         android:layout_height="wrap_content"
26         android:text="startService" />
27     
28     <Button
29         android:id="@+id/btn_stop_service"
30         android:layout_width="fill_parent"
31         android:layout_height="wrap_content"
32         android:text="stopService" />
33     
34     <Button
35         android:id="@+id/btn_bind_service"
36         android:layout_width="fill_parent"
37         android:layout_height="wrap_content"
38         android:text="bindService" />
39     
40     <Button
41         android:id="@+id/btn_unbind_service"
42         android:layout_width="fill_parent"
43         android:layout_height="wrap_content"
44         android:text="unbindService" />
45     
46     <Button
47         android:id="@+id/btn_sync_data"
48         android:layout_width="fill_parent"
49         android:layout_height="wrap_content"
50         android:text="同步數據" />
51 
52 </LinearLayout>

三、准備Activity

   MainActivity代碼如下:

  1 public class MainActivity extends Activity implements OnClickListener {
  2 
  3     private Intent intent = null;
  4     private Button btn_start_service;
  5     private Button btn_stop_service;
  6     private Button btn_bind_service;
  7     private Button btn_unbind_service;
  8     private Button btn_sync_data;
  9     private EditText et_data;
 10     private TextView tv_out;
 11     MyServiceConn myServiceConn;
 12     MyService.MyBinder binder = null;
 13 
 14     @Override
 15     protected void onCreate(Bundle savedInstanceState) {
 16         super.onCreate(savedInstanceState);
 17         setContentView(R.layout.activity_main);
 18         intent = new Intent(this, MyService.class);
 19         myServiceConn = new MyServiceConn();
 20         setOnClick();
 21     }
 22     
 23     @Override
 24     public void onClick(View v) {
 25         switch (v.getId()) {
 26         case R.id.btn_start_service:
 27             //用intent啟動Service並傳值
 28             intent.putExtra("data", et_data.getText().toString());
 29             startService(intent);
 30             break;
 31         case R.id.btn_stop_service:
 32             //停止Service
 33             stopService(intent);
 34             break;
 35         case R.id.btn_bind_service:
 36             //綁定Service
 37             bindService(intent, myServiceConn, Context.BIND_AUTO_CREATE);
 38             break;
 39         case R.id.btn_unbind_service:
 40             //解綁Service
 41             if (binder != null) {
 42                 unbindService(myServiceConn);
 43             }
 44             break;
 45         case R.id.btn_sync_data:
 46             //注意:需要先綁定,才能同步數據
 47             if (binder != null) {
 48                 binder.setData(et_data.getText().toString());
 49             }
 50             break;
 51         default:
 52             break;
 53         }
 54     }
 55 
 56     class MyServiceConn implements ServiceConnection {
 57         // 服務被綁定成功之后執行
 58         @Override
 59         public void onServiceConnected(ComponentName name, IBinder service) {
 60             // IBinder service為onBind方法返回的Service實例
 61             binder = (MyService.MyBinder) service;
 62             binder.getService().setDataCallback(new MyService.DataCallback() {
 63                 //執行回調函數
 64                 @Override
 65                 public void dataChanged(String str) {
 66                     Message msg = new Message();
 67                     Bundle bundle = new Bundle();
 68                     bundle.putString("str", str);
 69                     msg.setData(bundle);
 70                     //發送通知
 71                     handler.sendMessage(msg);
 72                 }
 73             });
 74         }
 75 
 76         @SuppressLint("HandlerLeak") 
 77         Handler handler = new Handler() {
 78             public void handleMessage(android.os.Message msg) {
 79                 //在handler中更新UI
 80                 tv_out.setText(msg.getData().getString("str"));
 81             };
 82         };
 83 
 84         // 服務奔潰或者被殺掉執行
 85         @Override
 86         public void onServiceDisconnected(ComponentName name) {
 87             binder = null;
 88         }
 89     }
 90     
 91     private void loadUI() {
 92         btn_start_service = (Button) findViewById(R.id.btn_start_service);
 93         btn_stop_service = (Button) findViewById(R.id.btn_stop_service);
 94         btn_bind_service = (Button) findViewById(R.id.btn_bind_service);
 95         btn_unbind_service = (Button) findViewById(R.id.btn_unbind_service);
 96         btn_sync_data = (Button) findViewById(R.id.btn_sync_data);
 97         et_data = (EditText) findViewById(R.id.et_data);
 98         tv_out = (TextView) findViewById(R.id.tv_out);
 99     }
100 
101     private void setOnClick() {
102         loadUI();
103         btn_start_service.setOnClickListener(this);
104         btn_stop_service.setOnClickListener(this);
105         btn_bind_service.setOnClickListener(this);
106         btn_unbind_service.setOnClickListener(this);
107         btn_sync_data.setOnClickListener(this);
108     }
109 
110 }

   代碼分析:

  1、加載UI,初始化變量啥的跳過了,主要說一下關鍵代碼,在第28代碼中,與啟動一個Activity類似,通過Intent想要啟動的Service傳遞參數。

  2、在37行通過bindService綁定Service,然后在ServiceConnection中獲取Service類中onBind方法返回的實例,獲取實例Service實例后,我們就可以通過調用Service中MyBinder的setData()方法對Service進行同步數據,如48行所示。

  3、整個過程,在Service的onCreate方法中都會循環調用回調函數,同時我們在MainActivity中重寫回調方法以實現更新UI。

四、測試

  1、啟動示例后,在輸入框輸入你好,然后點擊startService,界面和對應的日志如下:

    

  看了下面的代碼后就會知道,此時因為沒有綁定service,所以辦法執行回調函數更新UI,所以顯示區域沒有更新。

  2、點擊bindService后,界面如下:

   

  當執行bindService后,在ServiceConnection方法中就會執行執行回調函數更新UI,此時顯示區域開始更新。

  3、改變輸入框內容,點擊同步數據,界面和對應的日志如下:

   

  因本人水平有限,如在文中發現錯誤或者描述不當的地方,敬請指正,感激不盡!

  聲明:歡迎轉載,轉載是請注明本文鏈接,謝謝!


免責聲明!

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



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