ไคลเอนต์ Android REST ตัวอย่าง?


115

แม้ว่าเธรดนี้จะยอมรับคำตอบแล้วก็ตามอย่าลังเลที่จะเสนอแนวคิดอื่น ๆ ที่คุณใช้หรือชอบ


ฉันได้พบกับบทความเหล่านี้:

และนั่นนำฉันไปสู่วิดีโอ Google I / O 2010 เกี่ยวกับแอปพลิเคชันไคลเอนต์ REST

ตั้งแต่ตอนนี้ฉันได้สร้างส่วนประกอบ REST เป็นส่วนประกอบแบบคงที่ในคลาสตัวควบคุมแอปพลิเคชันของฉัน

จากนี้ฉันคิดว่าฉันควรเปลี่ยนรูปแบบ ใครบางคนชี้ให้เห็นว่าแอปพลิเคชันGoogle IOSchedเป็นตัวอย่างที่ดีในการเขียนไคลเอนต์ REST บน Android มีคนบอกว่าวิธีนี้ซับซ้อนเกินไป

แล้วใครช่วยแสดงให้เราเห็นว่าแนวทางปฏิบัติที่ดีที่สุดคืออะไร? ในวิธีที่สั้นและเรียบง่าย
แอปพลิเคชัน IOSched ซับซ้อนเกินไปสำหรับกรณีการใช้งานตัวอย่าง


สวัสดีโดยทั่วไปฉันพัฒนาแพคเกจแยกต่างหากสำหรับบริการเว็บชื่อ "ws" ฉันมีการสรุปคลาสชื่อ "WebServicUtils.java" คลาส WebServiceUtils.java มีวิธีการเข้าถึงบริการเว็บ ฉันไม่แน่ใจว่าเทคนิคของฉันดีที่สุดหรือไม่ แต่สามารถใช้ซ้ำได้ทุกครั้งที่ฉันคัดลอกแพ็คเกจ ws ในแอปพลิเคชัน Android โปรดแจ้งให้เราทราบหากคุณต้องการทราบข้อมูลเพิ่มเติมเกี่ยวกับเทคนิคของฉัน
Ketan Parmar

ฉันไม่คิดว่าผู้แสดงความคิดเห็น youtube มีทางเลือกอื่นที่ดีกว่านี้ เราต้องทำงานภายใน API ของ Android แม้ว่าพวกเขามักจะเป็นเรื่องไร้สาระที่ซับซ้อนเกินไป
Timmmm

ตามหมายเหตุด้านข้าง Mechanoid ซึ่งเป็นปลั๊กอินโอเพนซอร์สคราสสำหรับ Android สามารถสร้างไคลเอ็นต์ JSON-REST โดยใช้ DSL แบบง่ายคำแนะนำเกี่ยวกับวิธีการใช้งานสามารถพบได้ที่robotoworks.com/mechanoid-plugin/service-client-dsl (ฉันเป็นผู้เขียนปลั๊กอินนี้ขออภัยสำหรับปลั๊กไร้ยางอาย!)
Ian Warwick

สิ่งนี้อาจมีประโยชน์มากสำหรับผู้ที่เรียนรู้การใช้งานไคลเอนต์ Android REST การนำเสนอของ Dobjanschi ที่ถอดความเป็น PDF: drive.google.com/file/d/0B2dn_3573C3RdlVpU2JBWXdSb3c/…
Kay Zed

คำตอบ:


99

แก้ไข 2 (ตุลาคม 2560):

มันคือปี 2017 เพียงแค่ใช้ Retrofit แทบไม่มีเหตุผลที่จะใช้อย่างอื่นเลย

แก้ไข:

คำตอบเดิมมีอายุมากกว่าหนึ่งปีครึ่งในขณะที่ทำการแก้ไขนี้ แม้ว่าแนวคิดที่นำเสนอในคำตอบเดิมยังคงมีอยู่ดังที่คำตอบอื่น ๆ ชี้ให้เห็น แต่ตอนนี้มีห้องสมุดที่ทำให้งานนี้ง่ายขึ้นสำหรับคุณ ที่สำคัญกว่านั้นไลบรารีเหล่านี้บางส่วนจะจัดการกับการเปลี่ยนแปลงการกำหนดค่าอุปกรณ์ให้คุณ

