第三章 Robotium Solo API简介

简介:   Robotium是一个通用的测试Android App的框架,所以官方提供了一套通用的PKI文档。我们在下载Solo包的时候同时可以下载一个javadoc包,解压后就能看到官方文档了,不过官方文档是英文版本的,如果通读一遍的话也是挺有难度的。

  Robotium是一个通用的测试Android App的框架,所以官方提供了一套通用的PKI文档。我们在下载Solo包的时候同时可以下载一个javadoc包,解压后就能看到官方文档了,不过官方文档是英文版本的,如果通读一遍的话也是挺有难度的。还好网上也有对应的翻译,所以我们不打算讲述所有的API,只讲解一些儿常用的。

3.1 点击类操作

在手机App操作中,点击操作是非常常用的,下面几个API是基本的点击操作:

1clickOnText(String text)

clickOnText(java.lang.String text, int match)  clickOnText(java.lang.String text, int match,boolean scroll) 

功能:点击包含该文字的地方,其中text可以用正则表达式表示。如:(?i)----忽略大小写。默认情况是大小写敏感的,正则表达式与java保持一致。Scrolltrue如果可滚动Match:匹配

2clickLongOnText(String text)

clickLongOnText(java.lang.String text, int match)  clickLongOnText(java.lang.String text, intmatch, boolean scroll) clickLongOnText(java.lang.String text, int match, int time) 

功能:长按一个包含该文字的地方。

参数:

Scrolltrue如果可滚动,Time:长按的时间 Match:匹配

3)clickOnButton(intindex) 

clickOnButton(java.lang.Stringname)  

功能:以序号或是名称单击各种按钮,index代表这个按钮在view中的序号,name是属性值。

(4)clickOnImageButton(intindex)   

功能:以序号的方式单击一个图片按钮。

(5)clickOnCheckBox(intindex) 

功能:以序号的方式单击一个复选按钮。

6clickOnRadioButton(int index) 

功能:以序号来单击一个单选按钮。

7clickOnToggleButton(java.lang.String name)

功能:点击一个切换按钮

(8) clickOnEditText(int index)

功能:以序号来单击文本框,使其获得焦点,然后执行后序的操作。

9clickOnScreen(float x, float y)

clickLongOnScreen(float x, float y) 

    clickLongOnScreen(floatx, float y, int time) 

功能:单击屏幕的某一坐标。参数x,y标识出坐标的位置,time 代表长按的时间。

10clickLongOnTextAndPress(java.lang.String text, intindex) 

功能:长按后从list中选择一个项目 

11clickOnView(android.view.View view) clickLongOnView(android.view.View view) clickLongOnView(android.view.View view, int time) 

功能:单击一个View,或是长按一个View,参数time代码长按的时间。

12clickOnImage(int index)  

功能:以序号来执行单击图片操作。

13clickOnMenuItem(java.lang.String text) clickOnMenuItem(java.lang.String text, boolean subMenu) 

功能:以菜单项来单击选择菜单,参数subMenu:子菜单,如果可以设置在子菜单中 

3.2 输入类操作

  对要操作的App进行输入操作,这也是常见的操作,Robotium输入操作有如下几种:

(1)voidenterText(android.widget.EditText editText, String text)

  功能:向一个符合条件的EditText输入特定的文本。

(2)voidenterText(int index, String text)

  功能:对特定的符合序号index的文本框输入文本。

(3)voidenterTextInWebElement(By by, String text)

   功能:通过By定位,对页面元素输入文本。

4void clearEditText(android.widget.EditText editText)

  功能:清除特定的文本框中的内容。

5void clearEditText(int index)

  功能:清除符合序号index的文本框中的内容。

6void clearLog()

  功能:清除日志文件

7void clearTextInWebElement(By by)

  功能:清除页面元素文本框内的信息。

这些儿是常用的输入操作,为了不影响操作效果,建议在对文本框进行输入之前,先清空一下。当然像菜单选择,单选框,多选框也能输入的,这些儿操作在上一节已经讲过。

