我正在寻找一个服务,我可以使用它来调用基于web的REST API。

我想在app init上启动一个服务然后我想让这个服务请求一个url并返回结果。与此同时,我希望能够显示一个进度窗口或类似的东西。

我目前创建了一个使用IDL的服务,我在某处读到过,你只需要它来跨应用程序通信,所以认为这些需求剥离,但不确定如何做回调没有它。另外,当我点击帖子(Config.getURL(“login”),值)时,应用程序似乎暂停了一段时间(看起来很奇怪-以为服务背后的想法是它在不同的线程上运行!)

目前,我有一个服务与post和获取http方法在里面,一对AIDL文件(双向通信),一个ServiceManager处理启动,停止,绑定等到服务,我动态创建一个处理器与特定的代码回调需要。

我不希望任何人给我一个完整的代码库来工作,但一些指针将非常感激。

代码(大部分)完整:

public class RestfulAPIService extends Service  {

final RemoteCallbackList<IRemoteServiceCallback> mCallbacks = new RemoteCallbackList<IRemoteServiceCallback>();

public void onStart(Intent intent, int startId) {
    super.onStart(intent, startId);
}
public IBinder onBind(Intent intent) {
    return binder;
}
public void onCreate() {
    super.onCreate();
}
public void onDestroy() {
    super.onDestroy();
    mCallbacks.kill();
}
private final IRestfulService.Stub binder = new IRestfulService.Stub() {
    public void doLogin(String username, String password) {

        Message msg = new Message();
        Bundle data = new Bundle();
        HashMap<String, String> values = new HashMap<String, String>();
        values.put("username", username);
        values.put("password", password);
        String result = post(Config.getURL("login"), values);
        data.putString("response", result);
        msg.setData(data);
        msg.what = Config.ACTION_LOGIN;
        mHandler.sendMessage(msg);
    }

    public void registerCallback(IRemoteServiceCallback cb) {
        if (cb != null)
            mCallbacks.register(cb);
    }
};

private final Handler mHandler = new Handler() {
    public void handleMessage(Message msg) {

        // Broadcast to all clients the new value.
        final int N = mCallbacks.beginBroadcast();
        for (int i = 0; i < N; i++) {
            try {
                switch (msg.what) {
                case Config.ACTION_LOGIN:
                    mCallbacks.getBroadcastItem(i).userLogIn( msg.getData().getString("response"));
                    break;
                default:
                    super.handleMessage(msg);
                    return;

                }
            } catch (RemoteException e) {
            }
        }
        mCallbacks.finishBroadcast();
    }
    public String post(String url, HashMap<String, String> namePairs) {...}
    public String get(String url) {...}
};

几个AIDL文件:

package com.something.android

oneway interface IRemoteServiceCallback {
    void userLogIn(String result);
}

and

package com.something.android
import com.something.android.IRemoteServiceCallback;

interface IRestfulService {
    void doLogin(in String username, in String password);
    void registerCallback(IRemoteServiceCallback cb);
}

服务经理:

public class ServiceManager {

    final RemoteCallbackList<IRemoteServiceCallback> mCallbacks = new RemoteCallbackList<IRemoteServiceCallback>();
    public IRestfulService restfulService;
    private RestfulServiceConnection conn;
    private boolean started = false;
    private Context context;

    public ServiceManager(Context context) {
        this.context = context;
    }

    public void startService() {
        if (started) {
            Toast.makeText(context, "Service already started", Toast.LENGTH_SHORT).show();
        } else {
            Intent i = new Intent();
            i.setClassName("com.something.android", "com.something.android.RestfulAPIService");
            context.startService(i);
            started = true;
        }
    }

    public void stopService() {
        if (!started) {
            Toast.makeText(context, "Service not yet started", Toast.LENGTH_SHORT).show();
        } else {
            Intent i = new Intent();
            i.setClassName("com.something.android", "com.something.android.RestfulAPIService");
            context.stopService(i);
            started = false;
        }
    }

    public void bindService() {
        if (conn == null) {
            conn = new RestfulServiceConnection();
            Intent i = new Intent();
            i.setClassName("com.something.android", "com.something.android.RestfulAPIService");
            context.bindService(i, conn, Context.BIND_AUTO_CREATE);
        } else {
            Toast.makeText(context, "Cannot bind - service already bound", Toast.LENGTH_SHORT).show();
        }
    }

    protected void destroy() {
        releaseService();
    }

