首页 文章 精选 留言 我的

精选列表

搜索[安卓设备管理],共8540篇文章
优秀的个人博客,低调大师

安卓平台上的依赖注入(一)

刚开始学习软件工程的时候,我们经常会碰到像这样的事情: 软件应该符合 SOLID 原则。 但这句话实际是什么意思?让我们看看 SOLID 中每个字母在架构里所代表的重要含义,例如: S - 单职责原则 O - 开闭原则 L - Liskov 替换原则 I - 接口分离原则 D - 依赖反转原则这也是依赖注入dependency injection的核心概念。 简单来说,我们需要提供一个类,这个类有它所需要的所有对象,以便实现其功能。 概述 依赖注入听起来像是描述非常复杂的东西的一个术语,但实际上它很简单,看下面这个例子你就明白了: class NoDependencyInjection { private Dependency d; public NoDependencyInjection() { d = new Dependency(); } } class DependencyInjection { private Dependency d; public DependencyInjection(Dependency d) { this.d = d; } } 正如我们所见,第一种情况是我们在构造器里创建了依赖对象,但在第二种情况下,它作为参数被传递给构造器,这就是我们所说的依赖注入dependency injection。这样做是为了让我们所写的类不依靠特定依赖关系的实现,却能直接使用它。 参数传递的目标是构造器,我们就称之为构造器依赖注入;或者是某个方法,就称之为方法依赖注入: class Example { private ConstructorDependency cd; private MethodDependency md; Example(ConstructorDependency cd) { this.cd = cd; //Constructor Dependency Injection } public setMethodDependency(MethodDependency md) { this.md = md; //Method Dependency Injection } } 要是你想总体深入地了解依赖注入,可以看看由Dan Lew发表的精彩的演讲,事实上是这个演讲启迪了这篇概述。 在 Android 平台,当需要框架来处理依赖注入这个特殊的问题时,我们有不同的选择,其中最有名的框架就是Dagger 2。它最开始是由 Square 公司(LCTT 译注:Square 是美国一家移动支付公司)的一些很棒的开发者开发出来的,然后慢慢发展成由 Google 自己开发。首先开发出来的是 Dagger 1,然后 Big G 接手这个项目发布了第二个版本,做了很多改动,比如以注解annotation为基础,在编译的时候完成其任务。 导入框架 安装 Dagger 并不难,但需要导入android-apt插件,通过向项目的根目录下的build.gradle文件中添加它的依赖关系: buildscript{ ... dependencies{ ... classpath ‘com.neenbedankt.gradle.plugins:android-apt:1.8’ } } 然后,我们需要将android-apt插件应用到项目build.gradle文件,放在文件顶部 Android application 那一句的下一行: apply plugin: ‘com.neenbedankt.android-apt’ 这个时候,我们只用添加依赖关系,然后就能使用库及其注解annotation了: dependencies{ ... compile ‘com.google.dagger:dagger:2.6’ apt ‘com.google.dagger:dagger-compiler:2.6’ provided ‘javax.annotation:jsr250-api:1.0’ } 需要加上最后一个依赖关系是因为 @Generated 注解在 Android 里还不可用,但它是原生的 Java 注解。 Dagger 模块 要注入依赖,首先需要告诉框架我们能提供什么(比如说上下文)以及特定的对象应该怎样创建。为了完成注入,我们用@Module注释对一个特殊的类进行了注解(这样 Dagger 就能识别它了),寻找@Provide注解的方法,生成图表,能够返回我们所请求的对象。 看下面的例子,这里我们创建了一个模块,它会返回给我们ConnectivityManager,所以我们要把Context对象传给这个模块的构造器。 @Module public class ApplicationModule { private final Context context; public ApplicationModule(Context context) { this.context = context; } @Provides @Singleton public Context providesContext() { return context; } @Provides @Singleton public ConnectivityManager providesConnectivityManager(Context context) { return (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); } } Dagger 中十分有意思的一点是简单地注解一个方法来提供一个单例(Singleton),就能处理所有从 Java 中继承过来的问题。 组件 当我们有一个模块的时候,我们需要告诉 Dagger 想把依赖注入到哪里:我们在一个组件Component里完成依赖注入,这是一个我们特别创建的特殊注解接口。我们在这个接口里创造不同的方法,而接口的参数是我们想注入依赖关系的类。 下面给出一个例子并告诉 Dagger 我们想要MainActivity类能够接受ConnectivityManager(或者在图表里的其它依赖对象)。我们只要做类似以下的事: @Singleton @Component(modules = {ApplicationModule.class}) public interface ApplicationComponent { void inject(MainActivity activity); } 正如我们所见,@Component 注解有几个参数,一个是所支持的模块的数组,代表它能提供的依赖。这里既可以是Context也可以是ConnectivityManager,因为它们在ApplicationModule类中有声明。 用法 这时,我们要做的是尽快创建组件(比如在应用的onCreate阶段)并返回它,那么类就能用它来注入依赖了: 为了让框架自动生成DaggerApplicationComponent,我们需要构建项目以便 Dagger 能够扫描我们的代码,并生成我们需要的部分。 在MainActivity里,我们要做的两件事是用@Inject注解符对想要注入的属性进行注解,调用我们在ApplicationComponent接口中声明的方法(请注意后面一部分会因我们使用的注入类型的不同而变化,但这里简单起见我们不去管它),然后依赖就被注入了,我们就能自由使用他们: public class MainActivity extends AppCompatActivity { @Inject ConnectivityManager manager; @Override protected void onCreate(Bundle savedInstanceState) { ... ((App) getApplication()).getComponent().inject(this); } } 总结 当然了,我们可以手动注入依赖,管理所有不同的对象,但 Dagger 消除了很多比如模板这样的“噪声”,给我们提供有用的附加品(比如Singleton),而仅用 Java 处理将会很糟糕。 原文发布时间为:2017-12-27 本文来自云栖社区合作伙伴“Linux中国”

优秀的个人博客,低调大师

安卓平台Flutter启动过程全解析

前言 今天主要带大家一起分析下flutter是如何启动、初始化和加载dart代码的。这里有几点需要提前告知: 由于篇幅的问题,关于flutter界面创建、绘制过程将略过; 由于相关的c++代码比较多,而且较为复杂,建议先下载flutter engine的完整开发环境代码,阅读本文更方便; 本文只分析启动过程,参考的项目是基于android studio创建的一个默认flutter项目,以下简称demo。 (文章干货很长 请耐心看完 文末有福利!) 正文 java层启动过程 熟悉android的朋友都知道,一个APP启动会先执行Application再执行Activity(AndroidManifest.xml中配置的启动Activity),结合这个,我们先看看Application里做了什么,在分析过程中我们将挑取一些关键的native方法作为

优秀的个人博客,低调大师

安卓应用安全指南 4.8 输出到 LogCat

4.8 输出到 LogCat 原书:Android Application Secure Design/Secure Coding Guidebook 译者:飞龙 协议:CC BY-NC-SA 4.0 在 Android 中有一种名为 LogCat 的日志机制,不仅系统日志信息,还有应用日志信息也会输出到 LogCat。 LogCat 中的日志信息可以从同一设备中的其他应用中读出 [17],因此向L ogcat 输出敏感信息的应用,被认为具有信息泄露的漏洞。 敏感信息不应输出到 LogCat。 [17] 输出到 LogCat 的日志信息,可以由声明READ_LOGS权限的应用读取。 但是,在 Android 4.1 及更高版本中,无法读取其他应用输出的日志信息。 但智能手机用户可以通过 ADB ,阅读输出到 logcat 的每个日志信息。 从安全角度来看,在发行版应用中,最好不要输出任何日志。 但是,即使在发行版应用的情况下,在某些情况下也会出于某种原因输出日志。 在本章中,我们将介绍一些方法,以安全的方式将消息输出到 LogCat,即使在发行版应用中也是如此。 除此解释外,请参考“4.8.3.1 发行版应用中日志输出的两种思路”。 4.8.1 示例代码 接下来是在发行版应用中,通过 ProGuard 控制输出到 LogCat 的日志的方法。 ProGuard 是自动删除不需要的代码(如未使用的方法等)的优化工具之一。 android.util.Log类有五种类型的日志输出方法:Log.e(), Log.w(), Log.i(), Log.d(), Log.v()。对于日志信息,有意输出的日志信息(以下称为“操作日志信息”)应该区分于不适合发行版应用的信息(以下称为开发日志信息),例如调试日志。建议使用Log.e()/w()/i()输出操作日志信息,并使用Log.d()/v()输出开发日志。正确使用五种日志输出方法的详细信息,请参阅“4.8.3.2 日志级别和日志输出方法的选择标准”,另外请参考“4.8.3.3 调试日志和VERBOSE日志并不总是自动删除”。 这是一个以安全方式使用 LogCat 的例子。此示例包括用于输出调试日志的Log.d()和Log.v()。如果应用用于发布,这两种方法将被自动删除。在此示例代码中,ProGuard 用于自动删除调用Log.d()/v()的代码块。 要点: 1) 敏感信息不能由Log.e()/w()/i(),System.out / err输出。 2) 敏感信息应由Log.d()/v()在需要时输出。 3) 不应使用Log.d()/v()的返回值(以替换或比较为目的)。 4) 当你构建应用来发布时,你应该在代码中引入机制,自动删除不合适的日志记录方法(如Log.d()或Log.v())。 5) 必须使用发行版构建配置来创建用于(发布)发行的 APK 文件。 ProGuardActivity.java package org.jssec.android.log.proguard; import android.app.Activity; import android.os.Bundle; import android.util.Log; public class ProGuardActivity extends Activity { final static String LOG_TAG = "ProGuardActivity"; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_proguard); // *** POINT 1 *** Sensitive information must not be output by Log.e()/w()/i(), System.out/err. Log.e(LOG_TAG, "Not sensitive information (ERROR)"); Log.w(LOG_TAG, "Not sensitive information (WARN)"); Log.i(LOG_TAG, "Not sensitive information (INFO)"); // *** POINT 2 *** Sensitive information should be output by Log.d()/v() in case of need. // *** POINT 3 *** The return value of Log.d()/v()should not be used (with the purpose of substitution or comparison). Log.d(LOG_TAG, "sensitive information (DEBUG)"); Log.v(LOG_TAG, "sensitive information (VERBOSE)"); } } proguard-project.txt # prevent from changing class name and method name etc. -dontobfuscate # *** POINT 4 *** In release build, the build configurations in which Log.d()/v() are deleted automatica lly should be constructed. -assumenosideeffects class android.util.Log { public static int d(...); public static int v(...); } 要点 5:必须使用发行版构建配置来创建用于(发布)发行的 APK 文件。 开发版应用(调试版本)和发行版应用(发布版本)之间的LogCat 输出差异如下图 4.8-2 所示。 4.8.2 规则书 输出消息记录时,遵循以下规则: 4.8.2.1 操作日志信息中不能包含敏感信息(必需) 输出到 LogCat 的日志可以从其他应用中读取,因此敏感信息(如用户的登录信息)不应该由发行版应用输出。 在开发过程中,不必编写输出敏感信息的代码,或者在发布之前需要删除所有这些代码。 为了遵循这个规则,首先,不要在操作日志信息中包含敏感信息。 此外,建议构建系统,在构建发行版时,删除输出敏感信息的代码。 请参阅“4.8.2.2 构建生成系统,在构建发行版时,自动删除输出开发日志信息的代码(推荐)”。 4.8.2.2 构建生成系统,在构建发行版时,自动删除输出开发日志信息的代码(推荐) 开发应用时,有时最好将敏感信息输出到日志中,来检查过程内容和调试,例如复杂逻辑过程中的临时操作结果,程序内部状态信息,通信协议的数据结构。在开发过程中,将敏感信息作为调试日志输出并不重要,在这种情况下,相应的日志输出代码应该在发布之前删除,如“4.8.2.1 操作日志信息中不能包含敏感信息(必需)”所述。 为了在构建发行版时,确实删除了输出开发日志信息的代码,应该构建系统,使用某些工具自动执行代码删除。 “4.8.1 示例代码”中介绍的 ProGuard 可以用于此方法。如下所述,用 ProGuard 删除代码有一些值得注意的地方。这里应该将系统用于一些应用,它通过Log.d()/ v()输出开发日志信息,根据“4.8.3.2 日志级别和日志输出方法的选择标准”。 ProGuard 会自动删除不需要的代码,如未使用的方法。通过指定Log.d()/ v()作为-assumenosideeffects选项的参数,Log.d(),Log.v()`的调用被视为不必要的代码,并且这些代码将被删除。 -assumenosideeffects class android.util.Log { public static int d(...); public static int v(...); } 如果使用这个自动删除系统,请注意Log.d(),Log.v()代码在使用其返回值时不会被删除,因此不应该使用Log.d(),Log.v()的返回值。 例如,下一个代码中的Log.v()不会被删除。 int i = android.util.Log.v("tag", "message"); System.out.println(String.format("Log.v() returned %d. ", i)); //Use the returned value of Log.v() for examination 如果你想重复使用源代码,则应保持项目环境的一致性,包括 ProGuard 设置。 例如,预设Log.d()和Log.v()的源代码将被上面的 ProGuard 设置自动删除。 如果在未设置 ProGuard 的其他项目中使用此源代码,则不会删除Log.d()和Log.v(),因此可能会泄露敏感信息。 重用源代码时,应确保包括 ProGuard 设置在内的项目环境的一致性。 4.8.2.3 输出Throwable对象时,使用Log.d()/v()(推荐) 如“4.8.1 示例代码”和“4.8.3.2 日志级别和日志输出方法的选择标准”中所述,输出敏感信息不应通过Log.e()/w()/i()输出来记录。 另一方面,为了使开发者输出程序异常的细节来记录,当异常发生时,在某些情况下,堆栈踪迹通过Log.e(..., Throwable tr)/w(..., Throwable tr)/i(..., Throwable tr)输出到 LogCat。 但是,敏感信息有时可能包含在堆栈踪迹中,因为它显示程序的详细内部结构。 例如,当SQLiteException按原样输出时,会输出 SQL 语句的类型,因此可能会提供 SQL 注入攻击的线索。 因此,建议在输出Throwable对象时,仅使用Log.d()/v()方法。 4.8.2.4 仅仅将android.util.Log类的方法用于日志输出(推荐) 在开发过程中,你可以通过System.out / err输出日志,来验证应用的行为是否按预期工作。 当然,日志可以通过System.out / err的print()/ println()方法输出到 LogCat,但强烈建议仅使用android.util.Log类的方法,原因如下。 在输出日志时,一般根据信息的紧急程度,正确使用最合适的输出方法,并控制输出。 例如,使用严重错误,注意,简单应用的信息通知等类别。 然而,在这种情况下,在发布时需要输出的信息(操作日志信息),和可以包括敏感信息(开发日志信息)的信息,通过相同的方法输出。 所以,当删除输出敏感信息的代码时,可能会存在一些删除操作被忽略掉的危险。 除此之外,当使用android.util.Log和System.out / err进行日志输出时,与仅使用android.util.Log相比,需要考虑的因素会增加,因此可能会出现一些错误,比如 一些删除被忽略掉了。 为了减少上述错误发生的风险,建议仅使用android.util.Log类的方法。 4.8.3 高级话题 4.8.3.1 发布版应用中日志输出的两种思路 发布版应用中有两种思考日志输出的方式。一个是任何日志都不应该输出,另一个是用于以后分析的必要信息应该作为日志输出。从安全角度来看,最好是,任何日志都不应该在发行版应用中输出,但有时候,即使在发行版本应用中,出于各种原因也会输出日志。每种思考方式按照以下描述。 前者是“任何日志都不应该输出”,这是因为,在发行版应用中输出日志没有那么重要,并且存在泄露敏感信息的风险。这是因为开发人员没有办法在 Android 应用运行环境中收集发行版应用的日志信息,这与许多 Web 应用的运行环境不同。基于这种思想,日志代码仅用于开发阶段,并且在构建发行版应用时删除所有日志代码。 后者是“必要的信息应作为日志输出,以供日后分析”,作为客户支持中,分析应用错误的最终选项,以防你的客户支持有任何疑问。 基于这个想法,如上所述,有必要准备系统来防止人为错误并将其引入到项目中,因为如果你没有系统,则必须记住避免在发行版应用中记录敏感信息。 更多日志方法的信息,请参考下面的链接: 适用于贡献者/日志的代码风格指南 http://source.android.com/source/code-style.html#log-sparingly 4.8.3.2 日志级别和日志输出方法的选择标准 在 Android 中的android.util.Log类中定义了五个日志级别(ERROR,WARN,INFO,DEBUG,VERBOSE)。 使用android.util.Log类输出日志消息时,应该选择最合适的方法,如表 4.8-1 所示,它展示了日志级别和方法的选择标准。 表 4.8-1 日志级别和方法的选择标准 日志级别 方法 要输出的日志信息 ERROR Log.e() 应用处于错误状态时,输出的日志信息 WARN Log.w() 应用面临非预期严重情况时,输出的日志信息 INFO Log.i() 与上面不同,用于提示应用状态中任何值得注意的更改或者结果 DEBUG Log.d() 应用的内部状态信息,开发应用时,需要临时输出,用于分析特定 bug 的成因 VERBOSE Log.v() 不属于上面任何一个的日志信息。应用开发者以多种目的输出。例如,输出服务器通信信息来转储。 发行版应用的注意事项: e/w/i: 日志信息可能由用户参考,因此可以在开发版应用和发行版应用中输出。 因此,敏感信息不应该在这些级别输出。 d/v: 日志信息仅适用于应用开发人员。 因此,这种类型的信息不应该在发行版的情况下输出。 更多日志方法的信息,请参考下面的链接: 适用于贡献者/日志的代码风格指南 http://source.android.com/source/code-style.html#log-sparingly 4.8.3.3 DEBUG和VERBOSE日志并不总是自动删除 以下引用自android.util.Log类 [18] 的开发人员参考。 [18] ttp://developer.android.com/reference/android/util/Log.html 按照啰嗦程度的顺序排列,从最少到最多是ERROR,WARN,INFO,DEBUG,VERBOSE。 除了在开发期间,绝不应该将VERBOSE编译进应用。 DEBUG日志被编译但在运行时剥离。 始终保留ERROR,WARN,INFO日志。 在阅读了上述文章之后,一些开发人员可能会误解Log类的行为,如下所示。 构建发行版时不编译Log.v()调用,VERBOSE日志从不输出。 编译Log.v()调用,但执行时绝不输出DEBUG日志。 但是,日志记录方法从来不会表现成这样,并且无论使用调试模式还是发布模式编译,都会输出所有消息。 如果仔细阅读文档,你将能够认识到,文档的要点与日志方法的行为无关,而是日志的基本策略。 在本章中,我们通过使用 ProGuard 引入了示例代码以获得上述的预期结果。 4.8.3.4 从汇编中移除敏感信息 如果为了删除Log.d()方法而使用 ProGuard 构建以下代码,有必要记住,ProGuard会保留为日志信息构造字符串的语句(代码的第一行),即使它删除了 Log.d()方法的调用(代码的第二行)。 String debug_info = String.format("%s:%s", "Sensitive information1", "Sensitive information2"); if (BuildConfig.DEBUG) android.util.Log.d(TAG, debug_info); 以下反汇编显示了使用 ProGuard 发布上述代码的结果。 实际上,没有Log.d()调用过程,但你可以看到字符串一致性定义,例如Sensitive information1,和String#format()方法的调用过程,不会被删除并仍然存在。 const-string v1, "%s:%s" const/4 v2, 0x2 new-array v2, v2, [Ljava/lang/Object; const/4 v3, 0x0 const-string v4, "Sensitive information 1" aput-object v4, v2, v3 const/4 v3, 0x1 const-string v4, "Sensitive information 2" aput-object v4, v2, v3 invoke-static {v1, v2}, Ljava/lang/String;->format(Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang /String; move-result-object v0 实际上,找到反汇编 APK 文件的组装日志输出信息特定部分并不容易。 但是,在某些处理机密信息的应用中,这种类型的过程在某些情况下不应保留在 APK 文件中。 你应该像下面那样实现你的应用,来避免在字节码中保留敏感信息的后果。 在发行版中,编译器优化将完全删除以下代码。 if (BuildConfig.DEBUG) { String debug_info = String.format("%s:%s", " Snsitive information 1", "Sensitive information 2"); if (BuildConfig.DEBUG) android.util.Log.d(TAG, debug_info); } 此外,ProGuard 无法删除以下日志消息代码("result:" + value)。 Log.d(TAG, "result:" + value); 在这种情况下,你可以通过以下方式解决问题。 if (BuildConfig.DEBUG) Log.d(TAG, "result:" + value); 4.8.3.5 意图的内容输出到了 LogCat 使用活动时,需要注意,因为ActivityManager将意图的内容输出到 LogCat。 请参阅“4.1.3.5 使用活动时的日志输出”。 4.8.3.6 限制输出到System.out / err的日志 System.out / err方法将所有消息输出到 LogCat。 即使开发者没有在他们的代码中使用这些方法,Android 也可以向System.out / err发送一些消息,例如,在以下情况下,Android 会将堆栈踪迹发送到System.err方法。 使用Exception#printStackTrace()时 隐式输出到System.err时(当异常没有被应用捕获时,它会由系统提供给Exception#printStackTrace()。) 你应该适当地处理错误和异常,因为堆栈踪迹包含应用的独特信息。 我们介绍一种改变System.out / err默认输出目标的方法。 当你构建发行版应用时,以下代码将System.out / err方法的输出重定向到任何地方。 但是,你应该考虑此重定向是否会导致应用或系统故障,因为代码会暂时覆盖System.out / err方法的默认行为。 此外,这种重定向仅对你的应用有效,对系统进程毫无价值。 OutputRedirectApplication.java package org.jssec.android.log.outputredirection; import java.io.IOException; import java.io.OutputStream; import java.io.PrintStream; import android.app.Application; public class OutputRedirectApplication extends Application { // PrintStream which is not output anywhere private final PrintStream emptyStream = new PrintStream(new OutputStream() { public void write(int oneByte) throws IOException { // do nothing } }); @Override public void onCreate() { // Redirect System.out/err to PrintStream which doesn't output anywhere, when release build. // Save original stream of System.out/err PrintStream savedOut = System.out; PrintStream savedErr = System.err; // Once, redirect System.out/err to PrintStream which doesn't output anywhere System.setOut(emptyStream); System.setErr(emptyStream); // Restore the original stream only when debugging. (In release build, the following 1 line is deleted byProGuard.) resetStreams(savedOut, savedErr); } // All of the following methods are deleted byProGuard when release. private void resetStreams(PrintStream savedOut, PrintStream savedErr) { System.setOut(savedOut); System.setErr(savedErr); } } AndroidManifest.xml <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="org.jssec.android.log.outputredirection" > <application android:icon="@drawable/ic_launcher" android:label="@string/app_name" android:name=".OutputRedirectApplication" android:allowBackup="false" > <activity android:name=".LogActivity" android:label="@string/app_name" android:exported="true" > <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application> </manifest> proguard-project.txt # Prevent from changing class name and method name, etc -dontobfuscate # In release build, delete call from Log.d()/v() automatically. -assumenosideeffects class android.util.Log { public static int d(...); public static int v(...); } # In release build, delete resetStreams() automatically. -assumenosideeffects class org.jssec.android.log.outputredirection.OutputRedirectApplication { private void resetStreams(...); } 开发版应用(调试版)和发布版应用(发行版)之间的 LogCat 输出差异如下图 4.8-3 所示。