3.3 Get相关操作

在对App元素进行操作之前,首先要获取这个元素的句柄,然后再去执行相关的操作。所以获取到要操作的元素是第一步的,这也是自动化操作最基本的。

1getButton(int index) 

getButton(java.lang.String text)  

getButton(java.lang.String text, booleanonlyVisible) 

功能:通过序号,文本获取按钮,参数onlyVisibletrue时只获取可见的按钮。

2getCurrentActivity() 

功能:返回当前activity

3getCurrentButtons() 

功能:返回buttonlist

(4)getCurrentCheckBoxes() 

功能:返回复选框的list

5getCurrentDatePickers() 

功能:返回日期选择器的list

6 getCurrentEditTexts() 

功能:返回当前文本框的list

7getCurrentGridViews()  

功能:获取当前网格视图List

8 getCurrentImageButtons()

getImageButton(intindex)

功能:获取当前图片按钮list;根据序号获取图片按钮。

9getCurrentImageViews() 

功能:获取当前ImageView列表。

10getCurrentListViews()

功能:获取当前ListView列表。

11getCurrentProgressBars() 

功能:获取当前进度条信息。

12getCurrentRadioButtons() 

功能:获取当前单选按钮列表。

13getCurrentScrollViews() 

功能:获取当前滚动条列表。

14getCurrentTextViews(android.view.View parent) 

功能:获取当前TextView的列表。

15getCurrentViews()

getView(int id) getViews()  

getViews(android.view.View parent)   

功能:获取当前View,或是所有View的列表。 

16getEditText(int index) 

getEditText(java.lang.String text)  

getEditText(java.lang.String text, booleanonlyVisible)  

功能:根据序号,文本获取EditText的句柄。

17getText(int index) 

getText(java.lang.Stringtext)  

getText(java.lang.Stringtext, boolean onlyVisible) 

功能:获取文本

3.4 Search相关操作

   在要操作的App中查找到相关的元素或是文本,这个是判读执行结果的时候很重要的一步作,下面我们将常用的查找元素的方法介绍一下:

1boolean searchButton (String text [, int minimumNumberOfMatches,boolean onlyVisible])

功能:判断当前的屏幕中是否能找到指定的button

参数:

text-查找的button的文字

minimumNumberOfMatches-最小指定多少才算是通过,0表示1个或者多个

onlyVisible-只记录可见的

返回:

true-如果找到了该控件

false-如果没有找到该控件

2boolean searchText (String text [, intminimumNumberOfMatches, boolean scroll, boolean onlyVisible])

功能:判断当前的屏幕中是否能找到指定的text,即文本。

参数:

text-查找的Text的文字

minimumNumberOfMatches-最小指定多少才算是通过,0表示1个或者多个

scroll-是否允许滚动搜索,true表示支持,false表示只能在当前屏幕内查找

onlyVisible-只记录可见的

返回:

true-如果找到了该文本

false-如果没有找到该文本

3boolean searchToggleButton (String text [, int minimumNumberOfMatches])

功能:判断当前的屏幕中是否能找到指定的ToggleButton

参数:text-查找的ToggleButton的文字。

minimumNumberOfMatches-最小指定多少才算是通过,0表示1个或者多个。

返回:

true-如果找到了该控件

false-如果没有找到该控件

4boolean searchEditText (String text)

判断当前的屏幕中是否能找到指定的EditText

参数:text-查找的Text的文字

返回:

true-如果找到了该文本

false-如果没有找到该文本

3.5 Assert断言相关操作

   断言是自动化测试的关键,任何测试操作都是由断言来检测用例执行的是否正确。好的断言设置是体现自动化测试人员水平的关键,我们还是先了解一下robotium的断言函数吧!

1void assertCurrentActivity (String message, ClassactivityClass [,boolean isNewInstance])

功能:断言当前的activity是否是预期的

