1.BaseApplication整个应用的开始

1.1.看一下代码

  1. /*
  2. * Copyright 2017 GcsSloop
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. *
  16. * Last modified 2017-03-11 22:24:54
  17. *
  18. * GitHub: https://github.com/GcsSloop
  19. * Website: http://www.gcssloop.com
  20. * Weibo: http://weibo.com/GcsSloop
  21. */
  22.  
  23. package com.gcssloop.diycode.base.app;
  24.  
  25. import android.app.Application;
  26.  
  27. import com.gcssloop.diycode.utils.Config;
  28. import com.gcssloop.diycode.utils.CrashHandler;
  29. import com.gcssloop.diycode_sdk.api.Diycode;
  30. import com.squareup.leakcanary.LeakCanary;
  31.  
  32. public class BaseApplication extends Application {
  33.  
  34. public static final String client_id = "7024a413";
  35. public static final String client_secret = "8404fa33ae48d3014cfa89deaa674e4cbe6ec894a57dbef4e40d083dbbaa5cf4";
  36.  
  37. @Override
  38. public void onCreate() {
  39. super.onCreate();
  40. if (LeakCanary.isInAnalyzerProcess(this)) {
  41. return;
  42. }
  43. LeakCanary.install(this);
  44.  
  45. CrashHandler.getInstance().init(this);
  46.  
  47. Diycode.init(this, client_id, client_secret);
  48.  
  49. Config.init(this);
  50. }
  51. }

1.2.代码预览

  

  首先是两个静态变量,是客户的的id和密钥。

  然后是一个onCreate函数,进行一些必要的初始化。

1.3.然后调用Application的onCreate函数  

  

  这里用了一个第三方库LeakCanary。

  首先install这个application

  这里的CrashHandler的作用是崩溃处理办法。

  然后就用将上面的客户的id和客户的密码来初始化了。

  用户设置Config初始化也是这里进行。

2.LeakCanary内存泄露检测

2.1.首先了解一下什么是LeakCanary。

  LeakCanary是检测APP内存泄露的工具,内存泄漏是Android开发中常用的问题,导致程序的稳定性下降。

  github原地址点我。

  参考文章:使用Leak Canary检测应用的内存泄漏。

2.2.在build.gradle中加入:

  

  

  

2.3.在Application中初始化

  

   配置非常简单,会增加一个附属应用,去掉Application的引用,就可以移除这个附属应用了。

  建议在开发模式不要去掉这个引用,在发布版本一定就要移除这个引用了。 

  这个开源项目的效果是这样的(这是开发模式)

  

2.4.注意在清单中定义这个Application

  