优秀的个人博客,低调大师

安卓如何限制横屏和竖屏

在开发android的应用中,有时候需要限制横竖屏切换。 只需要在AndroidManifest.xml文件中加入android:screenOrientation属性限制。 android:screenOrientation="landscape"是限制此页面横屏显示, android:screenOrientation="portrait"是限制此页面数竖屏显示。 例如:我现在想让所有的页面不管怎么样都竖屏显示, 则我的AndroidManifest.xml应该这样写: <activity android:name=".OneDomeActivity" android:screenOrientation="portrait" android:launchMode="singleTask"/> <activity android:name=".TowDomeActivity" android:screenOrientation="portrait" android:launchMode="singleTask"/> <activity android:name=".ThreeDomeActivity" android:screenOrientation="portrait" android:launchMode="singleTask"/> <activity android:name=".FourDomeActivity" android:screenOrientation="portrait" android:launchMode="singleTask"/> <activity android:name=".FiveDomeActivity" android:screenOrientation="portrait" android:launchMode="singleTask"/> 本文转自 netcorner 博客园博客,原文链接:http://www.cnblogs.com/netcorner/p/4644656.html ,如需转载请自行联系原作者

资源下载

更多资源
Mario

Mario

马里奥是站在游戏界顶峰的超人气多面角色。马里奥靠吃蘑菇成长,特征是大鼻子、头戴帽子、身穿背带裤,还留着胡子。与他的双胞胎兄弟路易基一起,长年担任任天堂的招牌角色。

腾讯云软件源

腾讯云软件源

为解决软件依赖安装时官方源访问速度慢的问题,腾讯云为一些软件搭建了缓存服务。您可以通过使用腾讯云软件源站来提升依赖包的安装速度。为了方便用户自由搭建服务架构,目前腾讯云软件源站支持公网访问和内网访问。

Sublime Text

Sublime Text

Sublime Text具有漂亮的用户界面和强大的功能,例如代码缩略图,Python的插件,代码段等。还可自定义键绑定,菜单和工具栏。Sublime Text 的主要功能包括:拼写检查,书签,完整的 Python API , Goto 功能,即时项目切换,多选择,多窗口等等。Sublime Text 是一个跨平台的编辑器,同时支持Windows、Linux、Mac OS X等操作系统。

WebStorm

WebStorm

WebStorm 是jetbrains公司旗下一款JavaScript 开发工具。目前已经被广大中国JS开发者誉为“Web前端开发神器”、“最强大的HTML5编辑器”、“最智能的JavaScript IDE”等。与IntelliJ IDEA同源,继承了IntelliJ IDEA强大的JS部分的功能。

用户登录
用户注册