参数:message-如果运行失败打印的消息。

activityClass-预期的activity

isNewInstance-可选项,实际activity是否是预期activity的继承,如果为true则只要是继承关系就通过,false则必须是当前activity

返回:无

2void assertCurrentActivity (String message, Stringname [,boolean isNewInstance])

功能:断言当前的activity是否是预期的

参数:

message-如果运行失败打印的消息

name-预期activity的名字

isNewInstance-可选项,实际activity是否是预期activity的继承,如果为true则只要是继承关系就通过,false则必须是当前activity

返回:无

3void assertMemoryNotLow()

功能:断言目前系统可用内存是否过低,内存空间足够则通过

返回:无

is方法,虽然不是断言,但可以当断言来用。

4boolean isCheckBoxChecked (int index | String text)

功能:判断checkBox是否处于被选中的状态,可以通过indextext两种方法定位

参数:

index-检查的checkBox的索引值,如果只有一个可用则为0

text-检查的checkBox的文字,可使用正则表达式

返回:

true-如果被选中

false-如果没有被选中

5boolean isRadioButtonChecked (int index | String text)

功能:判断RadioButton是否处于被选中的状态,可以通过indextext两种方法定位

参数:

index-检查的RadioButton的索引值,如果只有一个可用则为0

text-检查的RadioButton的文字,可使用正则表达式

返回:

true-如果被选中

false-如果没有被选中

6boolean isRadioButtonChecked (int index | String text)

功能:判断RadioButton是否处于被选中的状态,可以通过indextext两种方法定位

参数:

index-检查的RadioButton的索引值,如果只有一个可用则为0

text-检查的RadioButton的文字,可使用正则表达式

返回:

true-如果被选中

false-如果没有被选中

7boolean isToggleButtonChecked (int index | Stringtext)

功能:判断ToggleButton是否处于被选中的状态,可以通过indextext两种方法定位。

参数:

index-检查的ToggleButton的索引值,如果只有一个可用则为0

text-检查的ToggleButton的文字,可使用正则表达式。

返回:

true-如果被选中

false-如果没有被选中

8boolean isTextChecked (String text)

功能:判断text是否处于被选中的状态,可以通过text定位

参数:text-检查的text的文字,可使用正则表达式

返回:

true-如果被选中

false-如果没有被选中

3.6 本章小结

  本章我们介绍了Robotium API,solo相关函数,这些儿只是基本的,常用的,但是还有很多相关的函数,随着版本的更新,会增加新的方法。所以建议大家去官方下载最新的API文档,随时了解最新的函数情况,以便更好的编写自己的自动化测试代码。本章我们先了解有哪些儿方法,其功能是什么,参数有哪些儿,致于如何使用,下面的章节我们将会慢慢地介绍到。

我的demo


package com.slicejobs.ailinggong.net;

import android.app.Activity;
import android.app.ActivityManager;
import android.app.Instrumentation;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.support.v7.widget.RecyclerView;
import android.test.ActivityInstrumentationTestCase2;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.FrameLayout;
import android.widget.LinearLayout;
import android.widget.RelativeLayout;
import android.widget.TextView;
import android.widget.Toast;

import com.markettask.android.R;
import com.robotium.solo.Solo;
import com.slicejobs.ailinggong.ui.activity.AddMarketActivity;
import com.slicejobs.ailinggong.ui.activity.ChooseCityActivity;
import com.slicejobs.ailinggong.ui.activity.LoginActivity;
import com.slicejobs.ailinggong.ui.activity.MainActivity;
import com.slicejobs.ailinggong.ui.activity.MyCameraActivity;
import com.slicejobs.ailinggong.ui.activity.SettingActivity;
import com.slicejobs.ailinggong.ui.activity.TabTaskActivity;
import com.slicejobs.ailinggong.ui.activity.TaskDetailActivity;
import com.slicejobs.ailinggong.ui.activity.TaskStepsJumpActivity;
import com.slicejobs.ailinggong.ui.activity.UploadCacheActivity;
import com.slicejobs.ailinggong.util.Logger;

