บริการ API ที่สงบ


226

ฉันกำลังมองหาบริการที่สามารถใช้โทรหา REST API บนเว็บได้

โดยทั่วไปฉันต้องการเริ่มบริการบนแอพ 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;
    }
}

บริการ init และผูก:

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

    }

};

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

เนื่องจากหลายคนแนะนำการนำเสนอของ Virgil Dobjanschi และลิงก์ไปยัง IO 2010 ถูกทำลายในขณะนี้ต่อไปนี้เป็นลิงก์โดยตรงไปยังวิดีโอ YT: youtube.com/watch?v=xHXn3Kg2IQE
Jose_GD

คำตอบ:


283

หากบริการของคุณจะเป็นส่วนหนึ่งของแอปพลิเคชันของคุณคุณจะต้องทำให้มันซับซ้อนกว่าที่จำเป็น ตั้งแต่คุณมีกรณีการใช้งานที่เรียบง่ายของการรับข้อมูลบางส่วนจากบริการเว็บสงบคุณควรมีลักษณะเป็นResultReceiverและIntentService

รูปแบบ Service + ResultReceiver นี้ทำงานโดยเริ่มต้นหรือเชื่อมโยงกับบริการด้วยstartService ()เมื่อคุณต้องการดำเนินการบางอย่าง คุณสามารถระบุการดำเนินการที่จะดำเนินการและส่งผ่าน ResultReceiver ของคุณ (กิจกรรม) ผ่านความพิเศษใน Intent

ในบริการที่คุณใช้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);
        }
    }
}

1
ยอดเยี่ยม - ขอบคุณ! ฉันลงเอยด้วยการใช้แอปพลิเคชัน Google iosched และว้าว ... มันค่อนข้างซับซ้อน แต่ฉันมีบางอย่างที่ใช้งานได้ตอนนี้ฉันแค่ต้องคิดออกว่าทำไมมันถึงได้ผล! แต่ใช่นี่เป็นรูปแบบพื้นฐานที่ฉันมี ขอบคุณมาก.
Martyn

29
อีกหนึ่งคำตอบเล็ก ๆ น้อย ๆ : เมื่อคุณทำ mReceiver.setReceiver (null); ในวิธีการ onPause คุณควรทำ mReceiver.setReceiver (นี่); ในวิธีการ onResume มิเช่นนั้นคุณอาจไม่ได้รับกิจกรรมหากกิจกรรมของคุณดำเนินการต่อโดยไม่ถูกสร้างขึ้นใหม่
Vincent Mimoun-Prat

7
เอกสารไม่ได้บอกว่าคุณไม่จำเป็นต้องโทรหา StopSelf เพราะ IntentService ทำเพื่อคุณหรือไม่
Mikael Ohlson

2
@MikaelOhlson ที่ถูกต้องคุณควรไม่โทรstopSelfถ้าคุณ subclass IntentServiceเพราะถ้าคุณทำคุณจะสูญเสียการร้องขอที่รอดำเนินการใด ๆ IntentServiceไปเหมือนกัน
quietmint

1
IntentServiceจะฆ่าตัวตายเมื่อภารกิจเสร็จสิ้นดังนั้นจึงthis.stopSelf()ไม่จำเป็น
Euporie

17

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

การพัฒนาแอปพลิเคชันไคลเอนต์ Android REST
ผู้แต่ง: Virgil Dobjanschi
คำอธิบาย:

เซสชั่นนี้จะนำเสนอข้อพิจารณาทางสถาปัตยกรรมสำหรับการพัฒนาแอพพลิเคชั่น RESTful บนแพลตฟอร์ม Android มันมุ่งเน้นไปที่รูปแบบการออกแบบการรวมแพลตฟอร์มและปัญหาด้านประสิทธิภาพเฉพาะกับแพลตฟอร์ม Android

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


4
+1 สิ่งนี้มีข้อควรพิจารณาหลายประการที่คุณไม่เคยนึกถึงเมื่อเริ่มต้น
โทมัส Ahle

ใช่การจู่โจมครั้งแรกของฉันในการพัฒนาไคลเอนต์ที่เหลือเกือบจะเป็นคำอธิบายของเขาในสิ่งที่ไม่ควรทำ (โชคดีที่ฉันรู้ว่าสิ่งนี้ผิดก่อนที่ฉันจะดูสิ่งนี้) ฉันออกไปพูดสักหน่อย
Terrance

