文档章节

Robolectric介绍

磁针石
 磁针石
发布于 2016/03/03 14:17
字数 2147
阅读 493
收藏 2
点赞 1
评论 0

简介

在Android模拟器或设备运行测试很慢!构建,部署和启动应用程序往往需要一分钟或更长时间。
在Android直接从您的IDE内部运行测试岂不是很好?但容易碰到java.lang.RuntimeException: Stub!
Robolectric不依赖Android SDK jar的安卓单元测试框架,测试在JVM上运行,可以在几秒内完成。
示例:

@RunWith(RobolectricTestRunner.class)
public class MyActivityTest {

  @Test
  public void clickingButton_shouldChangeResultsViewText() throws Exception {
    MyActivity activity = Robolectric.setupActivity(MyActivity.class);

    Button button = (Button) activity.findViewById(R.id.button);
    TextView results = (TextView) activity.findViewById(R.id.results);

    button.performClick();
    assertThat(results.getText().toString()).isEqualTo("Robolectric Rocks!");
  }
}


Robolectric改写Android SDK中的类,使他们可能在普通的JVM上运行。
Robolectric视图和资源及本地C代码实现等东东加载,很容易地提供我们自己的特定的SDK方法实现,比如模拟错误情况或现实世界的sensor行为。

运行测试模拟器之外
Robolectric,您可以在工作站上运行测试,或在常规JVM持续集成环境,没有一个模拟器。正因为如此,德兴,包装和安装 - 上的仿真器的步骤是没有必要的,减少分钟的测试周期秒钟,这样可以快速迭代,并有信心重构代码。
Robolectric的风格更接近黑盒测试,通常不需要Mockito等模拟框架,当然依旧可以与Mockito配合使用。


快速入门

Robolectric与Gradle或Maven配合比较好。新项目推荐使用Gradle。

build.gradle

testCompile“org.robolectric:robolectric:3.0”


测试使用注解即可:

@RunWith(RobolectricGradleTestRunner.class)
@Config(constants = BuildConfig.class)
public class SandwichTest {
}


注意必须指定constants指向由生成系统生成的BuildConfig.class。Robolectric使用类的常量来计算输出路径(Gradle使用), 否则Robolectric将无法找到manifest,resources或asset。

使用Maven构建

pom.xml:

<dependency>
   <groupId>org.robolectric</groupId>
   <artifactId>robolectric</artifactId>
   <version>3.0</version>
   <scope>test</scope>
</dependency>

测试使用注解即可:

@RunWith(RobolectricTestRunner.class)
public class SandwichTest {
}


如果你引用项目以外的资源(即在AAR依赖)项目以外的资源,需要提供Robolectric一个指针指向AAR在构建系统。

Android Studio

Robolectric适用于Android Studio 1.1.0及以后版本。在"Build Variants" 中开启 unit test支持即可。Linux和Mac中需要编辑“run configurations”,设置工作目录为$MODULE_DIR$。

Android Enable Unit Tests

Android Studio Configure Defaults

Eclipse已经不推荐使用,但是可以安装m2e-android(不支持AAR)插件,导入Maven 工程, 点击 "Plugin execution not covered by lifecycle configuration"。
示例工程参见:https://github.com/robolectric/robolectric-samples

第一个测试

activity的layout如下:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <Button
        android:id="@+id/login"
        android:text="Login"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"/>

</LinearLayout>



我们要编写测试断言当用户点击按钮时应用程序启动LoginActivity。

public class WelcomeActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.welcome_activity);

        final View button = findViewById(R.id.login);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                startActivity(new Intent(WelcomeActivity.this, LoginActivity.class));
            }
        });
    }
}



检查启动了对应的intent即可:

@RunWith(RobolectricTestRunner.class)
public class WelcomeActivityTest {

    @Test
    public void clickingLogin_shouldStartLoginActivity() {
        WelcomeActivity activity = Robolectric.setupActivity(WelcomeActivity.class);
        activity.findViewById(R.id.login).performClick();

        Intent expectedIntent = new Intent(activity, WelcomeActivity.class);
        assertThat(shadowOf(activity).getNextStartedActivity()).isEqualTo(expectedIntent);
    }
}

注意:目前Robolectric只有API 16/19/21的实例。貌似还不支持API 23等。上述代码不能实际执行,实际执行的参见demo。

