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后一定能重启。

 

posted on 2015-01-19 14:47  Joyfulmath  阅读(309)  评论(0编辑  收藏  举报

导航