service 绑定有三种实现方式:
1. 直接继承Binder类实现。
条件: 同一应用,同一进程
2. 使用Messenger实现。
条件:要在不同的进程间通信,这种方式不用考虑线程安全性。(单线程操作时使用)
3. 使用AIDL实现。
条件:要在不同的进程间通信,并且需要多线程处理。要考虑线程之间的安全性。
使用AIDL实现:
三大基本步骤
- 创建.aidl文件
- 实现接口
- 公开接口
- 方法定义有0个或者多个参数,可以返回一个值或者是void.
- 方法中不是基本类型的参数,需要在方法参数前面加入in , out or inout
- 包含在.aidl中所有的注释在IBinder接口中都会生成(除了在import和package之前的注释)
- 仅仅支持方法,不支持静态的成员变量。
package com.hualu.servicemy; import com.hualu.servicemy.Book; interface IRemoteService{ int getPID() ; void basicInt(int i) ; void basicByte(byte b) ; void basicLong(long l) ; void basicDouble(double d) ; void basicFloat(float f) ; void basicString(String s) ; void basicBoolean(boolean b) ; void basicCharSequence(char c) ; void basicList(inout List<String> l) ; void basicMap(in Map m) ; Book getBook() ; }
实现接口
- 不能保证是从主线程里发起的调用,因此在使用的时候,需要考虑多线程启动和保证service运行时的线程安全性。
- 默认情况,远程调用是同步的。
- Service不会返回任何开发者自己抛出的异常到调用者。
package com.hualu.servicemy; import java.util.List; import java.util.Map; import android.app.Service; import android.content.Intent; import android.os.IBinder; import android.os.Process; import android.os.RemoteException; public class RemoteService extends Service { @Override public IBinder onBind(Intent intent) { return iBinder; } private final IRemoteService.Stub iBinder = new IRemoteService.Stub() { //实现接口 @Override public int getPID() throws RemoteException { return Process.myPid(); } @Override public Book getBook() throws RemoteException { Book book = new Book() ; book.setName("心善") ; return book; } @Override public void basicString(String s) throws RemoteException { System.out.println("string = "+s); } @Override public void basicMap(Map m) throws RemoteException { System.out.println("Map size = "+m.size()); } @Override public void basicLong(long l) throws RemoteException { System.out.println("long = "+l); } @Override public void basicList(List<String> l) throws RemoteException { System.out.println("List size = "+l.size()); } @Override public void basicInt(int i) throws RemoteException { System.out.println("int = "+i); } @Override public void basicFloat(float f) throws RemoteException { System.out.println("float = "+f); } @Override public void basicDouble(double d) throws RemoteException { System.out.println("double = "+d); } @Override public void basicCharSequence(char c) throws RemoteException { System.out.println("char = "+c); } @Override public void basicByte(byte b) throws RemoteException { } @Override public void basicBoolean(boolean b) throws RemoteException { } }; }
公开接口
package com.hualu.servicemy; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import android.app.Activity; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; import android.os.Bundle; import android.os.IBinder; import android.os.RemoteException; import android.view.View; import android.view.View.OnClickListener; import com.hualu.serviceexample.R; public class RemoteActivity extends Activity { private IRemoteService remoteService ; private boolean bindFlag = false ; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.remote_service) ; findViewById(R.id.remote).setOnClickListener(l) ; } private OnClickListener l = new OnClickListener(){ @Override public void onClick(View v) { if(remoteService != null){ try { remoteService.basicByte((byte)4) ; remoteService.basicBoolean(true) ; remoteService.basicInt(132) ; remoteService.basicLong(146) ; remoteService.basicFloat(83646.3f) ; remoteService.basicDouble(2.12) ; remoteService.basicString("remoteService") ; remoteService.basicCharSequence('r') ; List<String> l = new ArrayList<String>() ; l.add("Remote") ; l.add("Service") ; remoteService.basicList(l) ; Map<String,String> m = new HashMap<String,String>(); m.put("a", "a") ; remoteService.basicMap(m) ; Book b = remoteService.getBook() ; System.out.println(b.getName()); } catch (RemoteException e) { e.printStackTrace(); } } } } ; @Override protected void onStart() { super.onStart(); binderService() ; } @Override protected void onStop() { super.onStop(); unBindService() ; remoteService = null ; } private ServiceConnection conn = new ServiceConnection(){ //公开接口 @Override public void onServiceConnected(ComponentName name, IBinder service) { remoteService = IRemoteService.Stub.asInterface(service) ; bindFlag = true ; } @Override public void onServiceDisconnected(ComponentName name) { remoteService = null ; bindFlag = false ; } } ; private void binderService(){ bindService(new Intent(RemoteService.class.getName()), conn, Context.BIND_AUTO_CREATE) ; } private void unBindService(){ unbindService(conn) ; bindFlag = false ; conn = null ; } }
在aidl文件传递对象:
package com.hualu.servicemy; parcelable Book ;
Book.java
package com.hualu.servicemy; import android.os.Parcel; import android.os.Parcelable; public class Book implements Parcelable { private String name ; @Override public int describeContents() { return 0; } public Book(){} private Book(Parcel in) { readFromParcel(in); } @Override public void writeToParcel(Parcel dest, int flags) { dest.writeString(name) ; } public void readFromParcel(Parcel in){ name = in.readString() ; } public final static Parcelable.Creator<Book> CREATOR = new Parcelable.Creator<Book>(){ @Override public Book createFromParcel(Parcel source) { return new Book(source); } @Override public Book[] newArray(int size) { return new Book[size]; } } ; public void setName(String name){ this.name = name ; } public String getName(){ return this.name ; } }
Layout文件:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:gravity="center" android:orientation="vertical" > <Button android:id="@+id/remote" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/get_remote_data" /> <TextView android:id="@+id/textView1" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/remote_service" /> </LinearLayout>
Manifest文件中定义的Activity和service:
<activity android:name="com.hualu.servicemy.RemoteActivity"> <intent-filter> <action android:name="android.intent.action.MAIN"></action> <category android:name="android.intent.category.LAUNCHER"></category> </intent-filter> </activity> <service android:name="com.hualu.servicemy.RemoteService"> <intent-filter > <action android:name="com.hualu.servicemy.RemoteService"/> </intent-filter> </service>
运行结果:
拿到一台支持NFC手机或是平板设备时,在Settings->more可以看到NFC的enble,disnable的选项,还有android Beam这个东西。现在来分析NFC enable的过程
wireless_settings.xml 在Settings配置了NFC功能项:
<CheckBoxPreference android:key="toggle_nfc" android:title="@string/nfc_quick_toggle_title" android:summary="@string/nfc_quick_toggle_summary" android:persistent="false" /> <PreferenceScreen android:fragment="com.android.settings.nfc.AndroidBeam" android:key="android_beam_settings" android:title="@string/android_beam_settings_title" />xml配置的对应处理代码在:packages/apps/Settings/src/com/android/settings/WirelessSettings.java
public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); addPreferencesFromResource(R.xml.wireless_settings); CheckBoxPreference nfc = (CheckBoxPreference) findPreference(KEY_TOGGLE_NFC); PreferenceScreen androidBeam = (PreferenceScreen) findPreference(KEY_ANDROID_BEAM_SETTINGS); //setp1:获取NfcEnable的实例 mNfcEnabler = new NfcEnabler(activity, nfc, androidBeam); ...... // Remove NFC if its not available //setp2:获取NfcAdapter实例,查看NFC功能是否可能 mNfcAdapter = NfcAdapter.getDefaultAdapter(activity); if (mNfcAdapter == null) { getPreferenceScreen().removePreference(nfc); getPreferenceScreen().removePreference(androidBeam); mNfcEnabler = null; } }
setp1:NFcEnable实例 packages/apps/Settings/src/com/android/settings/nfc/NfcEnabler.java 实现Preference.OnPreferenceChangeListener接口监听NFC Enable,disnable事件
public boolean onPreferenceChange(Preference preference, Object value) { // Turn NFC on/off final boolean desiredState = (Boolean) value; mCheckbox.setEnabled(false); if (desiredState) { //setp3:用setp2一样的方式获取NfcAdapter实例出来nfc enable mNfcAdapter.enable(); } else { mNfcAdapter.disable(); } return false; }同时也是在这个NfcEnabler.java中处理了,Nfc enble,diable时,android beam的灰亮显示问题。
Setp2:现在所有的分析点集中到了NfcAdaper这个类中,首先来看下NfcAdapter实例的获取
源代码的路径:frameworks/base/core/java/android/nfc/NfcAdapter.java
public static NfcAdapter getDefaultAdapter(Context context) { ...... /* use getSystemService() for consistency */ NfcManager manager = (NfcManager) context.getSystemService(Context.NFC_SERVICE); if (manager == null) { // NFC not available return null; } //setp2-1 return manager.getDefaultAdapter(); }在NFC学习——NfcService 启动过程分析 文章中NfcService的onCreate()方法中有如下code:
//把mNfcAdapter 作为Service 添加到系统服务中,ServiceManager.getService("nfc")可以获取到binder ServiceManager.addService(SERVICE_NAME, mNfcAdapter);NfcManager manager = (NfcManager) context.getSystemService(Context.NFC_SERVICE);获取到的Service 就是之前add的service。
Setp2-1:manager.getDefaultAdapter其实饶了一圈又回到NfcAdapter类中获取NfcAdapter实例,具体看下面code
code路径:frameworks/base/core/java/android/nfc/NfcManager.java
public NfcAdapter getDefaultAdapter() { return mAdapter; }这个mAdaper如何来的呢?
public NfcManager(Context context) { NfcAdapter adapter; ...... try { adapter = NfcAdapter.getNfcAdapter(context); } catch (UnsupportedOperationException e) { adapter = null; } mAdapter = adapter; }最终又回到NfcAdapter.gerNfcAdapter(context)这个方法上获取实例,具体来分析:
public static synchronized NfcAdapter getNfcAdapter(Context context) { if (!sIsInitialized) { ...... //setp2-2:应用INfcAdapter.aidl和NfcService通信 sService = getServiceInterface(); if (sService == null) { Log.e(TAG, "could not retrieve NFC service"); throw new UnsupportedOperationException(); } try { //note2 sTagService = sService.getNfcTagInterface(); } catch (RemoteException e) { Log.e(TAG, "could not retrieve NFC Tag service"); throw new UnsupportedOperationException(); } sIsInitialized = true; } if (context == null) { if (sNullContextNfcAdapter == null) { sNullContextNfcAdapter = new NfcAdapter(null); } return sNullContextNfcAdapter; } //sNFcAdapter 是个HashMap,从中取出之前创建的NfcAdapter实例 NfcAdapter adapter = sNfcAdapters.get(context); if (adapter == null) { adapter = new NfcAdapter(context); sNfcAdapters.put(context, adapter); } return adapter; }Setp2-2:应用INfcAdapter.aidl和NfcService通信 ,INfcAdapter的方法都在NfcService的内部类NfcAdapterService中实现。
/** get handle to NFC service interface */ private static INfcAdapter getServiceInterface() { /* get a handle to NFC service */ IBinder b = ServiceManager.getService("nfc"); if (b == null) { return null; } return INfcAdapter.Stub.asInterface(b); }现在Setp2的NfcAdapter已经得到了它的实例,接下来就Setp3,NfcAdapter.enable()和NfcAdapter.disable():
public boolean enable() { try { //从setp2-2可以知道sService.enable()通过NfcAdapterService来实现的 return sService.enable(); } catch (RemoteException e) { attemptDeadServiceRecovery(e); return false; } }接下来看NfcAdapterService extends INfcAdapter.Stub 中的enable()方法:
public boolean enable() throws RemoteException { //Question 1: NfcService.enforceAdminPerm(mContext); //把NFC 打开状态写到SharedPreferences中保存起来 saveNfcOnSetting(true); ...... //AsyncTash后台处理NFC的打开 new EnableDisableTask().execute(TASK_ENABLE); return true; }
上面code 提到一个Question 1,其实它就是给NFC 赋予一个写的权限。
EnableDisableTask 在doInBackground中调用enableInternal();来处理NFC turn on。
boolean enableInternal() { //logcat 信息可以看到的log信息 Log.i(TAG, "Enabling NFC"); updateState(NfcAdapter.STATE_TURNING_ON); //setp3-1:调用jni initialize做init相关的动作 if (!mDeviceHost.initialize()) { Log.w(TAG, "Error enabling NFC"); updateState(NfcAdapter.STATE_OFF); return false; } synchronized(NfcService.this) { mObjectMap.clear(); //setp 3-2:开启一些循环监听的线程服务 mP2pLinkManager.enableDisable(mIsNdefPushEnabled, true); updateState(NfcAdapter.STATE_ON); } initSoundPool(); /* Start polling loop */ applyRouting(true); return true; }
setp3-1:initialize()对应的是在NativeNfcManager.java 中initialize()实现的,NativeNfcManager.initialize()直接调用的是jni方法doInitialize(),doInitialize()对应的在jni的方法:com_android_nfc_NfcManager_initialize(JNIEnv *e, jobject o),这部分将在NFC学习——NFC Enable 过程分析(二) 分析。
setp3-2:这一部分将在NFC学习——NFC Enable 过程分析(三) 分析
以上部分的分析配合下面这张图就更清晰了
以上图片来自:NFC framework introduce(一)
在SetAdapter后加上:registerForContextMenu(listView);// 上下文菜单和listview结合的纽带
然后实现长按事件:其中:menuInfo.position是获取当前长按的是哪一项
// 上下文的点击事件 @Override public boolean onMenuItemSelected(int featureId, MenuItem item) { if (item.getMenuInfo() instanceof AdapterContextMenuInfo) { AdapterContextMenuInfo menuInfo = (AdapterContextMenuInfo) item .getMenuInfo(); // 处理菜单的点击事件 switch (item.getItemId()) { 其中:menuInfo.position能获取当前长按的item case 1: break; case 2: Toast.makeText(this, "查看功能" + menuInfo.position, Toast.LENGTH_SHORT).show(); break; case 3: break; case 4: Toast.makeText(this, "取消功能" + menuInfo.position, Toast.LENGTH_SHORT).show(); break; } } return super.onContextItemSelected(item); }