import junit.framework.Assert;

/**
 * Created by keller.zhou on 16/11/22.
 */
public class MainActivityTest extends ActivityInstrumentationTestCase2<MainActivity> {
    private Solo solo;

    public MainActivityTest() {
        super(MainActivity.class);
    }

    public void setUp() throws Exception {
        super.setUp();
        solo = new Solo(getInstrumentation(), getActivity());
    }


    public void testMainActivity() throws  Exception{
        solo.unlockScreen();//屏幕解锁

        solo.waitForActivity(MainActivity.class, 1000);//入口等待首页被唤醒


        if (clickMe()) {//点击个人中心
            LinearLayout setting = (LinearLayout) solo.getView(R.id.action_setting);
            solo.clickOnView(setting);//点击设置

            solo.waitForActivity(SettingActivity.class, 1000);
            changeSliceCamera();//修改爱零工相机
            solo.sleep(500);
            testLogout();//退出登陆
            solo.waitForActivity(LoginActivity.class, 1000);
            testLogin();//登陆

            solo.waitForActivity(MainActivity.class, 1000);

            TextView cityTxt = (TextView) solo.getView(R.id.action_city);//选择城市
            solo.clickOnView(cityTxt);

            solo.waitForActivity(ChooseCityActivity.class, 1000);
            testSelectCity();//选择城市
            if (clickMe()) {//点击个人中心
                testAddMarket();//切换门店-爱零工
            }


            clickHome();//点击首页
            LinearLayout marketTask = (LinearLayout) solo.getView(R.id.hall_cell);
            solo.clickOnView(marketTask);//点击门店任务
            //遍历查找任务
            searchTask(null, null);
            solo.waitForActivity(TaskDetailActivity.class, 500);//打开任务详情
            solo.sleep(1000);//等待刷新任务详情

            Button btVieTask = (Button) solo.getView(R.id.btn_start);
            solo.clickOnView(btVieTask);
            solo.sleep(10000);//抢单等待10秒
            solo.clickOnView(btVieTask);
            if (startTask()){
                doTask();
            } else {
                Activity act =  solo.getCurrentActivity();//得到当前activity
                Context context = act.getApplicationContext();
                Toast.makeText(context, "签到验证不通过,程序终止!", Toast.LENGTH_SHORT);
            }

        }

        solo.sleep(10000);
    }






    /**
     * 测试登陆界面
     */
    private void testLogin() {
        solo.sleep(1000);
        EditText etPhone = (EditText)solo.getView(R.id.et_telephone);//用户名
        solo.enterText(etPhone, "15921621403");//向输入框,输入用户名
        solo.sleep(1000);
        EditText etPassword = (EditText)solo.getView(R.id.et_password);//密码
        solo.enterText(etPassword, "7777777");
        solo.sleep(1000);
        Button btLogin = (Button)solo.getView(R.id.btn_login);//登陆按钮
        solo.clickOnView(btLogin);//点击登陆按钮
    }

    /**
     * 测试退出登陆界面
     */
    private void testLogout() {
        FrameLayout logout = (FrameLayout) solo.getView(R.id.logout);
        solo.clickOnView(logout);
        Button b = (Button) solo.getView(R.id.queding);
        solo.clickOnView(b);
    }


