本地服务启动和通过广播的方式回调是非常简单的。
下面介绍远程服务和通过远程回调的方式通知activity的方式。
1.service定义:
package com.joyfulmath.globalsearch.imagesearch.service; import java.util.ArrayList; import com.joyfulmath.globalsearch.imagesearch.service.aidl.IBuildService; import com.joyfulmath.globalsearch.imagesearch.service.aidl.ICallBack; import com.joyfulmath.globalsearch.imagesearch.service.aidl.PersonImageItem; import android.app.Service; import android.content.Intent; import android.os.IBinder; import android.os.RemoteCallbackList; import android.os.RemoteException; import android.util.Log; /* * image operator running in service thread and listener the contentobserver * */ public class ImageSearchBuildService extends Service implements onServiceEngineListener{ private static final String TAG = "ImageSearch.BuildService"; public final static String ACTION = "com.joyfulmath.intent.imagesearch.BUILD_IMAGE"; public final static String ACTION_METHOD = "action.method"; public final static String ACTION_REBUILD_DB = "action.rebuild.db"; private ImageSearchOperator mImageSearchOperator = null; private ImageSearchBuildServiceImpl iServiceImpl = null; private RemoteCallbackList<ICallBack> mCallbacks = new RemoteCallbackList<ICallBack>(); private long mCurrentCookie = -1; @Override public IBinder onBind(Intent intent) { mCurrentCookie = intent.getLongExtra("cookie",-1); Log.i(TAG, "[onBind] mCurrentCookie:"+mCurrentCookie); iServiceImpl = new ImageSearchBuildServiceImpl(mImageSearchOperator); return iServiceImpl; } @Override public void onCreate() { Log.i(TAG, "[onCreate]"); super.onCreate(); mImageSearchOperator = new ImageSearchOperator(this,this); mImageSearchOperator.startOperatorThread(); } @Override public void onDestroy() { Log.i(TAG, "[onDestroy]"); super.onDestroy(); if(mImageSearchOperator!=null) { mImageSearchOperator.exitOperatorThread(); } iServiceImpl = null; } @Override public int onStartCommand(Intent intent, int flags, int startId) { Log.i(TAG, "[onStartCommand]"); String method = intent.getStringExtra(ACTION_METHOD); if(method!=null) { if(method.equals(ACTION_REBUILD_DB)) { //start rebuild db mImageSearchOperator.rebuildImageSearchDbHandle(); } } return Service.START_NOT_STICKY; } @Override public boolean onUnbind(Intent intent) { Log.i(TAG, "[onUnbind]"); if(mImageSearchOperator!=null) { mImageSearchOperator.abortFindPersonHandle(); } return super.onUnbind(intent); } private class ImageSearchBuildServiceImpl extends IBuildService.Stub{ public ImageSearchOperator mOperator = null; public ImageSearchBuildServiceImpl(ImageSearchOperator operator) { mOperator = operator; } @Override public int getServiceStatus() throws RemoteException { return mOperator.mOperatorStatus; } @Override public int findPerson(String path, int filter) throws RemoteException { Log.i(TAG, "findPerson imageFilter:"+filter+" path: "+path); mOperator.findPersonHandle(path, filter,mCurrentCookie); return 0; } @Override public void registerCallback(ICallBack cb) throws RemoteException { // TODO Auto-generated method stub if(cb!=null) { mCallbacks.register(cb); } } @Override public void unregisterCallback(ICallBack cb) throws RemoteException { // TODO Auto-generated method stub if(cb!=null) { mCallbacks.unregister(cb); } } } @Override public void onEngineStatusUpdate(int status) { nofityStatusChanged(status); } @Override public void onQueryResult(long sessionid,String srcPath, int imageFilter, ArrayList<PersonImageItem> mResultItems) { if(mCurrentCookie == sessionid) { onFindPersonResult(srcPath,imageFilter,mResultItems); } else { Log.w(TAG, "onQueryResult old session:"+sessionid+" currentSession:"+mCurrentCookie); } } private void nofityStatusChanged(int status) { // synchronized (this) { // int n = mCallbacks.beginBroadcast(); // Log.i(TAG, "nofityStatusChanged n:"+n); // try { // for (int i = 0; i < n; i++) { // mCallbacks.getBroadcastItem(i).onServiceStatusChanged(status); // } // } catch (RemoteException e) { // Log.e(TAG, "RemoteException:"+e.getMessage()); // } // mCallbacks.finishBroadcast(); // } } private void onFindPersonResult(String srcPath, int imageFilter, ArrayList<PersonImageItem> mResultItems) { try{ synchronized (this) { Log.i(TAG, "onFindPersonResult filter:"+imageFilter); int n = mCallbacks.beginBroadcast(); try { for (int i = 0; i < n; i++) { mCallbacks.getBroadcastItem(i).onQueryResult(mResultItems, srcPath, imageFilter); } } catch (RemoteException e) { Log.e(TAG, "remote error:"+e); } mCallbacks.finishBroadcast(); } }catch (Exception e) { Log.i(TAG, "onFindPersonResult Wrong:"+e.getMessage()); } } }
2.IBuildService
package com.joyfulmath.globalsearch.imagesearch.service.aidl; import com.joyfulmath.globalsearch.imagesearch.service.aidl.PersonImageItem; import com.joyfulmath.globalsearch.imagesearch.service.aidl.ICallBack; interface IBuildService{ int getServiceStatus(); int findPerson(String path,int filter); void registerCallback(ICallBack cb); void unregisterCallback(ICallBack cb); }
3.数据传递类型:
package com.joyfulmath.globalsearch.imagesearch.service.aidl; import android.os.Parcel; import android.os.Parcelable; public class PersonImageItem implements Parcelable { public int persion_id; public int contact_id = -1; public int type = -1; public int image_id = -1; public int imageSize; public String Bitmap_Path = ""; public PersonImageItem(int personId,int contact_id,String path,int imageId, int imageSize,int type) { this.persion_id = personId; this.contact_id = contact_id; this.Bitmap_Path = path; this.image_id = imageId; this.imageSize = imageSize; this.type = type; } public PersonImageItem(Parcel source) { readFromParcel(source); } public PersonImageItem(PersonImageItem person) { this.persion_id = person.persion_id; this.contact_id = person.contact_id; this.Bitmap_Path =person.Bitmap_Path; this.image_id = person.image_id; this.imageSize = person.imageSize; this.type = person.type; } public PersonImageItem(int persionId, int contact_id,int type) { this.persion_id = persionId; this.contact_id = contact_id; this.type = type; } public final static Parcelable.Creator<PersonImageItem> CREATOR = new Parcelable.Creator<PersonImageItem>() { @Override public PersonImageItem createFromParcel(Parcel source) { return new PersonImageItem(source); } @Override public PersonImageItem[] newArray(int size) { // TODO Auto-generated method stub return new PersonImageItem[size]; } }; @Override public int describeContents() { // TODO Auto-generated method stub return 0; } @Override public void writeToParcel(Parcel dest, int flags) { // TODO Auto-generated method stub dest.writeInt(persion_id); dest.writeInt(contact_id); dest.writeInt(image_id); dest.writeInt(imageSize); dest.writeInt(type); dest.writeString(Bitmap_Path); } public void readFromParcel(Parcel source) { persion_id = source.readInt(); contact_id = source.readInt(); image_id = source.readInt(); imageSize = source.readInt(); type = source.readInt(); Bitmap_Path = source.readString(); } }
package com.joyfulmath.globalsearch.imagesearch.service.aidl; parcelable PersonImageItem;
PersonImageItem是service返回给client端的结果类型。
package com.joyfulmath.globalsearch.imagesearch.service.aidl; import com.joyfulmath.globalsearch.imagesearch.service.aidl.PersonImageItem; interface ICallBack{ void onQueryResult(in List<PersonImageItem> items,String path,int filter); void onServiceStatusChanged(int status); }
client端engine,启动service,调用service服务以及观察service返回的结果。
package com.joyfulmath.globalsearch.imagesearch.client; import java.util.ArrayList; import java.util.List; import com.joyfulmath.globalsearch.imagesearch.AlertListAdapter.ListAdapterInfo; import com.joyfulmath.globalsearch.imagesearch.client.resulttype.ContactResultItem; import com.joyfulmath.globalsearch.imagesearch.client.resulttype.SdcardImageResultItem; import com.joyfulmath.globalsearch.imagesearch.service.FaceRecognizeEngine; import com.joyfulmath.globalsearch.imagesearch.service.ImageSearchBuildService; import com.joyfulmath.globalsearch.imagesearch.service.aidl.IBuildService; import com.joyfulmath.globalsearch.imagesearch.service.aidl.ICallBack; import com.joyfulmath.globalsearch.imagesearch.service.aidl.PersonImageItem; import com.joyfulmath.globalsearch.imagesearch.utils.ImageSearchUtils; import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.database.Cursor; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.drawable.Drawable; import android.net.Uri; import android.os.Bundle; import android.os.Handler; import android.os.IBinder; import android.os.Looper; import android.os.Message; import android.os.Process; import android.os.RemoteException; import android.provider.MediaStore; import android.util.Log; public class SearchEngine extends BroadcastReceiver implements Runnable{ private static final String TAG = "ImageSearch.Engine"; private Context mContext = null; private ContentResolver mContentResolver = null; private onEngineListener mListener=null; public Handler mEngineHandler =null; public Looper mEngineLooper =null; public SearchWorkQueue mWorkQueue = null; public int mbgEngineStatus = ImageSearchUtils.OPERATOR_STATUS_NONE; private IBuildService iServce = null; private long mSessionCookie = -1; public SearchEngine(Context context,onEngineListener listener) { mContext = context; mContentResolver = mContext.getContentResolver(); mListener = listener; mbgEngineStatus = ImageSearchUtils.OPERATOR_STATUS_NONE; mSessionCookie = generateCookie(); startHandleThread(); registerBroadCast(); bindService(); } //监听service的回调 private ICallBack.Stub mCallback = new ICallBack.Stub() { @Override public void onServiceStatusChanged(int status) throws RemoteException { // TODO Auto-generated method stub mbgEngineStatus = status; } @Override public void onQueryResult(List<PersonImageItem> items, String path, int filter) throws RemoteException { Log.i(TAG, "ICallBack onQueryResult filter:"+filter); if(items.size()>0) { Log.i(TAG, "ICallBack onQueryResult items:"+items.size()); ArrayList<GenericResultItem> mReults = new ArrayList<GenericResultItem>(items.size()); switch(filter) { case ImageSearchUtils.FIND_TYPE_CONTACT: for(PersonImageItem person:items) { ContactResultItem contactitem = new ContactResultItem(person, mContext); mReults.add(contactitem); } break; case ImageSearchUtils.FIND_TYPE_IMAGE_STORE: for(PersonImageItem person:items) { SdcardImageResultItem contactitem = new SdcardImageResultItem(person); mReults.add(contactitem); } break; } if(mWorkQueue!=null) { mWorkQueue.notifyWorkQueue(mReults,filter); } } else { Log.i(TAG, "ICallBack onQueryResult items null"); if(mWorkQueue!=null) { mWorkQueue.notifyWorkQueue(null,filter); } } } };
//service connect private ServiceConnection conn = new ServiceConnection() { @Override public void onServiceDisconnected(ComponentName name) { // TODO Auto-generated method stub Log.i(TAG, "onServiceDisconnected"); iServce = null; } @Override public void onServiceConnected(ComponentName name, IBinder service) { iServce = IBuildService.Stub.asInterface(service); Log.i(TAG, "onServiceConnected"); try { iServce.registerCallback(mCallback); } catch (RemoteException e) { Log.w(TAG, "onServiceConnected wrong:"+e.getMessage()); } } };
//启动service并绑定 private void bindService() { Log.i(TAG, "[bindService] cookie:"+mSessionCookie); Intent intent = new Intent(); intent.setAction(ImageSearchBuildService.ACTION); intent.putExtra("cookie", mSessionCookie); mContext.startService(intent); mContext.bindService(intent, conn, Context.BIND_AUTO_CREATE); }
//断开service private void unBindService() { try { if (iServce != null) { iServce.unregisterCallback(mCallback); } mContext.unbindService(conn); } catch (Exception e) { } } public void findPersonInService(String path, int imageFilter) throws RemoteException { Log.i(TAG, "findPersonInService imageFilter:"+imageFilter); if(iServce!=null) { iServce.findPerson(path, imageFilter); } } private void startHandleThread() { new Thread(this).start(); } private void exitHandleThread(){ if(mEngineLooper!=null) { mEngineLooper.quit(); mEngineLooper = null; } mEngineHandler = null; mContentResolver = null; } public boolean isDeviceSupportEngine() { return FaceRecognizeEngine.checkDeviceSupport(); } public void Release() { unBindService(); unRegisterReceiver(); exitHandleThread(); Log.i(TAG, "[Release] done"); } public void abortSearchImageAsync() { } public void SearchImageAsyncHandle(String path,int imageFilter) { Log.i(TAG, "SearchImageAsyncHandle imageFilter"+imageFilter); //send message to async all search image one by one Message msg = mEngineHandler.obtainMessage(); msg.what = ImageSearchUtils.ENGINE_START_SEARCH; Bundle data = new Bundle(); data.putString("path", path); data.putInt("filter", imageFilter); msg.setData(data); mEngineHandler.sendMessage(msg); } private void searchImageTask(String path,int imageFilter) { try{ if(mWorkQueue == null) { mWorkQueue = new SearchWorkQueue("clientEngine",this); } mWorkQueue.prepareWorkQueue(imageFilter, path); mWorkQueue.query(); }catch (Exception e) { // TODO: handle exception } } public void searhImageQueueHandle() { Message msg = mEngineHandler.obtainMessage(); msg.what = ImageSearchUtils.ENGINE_SEARCH_QUEUE; mEngineHandler.sendMessage(msg); } private void searchImageQueueTask() { if(mWorkQueue!=null) { mWorkQueue.query(); } } public void onSearchResult(String path, ArrayList<GenericResultItem> itmes) { if(mListener!=null) { mListener.onSearchResult(path, itmes); } } @Override public void onReceive(Context context, Intent intent) { } public void registerBroadCast() { } public void unRegisterReceiver() { } public void decodeUriToBitmapHandle(Uri uri) { Message msg = mEngineHandler.obtainMessage(); msg.what = ImageSearchUtils.ENGINE_OP_DEOCDE_URI; Bundle data = new Bundle(); data.putParcelable("uri", uri); msg.setData(data); mEngineHandler.sendMessage(msg); } public void prepareSrcImageItemsHandle() { Message msg = mEngineHandler.obtainMessage(); msg.what = ImageSearchUtils.ENGINE_OP_PREPARE_SRC_APP; mEngineHandler.sendMessage(msg); } private void decodeUriTask(Uri uri) { try { Log.i(TAG, "DecodeUriTask uri" + uri.toString()); String[] projects = new String[1]; projects[0] = MediaStore.Images.Media.DATA; Cursor cursor = mContentResolver.query(uri, projects, null, null, null); String path = null; if (cursor != null) { while (cursor.moveToNext()) { path = cursor.getString(cursor .getColumnIndex(MediaStore.Images.Media.DATA)); break; } cursor.close(); } if (!path.isEmpty()) { Bitmap bitmap = BitmapFactory.decodeFile(path); if (mListener != null) { mListener.onEngineDecodeUri(bitmap, path); } } else { if (mListener != null) { mListener.onEngineDecodeUri(null, null); } } } catch (Exception e) { Log.i(TAG, "DecodeUriTask error:" + e.getMessage()); if (mListener != null) { mListener.onEngineDecodeUri(null, null); } } finally { } } private void prepareSrcSelectItemTask() { try { PackageManager pm = mContext.getPackageManager(); Intent mainIntent = new Intent(ImageSearchUtils.ACTION_CAMERA_PICKER); List<ResolveInfo> resolveInfos = pm .queryIntentActivities(mainIntent, PackageManager.MATCH_DEFAULT_ONLY); ArrayList<ListAdapterInfo> infos = new ArrayList<ListAdapterInfo>(2); if(!resolveInfos.isEmpty()&& resolveInfos.size()>0) { Log.i(TAG, "PrepareSrcSelectItemTask Camera is find"); ResolveInfo reInfo = resolveInfos.get(0); String appLabel = (String) reInfo.loadLabel(pm); // 获得应用程序的Label Drawable icon = reInfo.loadIcon(pm); // 获得应用程序图标 ListAdapterInfo info = new ListAdapterInfo(appLabel,icon,ImageSearchUtils.REQUEST_CAMERA_PICK); infos.add(info); } Intent mainIntent2 = new Intent(ImageSearchUtils.ACTION_GALLERY_PICKER); List<ResolveInfo> resolveInfos2 = pm .queryIntentActivities(mainIntent2, PackageManager.MATCH_DEFAULT_ONLY); if(!resolveInfos2.isEmpty() && resolveInfos2.size()>0) { Log.i(TAG, "PrepareSrcSelectItemTask Gallery is find"); ResolveInfo reInfo2 = resolveInfos2.get(0); // String activityName2 = reInfo2.activityInfo.name; // 获得该应用程序的启动Activity的name // String pkgName2 = reInfo2.activityInfo.packageName; // 获得应用程序的包名 String appLabel2 = (String) reInfo2.loadLabel(pm); // 获得应用程序的Label Drawable icon2 = reInfo2.loadIcon(pm); // 获得应用程序图标 ListAdapterInfo info2 = new ListAdapterInfo(appLabel2,icon2,ImageSearchUtils.REQUEST_GALLERY_PICK); infos.add(info2); } if(mListener!=null) { mListener.onPrepareActivityInfo(infos); } } catch (Exception e) { Log.i(TAG, "PrepareSrcSelectItemTask error:"+e.getMessage()); } finally{ } } public static class EngineHandler extends Handler{ private SearchEngine mSearchEngine = null; public EngineHandler(Looper loop, SearchEngine engine) { super(loop); mSearchEngine = engine; } @Override public void handleMessage(Message msg) { switch(msg.what) { case ImageSearchUtils.ENGINE_OP_DEOCDE_URI: Bundle datauri = msg.getData(); Uri uri = datauri.getParcelable("uri"); mSearchEngine.decodeUriTask(uri); break; case ImageSearchUtils.ENGINE_OP_PREPARE_SRC_APP: mSearchEngine.prepareSrcSelectItemTask(); break; case ImageSearchUtils.ENGINE_START_SEARCH: Bundle data = msg.getData(); String path = data.getString("path"); int imageFilter = data.getInt("filter"); mSearchEngine.searchImageTask(path, imageFilter); break; case ImageSearchUtils.ENGINE_SEARCH_QUEUE: mSearchEngine.searchImageQueueTask(); break; } } } @Override public void run() { Looper.prepare(); mEngineLooper = Looper.myLooper(); mEngineHandler = new EngineHandler(mEngineLooper,this); Log.i(TAG, "frontground engine handle running TID:"+Process.myTid()); Looper.loop(); } private long generateCookie() { return System.currentTimeMillis(); } }
onstartcommand返回类型?
START_STICKY: 如果service进程被kill掉,保留service的状态为开始状态,但不保留递送的intent对象。随后系统会尝试重新创建service,由 于服务状态为开始状态,所以创建服务后一定会调用onStartCommand(Intent,int,int)方法。如果在此期间没有任何启动命令被传 递到service,那么参数Intent将为null
START_NOT_STICKY:“非粘性的”。使用这个返回值时,如果在执行完onStartCommand后,服务被异常kill掉,系统不会自动重启该服务。