2.5.什么情况会发生内存泄露?(参考文章:使用LeakCanary检测内存泄露

  假设我们有一个MainActivity,它的布局很简单,里面只有一个TextView。

  

  现在我们写一个单例xxxHelper之类的业务类==>用来给主活动中的TextView固定设一个值。但是这个值要从res

  中读取,所以我们得用到Context。

  

  现在我们回到MainActivity中来使用这个单例:

  

  然后附属应用直接发出内存泄漏的提示。

                

   为什么会发生内存泄漏呢?

  ==>LeakCanary已经把问题很明显地带到我们面前。这是一个典型的单例导致的Context泄漏问题。我们知道

  Android的Context分为Activity Context和Application Context。

  关于他们的区别,请参考一下这篇文章。

  果没时间看也没关系,其实一个返回的是当前Activity的实例,另一个是项目的Application的实例。Context的

  应用场景参考一下下方的图片。

  

  从程序的角度上来理解:Context是个抽象类,而Activity,Service,Application等都是该类的一个实现。

  在上方那段简单的代码中,我们的xxxHelper的静态实例ourInstance由于有一个对mTextView的引用,而

  mTextView由于要setText(),所以持有了一个对Context的引用,而我们在MainActivity里获取xxxHelper

  实例时因为传入了MainActivity和Context,这使得一旦这个Activity不在了之后,xxxHelper依然会hold住

  它的Context不放,而这个时候因为Activity已经不在了,所以内存泄漏自然就产生了。

  尝试解决==>

  

  这种写法治标不治本。尽管我们的Context已经是Application层级的Context了,但是这种写法依然会导致

  mTextView在退出后依旧hold住整个Application的Context,最终还是导致内存泄漏。

  正确解决方案==>

  

  采用Application级别的Context+增加一个移除TextView的引用。在onDestroy调用移除函数即可。

3.CrashHandler异常处理

3.1.这个类是一个异常处理类。

  有点类似腾讯的Bugly==>也是一个异常处理的一个强大第三方库。它可以知道每次APP崩溃的原因,以及提示

  解决方案。之前的“大学喵”项目我就是用的Bugly,每次异常,它都会给我发送一封邮件,告诉我哪里崩溃,

  哪里异常,每天早上也会统计今日崩溃次数等。非常简单实用的第三方库。

  这里提供一下Bugly的链接,点击了解详情。

3.2.这里提供一下开源项目Diycode的异常处理类的源码。  

  1. /*
  2. * Copyright 2017 GcsSloop
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. *
  16. * Last modified 2017-03-08 01:01:18
  17. *
  18. * GitHub: https://github.com/GcsSloop
  19. * Website: http://www.gcssloop.com
  20. * Weibo: http://weibo.com/GcsSloop
  21. */
  22.  
  23. package com.gcssloop.diycode.utils;
  24.  
  25. import android.content.Context;
  26. import android.content.pm.PackageInfo;
  27. import android.content.pm.PackageManager;
  28. import android.os.Build;
  29. import android.os.Environment;
  30. import android.os.Process;
  31. import android.util.Log;
  32.  
  33. import java.io.BufferedWriter;
  34. import java.io.File;
  35. import java.io.FileWriter;
  36. import java.io.IOException;
  37. import java.io.PrintWriter;
  38. import java.text.SimpleDateFormat;
  39. import java.util.Date;
  40.  
  41. public class CrashHandler implements Thread.UncaughtExceptionHandler {
  42. private static final String TAG = "CrashHandler";
  43. private static final boolean DEBUG = true;
  44.  
  45. private static final String PATH = Environment.getExternalStorageDirectory().getPath() +
  46. "/ryg_test/log/";
  47. private static final String FILE_NAME = "crash";
  48.  
  49. //log文件的后缀名
  50. private static final String FILE_NAME_SUFFIX = ".trace";
  51.  
  52. private static CrashHandler sInstance = new CrashHandler();
  53.  
  54. //系统默认的异常处理(默认情况下,系统会终止当前的异常程序)
  55. private Thread.UncaughtExceptionHandler mDefaultCrashHandler;
  56.  
  57. private Context mContext;
  58.  
  59. //构造方法私有,防止外部构造多个实例,即采用单例模式
  60. private CrashHandler() {
  61. }
  62.  
  63. public static CrashHandler getInstance() {
  64. return sInstance;
  65. }
  66.  
  67. //这里主要完成初始化工作
  68. public void init(Context context) {
  69. //获取系统默认的异常处理器
  70. mDefaultCrashHandler = Thread.getDefaultUncaughtExceptionHandler();
  71. //将当前实例设为系统默认的异常处理器
  72. Thread.setDefaultUncaughtExceptionHandler(this);
  73. //获取Context,方便内部使用
  74. mContext = context.getApplicationContext();
  75. }
  76.  
  77. /**
  78. * 这个是最关键的函数,当程序中有未被捕获的异常,系统将会自动调用#uncaughtException方法
  79. * thread为出现未捕获异常的线程,ex为未捕获的异常,有了这个ex,我们就可以得到异常信息。
  80. */
  81. @Override
  82. public void uncaughtException(Thread thread, Throwable ex) {
  83. try {
  84. //导出异常信息到SD卡中
  85. dumpExceptionToSDCard(ex);
  86. //这里可以通过网络上传异常信息到服务器,便于开发人员分析日志从而解决bug
  87. uploadExceptionToServer();
  88. } catch (IOException e) {
  89. e.printStackTrace();
  90. }
  91.  
  92. //打印出当前调用栈信息
  93. ex.printStackTrace();
  94.  
  95. //如果系统提供了默认的异常处理器,则交给系统去结束我们的程序,否则就由我们自己结束自己
  96. if (mDefaultCrashHandler != null) {
  97. mDefaultCrashHandler.uncaughtException(thread, ex);
  98. } else {
  99. Process.killProcess(Process.myPid());
  100. }
  101.  
  102. }
  103.  
  104. private void dumpExceptionToSDCard(Throwable ex) throws IOException {
  105. //如果SD卡不存在或无法使用,则无法把异常信息写入SD卡
  106. if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
  107. if (DEBUG) {
  108. Log.w(TAG, "sdcard unmounted,skip dump exception");
  109. return;
  110. }
  111. }
  112.  
  113. File dir = new File(PATH);
  114. if (!dir.exists()) {
  115. dir.mkdirs();
  116. }
  117. long current = System.currentTimeMillis();
  118. String time = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date(current));
  119. //以当前时间创建log文件
  120. File file = new File(PATH + FILE_NAME + time + FILE_NAME_SUFFIX);
  121.  
  122. try {
  123. PrintWriter pw = new PrintWriter(new BufferedWriter(new FileWriter(file)));
  124. //导出发生异常的时间
  125. pw.println(time);
  126.  
  127. //导出手机信息
  128. dumpPhoneInfo(pw);
  129.  
  130. pw.println();
  131. //导出异常的调用栈信息
  132. ex.printStackTrace(pw);
  133.  
  134. pw.close();
  135. } catch (Exception e) {
  136. Log.e(TAG, "dump crash info failed");
  137. }
  138. }
  139.  
  140. private void dumpPhoneInfo(PrintWriter pw) throws PackageManager.NameNotFoundException {
  141. //应用的版本名称和版本号
  142. PackageManager pm = mContext.getPackageManager();
  143. PackageInfo pi = pm.getPackageInfo(mContext.getPackageName(), PackageManager
  144. .GET_ACTIVITIES);
  145. pw.print("App Version: ");
  146. pw.print(pi.versionName);
  147. pw.print('_');
  148. pw.println(pi.versionCode);
  149.  
  150. //android版本号
  151. pw.print("OS Version: ");
  152. pw.print(Build.VERSION.RELEASE);
  153. pw.print("_");
  154. pw.println(Build.VERSION.SDK_INT);
  155.  
  156. //手机制造商
  157. pw.print("Vendor: ");
  158. pw.println(Build.MANUFACTURER);
  159.  
  160. //手机型号
  161. pw.print("Model: ");
  162. pw.println(Build.MODEL);
  163.  
  164. //cpu架构
  165. pw.print("CPU ABI: ");
  166. pw.println(Build.CPU_ABI);
  167. }
  168.  
  169. private void uploadExceptionToServer() {
  170. //TODO Upload Exception Message To Your Web Server
  171. }
  172.  
  173. }