ฉันเห็นวิดีโอนี้มากกว่าหนึ่งครั้งและฉันใช้รูปแบบที่สอง ปัญหาของฉันคือฉันต้องใช้ธุรกรรมในรูปแบบฐานข้อมูลที่ซับซ้อนของฉันเพื่ออัปเดตข้อมูลในเครื่องจากข้อมูลใหม่ที่มาจากเซิร์ฟเวอร์และส่วนติดต่อ ContentProvider ไม่ได้ให้วิธีฉันในการทำเช่นนั้น คุณมีข้อเสนอแนะ Terrance หรือไม่?
Flávio Faria

2
หมายเหตุ: ความคิดเห็นโดย Dobjanschi บน HttpClient ไม่ถูกระงับอีกต่อไป ดูstackoverflow.com/a/15524143/939250
Donal Lafferty

ใช่HttpURLConnectionเป็นที่ต้องการในขณะนี้ นอกจากนี้ด้วยการเปิดตัว Android 6.0, สนับสนุนสำหรับ Apache HTTP ไคลเอนต์ได้อย่างเป็นทางการถูกลบออก
RonR

16

นอกจากนี้เมื่อฉันกดโพสต์ (Config.getURL ("เข้าสู่ระบบ") ค่า) แอพดูเหมือนว่าจะหยุดชั่วขณะหนึ่ง (ดูเหมือนแปลก - คิดว่าความคิดที่อยู่เบื้องหลังบริการคือว่ามันทำงานบนกระทู้ที่แตกต่างกัน!)

ไม่ว่าคุณจะต้องสร้างเธรดด้วยตนเองบริการLocalจะทำงานในเธรด UI โดยค่าเริ่มต้น


11

ฉันรู้ว่า @Martyn ไม่ต้องการรหัสเต็ม แต่ฉันคิดว่าคำอธิบายประกอบนี้ดีสำหรับคำถามนี้:

แอพ Android 10 โอเพ่นซอร์สที่นักพัฒนาระบบ Android ทุกคนจะต้องค้นหา

Foursquared สำหรับ Android เป็นโอเพนซอร์ซและมีรูปแบบรหัสที่น่าสนใจที่โต้ตอบกับ foursquare REST API


6

ฉันอยากจะแนะนำลูกค้าส่วนที่เหลือติดตั้งเพิ่ม

ฉันพบว่าการโพสต์บล็อกที่เขียนได้ดีมีประโยชน์อย่างยิ่งมันยังมีโค้ดตัวอย่างง่าย ๆ ผู้เขียนใช้Retrofitในการโทรผ่านเครือข่ายและOttoเพื่อใช้รูปแบบบัสข้อมูล:

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


5

แค่ต้องการชี้ให้คุณทุกคนในทิศทางของคลาสสแตนด์อโลนที่ฉันหมุนซึ่งรวมเอาฟังก์ชันทั้งหมดไว้

http://github.com/StlTenny/RestService

มันดำเนินการตามคำขอเป็นที่ไม่ปิดกั้นและส่งกลับผลลัพธ์ในการดำเนินการจัดการที่ง่าย แม้มาพร้อมกับตัวอย่างการใช้งาน


4

ให้บอกว่าฉันต้องการเริ่มบริการในเหตุการณ์ - onItemClicked () ของปุ่ม กลไกตัวรับสัญญาณจะไม่ทำงานในกรณีนั้นเนื่องจาก: -
ก) ฉันส่งตัวรับไปยังบริการ (ตามที่ต้องการเพิ่ม) จาก onItemClicked ()
b) กิจกรรมย้ายไปที่พื้นหลัง ใน onPause () ฉันตั้งค่าการอ้างอิงผู้รับภายใน ResultReceiver เป็น null เพื่อหลีกเลี่ยงการรั่วไหลของกิจกรรม
c) กิจกรรมถูกทำลาย
d) กิจกรรมถูกสร้างขึ้นอีกครั้ง อย่างไรก็ตาม ณ จุดนี้บริการจะไม่สามารถโทรกลับไปที่กิจกรรมเนื่องจากการอ้างอิงของผู้รับนั้นหายไป
กลไกของการออกอากาศแบบ จำกัด หรือ PendingIntent ดูเหมือนว่าจะมีประโยชน์มากกว่าในกรณีดังกล่าว - อ้างถึงกิจกรรมแจ้งเตือนจากบริการ


