편안한 API 서비스 void registerCallback(IRemoteServiceCallback cb) {

웹 기반 REST API를 호출하는 데 사용할 수있는 서비스를 만들고 싶습니다.

기본적으로 앱 init에서 서비스를 시작한 다음 해당 서비스에 URL을 요청하고 결과를 반환하도록 요청할 수 있기를 원합니다. 그 동안 진행률 창이나 비슷한 것을 표시하고 싶습니다.

나는 현재 IDL을 사용하는 서비스를 만들었습니다. 앱 간 통신을 위해 실제로 필요한 부분을 읽었 으므로이 부분을 제거해야하지만 콜백을 수행하는 방법이 확실하지 않다고 생각하십시오. 또한 post(Config.getURL("login"), values)응용 프로그램을 칠 때 응용 프로그램이 잠시 일시 중지 된 것처럼 보입니다 (이상한 것으로 보입니다-서비스의 아이디어는 다른 스레드에서 실행된다는 생각이었습니다!)

현재 내부에 post 및 get 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);
}

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

    }

};


답변

서비스가 응용 프로그램의 일부가 될 경우 필요 이상으로 복잡해집니다. RESTful 웹 서비스에서 일부 데이터를 가져 오는 간단한 유스 케이스가 있으므로 ResultReceiverIntentService조사 해야합니다 .

이 Service + ResultReceiver 패턴은 조치를 수행하려고 할 때 startService () 를 사용하여 서비스를 시작하거나 바인딩하여 작동합니다. 인 텐트의 추가 항목을 통해 ResultReceiver (활동)를 수행하고 전달할 조작을 지정할 수 있습니다.

서비스에서 onHandleIntent 를 구현 하여 Intent에 지정된 작업을 수행합니다. 작업이 완료되면 전달 된 ResultReceiver를 사용하여 onReceiveResult 가 호출 될 활동에 메시지를 다시 보냅니다 .

예를 들어 웹 서비스에서 일부 데이터를 가져 오려고합니다.

  1. 의도를 작성하고 startService를 호출하십시오.
  2. 서비스의 작업이 시작되고 시작되었다는 메시지를 활동에 보냅니다.
  3. 활동은 메시지를 처리하고 진행 상황을 보여줍니다.
  4. 서비스가 작업을 마치고 일부 데이터를 활동으로 다시 보냅니다.
  5. 활동이 데이터를 처리하고 목록보기에 넣습니다.
  6. 서비스는 완료되었다는 메시지를 보내며 자체적으로 종료됩니다.
  7. 활동은 완료 메시지를 받고 진행 대화 상자를 숨 깁니다.

코드 기반을 원하지 않는다고 언급했지만 오픈 소스 Google I / O 2010 앱은 내가 설명하는 방식으로 서비스를 사용합니다.

샘플 코드를 추가하도록 업데이트되었습니다.

활동.

public class HomeActivity extends Activity implements MyResultReceiver.Receiver {

    public MyResultReceiver mReceiver;

    public void onCreate(Bundle savedInstanceState) {
        mReceiver = new MyResultReceiver(new Handler());
        mReceiver.setReceiver(this);
        ...
        final Intent intent = new Intent(Intent.ACTION_SYNC, null, this, QueryService.class);
        intent.putExtra("receiver", mReceiver);
        intent.putExtra("command", "query");
        startService(intent);
    }

    public void onPause() {
        mReceiver.setReceiver(null); // clear receiver so no leaks.
    }

    public void onReceiveResult(int resultCode, Bundle resultData) {
        switch (resultCode) {
        case RUNNING:
            //show progress
            break;
        case FINISHED:
            List results = resultData.getParcelableList("results");
            // do something interesting
            // hide progress
            break;
        case ERROR:
            // handle the error;
            break;
    }
}

서비스:

public class QueryService extends IntentService {
    protected void onHandleIntent(Intent intent) {
        final ResultReceiver receiver = intent.getParcelableExtra("receiver");
        String command = intent.getStringExtra("command");
        Bundle b = new Bundle();
        if(command.equals("query") {
            receiver.send(STATUS_RUNNING, Bundle.EMPTY);
            try {
                // get some data or something           
                b.putParcelableArrayList("results", results);
                receiver.send(STATUS_FINISHED, b)
            } catch(Exception e) {
                b.putString(Intent.EXTRA_TEXT, e.toString());
                receiver.send(STATUS_ERROR, b);
            }    
        }
    }
}

ResultReceiver 확장-MyResultReceiver.Receiver를 구현하기 위해 편집 됨

public class MyResultReceiver implements ResultReceiver {
    private Receiver mReceiver;