    /**
     * 测试添加门店
     */
    private void testAddMarket() {
        RelativeLayout layout = (RelativeLayout) solo.getView(R.id.my_market);//关注门店
        solo.clickOnView(layout);

        solo.waitForActivity(AddMarketActivity.class, 1000);
        RecyclerView myMarket = (RecyclerView) solo.getView(R.id.market_list);//获取list
        solo.waitForView(myMarket, 1000, true);
        //solo.scrollListToLine(practiceTypeList, selectIndex);
        View addMarket = myMarket.getChildAt(1);
        solo.clickOnView(addMarket);

        Context context = solo.getCurrentActivity().getApplicationContext();
        solo.waitForFragmentById(R.id.container, 1000);
        TextView cityText = (TextView) solo.getView(R.id.action_city);
        if (cityText.getText().equals(context.getResources().getString(R.string.choose_city))) {
            solo.clickOnView(cityText);
            solo.waitForActivity(ChooseCityActivity.class, 1000);
            testSelectCity();
        }

        EditText etSearch = (EditText) solo.getView(R.id.et_search_market);
        solo.enterText(etSearch, "爱零工");
        solo.sleep(1000);

        RecyclerView rvMarketList = (RecyclerView) solo.getView(R.id.market_list_data);

        solo.waitForView(rvMarketList, 1000, true);
        View marketView = rvMarketList.getChildAt(0);

        solo.sleep(1000);

        solo.clickOnView(marketView);
        solo.sleep(500);
        TextView tvGoBack = (TextView) solo.getView(R.id.action_redo);
        solo.clickOnView(tvGoBack);
    }

    /**
     * 测试选择城市
     */
    private void testSelectCity() {
        EditText cityKeyword = (EditText) solo.getView(R.id.city_search);
        solo.enterText(cityKeyword, "上海");
        solo.sleep(1000);

        RecyclerView citys = (RecyclerView) solo.getView(R.id.city_list);
        solo.waitForView(citys, 1000, true);

        View cityView = citys.getChildAt(1);
        solo.clickOnView(cityView);

        if (solo.waitForDialogToOpen(1000)) {
            Button defineChange = (Button) solo.getView(R.id.dialog_define);
            solo.clickOnView(defineChange);
        } else {
            Logger.d("----------", "没有弹出对话框");
        }
    }


    /**
     * 点击个人中心
     */
    private boolean clickMe() {
        TextView me = (TextView)solo.getView(R.id.tab_me);
        solo.clickOnView(me);//点击个人中心
        Activity act =  solo.getCurrentActivity();//得到当前activity
        //ApplicationInfo appInfo = act.getApplicationInfo();
        Context context = act.getApplicationContext();
        return solo.waitForFragmentByTag(context.getString(R.string.title_me));
    }

    /**
     * 点击首页
     */
    private boolean clickHome() {
        TextView me = (TextView)solo.getView(R.id.tab_home);
        solo.clickOnView(me);//点击个人中心
        Activity act =  solo.getCurrentActivity();//得到当前activity
        //ApplicationInfo appInfo = act.getApplicationInfo();
        Context context = act.getApplicationContext();
        return solo.waitForFragmentByTag(context.getString(R.string.title_home));
    }

    /**
     * 点击我的任务
     */
    private void clickMyTask() {

    }

    /**
     * 遍历所有任务
     */
    private void searchTask(String userid, String title) {

        RecyclerView taskList = (RecyclerView) solo.getView(R.id.task_list);

        solo.scrollRecyclerViewToBottom(0);//滑动到底部
        solo.sleep(1000);
        int itemCount = taskList.getChildCount();

        int flag =itemCount - 1;

        View taskView = taskList.getChildAt(flag);

        solo.clickOnView(taskView);
    }

    /**
     * 开始签到
     */
    private boolean startTask() {
        solo.sleep(500);
        Activity act =  solo.getCurrentActivity();//得到当前activity
        Context context = act.getApplicationContext();
        TextView tvStartStatus = (TextView) solo.getView(R.id.tv_check_is_success);
        if (tvStartStatus.getText().equals(context.getResources().getString(R.string.check_in_success))) {//签到成功
            Button btKnow = (Button) solo.getView(R.id.btn_confirm_check_in);
            solo.clickOnView(btKnow);
            solo.waitForActivity(TaskStepsJumpActivity.class, 1000);
            return true;
        } else if (tvStartStatus.getText().equals(context.getResources().getString(R.string.sign_in_fail))) {//签到失败
            Button btServerCheck = (Button) solo.getView(R.id.btn_again_check);
            solo.clickOnView(btServerCheck);//服务器签到
            if (solo.waitForActivity(TaskStepsJumpActivity.class, 1000)) {//服务器签到成功
                    return true;
            }
        }

        return false;
    }