3.3.首先看一下成员变量。

    

  首先是一个TAG,方便在logcat中进行输出。

  然后是一个本项目是debug模式还是release模式,然后做出相应的改变。

  然后是一个确定将异常信息输出到哪个地址,这里放在一个log中的。

  然后输出的文件名为“crash”

  然后确定log文件的后缀名为.trace

  然后是系统默认的异常处理,默认情况下,系统会终止当前的异常程序。

  当然,Context也是不能少的,这里用来获取真实的上下文的。

  注意这里已经new了一个CrashHandler()了。

3.4.构造函数+单例模式

  

  这里的单例模式,直接返回在成员变量中new的一个CrashHandler。

3.5.初始化工作==>在Application中调用

  

  可以获取系统默认的异常处理器,然后可以将当前实例设为系统默认的异常处理器。

  这里获得了一个全局的上下文。

3.6.关键Override的函数

  

  异常信息调用函数dumpExceptionToSDCard来输入SD卡中。

  还可以自己写一个函数uploadExceptionToServer上次到服务器中,方便调试。

  然后打印出当前调用栈信息。

  如果系统提供了默认的异常处理器,则交给系统去结束我们的程序,否则就由我们自己结束自己。

  如何自己结束自己呢?

  Process.killProcess(Process.myPid())==>有时候,关闭APP,也会采用这种极端的方法。

3.7.如何输入SD卡中?

  

  首先判断SD卡是否存在而且是否可用,还要判断是否是DEBUG模式。

  然后判断路径是否存在。

  然后获取系统当前时间,文件名有3部分组成:路径+名称+后缀

  然后调用系统的io类PrintWriter来导出时间,手机信息,调用栈新消息。

3.8.如何导出手机信息?

  

  这里利用上下文,获得包管理器getPackageManager。

  利用包管理器再得到包信息,pm.getPackageInfo

  然后可以得到android版本号,手机制造商,手机型号,cpu架构。