后期有空会把上面代码串成实例。


配置Robolectric

  • 配置注解

定制Robolectric的主要方式是通过@Config注解。注释可以应用到的类和方法,同事也可以在基类中定制。

  • 配置SDK级别

Robolectric基于manifest的targetSdkVersion运行代码,可更改设置SDK版本:

@Config(sdk = Build.VERSION_CODES.JELLY_BEAN)
public class SandwichTest {

    @Config(sdk = Build.VERSION_CODES.KITKAT)
    public void getSandwich_shouldReturnHamSandwich() {
    }
}
  • 配置应用程序类

@Config(application = CustomApplication.class)
public class SandwichTest {

    @Config(application = CustomApplicationOverride.class)
    public void getSandwich_shouldReturnHamSandwich() {
    }
}
  • 配置资源路径

Gradle和Maven有默认值,也可自定义:

@Config(manifest = "some/build/path/AndroidManifest.xml")
public class SandwichTest {

    @Config(manifest = "other/build/path/AndroidManifest.xml")
    public void getSandwich_shouldReturnHamSandwich() {
    }
}

默认资源目录res和assets。通过添加resourceDir和assetDir选项到@Config可改变这些值。

  • 配置属性

@Config注解的内容可以在文件中定义robolectric.properties:

sdk=18
manifest=some/build/path/AndroidManifest.xml
shadows=my.package.ShadowFoo,my.package.ShadowBar
  • 系统属性