    /**
     * 做任务
     */
    private void doTask() {
        RecyclerView photosList = (RecyclerView) solo.getView(R.id.step_result_photos);
        solo.waitForView(photosList, 1000, true);
        View photoitem = photosList.getChildAt(0);
        solo.clickOnView(photoitem);

        Button btupload = (Button) solo.getView(R.id.btn_upload);
        solo.clickOnView(btupload);

        if (solo.waitForDialogToOpen(1000)) {//弹出提示相机权限
//            Logger.d("-------------", "询问权限");
//            Button defineChange = (Button) solo.getView(R.id.dialog_define);
//            solo.clickOnView(defineChange);
        }

        doCameraPick();
        Button btNext = (Button) solo.getView(R.id.btn_next_step);
        solo.clickOnView(btNext);
        //第二题
        EditText etText = (EditText) solo.getView(R.id.step_text);
        solo.enterText(etText, "Test Text");

        solo.clickOnView(photoitem);

        Button btupload2 = (Button) solo.getView(R.id.btn_upload);
        solo.clickOnView(btupload2);

        doCameraPick();

        solo.clickOnView(btNext);
        //第三题(单选)
        LinearLayout optionLayout = (LinearLayout) solo.getView(R.id.step_options_container);
        int count = optionLayout.getChildCount();
        if (count > 0) {
            View option = optionLayout.getChildAt(0);
            solo.clickOnView(option);
        }
        //第四题
        solo.clickOnView(btNext);
        LinearLayout optionLayout2 = (LinearLayout) solo.getView(R.id.step_options_container);
        int count2 = optionLayout2.getChildCount();
        if (count2 > 0) {
            View option = optionLayout2.getChildAt(0);
            View option2 = optionLayout2.getChildAt(1);
            solo.clickOnView(option);
            solo.clickOnView(option2);
        }
        solo.clickOnView(photoitem);

        Button btupload4= (Button) solo.getView(R.id.btn_upload);
        solo.clickOnView(btupload4);

        doCameraPick();
        //第五题 (多项填空题)
        solo.clickOnView(btNext);
        Button btMText = (Button) solo.getView(R.id.bt_open_mtext);
        solo.clickOnView(btMText);
        solo.waitForActivity(TabTaskActivity.class, 1000);
        RecyclerView rvTabTaskList = (RecyclerView) solo.getView(R.id.table_task_list);
        int count3 = rvTabTaskList.getChildCount();
        for (int index = 0; index < count3; index ++) {
            solo.enterText(index, "Test Text");
        }

        Button tabFinish = (Button) solo.getView(R.id.btn_finish);
        solo.clickOnView(tabFinish);

        //点击提交
        solo.clickOnView(btNext);

        //跳到照片上传界面
        solo.waitForActivity(UploadCacheActivity.class, 1000);

        Button btUpload = (Button) solo.getView(R.id.btn_start_upload);
        solo.clickOnView(btUpload);


        if (solo.waitForDialogToOpen(20000)) {//监听20秒内上传完
            Button taskFinish = (Button) solo.getView(R.id.dialog_define);
            solo.clickOnView(taskFinish);
        }

    }

    private void doCameraPick() {
        solo.waitForActivity(MyCameraActivity.class, 1000);
        TextView pick= (TextView) solo.getView(R.id.btn_shutter_camera);
        solo.clickOnView(pick);
        solo.sleep(200);
        Button btconfirm = (Button) solo.getView(R.id.bt_confirm_photo);
        solo.clickOnView(btconfirm);
        solo.sleep(1000);
        Button exitCmaera = (Button) solo.getView(R.id.bt_exit_camera);
        solo.clickOnView(exitCmaera);

    }


