Robolectric使用教程

转载请标明出处:http://blog.csdn.net/shensky711/article/details/53561172

本文出自: 【HansChen的博客】

  • 概述
  • 怎样使用
    • 为项目加入依赖
    • 指定RobolectricTestRunner为执行器
    • 什么是Shadow类
    • Config配置
    • 配置SDK版本号
    • 配置Application类
    • 指定Resource路径
    • 使用第三方Library Resources
    • 使用限定的资源文件
    • Properties文件
    • 系统属性配置
    • 驱动Activity生命周期
    • 追加模块
    • 自己定义Shadow类
    • Shadow类中訪问原始类的field
    • 怎样在測试用例中让Shadow生效
  • 经常使用測试场景
    • 页面跳转验证
    • UI组件状态验证
    • 验证Dialog
    • 验证Toast
    • 验证Fragment
    • 验证BroadcastReceiver
    • 验证Service

概述

Android的单元測试能够分为两部分:

  1. Local unit tests:执行于本地JVM
  2. Instrumented test:执行于真机或者模拟器

假设使用Local測试,须要保证測试过程中不会调用Android系统API。否则会抛出RuntimeException异常,由于Local測试是直接跑在本机JVM的,而之所以我们能使用Android系统API,是由于编译的时候,我们依赖了一个名为“android.jar”的jar包,可是jar包里全部方法都是直接抛出了一个RuntimeException。是没有不论什么不论什么实现的。这仅仅是Android为了我们能通过编译提供的一个Stub!当APP执行在真实的Android系统的时候,由于类载入机制,会载入位于framework的具有真正实现的类。

由于我们的Local是直接在PC上执行的。所以调用这些系统API便会出错。

那么问题来了,我们既要使用Local測试,但測试过程又难免遇到调用系统API那怎么办?其中一个方法就是mock objects。比方借助Mockito,第二种方式就是使用Robolectric。 Robolectric就是为解决问题而生的。它实现一套JVM能执行的Android代码。然后在unit test执行的时候去截取android相关的代码调用。然后转到他们的他们实现的Shadow代码去执行这个调用的过程

怎样使用?

为项目加入依赖

testCompile "org.robolectric:robolectric:3.1.4"

Robolectric在第一次执行时,会下载一些sdk依赖包,每一个sdk依赖包大概50M。下载速度比較慢,用户能够直接在网上下载相应依赖包,放置在本地maven仓库地址中,默认路径为:C:\Users\username\.m2\repository\org\robolectric

指定RobolectricTestRunner为执行器

为測试用例加入注解,指定測试执行器为RobolectricTestRunner。注意。这里要通过Config指定constants = BuildConfig.class,Robolectric 会通过constants推导出输出路径。假设不进行配置。Robolectric可能不能找到你的manifest、resources和assets资源

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

}

什么是Shadow类

Shadow是Robolectric的立足之本,如其名,作为影子。一定是变幻莫測,时有时无,且依存于本尊。Robolectric定义了大量模拟Android系统类行为的Shadow类,当这些系统类被创建的时候,Robolectric会查找相应的Shadow类并创建一个Shadow类与原始类关联。每当系统类的方法被调用的时候,Robolectric会保证Shadow相应的方法会调用。这些Shadow对象,丰富了本尊的行为,能更方便的对Android相关的对象进行測试。

比方,我们能够借助ShadowActivity验证页面是否正确跳转了

    /**
     * 验证点击事件是否触发了页面跳转。验证目标页面是否预期页面
     *
     * @throws Exception
     */
    @Test
    public void testJump() throws Exception {
        // 默认会调用Activity的生命周期: onCreate->onStart->onResume
        MainActivity activity = Robolectric.setupActivity(MainActivity.class);
        // 触发button点击
        activity.findViewById(R.id.activity_main_jump).performClick();

        // 获取相应的Shadow类
        ShadowActivity shadowActivity = Shadows.shadowOf(activity);
        // 借助Shadow类获取启动下一Activity的Intent
        Intent nextIntent = shadowActivity.getNextStartedActivity();
        // 校验Intent的正确性
        assertEquals(nextIntent.getComponent().getClassName(), SecondActivity.class.getName());
    }

@Config配置

能够通过@Config定制Robolectric的执行时的行为。

