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);
}