    /**
     * 切换到爱零工相机
     */
    private void changeSliceCamera() {
        RelativeLayout modifyCmaera = (RelativeLayout) solo.getView(R.id.modify_camera_type);
        solo.clickOnView(modifyCmaera);

        if (solo.waitForDialogToOpen(1000)) {
            TextView tvSliceCamera = (TextView) solo.getView(R.id.tv_slicejobs_camera);
            solo.clickOnView(tvSliceCamera);//点击爱零工相机
        } else {
            Logger.d("----------", "没有弹出对话框");
        }
    }













}

注意遇到的坑,如果你项目有多个dex打包,需要在项目build.gradle  android{   里面  }添加

project.getConfigurations().all { config ->
    if (config.name.contains("AndroidTest")) {
        config.resolutionStrategy.eachDependency { DependencyResolveDetails details ->
            if (details.requested.name == "multidex") {
                details.useTarget("de.felixschulze.teamcity:teamcity-status-message-helper:1.2")
            }
        }
    }
}



目录
相关文章
|
1月前
|
分布式计算 API 数据处理
Flink【基础知识 01】(简介+核心架构+分层API+集群架构+应用场景+特点优势)(一篇即可大概了解flink)
【2月更文挑战第15天】Flink【基础知识 01】(简介+核心架构+分层API+集群架构+应用场景+特点优势)(一篇即可大概了解flink)
60 1
|
3月前
|
JavaScript 前端开发 IDE
Vue3【为什么选择Vue框架、Vue简介 、Vue API 风格 、Vue开发前的准备 、Vue项目目录结构 、模板语法、属性绑定 、 】(一)-全面详解(学习总结---从入门到深化)
Vue3【为什么选择Vue框架、Vue简介 、Vue API 风格 、Vue开发前的准备 、Vue项目目录结构 、模板语法、属性绑定 、 】(一)-全面详解(学习总结---从入门到深化)
50 1
|
9月前
|
Java API
Java 反射 API 简介
通常来说,使用反射 API 的第一步便是获取 Class 对象。在 Java 中常见的有这么三种。
66 0
|
8月前
|
前端开发 API
Promise(简介、基本使用、API、手写实现 Promise、async与await)(六)
Promise(简介、基本使用、API、手写实现 Promise、async与await)(六)
|
8月前
|
前端开发 API
Promise(简介、基本使用、API、手写实现 Promise、async与await)(五)
Promise(简介、基本使用、API、手写实现 Promise、async与await)(五)
|
8月前
|
前端开发 API
Promise(简介、基本使用、API、手写实现 Promise、async与await)(四)
Promise(简介、基本使用、API、手写实现 Promise、async与await)(四)
|
8月前
|
前端开发 API
Promise(简介、基本使用、API、手写实现 Promise、async与await)(三)
Promise(简介、基本使用、API、手写实现 Promise、async与await)(三)
|
8月前
|
前端开发 API
Promise(简介、基本使用、API、手写实现 Promise、async与await)(二)
Promise(简介、基本使用、API、手写实现 Promise、async与await)(二)
|
8月前
|
前端开发 JavaScript API
Promise(简介、基本使用、API、手写实现 Promise、async与await)(一)
Promise(简介、基本使用、API、手写实现 Promise、async与await)
|
10月前
|
JSON 安全 API
【淘宝/天猫】商品快递费用调用API接口封装简介
淘宝/天猫是国内最大的电商平台之一,每天都有数以亿计的人通过淘宝/天猫购买商品,而随着电商业务的不断发展,物流也成为了电商平台中不可或缺的一环。物流的速度和成本直接影响到消费者购物体验以及商家的盈利能力。为了让消费者和商家更好地掌握物流成本,淘宝/天猫提供了淘宝商品快递费用 API调用接口,方便开发者和商家获取淘宝商品的快递费用。