这个注解能够用来凝视类和方法,假设类和方法同一时候使用了@Config,那么方法的设置会覆盖类的设置。

你能够创建一个基类。用@Config配置測试參数。这样,其它測试用例就能够共享这个配置了

配置SDK版本号

Robolectric会依据manifest文件配置的targetSdkVersion选择执行測试代码的SDK版本号。假设你想指定sdk来执行測试用例。能够通过以下的方式配置

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

    @Config(sdk = Build.VERSION_CODES.KITKAT)
    public void getSandwich_shouldReturnHamSandwich() {
    }
}

配置Application类

Robolectric会依据manifest文件配置的Application配置去实例化一个Application类,假设你想在測试用例中又一次指定,能够通过以下的方式配置

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

    @Config(application = CustomApplicationOverride.class)
    public void getSandwich_shouldReturnHamSandwich() {
    }
}

指定Resource路径

Robolectric能够让你配置manifest、resource和assets路径,能够通过以下的方式配置

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

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

使用第三方Library Resources

当Robolectric測试的时候。会尝试载入全部应用提供的资源,但假设你须要使用第三方库中提供的资源文件,你可能须要做一些特别的配置。只是假设你使用gradle来构建Android应用,这些配置就不须要做了,由于Gradle Plugin会在build的时候自己主动合并第三方库的资源,但假设你使用的是Maven,那么你须要配置libraries变量:

@RunWith(RobolectricTestRunner.class)
@Config(libraries = {
    "build/unpacked-libraries/library1",
    "build/unpacked-libraries/library2"
})
public class SandwichTest {
}

使用限定的资源文件

Android会在执行时载入特定的资源文件。如依据设备屏幕载入不同分辨率的图片资源、依据系统语言载入不同的string.xml,在Robolectric測试其中,你也能够进行一个限定,让測试程序载入特定资源.多个限定条件能够用破折号拼接在在一起。

    /**
     * 使用qualifiers载入相应的资源文件
     *
     * @throws Exception
     */
    @Config(qualifiers = "zh-rCN")
    @Test
    public void testString() throws Exception {
        final Context context = RuntimeEnvironment.application;
        assertThat(context.getString(R.string.app_name), is("单元測试Demo"));
    }

Properties文件

假设你嫌通过注解配置上面的东西麻烦。你也能够把以上配置放在一个Properties文件之中。然后通过@Config指定配置文件,比方。首先创建一个配置文件robolectric.properties:

# 放置Robolectric的配置选项:
sdk=21
manifest=some/build/path/AndroidManifest.xml
assetDir=some/build/path/assetDir
resourceDir=some/build/path/resourceDir

然后把robolectric.properties文件放到src/test/resources文件夹下,执行的时候,会自己主动载入里面的配置

系统属性配置

  • robolectric.offline:true代表关闭执行时获取jar包
  • robolectric.dependency.dir:当处于offline模式的时候。指定执行时的依赖文件夹
  • robolectric.dependency.repo.id:设置执行时获取依赖的Maven仓库ID,默认是sonatype
  • robolectric.dependency.repo.url:设置执行时依赖的Maven仓库地址,默认是https://oss.sonatype.org/content/groups/public/
  • robolectric.logging.enabled:设置是否打开调试开关

以上设置能够通过Gradle进行配置,如:

android {

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

驱动Activity生命周期

利用ActivityController我们能够让Activity执行相应的生命周期方法,如:

    @Test
    public void testLifecycle() throws Exception {
        // 创建Activity控制器
        ActivityController<MainActivity> controller = Robolectric.buildActivity(MainActivity.class);
        MainActivity activity = controller.get();
        assertNull(activity.getLifecycleState());

        // 调用Activity的performCreate方法
        controller.create();
        assertEquals("onCreate", activity.getLifecycleState());

        // 调用Activity的performStart方法
        controller.start();
        assertEquals("onStart", activity.getLifecycleState());

        // 调用Activity的performResume方法
        controller.resume();
        assertEquals("onResume", activity.getLifecycleState());

        // 调用Activity的performPause方法
        controller.pause();
        assertEquals("onPause", activity.getLifecycleState());

        // 调用Activity的performStop方法
        controller.stop();
        assertEquals("onStop", activity.getLifecycleState());

        // 调用Activity的performRestart方法
        controller.restart();
        // 注意此处应该是onStart。由于performRestart不仅会调用restart,还会调用onStart
        assertEquals("onStart", activity.getLifecycleState());

        // 调用Activity的performDestroy方法
        controller.destroy();
        assertEquals("onDestroy", activity.getLifecycleState());
    }

通过ActivityController,我们能够模拟各种生命周期的变化。可是要注意,我们尽管能够任意调用Activity的生命周期,可是Activity生命周期切换有自己的检測机制,我们要遵循Activity的生命周期规律。比方。假设当前Activity并不是处于stop状态。測试代码去调用了controller.restart方法,此时Activity是不会回调onRestart和onStart的。

除了控制生命周期,还能够在启动Activity的时候传递Intent:

    /**
     * 启动Activity的时候传递Intent
     *
     * @throws Exception
     */
    @Test
    public void testStartActivityWithIntent() throws Exception {
        Intent intent = new Intent();
        intent.putExtra("test", "HelloWorld");
        Activity activity = Robolectric.buildActivity(MainActivity.class).withIntent(intent).create().get();
        assertEquals("HelloWorld", activity.getIntent().getExtras().getString("test"));
    }

onRestoreInstanceState回调中传递Bundle:

    /**
     * savedInstanceState会在onRestoreInstanceState回调中传递给Activity
     *
     * @throws Exception
     */
    @Test
    public void testSavedInstanceState() throws Exception {
        Bundle savedInstanceState = new Bundle();
        Robolectric.buildActivity(MainActivity.class).create().restoreInstanceState(savedInstanceState).get();
        // verify something
    }

在真实环境下,视图是在onCreate之后的某一时刻在attach到Window上的,在此之前。View是处于不可操作状态的,你不能点击它。

在Activity的onPostResume方法调用之后。View才会attach到Window之中。可是,在Robolectric之中,我们能够用控制器的visible方法使得View变为可见。变为可见之后。就能够模拟点击事件了

    @Test
    public void testVisible() throws Exception {
        ActivityController<MainActivity> controller = Robolectric.buildActivity(MainActivity.class);
        MainActivity activity = controller.get();

        // 调用Activity的performCreate而且设置视图visible
        controller.create().visible();
        // 触发点击
        activity.findViewById(R.id.activity_main_button1).performClick();

        // 验证
        assertEquals(shadowOf(activity).getNextStartedActivity().getComponent().getClassName(), SecondActivity.class.getName());
    }

追加模块

为了降低依赖包的大小。Robolectric的shadows类成了好几部分:

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

用户能够依据自身需求加入以下依赖包,如

dependencies {
    ... ...
    testCompile ‘org.robolectric:robolectric:3.1.4‘
    testCompile ‘org.robolectric:shadows-support-v4:3.1.4‘
    testCompile ‘org.robolectric:shadows-multidex:3.1.4‘
    testCompile ‘org.robolectric:shadows-play-services:3.1.4‘
    testCompile ‘org.robolectric:shadows-maps:3.1.4‘
    testCompile ‘org.robolectric:shadows-httpclient:3.1.4‘
}

自己定义Shadow类

  1. Shadow类须要一个public的无參构造方法以方便Robolectric框架能够实例化它。通过@Implements注解与原始类关联在一起
  2. 若原始类有有參构造方法。在Shadow类中定义public void类型的名为__constructor__的方法,且方法參数与原始类的构造方法參数一直
  3. 定义与原始类方法签名一致的方法,在里面重写实现,Shadow方法需用@Implementation进行注解

以下我们来创建RobolectricBean的Shadow类

原始类:

public class RobolectricBean {

    String name;
    int    color;

    public RobolectricBean(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public int getColor() {
        return color;
    }

    public void setColor(int color) {
        this.color = color;
    }
}

Shadow类:

/**
 * 创建{@link RobolectricBean}的影子类
 *
 * @author HansChen
 */
@Implements(RobolectricBean.class)
public class ShadowRobolectricBean {

    /**
     * 通过@RealObject注解能够訪问原始对象,但注意,通过@RealObject注解的变量调用方法,依旧会调用Shadow类的方法。而不是原始类的方法
     * 仅仅能用来訪问原始类的field
     */
    @RealObject
    RobolectricBean realBean;

    /**
     * 须要一个无參构造方法
     */
    public ShadowRobolectricBean() {

    }

    /**
     * 相应原始类的构造方法
     *
     * @param name 相应原始类构造方法的传入參数
     */
    public void __constructor__(String name) {
        realBean.name = name;
    }

    /**
     * 原始对象的方法被调用的时候,Robolectric会依据方法签名查找相应的Shadow方法并调用
     */
    @Implementation
    public String getName() {
        return "Hello, I ma shadow of RobolectricBean: " + realBean.name;
    }

    @Implementation
    public int getColor() {
        return realBean.color;
    }

    @Implementation
    public void setColor(int color) {
        realBean.color = color;
    }
}

Shadow类中訪问原始类的field

Shadow类中能够定义一个原始类的成员变量,并用@RealObject注解,这样。Shadow类就能訪问原始类的field了,可是注意,通过@RealObject注解的变量调用方法,依旧会调用Shadow类的方法,而不是原始类的方法,仅仅能用它来訪问原始类的field。

@Implements(Point.class)
public class ShadowPoint {
    @RealObject private Point realPoint;
    ...
    public void __constructor__(int x, int y) {
        realPoint.x = x;
        realPoint.y = y;
    }
}

怎样在測试用例中让Shadow生效

在Config注解中加入shadows參数,指定相应的Shadow生效

@RunWith(RobolectricTestRunner.class)
@Config(shadows = ShadowRobolectricBean.class)
public class RobolectricBeanTest {

    ... ...
}

注意。自己定义的Shadow类不能通过Shadows.shadowOf()获取,须要用ShadowExtractor.extract()来获取。获取之后进行类型转换:

ShadowRobolectricBean shadowBean = (ShadowRobolectricBean) ShadowExtractor.extract(bean);

经常使用測试场景

页面跳转验证

    /**
     * 验证点击事件是否触发了页面跳转,验证目标页面是否预期页面
     *
     * @throws Exception
     */
    @Test
    public void testJump() throws Exception {
        // 默认会调用Activity的生命周期: onCreate->onStart->onResume
        MainActivity activity = Robolectric.setupActivity(MainActivity.class);
        // 触发button点击
        activity.findViewById(R.id.activity_main_jump).performClick();

        // 获取相应的Shadow类
        ShadowActivity shadowActivity = Shadows.shadowOf(activity);
        // 借助Shadow类获取启动下一Activity的Intent
        Intent nextIntent = shadowActivity.getNextStartedActivity();
        // 校验Intent的正确性
        assertEquals(nextIntent.getComponent().getClassName(), SecondActivity.class.getName());
    }

UI组件状态验证

    /**
     * 验证UI组件状态
     *
     * @throws Exception
     */
    @Test
    public void testCheckBoxState() throws Exception {
        MainActivity activity = Robolectric.setupActivity(MainActivity.class);
        CheckBox checkBox = (CheckBox) activity.findViewById(R.id.activity_main_check_box);
        // 验证CheckBox初始状态
        assertFalse(checkBox.isChecked());

        // 点击button反转CheckBox状态
        activity.findViewById(R.id.activity_main_switch_check_box).performClick();
        // 验证状态是否正确
        assertTrue(checkBox.isChecked());

        // 点击button反转CheckBox状态
        activity.findViewById(R.id.activity_main_switch_check_box).performClick();
        // 验证状态是否正确
        assertFalse(checkBox.isChecked());
    }

验证Dialog

    /**
     * 验证Dialog是否正确弹出
     *
     * @throws Exception
     */
    @Test
    public void testDialog() throws Exception {
        MainActivity activity = Robolectric.setupActivity(MainActivity.class);
        AlertDialog dialog = ShadowAlertDialog.getLatestAlertDialog();
        // 推断Dialog尚未弹出
        assertNull(dialog);

        activity.findViewById(R.id.activity_main_show_dialog).performClick();
        dialog = ShadowAlertDialog.getLatestAlertDialog();
        // 推断Dialog已经弹出
        assertNotNull(dialog);
        // 获取Shadow类进行验证
        ShadowAlertDialog shadowDialog = shadowOf(dialog);
        assertEquals("AlertDialog", shadowDialog.getTitle());
        assertEquals("Oops, now you see me ~", shadowDialog.getMessage());
    }

验证Toast

    /**
     * 验证Toast是否正确弹出
     *
     * @throws Exception
     */
    @Test
    public void testToast() throws Exception {
        MainActivity activity = Robolectric.setupActivity(MainActivity.class);
        Toast toast = ShadowToast.getLatestToast();
        // 推断Toast尚未弹出
        assertNull(toast);

        activity.findViewById(R.id.activity_main_show_toast).performClick();
        toast = ShadowToast.getLatestToast();
        // 推断Toast已经弹出
        assertNotNull(toast);
        // 获取Shadow类进行验证
        ShadowToast shadowToast = shadowOf(toast);
        assertEquals(Toast.LENGTH_SHORT, shadowToast.getDuration());
        assertEquals("oops", ShadowToast.getTextOfLatestToast());
    }

验证Fragment

@RunWith(RobolectricTestRunner.class)
@Config(constants = BuildConfig.class, application = CustomApplication.class)
public class MyFragmentTest {

    private MyFragment myFragment;

    @Before
    public void setUp() throws Exception {
        myFragment = new MyFragment();
        // 把Fragment加入到Activity中
        FragmentTestUtil.startFragment(myFragment);
    }

    @Test
    public void testFragment() throws Exception {
        assertNotNull(myFragment.getView());
    }
}

验证BroadcastReceiver

首先看下广播接收器:

public class MyReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        // do something
    }
}