คำตอบเดิมจะถูกเก็บไว้ด้านล่างสำหรับการอ้างอิง แต่โปรดใช้เวลาตรวจสอบไลบรารีไคลเอ็นต์ Rest บางส่วนสำหรับ Android เพื่อดูว่าเหมาะสมกับกรณีการใช้งานของคุณหรือไม่ ต่อไปนี้เป็นรายชื่อไลบรารีบางส่วนที่ฉันได้ประเมินไว้ ไม่ได้มีวัตถุประสงค์เพื่อให้เป็นรายการที่ละเอียดถี่ถ้วน


คำตอบเดิม:

นำเสนอแนวทางของฉันในการมีไคลเอนต์ REST บน Android ฉันไม่ได้อ้างว่ามันดีที่สุด :) นอกจากนี้โปรดทราบว่านี่คือสิ่งที่ฉันคิดขึ้นเพื่อตอบสนองความต้องการของฉัน คุณอาจต้องมีเลเยอร์เพิ่มเติม / เพิ่มความซับซ้อนมากขึ้นหากกรณีการใช้งานของคุณต้องการ ตัวอย่างเช่นฉันไม่มีที่เก็บข้อมูลในตัวเครื่องเลย เนื่องจากแอปของฉันสามารถทนต่อการสูญเสียการตอบสนอง REST บางส่วนได้

แนวทางของฉันใช้เพียงแค่AsyncTaskอยู่ใต้ผ้าคลุม ในกรณีของฉันฉัน "เรียก" งานเหล่านี้จากActivityอินสแตนซ์ของฉัน แต่หากต้องการพิจารณากรณีต่างๆเช่นการหมุนหน้าจออย่างครบถ้วนคุณอาจเลือกที่จะโทรหาพวกเขาจากสิ่งServiceเหล่านี้

ฉันเลือกไคลเอนต์ REST ของฉันให้เป็น API อย่างมีสติ ซึ่งหมายความว่าแอปที่ใช้ไคลเอนต์ REST ของฉันไม่จำเป็นต้องตระหนักถึง REST URL จริงและรูปแบบข้อมูลที่ใช้

ลูกค้าจะมี 2 ชั้น:

  1. เลเยอร์บนสุด: วัตถุประสงค์ของเลเยอร์นี้คือเพื่อให้เมธอดที่สะท้อนการทำงานของ REST API ตัวอย่างเช่นคุณสามารถมีหนึ่งวิธี Java ที่สอดคล้องกับทุก URL ใน REST API ของคุณ (หรือสองอย่างสำหรับ GET และอีกวิธีหนึ่งสำหรับ POSTs)
    นี่คือจุดเข้าสู่ REST client API นี่คือเลเยอร์ที่แอปใช้ตามปกติ อาจเป็นซิงเกิลตัน แต่ไม่จำเป็น
    การตอบสนองของการเรียก REST จะแยกวิเคราะห์โดยเลเยอร์นี้เป็น POJO และส่งกลับไปยังแอป

  2. นี่คือAsyncTaskเลเยอร์ระดับล่างซึ่งใช้เมธอดไคลเอ็นต์ HTTP เพื่อออกไปข้างนอกและทำการเรียก REST นั้น

นอกจากนี้ฉันเลือกใช้กลไกการโทรกลับเพื่อสื่อสารผลลัพธ์ของAsyncTasks กลับไปที่แอป

มีข้อความเพียงพอ มาดูโค้ดกันเลย ให้ใช้ URL REST API สมมุติ - http://myhypotheticalapi.com/user/profile

เลเยอร์บนสุดอาจมีลักษณะดังนี้:

   /**
 * Entry point into the API.
 */
public class HypotheticalApi{   
    public static HypotheticalApi getInstance(){
        //Choose an appropriate creation strategy.
    }
    
