閱讀本文須要先閱讀Android Service(上)html
一 爲何須要bindService()java
綁定服務就是爲了和服務進行通信 能夠調用服務裏面的方法android
二 bindService()調用服務裏面方法的流程安全
1. 綁定服務app
2. 在onBind()方法返回加強IBinder對象ide
3. 把IBinder轉化爲接口對象ui
4. 調用服務裏面的方法this
三 bindService()之本地服務spa
調用者和Service在同一進程代理
彼此之間擁有共同的內存區域 因此對於某些數據的共享特別的方便和簡單
1. 新建一個Model 實現Parcelable接口
public class User implements Parcelable { public int id; public String name; public User() {} public User(int id, String name) { this.id = id; this.name = name; } protected User(Parcel in) { id = in.readInt(); name = in.readString(); } @Override public void writeToParcel(Parcel dest, int flags) { dest.writeInt(id); dest.writeString(name); } @Override public int describeContents() { return 0; } public static final Creator<User> CREATOR = new Creator<User>() { @Override public User createFromParcel(Parcel in) { return new User(in); } @Override public User[] newArray(int size) { return new User[size]; } }; }
2. 功能接口的抽取
public interface IUserDao { int selectUserCount(); User selectUser(User user); }
3. 新建一個IBinder
public class MyBinder extends Binder implements IUserDao { @Override public int selectUserCount() { return 20; } @Override public User selectUser(User user) { user.id = 4; return user; } }
4. 新建一個Service
public class MyService extends Service { MyBinder mBinder = new MyBinder(); @Nullable @Override public IBinder onBind(Intent intent) { Log.i("HUANG", "MyService pid=" + android.os.Process.myPid()); return mBinder; } }
5. AndroidManifest.xml application節點裏面配置service name屬性必須配置 其他可選
<service android:name=".service.MyService" />
6. bindService()調用服務裏面方法
public class MainActivity extends AppCompatActivity implements View.OnClickListener { ServiceConnection mConnection; //服務的鏈接對象 IUserDao mUserDao; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Log.i("HUANG", "MainActivity pid=" + android.os.Process.myPid()); findViewById(R.id.count).setOnClickListener(this); findViewById(R.id.user).setOnClickListener(this); // bindService() 是須要時間的 Intent service = new Intent(this, MyService.class); mConnection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { mUserDao = (IUserDao) service; } @Override public void onServiceDisconnected(ComponentName name) {} }; bindService(service, mConnection, Context.BIND_AUTO_CREATE); } @Override protected void onDestroy() { super.onDestroy(); unbindService(mConnection); } @Override public void onClick(View view) { switch (view.getId()) { case R.id.count: int count = mUserDao.selectUserCount(); Log.i("HUANG", "count=" + count); break; case R.id.user: User user = mUserDao.selectUser(new User(1, "1")); Log.i("HUANG", "id=" + user.id); Log.i("HUANG", "name=" + user.name); break; } } }
四 bindService()之遠程服務
調用者和Service在不一樣進程
由於Android系統安全的緣由 致使了咱們在不一樣進程間沒法使用通常方式共享數據
因此Android爲咱們提供了AIDL(Android Interface Definition Language)
AIDL是一種接口定義語言 用於約束兩個進程間的通信規則 供編譯器生成代碼 實現Android設備上的兩個進程間通訊(IPC)
進程之間的通訊信息 首先會被轉換成AIDL協議消息 而後發送給對方 對方收到AIDL協議消息後再轉換成相應的對象
因爲進程之間的通訊信息須要雙向轉換 因此Android採用代理類在背後實現了信息的雙向轉換 代理類由Android編譯器生成 對開發人員來講是透明的
AIDL自動生成Java文件後 開發人員使用Stub
注意: 實現調用者和Service在不一樣進程有兩種方式
1. 調用者和Service在同一個應用程序 配置Service時指定process屬性(必定要指定 不指定就在同一進程)
<service android:name=".service.MyService"
android:process=":test" />
2. 調用者在一個應用程序 Service在另外一個應用程序
下面演示採用第二種方式 調用者做爲一個應用程序名叫Client Service做爲一個應用程序名叫Service
Service端
1. 新建一個Model 實現Parcelable接口 須要多加一個readFromParcel()方法 不加報錯
public class User implements Parcelable { public int id; public String name; public User() {} public User(int id, String name) { this.id = id; this.name = name; } protected User(Parcel in) { id = in.readInt(); name = in.readString(); } public void readFromParcel(Parcel in) { id = in.readInt(); name = in.readString(); } @Override public void writeToParcel(Parcel dest, int flags) { dest.writeInt(id); dest.writeString(name); } @Override public int describeContents() { return 0; } public static final Creator<User> CREATOR = new Creator<User>() { @Override public User createFromParcel(Parcel in) { return new User(in); } @Override public User[] newArray(int size) { return new User[size]; } }; }
2. 新建一個AIDL文件聲明Model(User.aidl) 注意包結構 要和Model的Java文件類路徑同樣 不然報錯
package com.hy.service.model; parcelable User;
3. 功能接口的抽取(IUserDao.aidl) 注意包結構
package com.hy.service; import com.hy.service.model.User; interface IUserDao { int selectUserCount(); // in 服務端能夠收到調用者傳遞的完整數據 服務端修改傳參不會影響調用者中的對象(形參改變對實參沒有任何影響) User inUser(in User user); // out 服務端能夠收到調用者傳遞的空數據 服務端修改傳參會影響調用者中的對象(形參改變對實參有直接影響) User outUser(out User user); // inout 服務端能夠收到調用者傳遞的完整數據 服務端修改傳參會影響調用者中的對象(形參改變對實參有直接影響) User inoutUser(inout User user); // in out inout 用來修飾引用類型參數 必填 不填報錯 // 基本類型參數不須要指定in out inout 默認是而且只能是in }
4. Build -> Make Project 生成Java文件
5. 新建一個IBinder
public class MyBinder extends IUserDao.Stub { @Override public int selectUserCount() throws RemoteException { return 15; } @Override public User inUser(User user) throws RemoteException { Log.i("HUANG", "MyService inUser id=" + user.id); user.id = 4; return user; } @Override public User outUser(User user) throws RemoteException { Log.i("HUANG", "MyService outUser id=" + user.id); user.id = 5; return user; } @Override public User inoutUser(User user) throws RemoteException { Log.i("HUANG", "MyService inoutUser id=" + user.id); user.id = 6; return user; } }
6. 新建一個Service
public class MyService extends Service { MyBinder mBinder = new MyBinder(); @Nullable @Override public IBinder onBind(Intent intent) { Log.i("HUANG", "MyService pid=" + android.os.Process.myPid()); return mBinder; } }
7. AndroidManifest.xml application節點裏面配置service name屬性必須配置 其他可選
<service android:name=".service.MyService"> <intent-filter> <action android:name="com.hy.service.action.TEST" /> </intent-filter> </service>
Client端
1. 把Service端User.aidl IUserDao.aidl User.java 文件和包結構一塊兒拷貝過來
2. Build -> Make Project 生成Java文件
3. bindService()調用服務裏面方法
public class MainActivity extends AppCompatActivity implements View.OnClickListener { ServiceConnection mConnection; //服務的鏈接對象 IUserDao mUserDao; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Log.i("HUANG", "MainActivity pid=" + android.os.Process.myPid()); findViewById(R.id.count).setOnClickListener(this); findViewById(R.id.in).setOnClickListener(this); findViewById(R.id.out).setOnClickListener(this); findViewById(R.id.inout).setOnClickListener(this); // bindService() 是須要時間的 Intent service = new Intent(); service.setAction("com.hy.service.action.TEST"); service.setPackage("com.hy.service"); //兼容Android 5.0 mConnection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { // service是一個代理對象 代理對象不能直接使用 mUserDao = IUserDao.Stub.Stub.asInterface(service); //把代理對象變爲真實對象 } @Override public void onServiceDisconnected(ComponentName name) {} }; bindService(service, mConnection, Context.BIND_AUTO_CREATE); } @Override protected void onDestroy() { super.onDestroy(); unbindService(mConnection); } @Override public void onClick(View view) { switch (view.getId()) { case R.id.count: try { int count = mUserDao.selectUserCount(); Log.i("HUANG", "MainActivity count=" + count); } catch (RemoteException e) { e.printStackTrace(); } break; case R.id.in: try { User rawUser = new User(1, "1"); User user = mUserDao.inUser(rawUser); Log.i("HUANG", "MainActivity rawUser inUser id=" + rawUser.id); Log.i("HUANG", "MainActivity user inUser id=" + user.id); } catch (RemoteException e) { e.printStackTrace(); } break; case R.id.out: try { User rawUser = new User(2, "2"); User user = mUserDao.outUser(rawUser); Log.i("HUANG", "MainActivity rawUser outUser id=" + rawUser.id); Log.i("HUANG", "MainActivity user outUser id=" + user.id); } catch (RemoteException e) { e.printStackTrace(); } break; case R.id.inout: try { User rawUser = new User(3, "3"); User user = mUserDao.inoutUser(rawUser); Log.i("HUANG", "MainActivity rawUser inoutUser id=" + rawUser.id); Log.i("HUANG", "MainActivity user inoutUser id=" + user.id); } catch (RemoteException e) { e.printStackTrace(); } break; } } }