4.Config自定义通用类-用户设置

4.1.首先看一下Config类的源代码。

  1. /*
  2. * Copyright 2017 GcsSloop
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. *
  16. * Last modified 2017-03-28 04:48:02
  17. *
  18. * GitHub: https://github.com/GcsSloop
  19. * Website: http://www.gcssloop.com
  20. * Weibo: http://weibo.com/GcsSloop
  21. */
  22.  
  23. package com.gcssloop.diycode.utils;
  24.  
  25. import android.content.Context;
  26. import android.support.annotation.NonNull;
  27. import android.support.annotation.Nullable;
  28. import android.util.LruCache;
  29.  
  30. import com.gcssloop.diycode_sdk.utils.ACache;
  31.  
  32. import java.io.Serializable;
  33.  
  34. /**
  35. * 用户设置
  36. */
  37. public class Config {
  38. private static int M = 1024 * 1024;
  39. private volatile static Config mConfig;
  40. private static LruCache<String, Object> mLruCache = new LruCache<>(1 * M);
  41. private static ACache mDiskCache;
  42.  
  43. private Config(Context context) {
  44. mDiskCache = ACache.get(context, "config");
  45. }
  46.  
  47. public static Config init(Context context) {
  48. if (null == mConfig) {
  49. synchronized (Config.class) {
  50. if (null == mConfig) {
  51. mConfig = new Config(context);
  52. }
  53. }
  54. }
  55. return mConfig;
  56. }
  57.  
  58. public static Config getSingleInstance() {
  59. return mConfig;
  60. }
  61.  
  62. //--- 基础 -----------------------------------------------------------------------------------
  63.  
  64. public <T extends Serializable> void saveData(@NonNull String key, @NonNull T value) {
  65. mLruCache.put(key, value);
  66. mDiskCache.put(key, value);
  67. }
  68.  
  69. public <T extends Serializable> T getData(@NonNull String key, @Nullable T defaultValue) {
  70. T result = (T) mLruCache.get(key);
  71. if (result != null) {
  72. return result;
  73. }
  74. result = (T) mDiskCache.getAsObject(key);
  75. if (result != null) {
  76. mLruCache.put(key, result);
  77. return result;
  78. }
  79. return defaultValue;
  80. }
  81.  
  82. //--- 浏览器 ---------------------------------------------------------------------------------
  83.  
  84. private static String Key_Browser = "UseInsideBrowser_";
  85.  
  86. public void setUesInsideBrowser(@NonNull Boolean bool) {
  87. saveData(Key_Browser, bool);
  88. }
  89.  
  90. public Boolean isUseInsideBrowser() {
  91. return getData(Key_Browser, Boolean.TRUE);
  92. }
  93.  
  94. //--- 首页状态 -------------------------------------------------------------------------------
  95.  
  96. private String Key_MainViewPager_Position = "Key_MainViewPager_Position";
  97.  
  98. public void saveMainViewPagerPosition(Integer position) {
  99. mLruCache.put(Key_MainViewPager_Position, position);
  100. }
  101.  
  102. public Integer getMainViewPagerPosition() {
  103. return getData(Key_MainViewPager_Position, 0);
  104. }
  105.  
  106. //--- Topic状态 ------------------------------------------------------------------------------
  107.  
  108. private String Key_TopicList_LastPosition = "Key_TopicList_LastPosition";
  109. private String Key_TopicList_LastOffset = "Key_TopicList_LastOffset";
  110.  
  111. public void saveTopicListState(Integer lastPosition, Integer lastOffset) {
  112. saveData(Key_TopicList_LastPosition, lastPosition);
  113. saveData(Key_TopicList_LastOffset, lastOffset);
  114. }
  115.  
  116. public Integer getTopicListLastPosition() {
  117. return getData(Key_TopicList_LastPosition, 0);
  118. }
  119.  
  120. public Integer getTopicListLastOffset() {
  121. return getData(Key_TopicList_LastOffset, 0);
  122. }
  123.  
  124. private String Key_TopicList_PageIndex = "Key_TopicList_PageIndex";
  125.  
  126. public void saveTopicListPageIndex(Integer pageIndex) {
  127. saveData(Key_TopicList_PageIndex, pageIndex);
  128. }
  129.  
  130. public Integer getTopicListPageIndex() {
  131. return getData(Key_TopicList_PageIndex, 0);
  132. }
  133.  
  134. //--- News状态 ------------------------------------------------------------------------------
  135.  
  136. private String Key_NewsList_LastScroll = "Key_NewsList_LastScroll";
  137.  
  138. public void saveNewsListScroll(Integer lastScrollY) {
  139. saveData(Key_NewsList_LastScroll, lastScrollY);
  140. }
  141.  
  142. public Integer getNewsLastScroll() {
  143. return getData(Key_NewsList_LastScroll, 0);
  144. }
  145.  
  146. private String Key_NewsList_LastPosition = "Key_NewsList_LastPosition";
  147.  
  148. public void saveNewsListPosition(Integer lastPosition) {
  149. saveData(Key_NewsList_LastPosition, lastPosition);
  150. }
  151.  
  152. public Integer getNewsListLastPosition() {
  153. return getData(Key_NewsList_LastPosition, 0);
  154. }
  155.  
  156. private String Key_NewsList_PageIndex = "Key_NewsList_PageIndex";
  157.  
  158. public void saveNewsListPageIndex(Integer pageIndex) {
  159. saveData(Key_NewsList_PageIndex, pageIndex);
  160. }
  161.  
  162. public Integer getNewsListPageIndex() {
  163. return getData(Key_NewsList_PageIndex, 0);
  164. }
  165. }