robolectric.offline - Set to true to disable runtime fetching of jars.
robolectric.dependency.dir - When in offline mode, specifies a folder containing runtime dependencies.
robolectric.dependency.repo.id - Set the ID of the Maven repository to use for the runtime dependencies (default sonatype).
robolectric.dependency.repo.url - Set the URL of the Maven repository to use for the runtime dependencies (default https://oss.sonatype.org/content/groups/public/).
robolectric.logging.enabled - Set to true to enable debug loggin

Gradle可在all部分为单元测试配置系统属性。例如,覆盖Maven仓库URL和ID:

android {
  testOptions {
    unitTests.all {
      systemProperty 'robolectric.dependency.repo.url', 'https://local-mirror/repo'
      systemProperty 'robolectric.dependency.repo.id', 'local'
    }
  }
}



参考资料:
http://tools.android.com/tech-docs/unit-testing-support


Activity的生命周期

Robolectric2.2之前,大多数的测试直接调用构造函数 (new MyActivity()),然后手动调用的生命周期方法,如OnCreate()。ShadowActivity的方法(例如ShadowActivity.callOnCreate())也经常使用,他们是的ActivityController的前身。ActivityController在Robolectric2.0中引入的。

现在不会直接创建ActivityController,而是使用Robolectric.buildActivity()开始。通常可以这样:

Activity activity = Robolectric.buildActivity(MyAwesomeActivity.class).create().get();

上面创建MyAwesomeActivity实例并调用onCreate()。

要检查onResume()也简单:

ActivityController controller = Robolectric.buildActivity(MyAwesomeActivity.class).create().start();
Activity activity = controller.get();
// assert that something hasn't happened
activityController.resume();
// assert it happened!

类似的方法也包括start(), pause(), stop(), and destroy(),比如测试整个创建生命周期:

Activity activity = Robolectric.buildActivity(MyAwesomeActivity.class).create().start().resume().visible().get();

可以带intent启动Activity:

Intent intent = new Intent(Intent.ACTION_VIEW);
Activity activity = Robolectric.buildActivity(MyAwesomeActivity.class).withIntent(intent).create().get();

或恢复已保存实例的状态:

Bundle savedInstanceState = new Bundle();
Activity activity = Robolectric.buildActivity(MyAwesomeActivity.class)
    .create()
    .restoreInstanceState(savedInstanceState)
    .get();

    
更多资料参见http://robolectric.org/javadoc/latest/org/robolectric/util/ActivityController.html。


附加模块


为了减少对应用外部依赖,Robolectric的shadow被分成各种附加包。主Robolectric模块只提供基础的Android SDK提供的shadow。

SDK Package Robolectric Add-On Package
com.android.support.support-v4 org.robolectric:shadows-support-v4
com.android.support.multidex org.robolectric:shadows-multidex
com.google.android.gms:play-services org.robolectric:shadows-play-services
com.google.android.maps:maps org.robolectric:shadows-maps
org.apache.httpcomponents:httpclient org.robolectric:shadows-httpclient


Acitvity 真实实例

开发代码:

import android.app.Activity;
import android.os.*;
import android.view.View;
import android.widget.Toast;

import com.oppo.acs.st.STManager;
import com.oppo.acs.st.demo.R;

import java.util.HashMap;
import java.util.Map;

public class MainActivity extends Activity {
    /**
    Those data is just for test.
     */
    private static final String DATA_TYPE_EXPOSE="cpd-app-expose";
    private static final String DATA_TYPE_CLICK="cpd-app-click";
    private static final String DATA_TYPE_DOWNLOAD="cpd-app-down";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main_activity);
    }

    public void onBnExpose(View view){
        Toast.makeText(this, "expose", Toast.LENGTH_SHORT).show();
        /**
         *Record the expose event.
         */
        STManager.getInstance().onEvent(this, wrapKeyMap(view.getId()));
    }

    public void onBnClick(View view){
        Toast.makeText(this, "click", Toast.LENGTH_SHORT).show();
        /**
         *Record the click event.
         */
        STManager.getInstance().onEvent(this, wrapKeyMap(view.getId()));
    }

    public void onBnDownload(View view){
        Toast.makeText(this,"download", Toast.LENGTH_SHORT).show();
        /**
         *Record the download event.
         */
        STManager.getInstance().onEvent(this, wrapKeyMap(view.getId()));
    }

    public void onBnBatchExpose(View view){
        Toast.makeText(this,"batch expose.", Toast.LENGTH_SHORT).show();
        int i=0;
        while (i++<100){
            STManager.getInstance().onEvent(this,wrapKeyMap(view.getId()));
        }
    }

    private Map<String,String> wrapKeyMap(int viewId){
        Map<String,String> keyMap=new HashMap<String,String>();
        //those data is just for test.
        keyMap.put(STManager.KEY_ENTER_ID,"demo");
        keyMap.put(STManager.KEY_TAB_ID,"MainActivity");
        keyMap.put(STManager.KEY_AD_POS_ID,"1001");
        keyMap.put(STManager.KEY_CATEGORY_ID,"10001");
        keyMap.put(STManager.KEY_PAR_TAB_ID,"1001");
        keyMap.put(STManager.KEY_PAR_POS_ID,"10001");
        keyMap.put(STManager.KEY_AD_ID,"200");
        keyMap.put(STManager.KEY_AD_TYPE,"cpd");
        keyMap.put(STManager.KEY_AD_OWNER,"OPPO");
        keyMap.put(STManager.KEY_CONTENT_ID,"contentId");
        keyMap.put(STManager.KEY_CONTENT_CLS,"test");
        keyMap.put(STManager.KEY_CONTENT_SIZE,"20K");
        keyMap.put(STManager.KEY_PLAN_ID,"100");
        keyMap.put(STManager.KEY_PRICE,"200.00");
        keyMap.put(STManager.KEY_PAR_EVENT_ID,"001");
        keyMap.put(STManager.KEY_TRACE_ID,"001");
        keyMap.put(STManager.KEY_AB_TEST,"ATest");
        keyMap.put(STManager.KEY_GRADE,"good");
        keyMap.put(STManager.KEY_PROPERTY,"test demo");
        keyMap.put(STManager.KEY_DISPLAY_TIME, String.valueOf(System.currentTimeMillis()));
        //
        keyMap.put(STManager.KEY_MODULE_ID,"Test");
        keyMap.put(STManager.KEY_PAR_MODULE_ID,"Test_Par");
        keyMap.put(STManager.KEY_CHANNEL,"Test");
        keyMap.put(STManager.KEY_APP_ID,"1001");
        //
        switch (viewId){
            case R.id.expose_bn:
            case R.id.batch_expose_bn:
                /**
                 *Data type of expose event.
                 */
                keyMap.put(STManager.KEY_DATA_TYPE, DATA_TYPE_EXPOSE);
                break;
            case R.id.click_bn:
                /**
                 *Data type of click event.
                 */
                keyMap.put(STManager.KEY_DATA_TYPE,DATA_TYPE_CLICK);
                break;
            case R.id.download_bn:
                /**
                 *Data type of download event.
                 */
                keyMap.put(STManager.KEY_DATA_TYPE,DATA_TYPE_DOWNLOAD);
                break;
            default:
                break;
        }
        return keyMap;
    }

    @Override
    public void onBackPressed() {
        super.onBackPressed();
        /**
         *Report all statistics data before application exit.
         */
        STManager.getInstance().onExit(this, new STManager.ExitListener() {
            @Override
            public void onFinish(boolean result) {
                /**
                 * Exit the application when report statistics data finished.
                 */
                android.os.Process.killProcess(android.os.Process.myPid());
            }
        });
    }


}

