android 안드로이드 편안한 API 서비스




안드로이드 path-permission (9)

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

기본적으로 나는 app init에서 서비스를 시작하고 싶습니다. 그런 다음 서비스에 url을 요청하고 결과를 반환하도록 요청할 수 있기를 원합니다. 그 동안 나는 진도 창 또는 비슷한 것을 표시 할 수 있기를 원합니다.

나는 IDL을 사용하는 서비스를 만들었습니다. 어딘가에는 앱 간 통신을 위해이 애플리케이션이 정말로 필요하다는 것을 알았습니다. 따라서 이러한 필요성은 없어지지만 콜백을 수행하지 않는 방법은 확실치 않습니다. 또한 post(Config.getURL("login"), values) 치면 앱이 잠시 멈춘 것처럼 보입니다. (이상한 것 같습니다. 서비스 뒤에있는 아이디어가 다른 스레드에서 실행되는 것입니다!)

현재 게시물이있는 서비스가 있고 내부에 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);
        }

    }

};

Robby는 훌륭한 답변을 제공합니다. 그러나 나는 아직도 당신이 더 많은 정보를 찾고있는 것을 볼 수 있습니다. REST API를 구현하면 쉽게 잘못된 방법으로 호출됩니다. 이 Google I / O 비디오 를 보면서 내가 잘못 된 곳을 이해할 때가 아니 었습니다. AsyncTask와 HttpUrlConnection get / put 호출을 함께 사용하는 것만 큼 간단하지 않습니다.


Robby Pond의 솔루션은 다소 부족합니다. IntentService는 한 번에 하나의 인 텐트 만 처리하므로 한 번에 하나의 API 호출 만 할 수 있습니다. 종종 병렬 API 호출을 수행하려고합니다. 이 작업을 수행하려면 IntentService 대신 Service를 확장하고 고유 한 스레드를 만들어야합니다.



버튼의 이벤트 onItemClicked ()에서 서비스를 시작하려고한다고 가정 해 봅시다. 이 경우 Receiver 메커니즘이 작동하지 않습니다. -
a) OnItemClicked ()에서 Receiver를 Intent extra처럼 서비스에 전달했습니다.
b) 활동이 배경으로 이동합니다. onPause ()에서 Activity가 누출되지 않도록 ResultReceiver 내의 수신기 참조를 null로 설정했습니다.
c) 활동이 파괴됩니다.
d) 활동이 다시 생성됩니다. 그러나이 시점에서 서비스는 해당 수신자 참조가 손실되어 Activity에 대한 콜백을 수행 할 수 없습니다.
이러한 시나리오에서는 제한된 브로드 캐스트 또는 PendingIntent의 메커니즘이 더 유용 할 것 같습니다. 서비스에서 알림 활동을 참조하십시오.


귀하의 서비스가 귀하의 일부가 될 것이라면, 당신은 그것이 필요로하는 것보다 더 복잡한 방식으로 만들고 있습니다. RESTful 웹 서비스에서 일부 데이터를 가져 오는 간단한 사용 사례가 ResultReceiverIntentServiceResultReceiver 해야한다.

이 Service + ResultReceiver 패턴은 어떤 작업을 수행 할 때 startService() 서비스를 시작하거나 바인딩하여 작동합니다. Intent에서 엑스트라를 통해 ResultReceiver (활동)를 수행하고 전달할 작업을 지정할 수 있습니다.

이 서비스에서는 onHandleIntent 를 구현하여 Intent에 지정된 작업을 수행합니다. 작업이 완료되면 전달 된 ResultReceiver를 사용하여 onReceiveResult 가 호출 될 지점으로 액티비티로 메시지를 다시 send .

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

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

또한 게시물 (Config.getURL ( "login"), 값)을 치면 앱이 잠시 멈춘 것처럼 보입니다. (이상한 것 같습니다. 서비스 뒤에있는 아이디어가 다른 스레드에서 실행되는 것입니다!)

아니요 직접 스레드를 만들어야 합니다. 로컬 서비스는 기본적으로 UI 스레드에서 실행됩니다.


또한 게시물 (Config.getURL ( "login"), 값)을 치면 앱이 잠시 멈춘 것처럼 보입니다. (이상한 것 같습니다. 서비스 뒤에있는 아이디어가 다른 스레드에서 실행되는 것입니다!)

이 경우에는 다른 스레드에서 실행되고 완료시 ui 스레드로 결과를 리턴하는 asynctask를 사용하는 것이 더 좋습니다.


기본적으로 요청의 전체 관리를 잊어 버리는 데 도움이되는 또 다른 방법이 있습니다. 비동기 대기열 방법과 호출 가능 / 콜백 기반 응답을 기반으로합니다. 주요 이점은이 방법을 사용하면 전체 프로세스 (요청, 가져 오기 및 구문 분석 응답, sabe에 db)를 완전히 투명하게 만들 수 있다는 것입니다. 일단 응답 코드를 받으면 작업이 이미 완료된 것입니다. 그 후에 당신은 당신의 DB에 전화를 걸면 당신은 끝난다. 귀하의 활동이 활발하지 않을 때 어떤 일이 일어나는지에 대한 문제도 도움이됩니다. 여기서 일어날 일은 모든 데이터를 로컬 데이터베이스에 저장하지만 응답은 활동에 의해 처리되지 않는다는 것이 이상적인 방법입니다.

나는 여기에 일반적인 접근법에 대해 썼다. http://ugiagonzalez.com/2012/07/02/theres-life-after-asynctasks-in-android/

앞으로의 게시물에 특정 샘플 코드를 넣을 것입니다. 도움이되기를 바랍니다. 접근 방식을 공유하고 잠재적 인 의문이나 문제점을 해결하기 위해 저에게 연락하십시오.


모든 기능을 통합 한 독립 실행 형 클래스의 방향으로 모두를 가리키고 싶었습니다.

http://github.com/StlTenny/RestService

비 블록킹으로 요청을 실행하고 구현하기 쉬운 핸들러에 결과를 리턴합니다. 심지어 예제 구현과 함께 제공됩니다.







rest