(由於對java的回調機制和線程理解的不夠透徹,所以這塊內容我理解了好久,尤其是綁定服務傳遞數據,一句一句寫一句一句看,對我來說挺難理解的。以后還要多看幾遍--!)
既然單純的啟動或跳轉activity沒有意義,那么同樣的,單純的啟動或綁定service也是無意義的,實際應用中常常要攜帶數據啟動service或綁定service。
1.啟動service並傳遞數據
(1)新建一個Service:MyService
(2)在布局中添加兩個按鈕,啟動服務和停止服務,和一個EditText,用戶傳遞數據
(3)給兩個按鈕添加監聽器,分別執行startService方法和stopService方法
(4)在MyService類中onCreate方法添加一個控制台輸出語句,便於我們看到服務的狀態和數據的傳遞效果。
(5)在開始按鈕的onClick方法中添加intent.putExtra("data",etData.getText().toString()),用於在activity中初始化和傳遞數據。
(6)在MyService類中重寫onStartCommand方法,其中的intent參數用於接收activity傳來的數據。intent.getStringExtra("data");
此時運行程序,點擊啟動服務,會在控制台輸出界面輸入框中的數據,更改數據再次點擊啟動服務,控制台輸出的數據也隨之改變。點擊停止服務,控制台停止輸出。
代碼如下:
MyService.java:
public class MyService extends Service { private boolean running = false; private String data = "這是默認信息"; public MyService() { } @Override public IBinder onBind(Intent intent) { // TODO: Return the communication channel to the service. throw new UnsupportedOperationException("Not yet implemented"); } @Override public int onStartCommand(Intent intent, int flags, int startId) { data = intent.getStringExtra("data"); return super.onStartCommand(intent, flags, startId); } @Override public void onCreate() { super.onCreate(); running = true; new Thread() { @Override public void run() { super.run(); while(running){ System.out.println(data); try { sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } }.start(); } @Override public void onDestroy() { super.onDestroy(); running = false; } }
MainActivity.java:
import android.os.Bundle; import android.support.v7.widget.SwitchCompat; import android.view.View; import android.widget.EditText; import layout.MyService; public class MainActivity extends AppCompatActivity implements View.OnClickListener{ private EditText etData; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); findViewById(R.id.btnStartSevice).setOnClickListener(this); findViewById(R.id.btnStopSevice).setOnClickListener(this); etData = (EditText) findViewById(R.id.etData); } @Override public void onClick(View view) { switch (view.getId()) { case R.id.btnStartSevice: Intent i = new Intent(this, MyService.class); i.putExtra("data",etData.getText().toString()); startService(i); break; case R.id.btnStopSevice: stopService(new Intent(this,MyService.class)); break; } } }
activity_main.xml:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:paddingBottom="@dimen/activity_vertical_margin" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" android:orientation="vertical" tools:context="com.example.lzc.connectservice.MainActivity"> <EditText android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="默認信息" android:id="@+id/etData"/> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Hello World!" /> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="啟動服務" android:id="@+id/btnStartSevice" /> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="停止服務" android:id="@+id/btnStopSevice" /> </LinearLayout>
2.綁定服務並傳遞數據(執行服務的內部代碼)<由於偵聽服務狀態也包括執行服務內部代碼,所以第二部分和第三部分的代碼在第三部分下面一起貼出來>
(1)在布局中添加三個按鈕,綁定服務、解除綁定服務、同步數據,同步數據按鈕是用來同步activity傳到service的數據。
(2)給綁定服務和解除綁定服務按鈕添加監聽器,執行以下代碼
綁定服務:bindService(new Intent(this,MyService.class),this, Context.BIND_AUTO_CREATE);
解除綁定服務:unbindService(this);
(3)同時實現兩個方法onServiceConnected()和onServiceDisconnectid();
(4)在MyService類中添加一個Binder 類,該類內添加一個成員方方法setData()用來實時更改數據。
public class Binder extends android.os.Binder{ public void setData(String data){ MyService.this.data = data; } }
(5)在MyService類中的onBind方法中添加return new Binder();返回上一步新建的類的一個對象作為一個activity與service綁定的紐帶。
(6)回到MainActivity.java,創建一個binder:MyService.Binder binder = null;
(7)在onServiceConnected()方法中添加 binder = (MyService.Binder) iBinder;
(8)在同步數據按鈕的onClick()方法中執行以下代碼:
if(binder!=null){ binder.setData(etData.getText().toString()); }
至此完成綁定服務並傳遞數據。這樣的用法好處是不用每次同步數據時都發送一個intent,而是直接通過調用方法來同步數據,保證了代碼的高效性,也很快捷。
3.綁定服務並傳遞數據(偵聽服務內部狀態)
利用java的回調機制,從activity傳遞數據到service,service接到數據或數據改變時回調回來呈現在activity中
(1)在MyService類中添加一個CallBack接口,添加抽象方法onDataChange()。
(2)在MyService類中創建CallBack的對象,並添加get和set方法。
(3)在binder類中添加一個getService()方法,返回MyService.this.
(4)回到外部的MainActivity,布局添加一個TextView用於顯示服務的狀態。
(5)在onServiceConnected方法中添加binder.getService().setCallback(),給service添加回調函數,並且實現onDataChange方法,把數據通過Message傳給Handler。
(6)在MainActivity中添加一個Handler(由於安全機制,安卓的UI線程不允許隨意被調用。所以只能用Handler來改變UI),在Handler中改變TextView的值。
所有的代碼如下:
MyService.java:
import android.app.Service; import android.content.Intent; import android.os.Binder; import android.os.IBinder; public class MyService extends Service { private boolean running = false; private String data = "這是默認信息"; public MyService() { } @Override public IBinder onBind(Intent intent) { return new Binder(); } public class Binder extends android.os.Binder{ public void setData(String data){ MyService.this.data = data; } public MyService getService(){ return MyService.this; } } @Override public int onStartCommand(Intent intent, int flags, int startId) { data = intent.getStringExtra("data"); return super.onStartCommand(intent, flags, startId); } @Override public void onCreate() { super.onCreate(); running = true; new Thread() { @Override public void run() { super.run(); int i = 0; while(running){ i++; String str = i+":"+data; System.out.println(i+":"+data); if(callback!=null){ callback.onDataChange(str); } try { sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } }.start(); } @Override public void onDestroy() { super.onDestroy(); running = false; } private CallBack callback = null; public void setCallback(CallBack callback) { this.callback = callback; } public CallBack getCallback() { return callback; } public static interface CallBack{ void onDataChange(String data); } }
activity_main.xml和MainActivity:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:paddingBottom="@dimen/activity_vertical_margin" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" android:orientation="vertical" tools:context="com.example.lzc.connectservice.MainActivity"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="New Text" android:id="@+id/tvOut" /> <EditText android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="默認信息" android:id="@+id/etData"/> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Hello World!" /> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="啟動服務" android:id="@+id/btnStartSevice" /> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="停止服務" android:id="@+id/btnStopSevice" /> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="綁定服務" android:id="@+id/btnBindService" /> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="解除綁定服務" android:id="@+id/btnUnbindService" /> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="同步數據" android:id="@+id/btnSyncData" /> </LinearLayout>
import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; import android.os.Handler; import android.os.IBinder; import android.os.Message; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.support.v7.widget.SwitchCompat; import android.view.View; import android.widget.EditText; import android.widget.TextView; import layout.MyService; public class MainActivity extends AppCompatActivity implements View.OnClickListener, ServiceConnection { private EditText etData; private TextView tvOut; private MyService.Binder binder = null; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); etData = (EditText) findViewById(R.id.etData); findViewById(R.id.btnStartSevice).setOnClickListener(this); findViewById(R.id.btnStopSevice).setOnClickListener(this); findViewById(R.id.btnBindService).setOnClickListener(this); findViewById(R.id.btnUnbindService).setOnClickListener(this); findViewById(R.id.btnSyncData).setOnClickListener(this); tvOut = (TextView) findViewById(R.id.tvOut); } @Override public void onClick(View view) { switch (view.getId()) { case R.id.btnStartSevice: Intent i = new Intent(this, MyService.class); i.putExtra("data",etData.getText().toString()); startService(i); break; case R.id.btnStopSevice: stopService(new Intent(this,MyService.class)); break; case R.id.btnBindService: bindService(new Intent(this,MyService.class),this, Context.BIND_AUTO_CREATE); break; case R.id.btnUnbindService: unbindService(this); break; case R.id.btnSyncData: if(binder!=null){ binder.setData(etData.getText().toString()); } break; } } @Override public void onServiceConnected(ComponentName componentName, IBinder iBinder) { binder = (MyService.Binder) iBinder; binder.getService().setCallback(new MyService.CallBack() { @Override public void onDataChange(String data) { Message msg = new Message(); Bundle b = new Bundle(); b.putString("data",data); msg.setData(b); handler.sendMessage(msg); } }); } @Override public void onServiceDisconnected(ComponentName componentName) { } private Handler handler = new Handler(){ @Override public void handleMessage(Message msg) { super.handleMessage(msg); tvOut.setText(msg.getData().getString("data")); } }; }