测试代码:

import android.app.Activity;
import android.view.Menu;
import android.widget.Button;

import com.oppo.acs.st.demo.BuildConfig;
import com.oppo.acs.st.demo.R;

import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.Robolectric;
import org.robolectric.RobolectricGradleTestRunner;
import org.robolectric.annotation.Config;
import org.robolectric.shadows.ShadowToast;

import junit.framework.Assert;
import static org.robolectric.Shadows.shadowOf;

@RunWith(RobolectricGradleTestRunner.class)
@Config(constants = BuildConfig.class)
public class MainActivityTest {

    Activity activity;

    @Before
    public void setUp() throws Exception {
        activity = Robolectric.setupActivity(MainActivity.class);
        final Menu menu = shadowOf(activity).getOptionsMenu();
    }

    @After
    public void tearDown() throws Exception {

    }

    @Test
    public void testOnBnExpose() throws Exception {
        Button button = (Button) activity.findViewById(R.id.expose_bn);
        button.performClick();
        Assert.assertEquals("expose", ShadowToast.getTextOfLatestToast());
    }

    @Test
    public void testOnBnClick() throws Exception {
        Button button = (Button) activity.findViewById(R.id.click_bn);
        button.performClick();
        Assert.assertEquals("click", ShadowToast.getTextOfLatestToast());
    }

    @Test
    public void testOnBnDownload() throws Exception {
        Button button = (Button) activity.findViewById(R.id.download_bn);
        button.performClick();
        Assert.assertEquals("download", ShadowToast.getTextOfLatestToast());

    }

    @Test
    public void testOnBnBatchExpose() throws Exception {
        Button button = (Button) activity.findViewById(R.id.batch_expose_bn);
        button.performClick();
        Assert.assertEquals("batch expose.", ShadowToast.getTextOfLatestToast());
    }
}package com.oppo.acs.st;

import android.app.Application;

import java.util.List;
import com.oppo.acs.st.demo.BuildConfig;

import org.junit.runner.RunWith;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;

import org.powermock.core.classloader.annotations.PowerMockIgnore;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.robolectric.RobolectricGradleTestRunner;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;
import org.robolectric.shadows.ShadowLog;
import static org.junit.Assert.*;



@RunWith(RobolectricGradleTestRunner.class)
@Config(constants = BuildConfig.class)
@PowerMockIgnore({ "org.mockito.*", "org.robolectric.*", "android.*" })
@PrepareForTest({STManager.class})
public class STManagerTest {

    private STManager sTManager;
    private Application application;

    @Before
    public void setUp() throws Exception {
        sTManager = STManager.getInstance();
        sTManager.enableDebugLog();
        application = RuntimeEnvironment.application;
        ShadowLog.stream = System.out;
    }

    @After
    public void tearDown() throws Exception {

    }

    @Test
    public void init() throws Exception {
        sTManager.init(application);
        List logs = ShadowLog.getLogs();
        System.out.println(logs.size());
        String result = readFile();
        assertTrue(result.contains("has no initted.init!!"));
    }

    @Test (expected = Exception.class)
    public void initWithNullRaiseException() throws Exception {
        sTManager.init(null);
    }

    public String readFile() throws Exception {
        String result = "";
        List logs = ShadowLog.getLogs();
        for (int i = 0; i < logs.size(); i++) {
            // System.out.println(logs.get(i).toString());
            result = result + ((ShadowLog.LogItem) logs.get(i)).msg;
        }
        return result;
    }

}

安卓端基于日志的测试方法

package com.oppo.acs.st;

import android.app.Application;

import java.util.List;
import com.oppo.acs.st.demo.BuildConfig;
import com.oppo.acs.st.utils.Utils;

import org.junit.Rule;
import org.junit.runner.RunWith;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;