1
มีปัญหากับสิ่งที่คุณพูด นั่นคือเมื่อกิจกรรมเคลื่อนไปที่พื้นหลังมันจะไม่ถูกทำลาย ... ดังนั้นผู้รับยังคงมีอยู่และบริบทของกิจกรรมก็เช่นกัน
DArkO

@DArkO เมื่อกิจกรรมหยุดชั่วคราวหรือหยุดการทำงานระบบ Android อาจถูกฆ่าในกรณีที่หน่วยความจำเหลือน้อย อ้างถึงกิจกรรมระยะเวลาการ
jk7

4

โปรดทราบว่าการแก้ปัญหาจาก Robby Pond ขาดไป: ด้วยวิธีนี้คุณอนุญาตให้ทำการโทร api ครั้งละหนึ่งครั้งเท่านั้นเนื่องจาก IntentService จัดการได้ครั้งละหนึ่งเจตนาเท่านั้น บ่อยครั้งที่คุณต้องการดำเนินการเรียก api แบบขนาน หากคุณต้องการทำสิ่งนี้คุณต้องขยายบริการแทนการใช้ IntentService และสร้างเธรดของคุณเอง


1
คุณยังสามารถทำการเรียกหลายครั้งบน IntentService ได้โดยมอบหมายการเรียก webservice api ให้กับ executor-thread-service ซึ่งเป็นตัวแปรสมาชิกของคลาส IntentService ที่ได้รับของคุณ
Viren

2

นอกจากนี้เมื่อฉันกดโพสต์ (Config.getURL ("เข้าสู่ระบบ") ค่า) แอพดูเหมือนว่าจะหยุดชั่วขณะหนึ่ง (ดูเหมือนแปลก - คิดว่าความคิดที่อยู่เบื้องหลังบริการคือว่ามันทำงานบนกระทู้ที่แตกต่างกัน!)

ในกรณีนี้การใช้ asynctask จะดีกว่าหากใช้เธรดอื่นและส่งคืนผลลัพธ์กลับไปยังเธรด ui เมื่อเสร็จสิ้น


2

มีวิธีการอื่นที่นี่โดยทั่วไปจะช่วยให้คุณลืมเกี่ยวกับการจัดการทั้งหมดของการร้องขอ มันขึ้นอยู่กับวิธีการคิว async และการตอบสนองตาม callable / callback ข้อได้เปรียบหลักคือการใช้วิธีการนี้คุณจะสามารถสร้างกระบวนการทั้งหมด (การร้องขอรับและแยกการตอบสนอง sabe ถึง db) โปร่งใสอย่างสมบูรณ์สำหรับคุณ เมื่อคุณได้รับรหัสตอบกลับงานก็จะเสร็จสิ้น หลังจากนั้นคุณเพียงแค่ต้องโทรไปยังฐานข้อมูลของคุณและคุณเสร็จแล้ว มันช่วยได้เช่นกันกับปัญหาที่เกิดขึ้นเมื่อกิจกรรมของคุณไม่ทำงาน สิ่งที่จะเกิดขึ้นที่นี่คือคุณจะต้องบันทึกข้อมูลทั้งหมดในฐานข้อมูลท้องถิ่นของคุณ แต่การตอบสนองจะไม่ได้รับการดำเนินการโดยกิจกรรมของคุณนั่นคือวิธีที่เหมาะสมที่สุด

ฉันเขียนเกี่ยวกับวิธีการทั่วไปที่นี่ http://ugiagonzalez.com/2012/07/02/theres-life-after-asynctasks-in-android/

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


ลิ้งค์ โดเมนหมดอายุ
jk7

1

Robby ให้คำตอบที่ดีแม้ว่าฉันจะเห็นคุณยังคงมองหาข้อมูลเพิ่มเติม ฉันใช้งาน REST api เรียกวิธีที่ง่าย แต่ผิด มันไม่ได้จนกว่าจะดูวิดีโอGoogle I / O นี้ที่ฉันเข้าใจว่าฉันไปผิดที่ มันไม่ง่ายเหมือนการรวม AsyncTask เข้ากับ HttpUrlConnection รับ / วางสาย


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