4.2.定义的成员变量

  

  M是数据单位的意思。

  volatile关键字:

  

  LruCache是android系统的通用类,存放类似map类型的缓存数据。

  ACache是这个项目的SDK中定义最底层的缓存类。

4.3.构造函数+初始化+获取单例

  

4.4.基础是保存数据和获取数据,泛型好处理任何类型

  

  在自定义的mLruCache和SDK包中的mDiskCache都要保存key,value之间的对应关系。

4.5.是否保存浏览器中用户数据+首页是否保存第几个碎片

  

  估计应该就是配置是否保存一些东西吧。

4.6.话题的页码和上一个位置

  

  获取上一个话题的位置和上一个话题的offset。

4.7.news的页码和上一个位置

  

  获取上次滑动的位置,上一个news列表位置,上一个页面索引。

5.总结一下

5.1.以前不知道内存泄漏的严重性,当初学良让我注意android的内存泄漏的问题,我不以为然,现在看到别的项目无一

  不在解决内存泄漏的问题,而且这个名字听起来内心就起疙瘩。还好LeakCanary出来了,可以解决这个大问题,也

  是大难点,就不用到处寻找bug了,只要发生了内存泄漏,那个源头必然出现。

  

5.2.CrashHandler同样也是找bug能手,类似于腾讯的bugly,当然这个没有腾讯bugly强大。这个只能将异常输入到

  一个日志,而bugly可以直接在网上输出,并且会发送邮箱,当然这个CrashHandler很简单,不用配置什么东西,

  也许bugly就是根据这个来做出来的吧。

5.3.现在明白了Config是什么意思了,其实就是类似于我曾经定义的常量吧。不过这个不是常量,是一个记录的变量,

  比如记录当前滑动的位置,那么下次进来就能记住了。从某个角度看,有点类似于sharePerference记录一样,

  总之就是存储一些信息,然后之后来调用。

5.4.今天看的这个是Application,这是项目的大门,所以很多初始化的工作都是这里完成的。包括开启内存泄漏的检测

  ,开启异常日志的输出,用户设置的配置,Diycode项目SDK的初始化等。这个内存泄漏用的是第三方开源库,

  记住就好,对于内存泄漏也有了更深的理解。无非就是在活动中添加一个回收即可。