    /**
     * Request a User Profile from the REST server.
     * @param userName The user name for which the profile is to be requested.
     * @param callback Callback to execute when the profile is available.
     */
    public void getUserProfile(String userName, final GetResponseCallback callback){
        String restUrl = Utils.constructRestUrlForProfile(userName);
        new GetTask(restUrl, new RestTaskCallback (){
            @Override
            public void onTaskComplete(String response){
                Profile profile = Utils.parseResponseAsProfile(response);
                callback.onDataReceived(profile);
            }
        }).execute();
    }
    
    /**
     * Submit a user profile to the server.
     * @param profile The profile to submit
     * @param callback The callback to execute when submission status is available.
     */
    public void postUserProfile(Profile profile, final PostCallback callback){
        String restUrl = Utils.constructRestUrlForProfile(profile);
        String requestBody = Utils.serializeProfileAsString(profile);
        new PostTask(restUrl, requestBody, new RestTaskCallback(){
            public void onTaskComplete(String response){
                callback.onPostSuccess();
            }
        }).execute();
    }
}


/**
 * Class definition for a callback to be invoked when the response data for the
 * GET call is available.
 */
public abstract class GetResponseCallback{
    
    /**
     * Called when the response data for the REST call is ready. <br/>
     * This method is guaranteed to execute on the UI thread.
     * 
     * @param profile The {@code Profile} that was received from the server.
     */
    abstract void onDataReceived(Profile profile);
    
    /*
     * Additional methods like onPreGet() or onFailure() can be added with default implementations.
     * This is why this has been made and abstract class rather than Interface.
     */
}

/**
 * 
 * Class definition for a callback to be invoked when the response for the data 
 * submission is available.
 * 
 */
public abstract class PostCallback{
    /**
     * Called when a POST success response is received. <br/>
     * This method is guaranteed to execute on the UI thread.
     */
    public abstract void onPostSuccess();

}

โปรดทราบว่าแอปไม่ได้ใช้ JSON หรือ XML (หรือรูปแบบอื่นใด) ที่ส่งคืนโดย REST API โดยตรง Profileแต่แอปเท่านั้นเห็นถั่ว

จากนั้นเลเยอร์ล่าง (เลเยอร์ AsyncTask) อาจมีลักษณะดังนี้:

/**
 * An AsyncTask implementation for performing GETs on the Hypothetical REST APIs.
 */
public class GetTask extends AsyncTask<String, String, String>{
    
    private String mRestUrl;
    private RestTaskCallback mCallback;
    
    /**
     * Creates a new instance of GetTask with the specified URL and callback.
     * 
     * @param restUrl The URL for the REST API.
     * @param callback The callback to be invoked when the HTTP request
     *            completes.
     * 
     */
    public GetTask(String restUrl, RestTaskCallback callback){
        this.mRestUrl = restUrl;
        this.mCallback = callback;
    }
    
    @Override
    protected String doInBackground(String... params) {
        String response = null;
        //Use HTTP Client APIs to make the call.
        //Return the HTTP Response body here.
        return response;
    }
    
    @Override
    protected void onPostExecute(String result) {
        mCallback.onTaskComplete(result);
        super.onPostExecute(result);
    }
}

    /**
     * An AsyncTask implementation for performing POSTs on the Hypothetical REST APIs.
     */
    public class PostTask extends AsyncTask<String, String, String>{
        private String mRestUrl;
        private RestTaskCallback mCallback;
        private String mRequestBody;
        
        /**
         * Creates a new instance of PostTask with the specified URL, callback, and
         * request body.
         * 
         * @param restUrl The URL for the REST API.
         * @param callback The callback to be invoked when the HTTP request
         *            completes.
         * @param requestBody The body of the POST request.
         * 
         */
        public PostTask(String restUrl, String requestBody, RestTaskCallback callback){
            this.mRestUrl = restUrl;
            this.mRequestBody = requestBody;
            this.mCallback = callback;
        }
        
        @Override
        protected String doInBackground(String... arg0) {
            //Use HTTP client API's to do the POST
            //Return response.
        }
        
        @Override
        protected void onPostExecute(String result) {
            mCallback.onTaskComplete(result);
            super.onPostExecute(result);
        }
    }
    
    /**
     * Class definition for a callback to be invoked when the HTTP request
     * representing the REST API Call completes.
     */
    public abstract class RestTaskCallback{
        /**
         * Called when the HTTP request completes.
         * 
         * @param result The result of the HTTP request.
         */
        public abstract void onTaskComplete(String result);
    }