import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PowerMockIgnore;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.rule.PowerMockRule;
import org.robolectric.RobolectricGradleTestRunner;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;
import org.robolectric.shadows.ShadowLog;
import static org.junit.Assert.*;



@RunWith(RobolectricGradleTestRunner.class)
@Config(constants = BuildConfig.class, sdk = 21)
@PowerMockIgnore({ "org.mockito.*", "org.robolectric.*", "android.*" })
@PrepareForTest(Utils.class)
public class STManagerTest {

    private STManager sTManager;
    private Application application;

    @Rule
    public PowerMockRule rule = new PowerMockRule();

    @Before
    public void setUp() throws Exception {
        sTManager = STManager.getInstance();
        sTManager.enableDebugLog();
        application = RuntimeEnvironment.application;
        ShadowLog.stream = System.out;
    }

    @After
    public void tearDown() throws Exception {

    }

    @Test
    public void init() throws Exception {
        sTManager.init(application);
        List logs = ShadowLog.getLogs();
        System.out.println(logs.size());
        String result = readFile();
        assertTrue(result.contains("has no initted.init!!"));
    }

    @Test
    public void initWithoutNetwork() throws Exception {
        PowerMockito.mockStatic(Utils.class);
        PowerMockito.when(Utils.isNetworkAvailable(application)).thenReturn(false);
        assertFalse(Utils.isNetworkAvailable(application));
        sTManager.init(application);
        String result = readFile();
        assertTrue(result.contains("no net!"));
    }

    @Test (expected = Exception.class)
    public void initWithNullRaiseException() throws Exception {
        sTManager.init(null);
    }

    public String readFile() throws Exception {
        String result = "";
        List logs = ShadowLog.getLogs();
        for (int i = 0; i < logs.size(); i++) {
            // System.out.println(logs.get(i).toString());
            result = result + ((ShadowLog.LogItem) logs.get(i)).msg;
        }
        return result;
    }
}

上面例子要特别注意必须要添加Rule才行。

© 著作权归作者所有

共有 人打赏支持
磁针石
粉丝 175
博文 75
码字总数 300585
作品 0
邵阳
QA/测试工程师
Kotlin 写 Android 单元测试(四),Robolectric 在 JVM 上测试安卓相关代码

Kotlin 写 Android 单元测试系列: Kotlin 写 Android 单元测试(一),单元测试是什么以及为什么需要 Kotlin 写 Android 单元测试(二),JUnit 4 测试框架和 kotlin.test 库的使用 Kotlin...

JohnnyShieh
2017/12/20
0
0
在Android Studio中使用Roboletric和Espresso

前言 在Android开发中,测试是一个很少被提及的话题。在早期,Android并没有一个很好 的测试框架,你也很难找到一个测试全面的优秀开源项目。近些年,随着Android社区的成熟,出现了诸如Rob...

IT追寻者
2016/06/21
72
0
Robolectric 探索之路

layout: post title: Roboletric探索之路,从抗拒到依赖 description: Roboletric Android Unit Testing category: blog --- 我为什么以前抗拒Android Unit Testing 1、懒,人类最大的天敌;...

ice_Anson
2016/03/16
129
0
2014 非常好用的开源 Android 测试工具

当前有很大的趋势是转向移动应用平台,Android 是最广泛使用的移动操作系统,2014 年大约占 80% 以上的市场。在开发 Android 应用的时候要进行测试,现在市场上有大量的测试工具。 本文主要是...

oschina
2014/10/15
38.3K
11
使用Robolectric对android程序实现自动化测试

下面来介绍如何在eclipse下使用Robolectric 实现自动化测试: 1.新建android 项目. 并在项目中添加文件夹test 如下图所示: 2.新建测试项目.普通的java项目.并把该项目下的src移除build path,...

鉴客
2013/11/12
2.3K
0
【腾讯Bugly干货分享】安卓单元测试:What, Why and How

本文来自于腾讯bugly开发者社区,非经作者同意,请勿转载,原文地址:http://dev.qq.com/topic/57d28349101cd07a5404c415 Dev Club 是一个交流移动开发技术,结交朋友,扩展人脉的社群,成员...

腾讯Bugly
2016/09/26
20
0
【腾讯Bugly干货分享】安卓单元测试:What, Why and How