广播的測试点能够包括两个方面

  1. 验证应用程序是否注冊了该广播
  2. 验证广播接收器的处理逻辑是否正确,关于逻辑是否正确。能够直接人为的触发onReceive()方法。让然后进行验证
@RunWith(RobolectricTestRunner.class)
@Config(constants = BuildConfig.class, application = CustomApplication.class)
public class MyReceiverTest {

    @Test
    public void restRegister() throws Exception {
        ShadowApplication shadowApplication = ShadowApplication.getInstance();

        String action = "ut.cn.unittestdemo.receiver";
        Intent intent = new Intent(action);

        // 验证是否注冊了相应的Receiver
        assertTrue(shadowApplication.hasReceiverForIntent(intent));
    }

    @Test
    public void restReceive() throws Exception {

        String action = "ut.cn.unittestdemo.receiver";
        Intent intent = new Intent(action);
        intent.putExtra("EXTRA_USERNAME", "HansChen");

        MyReceiver myReceiver = new MyReceiver();
        myReceiver.onReceive(RuntimeEnvironment.application, intent);
        // verify something
    }
}

验证Service

Service和Activity一样。都有生命周期,Robolectric也提供了Service的生命周期控制器,使用方式和Activity相似,这里就不做详解了

@RunWith(RobolectricTestRunner.class)
@Config(constants = BuildConfig.class, application = CustomApplication.class)
public class TestServiceTest {