    private void releaseService() {
        if (conn != null) {
            context.unbindService(conn);
            conn = null;
            Log.d(LOG_TAG, "unbindService()");
        } else {
            Toast.makeText(context, "Cannot unbind - service not bound", Toast.LENGTH_SHORT).show();
        }
    }

    class RestfulServiceConnection implements ServiceConnection {
        public void onServiceConnected(ComponentName className, IBinder boundService) {
            restfulService = IRestfulService.Stub.asInterface((IBinder) boundService);
            try {
            restfulService.registerCallback(mCallback);
            } catch (RemoteException e) {}
        }

        public void onServiceDisconnected(ComponentName className) {
            restfulService = null;
        }
    };

    private IRemoteServiceCallback mCallback = new IRemoteServiceCallback.Stub() {
        public void userLogIn(String result) throws RemoteException {
            mHandler.sendMessage(mHandler.obtainMessage(Config.ACTION_LOGIN, result));

        }
    };

    private Handler mHandler;

    public void setHandler(Handler handler) {
        mHandler = handler;
    }
}

服务初始化和绑定:

// this I'm calling on app onCreate
servicemanager = new ServiceManager(this);
servicemanager.startService();
servicemanager.bindService();
application = (ApplicationState)this.getApplication();
application.setServiceManager(servicemanager);

服务函数调用:

// this lot i'm calling as required - in this example for login
progressDialog = new ProgressDialog(Login.this);
progressDialog.setMessage("Logging you in...");
progressDialog.show();

application = (ApplicationState) getApplication();
servicemanager = application.getServiceManager();
servicemanager.setHandler(mHandler);

try {
    servicemanager.restfulService.doLogin(args[0], args[1]);
} catch (RemoteException e) {
    e.printStackTrace();
}

...later in the same file...

Handler mHandler = new Handler() {
    public void handleMessage(Message msg) {

        switch (msg.what) {
        case Config.ACTION_LOGIN:

            if (progressDialog.isShowing()) {
                progressDialog.dismiss();
            }

            try {
                ...process login results...
                }
            } catch (JSONException e) {
                Log.e("JSON", "There was an error parsing the JSON", e);
            }
            break;
        default:
            super.handleMessage(msg);
        }

    }

};

当前回答

另外,当我点击帖子(Config.getURL(“login”),值)时,应用程序似乎暂停了一段时间(看起来很奇怪-以为服务背后的想法是它在不同的线程上运行!)

在这种情况下,最好使用asynctask,它在不同的线程上运行,并在完成时将结果返回给ui线程。

其他回答

There is another approach here which basically helps you to forget about the whole management of the requests. It is based on an async queue method and a callable/callback based response. The main advantage is that by using this method you'll be able to make the whole process (request, get and parse response, sabe to db) completely transparent for you. Once you get the response code the work is already done. After that you just need to make a call to your db and you are done. It helps as well with the problematic of what happens when your activity is not active. What will happen here is that you'll have all your data saved in your local database but the response won't be processed by your activity, that's the ideal way.

Lets say I want to start the service on an event - onItemClicked() of a button. The Receiver mechanism would not work in that case because :- a) I passed the Receiver to the service (as in Intent extra) from onItemClicked() b) Activity moves to the background. In onPause() I set the receiver reference within the ResultReceiver to null to avoid leaking the Activity. c) Activity gets destroyed. d) Activity gets created again. However at this point the Service will not be able to make a callback to the Activity as that receiver reference is lost. The mechanism of a limited broadcast or a PendingIntent seems to be more usefull in such scenarios- refer to Notify activity from service

另外,当我点击帖子(Config.getURL(“login”),值)时,应用程序似乎暂停了一段时间(看起来很奇怪-以为服务背后的想法是它在不同的线程上运行!)

在这种情况下,最好使用asynctask,它在不同的线程上运行,并在完成时将结果返回给ui线程。

我知道@Martyn不想要完整的代码,但我认为这个注释对这个问题有好处:

10个开源Android应用,每个Android开发者都必须看看

Android版foursquare是开源的,并且有一个有趣的代码模式与foursquare REST API交互。

当我点击 帖子(Config.getURL(“登录”), 值)应用程序似乎暂停了一个 虽然(看起来很奇怪——这个想法想 服务的背后是它运行在 不同的线程!)

不,你必须自己创建一个线程,默认情况下本地服务运行在UI线程中。