    public MyResultReceiver(Handler handler) {
        super(handler);
    }

    public void setReceiver(Receiver receiver) {
        mReceiver = receiver;
    }

    public interface Receiver {
        public void onReceiveResult(int resultCode, Bundle resultData);
    }

    @Override
    protected void onReceiveResult(int resultCode, Bundle resultData) {
        if (mReceiver != null) {
            mReceiver.onReceiveResult(resultCode, resultData);
        }
    }
}

답변

Android REST 클라이언트 애플리케이션을 개발 하는 것은 훌륭한 리소스였습니다. 스피커는 코드를 보여주지 않고 안드로이드에서 견고한 Rest Api를 구성하는 디자인 고려 사항과 기술을 거칩니다. 귀하의 팟 캐스트가 다소의 사람인지 아닌지, 나는 적어도 하나의 청취를 제공하는 것이 좋지만, 개인적으로 지금까지 4 ~ 5 번 들었으므로 아마 다시들을 것입니다.

Android REST 클라이언트 애플리케이션 개발
작성자 : Virgil Dobjanschi
설명 :

이 세션에서는 Android 플랫폼에서 RESTful 애플리케이션을 개발하기위한 아키텍처 고려 사항을 제시합니다. Android 플랫폼과 관련된 디자인 패턴, 플랫폼 통합 및 성능 문제에 중점을 둡니다.

그리고 제 API의 첫 번째 버전에서 실제로 고려하지 않은 많은 고려 사항이 있습니다.


답변

또한 post (Config.getURL ( “login”), values)를 눌렀을 때 앱이 잠시 일시 중지 된 것 같습니다.

스레드를 직접 만들 필요는 없습니다 . 로컬 서비스는 기본적으로 UI 스레드에서 실행됩니다.


답변

@Martyn은 전체 코드를 원하지 않지만이 주석은이 질문에 적합하다고 생각합니다.

모든 안드로이드 개발자가 고려해야 할 10 가지 오픈 소스 안드로이드 앱

Android 용 Foursquared는 오픈 소스 이며 foursquare REST API와 상호 작용하는 흥미로운 코드 패턴을 가지고 있습니다.


답변

REST 클라이언트 Retrofit을 적극 권장합니다 .

이 잘 작성된 블로그 게시물이 매우 유용하다는 것을 알았습니다. 간단한 예제 코드도 포함되어 있습니다. 저자는 Retrofit 을 사용하여 네트워크 호출을하고 Otto 는 데이터 버스 패턴을 구현합니다.

http://www.mdswanson.com/blog/2014/04/07/durable-android-rest-clients.html


답변

모든 기능을 통합 한 독립형 클래스의 방향으로 모든 것을 지시하고 싶었습니다.

http://github.com/StlTenny/RestService

요청을 비 블로킹으로 실행하고 구현하기 쉬운 핸들러로 결과를 반환합니다. 예제 구현도 제공됩니다.


답변

버튼의 이벤트-onItemClicked ()에서 서비스를 시작하려고한다고 가정 해 보겠습니다. 수신자 메커니즘 때문에이 경우에하지 작업 : –
onItemClicked에서 () 의도 추가와 같이 나는 서비스에 수신기를 통과) ()
b)는 배경 작업으로 이동합니다. onPause ()에서 Activity 누수를 피하기 위해 ResultReceiver 내의 수신자 참조를 null로 설정했습니다.
c) 활동이 파괴된다.
d) 활동이 다시 작성됩니다. 그러나이 시점에서 서비스는 수신자 참조가 유실되어 활동에 대한 콜백을 수행 할 수 없습니다.
제한된 방송 또는 PendingIntent의 메커니즘은 이러한 시나리오에서 더 유용 합니다. 서비스에서 활동 알림을 참조하십시오.