Diycode开源项目 BaseApplication分析+LeakCanary第三方+CrashHandler自定义异常处理的更多相关文章

  1. DiyCode开源项目 BaseActivity 分析

    1.首先将这个项目的BaseActivity源码拷贝过来. /* * Copyright 2017 GcsSloop * * Licensed under the Apache License, Ve ...

  2. Diycode开源项目 ImageActivity分析

    1.首先看一下效果 1.1做成了一个GIF 1.2.我用格式工厂有点问题,大小无法调到手机这样的大小,目前还没有解决方案. 1.3.网上有免费的MP4->GIF,参考一下这个网站吧. 1.4.讲 ...

  3. Diycode开源项目 UserActivity分析

    1.效果预览 1.1.实际界面预览 1.2. 这是MainActivity中的代码 这里执行了跳转到自己的用户界面的功能. 1.3.点击头像或者用户名跳转到别人的页面 UserActivity的结构由 ...

  4. Diycode开源项目 TopicContentActivity分析

    1.效果预览以及布局分析 1.1.实际效果预览 左侧话题列表的布局是通过TopicProvider来实现的,所以当初分析话题列表就没有看到布局. 这里的话题内容不是一个ListView,故要自己布局. ...

  5. Diycode开源项目 LoginActivity分析

    1.首先看一下效果 1.1.预览一下真实页面 1.2.分析一下: 要求输入Email或者用户名,点击编辑框,弹出键盘,默认先进入输入Email或用户名编辑框. 点击密码后,密码字样网上浮动一段距离,E ...

  6. Diycode开源项目 MainActivity分析

    1.分析MainActivity整体结构 1.1.首先看一下这个界面的整体效果. 1.2.活动源代码如下 /* * Copyright 2017 GcsSloop * * Licensed under ...

  7. DiyCode开源项目 AboutActivity分析

    1.首先看一下效果 这是手机上显示的效果: 1.1首先是一个标题栏,左侧一个左箭头,然后一个图标. 1.2然后下方是一个可以滑动的页面. 1.3分成了7个部分. 1.4DiyCode的图标. 1.5然 ...

  8. DiyCode开源项目 TopicActivity 分析

    1.首先看看TopActivity效果.    2.TopicActivity是一个继承BaseActivity的.前面分析过BaseActivity了.主要有一个标题栏,有返回的图标. 3.贴一下T ...

  9. Diycode开源项目 SitesListFragment分析

    1.效果预览 1.1.网站列表实际界面 1.2.注意这个界面没有继承SimpleRefreshRecycleFragment 前面的话题和新闻继承了SimpleRefreshRecyclerFragm ...

随机推荐

  1. 发布MVC网站的时候出现缺少WebHost等程序集问题的解决办法

    将一下几个dll 拷贝到bin文件夹下就行 链接:https://pan.baidu.com/s/17xhTdakzM_SQmOjJdZvviw 密码:c976

  2. tomcat的备份脚本

    reference:Crontab的20个例子  先科普一下date的使用方法,在sh脚本中经常会使用得到 date -d<字符串>:显示字符串所指的日期与时间.字符串前后必须加上双引号: ...

  3. CSS单词换行and断词,你真的完全了解吗

    背景 某天老板在群里反馈,英文单词为什么被截断了? 很显然,这是我们前端的锅,自行背锅.这个问题太简单了,css里加两行属性,分分钟搞定. 开心的提交代码,刷新页面.我擦,怎么还是没有断词?不可能啊! ...

  4. sass相关随笔

    安装 下载ruby并且安装 点击这里 打开命令行输入 gem install sass 我使用的是sublime text3 还需要下载三个插件 sass -- 可以帮助你语法高亮 sass buil ...

  5. 关于win10上安装.Net Framework3.5的解决办法

    1.首先下载. NET Framework 3.5的安装包,格式为cba格式; 2.将下载下来的NetFx3.cab 放进 C:\Windows 目录下; 3.打开控制面板->程序->启动 ...

  6. zip、rar压缩文件密码破解——使用ARCHPR Professional Edition

    直链下载地址: https://pan.abn.cc/weiyun/down.php?u=82441366e3c1f43fc69210e8ece93470.undefined.zip (压缩包内含解压 ...

  7. 用户在设置密码时,提醒请输入半角字符(vue+element+valid)

    要保证callback()只有一个出口 rules:{ newPassword: [{validator:(rule,newPassword,callback)=>{ var all = fal ...

  8. 【转】android调试工具DDMS的使用详解

    具体可见http://developer.android.com/tools/debugging/ddms.html. DDMS为IDE和emultor.真正的android设备架起来了一座桥梁.开发 ...

  9. 2017.12.24 Java序列化你不知道的事(二)

    1 序列化允许重构 序列化允许一定数量的类变种,甚至重构之后也是如此,ObjectInputStream 仍可以很好地将其读出来. Java Object Serialization 规范可以自动管理 ...

  10. python_70_内置函数2

    #hex() 转16进制 oct()转8进制 print(hex(255)) print(oct(10)) #id() 返回内存地址 print(id('a')) ''' isinstance(obj ...