    private ServiceController<TestService> controller;
    private TestService                    testService;

    @Before
    public void setUp() throws Exception {
        controller = Robolectric.buildService(TestService.class);
        testService = controller.get();
    }

    /**
     * 控制Service生命周期进行验证
     *
     * @throws Exception
     */
    @Test
    public void testLifecycle() throws Exception {

        controller.create();
        // verify something

        controller.startCommand(0, 0);
        // verify something

        controller.bind();
        // verify something

        controller.unbind();
        // verify something

        controller.destroy();
        // verify something
    }
}
时间: 2024-08-19 01:50:36

Robolectric使用教程的相关文章

配置同时使用PowerMock和Robolectric对Android进行单元测试

Robolectric官网上给了一个配置教程,但是我使用它的方法进行配置,发现使用Mockito.spy函数的时候会出现Exception. 后来在PowerMock官网上找到了另外一个教程,里面说使用PowerMockRule是不靠谱的,要使用PowerMock 1.6.0引入的新的@PowerMockRunnerDelegate annotation来进行配置 具体配置文件如下: module里面的build.gradle添加依赖: dependencies { ...... testCom

在Android Studio中用Gradle添加Robolectric

我们用Robolectric测试的话需要在gradle中进行配置,国内的详细教程太过简易,而且很多是低版本下的配置方案.所以经过本人的仔细摸索,找到了现在高版本中的配置方案,主要还是参考了官网的配置教程:https://github.com/robolectric/robolectric-gradle-plugin 下面贴上完整的步骤,看完后就能配置成功了~ 下面红字部分就是要添加的部分. 首先,在最外面的build.gradle中配置classpath // Top-level build f

Windows Git+TortoiseGit简易使用教程

转载自 http://blog.csdn.net/jarelzhou/article/details/8256139 官方教程:http://tortoisegit.org/docs/tortoisegit/(英文版) 为什么选择Git 效率 很多人有一种习惯吧,什么软件都要最新的,最好的.其实吧,软件就是工具,生产力工具,为的是提高我们的生产力.如果现有的工具已经可以满足生产力要求了,就没有必要换了.生产效率高低应当是选择工具的第一位. 历史 开源世界的版本控制系统,经历了这么几代: 第一代,

微信公众号中添加外部链接地址的图文教程

2017-9-18,长沙,有点闷,有点热. 本教程教大家如何在微信公众号中,添加外部的链接,网络有很多教程,但由于表述不太清楚,出个教程吧.最终实现在微信后台管理平台"原文链接"处插入外部链接,用户点击发布好的图文文章底部左下角的"阅读原文",就可以跳转到您添加的外部链接页面中去. 第1步. 使用微信公众管理帐号登陆微信管理后台 > 素材管理 > 图文消息 >  新建图文消息或者编辑文章都可以,如下图所示: 第2步. 之后,将页面向下拉,看到底部&

win2003从组策略关闭端口(445/135/137/138/139/3389等)教程

一些恶劣的病毒会从某些端口入侵计算机,因此关闭某些用不到的而又具有高风险的端口就显得很有必要,是服务器管理员要做的基本的安全防范.本文将介绍win2003系统在组策略关闭某一个端口的教程,文章以关闭445端口为例. 首先要说明的是,此方法不仅仅适用于win2003,也适用于win7,且操作方法完全相同,所以win7用户也可以按照本文教程操作. 1.打开组策略编辑器 按组合键WIN+R打开运行窗口,然后输入gpedit.msc,按回车键,即可进入组策略编辑器. 2.创建 IP 安全策略 展开选项"

微信小程序实例教程(一)

序言 开始开发应用号之前,先看看官方公布的「小程序」教程吧!(以下内容来自微信官方公布的「小程序」开发指南) 本文档将带你一步步创建完成一个微信小程序,并可以在手机上体验该小程序的实际效果.这个小程序的首页将会显示欢迎语以及当前用户的微信头像,点击头像,可以在新开的页面中查看当前小程序的启动日志. 1. 获取微信小程序的 AppID 首先,我们需要拥有一个帐号,如果你能看到该文档,我们应当已经邀请并为你创建好一个帐号.注意不可直接使用服务号或订阅号的 AppID. 利用提供的帐号,登录https

SQL Server2005安装材料及教程编制

首发:https://www.douban.com/note/579628972/ 系统为 win10SQL Server 安装材料下载 链接:http://p删除an.baidu.com/s/1中文miQ1ZQg 密码:pszf挂不补,可到bdsola.com搜索下载 1.安装教程(幸运E版):http://jingyan.baidu.com/article/acf728fd1a30d0f8e510a380.html 注意:当你的电脑是多核的时候可能会出现安装错误   2.此时推荐教程:htt

mac数据恢复软件EasyRecovery使用教程

mac数据恢复软件EasyRecovery使用教程 文件丢失是非常让人郁闷的!因为没有备份所以很多重要的东西都没有办法找回!小编为大家准备了EasyRecovery,这款数据恢复软件费非常好用,可以将您丢失的数据找回.小编还特意为大家准备了数据恢复软件EasyRecovery使用教程,下面就和小编一起来看看具体如何使用吧! <ignore_js_op> 数据恢复软件EasyRecovery使用教程介绍:第一步:我们需要在Mac中安装好EasyRecovery for Mac,然后启动EasyR

mac苹果电脑上Mathtype软件的安装教程

对于数学学习来将,Mathtype是一款非常不错的工具,在这里您可以方便快捷的计算出公式的结果,只需要将方程式填写进去就能出来结果.小编为大家准备了Mathtype安装教程,安装的过程是非常简单的,安装好了之后会让您的工作变得更加轻松,还能提升工作的效率哦! Mathtype安装教程介绍:第一步:打开下载的Mathtype文件 第二步:点击左边的安装文件,会得到提示,下图所示: 第三步:选择好,然后会在弹出提示窗口,点击继续,如下图所示: 第四步:因为暂时只有英文版的,所以选择英文版,点击继续安