本文来自于腾讯bugly开发者社区,非经作者同意,请勿转载,原文地址:http://dev.qq.com/topic/57d28349101cd07a5404c415 Dev Club 是一个交流移动开发技术,结交朋友,扩展人脉的社群,成员...

腾讯Bugly
2016/10/10
488
0
android unit test

Android Unit and Integration testing:https://github.com/codepath/android_guides/wiki/Android-Unit-and-Integration-testing Unit Testing with JUnit - Tutorial:http://www.vogella.c......

智能小松鼠
2015/03/10
0
0
安卓自动化单元测试,有会robolectric的专家请指导,谢谢

刚开始研究安卓的自动化单元测试,有几个问题求指导 1、要对自己写的各组件的API进行单元测试,可以使用哪些方法。由于我们这边采用的android junit test虽然能满足要求,但在hudson上构建时...

weihaoMT
2013/11/27
756
1
Robolectric进行Android单元测试

Robolectric进行Android单元测试 测试驱动android开发 在安卓模拟器或者真机上跑测试用例速度很慢。构建、部署、启动app,通常需要花费一分钟或者更久。这不是TDD(测试驱动开发)模式.Robo...

youthflies
2014/02/26
0
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

CoreText进阶(五)- 文字排版样式和效果

CoreText进阶(五)- 文字排版样式和效果 效果 以下是三个设置了不同属性的效果图 第一个设置了文字颜色为红色,字体为16号 第二个设置了文字颜色为灰色,字体为16号,对其为居中 第三个设置...

aron1992
25分钟前
1
0
10.23 linux任务计划cron~10.27 target介绍

crontab命令被用来提交和管理用户的需要周期性执行的任务,与windows下的计划任务类似,当安装完成操作系统后,默认会安装此服务工具,并且会自动启动crond进程,crond进程每分钟会定期检查是...

洗香香
35分钟前
0
0
告警系统主脚本、告警系统配置文件、告警系统监控项目

20.20 告警系统主脚本 告警系统主脚本 main.sh内容 #!/bin/bash#Written by aming.# 是否发送邮件的开关export send=1# 过滤ip地址export addr=`/sbin/ifconfig |grep -A1 "en...

lyy549745
39分钟前
0
0
Don’t Repeat Yourself

在软件工程中,Don’t Repeat Yourself(DRY)是软件开发的原则,旨在减少重复,用抽象代替它,使用数据规范化来避免冗余。 这个原则在维基百科上是说是由Andy Hunt和Dave Thomas《The Pragmat...

woshixin
40分钟前
0
0
搭建webpack项目框架

作者:汪娇娇 时间:2018年6月4日 一、说明 随着业务发展和前端人员的增加,搭建一个通用框架以及制定统一规范就成了必然。对于选型这方面,一开始好像就没考虑其他框架,直接选了webpack。w...

娇娇jojojo
48分钟前
0
0
Java基础——面向对象(内部类)

声明:本栏目所使用的素材都是凯哥学堂VIP学员所写,学员有权匿名,对文章有最终解释权;凯哥学堂旨在促进VIP学员互相学习的基础上公开笔记。 内部类: 1.有名内部类 2.无名内部类 内部类申请...

凯哥学堂
56分钟前
0
0
HttpClient内部三个超时时间的区别

RequestConfig requestConfig = RequestConfig.custom() .setConnectionRequestTimeout(config.connReqTimeout) //从连接池中获取连接的超时时间 ......

1713716445
今天
0
0
每天一个命令SCP

每天一个命令:SCP scp是secure copy的简写,用于在Linux下进行远程拷贝文件的命令,和它类似的命令有cp,不过cp只是在本机进行拷贝不能跨服务器,而且scp传输是加密的。可能会稍微影响一下速...

河图再现
今天
0
0
cron/chkconfig/systemd/unit/target

linux任务计划 : cron工具 任务计划在运维工作中用到的比较多,大部分系统管理工作都是通过定期自动执行某个脚本来完成。 查看linux中任务计划的配置文件: /etc/crontab [root@yolks-001 ~]...

Hi_Yolks
今天
1
0
ftp攻略

Vsftpd完全攻略(一)ftp原理与vsftp安装:https://www.iyunv.com/thread-44698-1-1.html Vsftpd完全攻略(二)设置匿名用户也支持下载和上传与创建目录:https://www.iyunv.com/forum.php?mo...

寰宇01
今天
1
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部