นี่คือวิธีที่แอปอาจใช้ API (ในActivityหรือService):

HypotheticalApi myApi = HypotheticalApi.getInstance();
        myApi.getUserProfile("techie.curious", new GetResponseCallback() {

            @Override
            void onDataReceived(Profile profile) {
                //Use the profile to display it on screen, etc.
            }
            
        });
        
        Profile newProfile = new Profile();
        myApi.postUserProfile(newProfile, new PostCallback() {
            
            @Override
            public void onPostSuccess() {
                //Display Success
            }
        });

ฉันหวังว่าความคิดเห็นจะเพียงพอที่จะอธิบายการออกแบบ แต่เรายินดีที่จะให้ข้อมูลเพิ่มเติม


ฉันชอบคำตอบนี้มากกว่าเนื่องจากตัวอย่างของโค้ดที่ค่อนข้างดี ขอบคุณ
Marek Sebera

1
อาจจะไม่มีค่าอะไรเลยนี่ไม่ได้เป็นไปตามรูปแบบ RESTful MVC ที่ถูกต้องตามที่ Virgil Dobjanschi อธิบายไว้ คุณจะต้องรวมเลเยอร์ ContentProvider แบบเต็มซึ่งใช้ฐานข้อมูล SQLite ที่แอปใช้โดยตรง มิฉะนั้นนี่เป็นไคลเอนต์ REST ที่ดีและมีน้ำหนักเบาสำหรับ Android
Cooper

1
สิ่งเล็ก ๆ อย่างหนึ่งคุณจะต้องเรียกใช้งานบน Get / PostTask's
Mo Kargas

1
นี่มันเยี่ยมจริงๆ ฉันจะทำให้ GetResponseCallback เป็นแบบทั่วไปมากขึ้นได้อย่างไรดังนั้นจึงไม่ส่งผ่านเฉพาะโปรไฟล์กลับหรือคุณจะแนะนำให้แยก GetResponseCallback สำหรับข้อมูลแต่ละประเภทจาก API

1
@MichaelHerbig ใช่มีวิธีที่จะทำให้GetResponseCallbackทั่วไปมากขึ้น สิ่งที่ฉันชอบคือการใช้อินเทอร์เฟซเครื่องหมาย : interface IGetResopnse{} ต้องการแสดงถึงคลาสทั้งหมดที่สามารถตอบสนองได้ จากนั้นฉันมีclass Profile implements IGetResponseฯลฯ สุดท้ายทำให้GetResponseCallbackทั่วไปด้วยIGetResponseเป็นขอบเขตบน : public abstract class GetResponseCallback<? extends IGetResponse>.
curioustechizen

11

"การพัฒนาแอปพลิเคชันไคลเอนต์ Android REST" โดย Virgil Dobjanschi ทำให้เกิดการถกเถียงกันมากเนื่องจากไม่มีการนำเสนอซอร์สโค้ดในระหว่างเซสชันหรือมีการให้บริการในภายหลัง

การใช้งานอ้างอิงเดียวที่ฉันรู้ (โปรดแสดงความคิดเห็นหากคุณทราบข้อมูลเพิ่มเติม) มีให้ที่Datadroid (เซสชัน Google IO ถูกกล่าวถึงภายใต้ / การนำเสนอ) เป็นห้องสมุดที่คุณสามารถใช้ในแอปพลิเคชันของคุณเอง

