Android四大组件之Service(续)

本地服务启动和通过广播的方式回调是非常简单的。

下面介绍远程服务和通过远程回调的方式通知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掉,系统不会自动重启该服务。

START_REDELIVER_INTENT:重传Intent。使用这个返回值时,如果在执行完onStartCommand后,服务被异常kill掉,系统会自动重启该服务,并将Intent的值传入。
START_STICKY的兼容版本,但不保证服务被kill后一定能重启。

 

你可能感兴趣的:(android)