ลิงก์ที่สองจะถามหาเฟรมเวิร์ก REST ที่ "ดีที่สุด" ซึ่งจะกล่าวถึงอย่างมากใน stackoverflow สำหรับฉันขนาดแอปพลิเคชันมีความสำคัญตามด้วยประสิทธิภาพของการใช้งาน

  • โดยปกติฉันใช้ org.json implemantation ธรรมดาซึ่งเป็นส่วนหนึ่งของ Android ตั้งแต่ระดับ API 1 ดังนั้นจึงไม่เพิ่มขนาดแอปพลิเคชัน
  • สำหรับฉันสิ่งที่น่าสนใจมากคือข้อมูลที่พบเกี่ยวกับประสิทธิภาพการแยกวิเคราะห์ JSONในความคิดเห็น: ในขณะที่ Android 3.0 Honeycomb ตัวแยกวิเคราะห์การสตรีมของ GSON รวมอยู่ใน android.util.JsonReader น่าเสียดายที่ไม่มีความคิดเห็นอีกต่อไป
  • Spring Android (ซึ่งฉันใช้บางครั้ง) รองรับ Jackson และ GSON ฤดูใบไม้ผลิ Android RestTemplate โมดูลเอกสารชี้ไปยังแอพพลิเคตัวอย่าง

ดังนั้นฉันจึงใช้ org.json หรือ GSON สำหรับสถานการณ์ที่ซับซ้อน สำหรับสถาปัตยกรรมของการนำ org.json ไปใช้งานฉันกำลังใช้คลาสแบบคงที่ซึ่งแสดงถึงกรณีการใช้งานเซิร์ฟเวอร์ (เช่น findPerson, getPerson) ฉันเรียกฟังก์ชันนี้จากบริการและใช้คลาสยูทิลิตี้ซึ่งกำลังทำการแมป (เฉพาะโปรเจ็กต์) และเครือข่าย IO (เทมเพลต REST ของฉันเองสำหรับ GET หรือ POST ธรรมดา) ฉันพยายามหลีกเลี่ยงการใช้เงาสะท้อน


4
หนังสือ O'Reilly, Programming Android นำเสนอรูปแบบ RESTful MVC ของ Dobjanschi อย่างสมบูรณ์ (บทที่ 12-13)
Cooper

ขอบคุณสำหรับคำแนะนำ: ฉันพบข้อความนี้ใน Amazon: "บทที่ 12 และ 13 จัดการกับผู้ให้บริการเนื้อหาการปฏิบัติต่อผู้ให้บริการเนื้อหาอย่างครอบคลุมด้วยโค้ดตัวอย่างและแอปพลิเคชันตัวอย่างทำให้ฉันได้รับข้อมูลเชิงลึกใหม่ ๆ เกี่ยวกับวิธีการทำงานของเทคโนโลยีนี้และวิธีการที่ทำได้ ใช้ในสถานการณ์การเขียนโปรแกรมจริงเฟรมเวิร์กผู้ให้บริการเนื้อหาสำหรับจัดเก็บและอ้างอิงข้อมูลโดยใช้ URI เป็นหนึ่งในคุณสมบัติใหม่ของระบบปฏิบัติการ Android ผลงานยอดเยี่ยมในการอธิบายเทคโนโลยีทีละขั้นตอน! "
ChrLipp

2
รหัสอยู่ในgithub.com/bmeike/ProgrammingAndroid2Examples (แต่ไม่มีบทคุณสามารถค้นหาได้ในรหัสรุ่นแรกgithub.com/bmeike/ProgrammingAndroidExamples )
ChrLipp

มีใครสามารถรับรหัสนี้ทำงานบน ICS + ได้บ้าง? ไฟล์สิ่งที่ต้องทำภายใต้ตัวอย่าง FinchVideo ระบุอย่างรวบรัดว่า "- Crashes under ICS" ฉันค่อนข้างผิดหวังหลังจากซื้อหนังสือเพราะพบว่าตัวอย่างโค้ดเสีย ...
eageranalyst

7

อย่าใช้ AsynTask เพื่อดำเนินการตามคำขอเครือข่ายหรืออะไรก็ตามที่จำเป็นต้องได้รับการยืนยัน Async Task เชื่อมโยงกับกิจกรรมของคุณเป็นอย่างมากและหากผู้ใช้เปลี่ยนการวางแนวของหน้าจอเนื่องจากแอปถูกสร้างขึ้นใหม่ AsyncTask จะหยุดทำงาน

ฉันขอแนะนำให้คุณใช้รูปแบบบริการกับ Intent Service และ ResultReceiver ลองดูไปRESTDroid เป็นไลบรารีที่ให้คุณดำเนินการตามคำขอ REST แบบอะซิงโครนัสและแจ้ง UI ของคุณด้วย Request Listeners ที่ใช้รูปแบบบริการของ Virgil Dobjanschi


3

มีไลบรารีอื่นที่มี API ที่สะอาดกว่าและข้อมูลประเภทที่ปลอดภัย https://github.com/kodart/Httpzoid

นี่คือตัวอย่างการใช้งานง่ายๆ

Http http = HttpFactory.create(context);
http.post("http://example.com/users")
    .data(new User("John"))
    .execute();

หรือซับซ้อนมากขึ้นด้วยการโทรกลับ

Http http = HttpFactory.create(context);
http.post("http://example.com/users")
    .data(new User("John"))
    .handler(new ResponseHandler<Void>() {
        @Override
        public void success(Void ignore, HttpResponse response) {
        }

        @Override
        public void error(String message, HttpResponse response) {
        }

        @Override
        public void failure(NetworkError error) {
        }

        @Override
        public void complete() {
        }
    }).execute();

เป็นของใหม่สด แต่ดูมีอนาคตมาก


ดูเหมือนว่าจะทำงานบน AsyncTask สิ่งที่ไม่ดีสำหรับคำขอที่ทำงานเป็นเวลานานและสลับไปมาระหว่างกิจกรรมต่างๆเนื่องจาก AsyncTask จะถูกฆ่าเมื่อกิจกรรมออก
Malachiasz

1

มีมากมายของห้องสมุดออกมีและฉันใช้คนนี้: https://github.com/nerde/rest-resource สิ่งนี้สร้างขึ้นโดยฉันและอย่างที่คุณเห็นในเอกสารวิธีนี้สะอาดและง่ายกว่าแบบอื่น ๆ มันไม่ได้เน้นที่ Android แต่ฉันใช้อยู่และมันก็ใช้งานได้ดี

รองรับ HTTP Basic Auth มันทำให้งานสกปรกในการทำให้เป็นอนุกรมและการแยกสถานะออบเจ็กต์ JSON คุณจะชอบมันเป็นพิเศษถ้า API ของคุณเป็นแบบ Rails


1

ข้อจำกัดความรับผิดชอบ: ฉันมีส่วนร่วมในโครงการโอเพ่นซอร์ส rest2mobile

อีกหนึ่งทางเลือกเป็นลูกค้าส่วนที่เหลือคือการใช้rest2mobile

วิธีการนี้แตกต่างกันเล็กน้อยเนื่องจากใช้ตัวอย่างส่วนที่เหลือที่เป็นรูปธรรมเพื่อสร้างรหัสไคลเอ็นต์สำหรับบริการ REST โค้ดจะแทนที่ REST URL และ JSON payloads ด้วยเมธอด java ดั้งเดิมและ POJO นอกจากนี้ยังจัดการการเชื่อมต่อเซิร์ฟเวอร์การเรียกใช้แบบอะซิงโครนัสและการแปลง POJO ไปยัง / จาก JSON โดยอัตโนมัติ

โปรดทราบว่าเครื่องมือนี้มีให้เลือกหลายรสชาติ (cli, plugins, android / ios / js support) และคุณสามารถใช้ปลั๊กอิน android studioเพื่อสร้าง API ลงในแอปของคุณได้โดยตรง

ทั้งหมดรหัสสามารถพบได้บนGitHub ที่นี่


3
โปรดแทนที่ลิงก์แรกด้วยเป้าหมายจริงแทนการโฆษณาไซต์ของคุณ
Skydan

0

เรามีไลบรารีไคลเอนต์ async REST ที่มีน้ำหนักเบาสำหรับ Android แบบโอเพ่นซอร์สคุณอาจพบว่ามีประโยชน์หากคุณมีข้อกำหนดขั้นต่ำและไม่ต้องการจัดการมัลติเธรดด้วยตัวเอง - เป็นเรื่องปกติมากสำหรับการสื่อสารขั้นพื้นฐาน แต่ไม่ใช่ไคลเอนต์ REST แบบเต็มรูปแบบ ห้องสมุด.

มันเรียกว่า libRESTfulClient และสามารถพบได้บน GitHub

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.