面试时容易忽略的细节

  1. 热更新原理
    客户端向服务器请求时,在请求头部带上range: bytes=2000070-,表示文件请求从2000070开始。服务端收到请求时,在响应中带上content-range=bytes 2000070-106786027/106786028,同时将return code从200改为206。
    而文件的续写,可以使用io包中的RandomAccessFile
Url url = new Url("http://www.jizhuomi.com/android/down.zip");  
Httpurlconnection httpconnection = (httpurlconnection)url.openconnection();  
//设置user-agent  
httpconnection.setrequestproperty("user-agent","netfox");  
//设置断点续传的开始位置  
httpconnection.setrequestproperty("range","bytes=2000070");  
//获得输入流  
Inputstream input = httpconnection.getinputstream(); 
//从输入流中取出的字节流就是down.zip文件从2000070开始的字节流。

randomaccess osavedfile = new randomaccessfile("local_down.zip","rw");  
long npos = 2000070;  
//定位文件指针到npos位置  
osavedfile.seek(npos);  
byte[] b = new byte[1024];  
int nread;  
//从输入流中读入字节流,然后写到文件中  
while((nread=input.read(b,0,1024)) > 0) {  
  osavedfile.write(b,0,nread);  
} 
  1. ANR
    anr最近三天的trace.txt会保存到/data/system/dropbox目录下。这是由DropBoxManager实现的,该类可以理解为一个持久化的logcat。读源码时发现,anr的logcat是在ActivityManagerService的appNotResponding()输出的,其中用到了一个很实用的工具类Os,里面有个Os.chmod(path,mode)方法可以便利地设置文件权限。而trace.txt的写入必须在20s内完成,ActivityManagerService通过FileObserver实现对文件读写的监听。一旦文件有修改,会触发FileObserver.onEvent()通知监听者。

3.热更新原理
Java运行时的类加载是通过抽象类ClassLoader实现的,而Android中其默认实现是PathClassLoader。后者的父类是BaseDexClassLoaderPathClassLoader只是简单地从文件系统中加载类文件,而BaseDexClassLoader重写了findClass()方法,并在其中委托DexPathList去调用它的findClass()

public Class findClass(String name, List<Throwable> suppressed) {
    for (Element element : dexElements) {
        DexFile dex = element.dexFile;

        if (dex != null) {
            Class clazz = dex.loadClassBinaryName(name, definingContext, suppressed);
            if (clazz != null) {
                return clazz;
            }
        }
    }
    if (dexElementsSuppressedExceptions != null) {
        suppressed.addAll(Arrays.asList(dexElementsSuppressedExceptions));
    }
    return null;
}

要实现热更新,就需要我们在运行时去更改PathClassLoader.pathList.dexElements.并将我们的hotpacth dex文件出现在dexElements列表的前面。

  1. 阅读错误报告(bugreport)

  2. 今日头条屏幕适配原理:屏幕宽度(px)/设计稿宽度(dp) = 目标屏幕宽度(px)/目标值(dp)。利用的是动态修改屏幕密度,使得两者屏幕密度相等(density = px / dp)。
    参考:
    Android 目前最稳定和高效的UI适配方案
    Android屏幕适配方案(出自今日头条)
    关于今日头条的屏幕适配方案学习理解

  3. 显示大图,但是不能压缩BitmapRegionDecoder

BitmapRegionDecoder bitmapRegionDecoder = BitmapRegionDecoder.newInstance(inputStream, false);
bitmapRegionDecoder.decodeRegion(rect, options);

7.本地缓存DiskLruCache
主要记住有一个journal.temp文件去保存每次缓存读写状态:DIRTY(写入缓存,但是没有commit),CLEAN(缓存已被commit),REMOVE,READ。同时内部LRU逻辑是通过LikedHashMap来实现,trimToSize()触发回收。

  1. 几个重要的自定义View用法
  • 渲染:Paint.setShader(Shader shader)。常用的shader:BitmapShader位图图像渲染实例、LinearGradient线性渲染、RadialGradient环形渲染、SweepGradient扫描渐变渲染/梯度渲染、ComposeShader组合渲染.

  • 图像相交 paint.setXfermode(Xfermode xfermode)。 常用的是XfermodePorterDuffXfermode子类

    PorterDuffXfermode效果

  • Alpha滤镜:setMaskFilter(MaskFilter maskfilter)。常用的是模糊滤镜BlurMaskFilter,浮雕滤镜EmbossMaskFilter

  • 绘制路径效果:Paint.setPathEffect(PathEffect effect)

    PathEffect

  • 颜色过滤:Paint.setColorFilter(ColorFilter filter),主要对RGB(A)进行过滤。

    ColorFilter

  • 曝光颜色过滤:Paint.setColorFilter(new LightingColorFilter(int mul, int add))。实质计算方式就是(mul * 原色值 + add)% 255,不过一定要注意,该过滤器是不处理Alpha的,也就是说只对图片里的RGB有效,A无效

  • 图层混合颜色过滤:PorterDuffColorFilter(int color, PorterDuff.Mode mode)

  1. 两个Activity跳转,必然会执行什么方法?
    一般地,Activity A 跳转到Activity B,A会先调用onPause(),然后B会调用onCreate(),onStart(),onResume()。
    如果此时B已经完全遮挡A,A会调用onStop();否则,如果B是个透明窗口,或者是对话框主题,则A不会调用onPause()。
    如果B已经存在于Activity栈中,则不会调用onCreate()。

  2. 如果同时使用startService 与bindService 方法启动Service,需要终止该Service时,要调用stopService 和unbindService 方法(unbindService 依附于启动它的Context,startServicec 并不依附于启动它的Context。如果先调用unbindService ,这时服务并不会被终止,当调用stopService 后,服务才会被终止;如果先调用stopService ,服务也不会被终止,当调用unbindService 或者之前调用bindService 的Context不存在了(如Activity被finish掉了)服务才会自动停止)

  3. 属性动画原理:属性动画的运行机制是通过不断地对值进行操作来实现的,而初始值和结束值之间的过渡则是通过ValueAnimator来负责计算。其中TypeEvaluator决定了初始值过渡到结束值的计算算法(y = sin(x)),TimeInterpolator决定了初始值过渡到结束值的节奏。

  4. SharedPreferences是线程安全的,但不是进程安全的。
    第一次getSharedPreferences()会开启一个单线程池去异步加载。读取磁盘文件,get/set之前有awaitLoadedLocked()同步操作,在磁盘文件加载完成前会卡住调用线程。加载完成之后会从内存中读取。
    apply是内存同步操作,但是会开启一个异步任务放到单线程队列中,更新磁盘缓存。commit同apply类似,但是会等磁盘任务完成才返回,会有boolean结果返回。每次 apply / commit 都会把全部的数据一次性写入磁盘, 所以单个的配置文件不应该过大, 影响整体性能。

  5. 在浏览器中输入一个网址,并得到网页呈现结果。这个过程中经历了什么?
    DNS域名解析->TCP三次握手->TCP建立并发送HTTP请求->服务器响应HTTP请求->浏览器解析HTTP代码,同时获取css,js,图片等资源->浏览器渲染页面并呈现结果

  6. 为什么是三次握手,四次挥手?
    关闭连接时,当Server端收到FIN报文时,很可能数据信息没有传完并不会立即关闭连接,所以只能先回复一个ACK报文(告诉Client端,"你发的FIN报文我收到了")。只有等到Server端所有的报文都发送完了,我才能发送FIN报文,因此不能一起发送。故需要四步挥手。

  7. Webview与js交互总结

类型 调用方式 优点 缺点 使用场景
Android调用js WebView.loadUrl("javascript:jsMethod()") 简洁方便 效率低,获取返回值麻烦 不需要返回值,对性能要求不高
Android调用js WebView.evaluateJavascript() 效率高 只兼容Android4.4以上 Android4.4以上
js调用Android WebView.addJavascriptInterface(new Object(), "objectName") 简洁方便 Android4.2以下存在安全漏洞 Android4.2以上相对简单的互调
js调用Android WebViewClient.shouldOverrideUrlLoading()回调拦截 url 不存在安全漏洞 使用复杂,需要进行协议约定;从Native层往Webview传值比较繁琐 不需要返回值的互调(IOS主要使用该方式)
js调用Android WebChromeClient 的onJsAlert()、onJsConfirm()、onJsPrompt()方法回调拦截JS对话框alert()、confirm()、prompt() 消息回调拦截 url 不存在安全漏洞 使用复杂,需要进行协议约定 满足大多数情况下的互调

16.进程保活

  • 黑色保活:不同APP之间相互配合,互相唤醒;利用系统广播唤醒,包括ACTION_NEW_PICTURE(拍照),ACTION_NEW_VIDEO(拍视频),CONNECTIVITY_ACTION(网络切换)等;
  • 白色保活:启动前台service
  • 灰色保活:利用系统的漏洞启动前台Service
  • Native进程:利用 Linux 中的 fork 机制创建 Native 进程,在 Native 进程中监控主进程的存活,当主进程挂掉后,在 Native 进程中立即对主进程进行拉活
  • JobScheduler:系统会定时调用该进程以使应用进行一些逻辑操作
  • 厂商白名单
    总结:进程保活本就是个伪命题,不存在不死的方法。只能从提升和优化自身应用的性能,降低进程的oom_adj。同时不同厂商从系统层对APP进行优待处理。
  1. notify和notifyAll的区别
    首先区分两个概念,锁池和等待池:
    锁池:等待当前锁的拥有者线程释放锁,以便执行该对象的synchronized方法的线程队列。
    等待池:线程调用wait方法释放锁,就会进入等待池。等待池中的线程不会去竞争该对象的锁。
    当拥有锁的线程调用notify时,会从等待池中随机将某一条线程移动到锁池中,去竞争锁的拥有权。而调用notifyAll,所有等待池中的线程都会被移动到锁池中竞争锁。优先级高的线程竞争到对象锁的概率大,假若某线程没有竞争到该对象锁,它还会留在锁池中,唯有线程再次调用 wait()方法,它才会重新回到等待池中。而竞争到对象锁的线程则继续往下执行,直到执行完了 synchronized 代码块,它会释放掉该对象锁,这时锁池中的线程会继续竞争该对象锁。

  2. 屏幕适配
    dp(dip,也就是密度无关像素)解决了同一控件数值在不同分辨率中展示相同大小的问题(也就是屏幕像素密度匹配,dpi),但是没法解决屏幕尺寸大小带来的匹配问题(也就是屏幕尺寸匹配)。

Nexus5的总宽度为360dp,我们现在在水平方向上放置两个按钮,一个是150dp左对齐,另外一个是200dp右对齐,那么中间留有10dp间隔;
但假如同样地设置在Nexus S(屏幕宽度是320dp),会发现,两个按钮会重叠,因为320dp<200+150dp

本质上是希望使得布局组件在不同屏幕密度上显示相同的像素效果,对于屏幕尺寸匹配,可以考虑百分比匹配

  1. Android动态加载ClassLoader


    JVM的ClassLoader体系

Android平台的ClassLoader

ClassLoader使用双亲委托机制来实现。其中热修复基本都是利用DexClassLoader来实现;而PatheClassLoader为Android用来加载应用类以及系统类,不推荐开发者使用。其父加载器为BootClassLoader。
参考:Android动态加载ClassLoader热修复入门

  1. 乐观锁和悲观锁
    乐观锁:不加锁,使用CAS和版本号机制实现。
    悲观锁:加锁,Java中使用synchronzied关键字实现。
    CAS,在Java中使用Unsafe对象实现,它只能保证单个变量的原子性,需要与volatile来保证线程安全。同时,它还存在ABA问题:
    假设有两个线程——线程1和线程2,两个 线程按照顺序进行以下操作:
  • 线程1读取内存中数据为A;
  • 线程2将该数据修改为B;
  • 线程2将该数据修改为A;
  • 线程1对数据进行CAS操作
    在第(4)步中,由于内存中数据仍然为A,因此CAS操作成功,但实际上该数据已经被线程2修改过了。这就是ABA问题。
    在AtomicInteger的例子中,ABA似乎没有什么危害。但是在某些场景下,ABA却会带来隐患,例如栈顶问题:一个栈的栈顶经过两次(或多次)变化又恢复了原值,但是栈可能已发生了变化。
    解决这个问题可以使用AtomicStampedReference
    同时,在高并发冲突下的失败重试,也会给CPU带来很大的开销。

因此总结使用场景:

  • 当竞争不激烈 (出现并发冲突的概率小)时,乐观锁更有优势,因为悲观锁会锁住代码块或数据,其他线程无法同时访问,影响并发,而且加锁和释放锁都需要消耗额外的资源。
  • 当竞争激烈(出现并发冲突的概率大)时,悲观锁更有优势,因为乐观锁在执行更新时频繁失败,需要不断重试,浪费CPU资源。
  1. 网络安全
  • 消息摘要&数字签名
    消息摘要:唯一一个对应一段文本的固定长度的值,由单向hash加密函数对消息进行运算生成。由于其唯一性,不可逆,可用于校验文本或文件是否篡改。不需要密钥。常用算法有MD(Message Digest,消息摘要算法)和SHA(Secure Hash Algorithm,安全散列算法),最具代表性对分别是MD5,SHA1。
MessageDigest md5Digest = MessageDigest.getInstance("MD5");
        // 更新要计算的内容
        md5Digest.update(msg.getBytes());
        // 完成哈希计算,得到摘要
        byte[] md5Encoded = md5Digest.digest();

        MessageDigest shaDigest = MessageDigest.getInstance("SHA");
        // 更新要计算的内容
        shaDigest.update(msg.getBytes());
        // 完成哈希计算,得到摘要
        byte[] shaEncoded = shaDigest.digest()
  • 数字签名:数字签名算法可以看做是一种带有密钥的消息摘要算法,并且这种密钥包含了公钥和私钥。也就是说,数字签名算法是非对称加密算法和消息摘要算法的结合体。要求能够验证数据完整性、认证数据来源,并起到抗否认的作用。其原理是利用私钥签名,公钥验签,其核心算法是消息摘要算法。最主要对算法有RAS,DSA。总结:公钥加密、私钥解密、私钥签名、公钥验签
      /**
       * 获取私钥
       * 
      * @param privateKey 私钥字符串
      * @return
      */
      public static PrivateKey getPrivateKey(String privateKey) throws Exception {
          KeyFactory keyFactory = KeyFactory.getInstance("RSA");
          byte[] decodedKey = Base64.decodeBase64(privateKey.getBytes());
          PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(decodedKey);
         return keyFactory.generatePrivate(keySpec);
     }
 
     /**
      * 获取公钥
      * 
      * @param publicKey 公钥字符串
      * @return
      */
     public static PublicKey getPublicKey(String publicKey) throws Exception {
         KeyFactory keyFactory = KeyFactory.getInstance("RSA");
         byte[] decodedKey = Base64.decodeBase64(publicKey.getBytes());
         X509EncodedKeySpec keySpec = new X509EncodedKeySpec(decodedKey);
         return keyFactory.generatePublic(keySpec);
     }

    /**
     * <p>
     * 用私钥对信息生成数字签名
     * </p>
     *
     * @param data       已加密数据
     * @param privateKey 私钥(BASE64编码)
     * @return
     * @throws Exception
     */
    public static String sign(byte[] data, String privateKey) throws Exception {
        byte[] keyBytes = Base64Utils.decode(privateKey);
        PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
        PrivateKey privateK = keyFactory.generatePrivate(pkcs8KeySpec);
        Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);
        signature.initSign(privateK);
        signature.update(data);
        return Base64Utils.encode(signature.sign());
    }
       /**
     * <p>
     * 用公钥校验数字签名
     * </p>
     *
     * @param data      已加密数据
     * @param publicKey 公钥(BASE64编码)
     * @param sign      数字签名
     * @return
     * @throws Exception
     */
    public static boolean verify(byte[] data, String publicKey, String sign)
            throws Exception {
        byte[] keyBytes = Base64Utils.decode(publicKey);
        X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
        PublicKey publicK = keyFactory.generatePublic(keySpec);
        Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);
        signature.initVerify(publicK);
        signature.update(data);
        return signature.verify(Base64Utils.decode(sign));
    }
    
    /**
     * <p>
     * 公钥加密
     * </p>
     *
     * @param data      源数据
     * @param publicKey 公钥(BASE64编码)
     * @return
     * @throws Exception
     */
    public static byte[] encryptByPublicKey(byte[] data, String publicKey)
            throws Exception {
        byte[] keyBytes = Base64Utils.decode(publicKey);
        X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
        Key publicK = keyFactory.generatePublic(x509KeySpec);
        // 对数据加密
        Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
        cipher.init(Cipher.ENCRYPT_MODE, publicK);
        int inputLen = data.length;
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        int offSet = 0;
        byte[] cache;
        int i = 0;
        // 对数据分段加密
        while (inputLen - offSet > 0) {
            if (inputLen - offSet > MAX_ENCRYPT_BLOCK) {
                cache = cipher.doFinal(data, offSet, MAX_ENCRYPT_BLOCK);
            } else {
                cache = cipher.doFinal(data, offSet, inputLen - offSet);
            }
            out.write(cache, 0, cache.length);
            i++;
            offSet = i * MAX_ENCRYPT_BLOCK;
        }
        byte[] encryptedData = out.toByteArray();
        out.close();
        return encryptedData;
    }
    
    /**
     * <p>
     * 公钥解密
     * </p>
     *
     * @param encryptedData 已加密数据
     * @param publicKey     公钥(BASE64编码)
     * @return
     * @throws Exception
     */
    public static byte[] decryptByPublicKey(byte[] encryptedData, String publicKey)
            throws Exception {
        byte[] keyBytes = Base64Utils.decode(publicKey);
        X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
        Key publicK = keyFactory.generatePublic(x509KeySpec);
        Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
        cipher.init(Cipher.DECRYPT_MODE, publicK);
        int inputLen = encryptedData.length;
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        int offSet = 0;
        byte[] cache;
        int i = 0;
        // 对数据分段解密
        while (inputLen - offSet > 0) {
            if (inputLen - offSet > MAX_DECRYPT_BLOCK) {
                cache = cipher.doFinal(encryptedData, offSet, MAX_DECRYPT_BLOCK);
            } else {
                cache = cipher.doFinal(encryptedData, offSet, inputLen - offSet);
            }
            out.write(cache, 0, cache.length);
            i++;
            offSet = i * MAX_DECRYPT_BLOCK;
        }
        byte[] decryptedData = out.toByteArray();
        out.close();
        return decryptedData;
    }
    
/**
     * <p>
     * 私钥加密
     * </p>
     *
     * @param data       源数据
     * @param privateKey 私钥(BASE64编码)
     * @return
     * @throws Exception
     */
    public static byte[] encryptByPrivateKey(byte[] data, String privateKey)
            throws Exception {
        byte[] keyBytes = Base64Utils.decode(privateKey);
        PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
        Key privateK = keyFactory.generatePrivate(pkcs8KeySpec);
        Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
        cipher.init(Cipher.ENCRYPT_MODE, privateK);
        int inputLen = data.length;
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        int offSet = 0;
        byte[] cache;
        int i = 0;
        // 对数据分段加密
        while (inputLen - offSet > 0) {
            if (inputLen - offSet > MAX_ENCRYPT_BLOCK) {
                cache = cipher.doFinal(data, offSet, MAX_ENCRYPT_BLOCK);
            } else {
                cache = cipher.doFinal(data, offSet, inputLen - offSet);
            }
            out.write(cache, 0, cache.length);
            i++;
            offSet = i * MAX_ENCRYPT_BLOCK;
        }
        byte[] encryptedData = out.toByteArray();
        out.close();
        return encryptedData;
    }


    /**
     * <P>
     * 私钥解密
     * </p>
     *
     * @param encryptedData 已加密数据
     * @param privateKey    私钥(BASE64编码)
     * @return
     * @throws Exception
     */
    public static byte[] decryptByPrivateKey(byte[] encryptedData, String privateKey)
            throws Exception {
        byte[] keyBytes = Base64Utils.decode(privateKey);
        PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
        Key privateK = keyFactory.generatePrivate(pkcs8KeySpec);
        Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
        cipher.init(Cipher.DECRYPT_MODE, privateK);
        int inputLen = encryptedData.length;
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        int offSet = 0;
        byte[] cache;
        int i = 0;
        // 对数据分段解密
        while (inputLen - offSet > 0) {
            if (inputLen - offSet > MAX_DECRYPT_BLOCK) {
                cache = cipher.doFinal(encryptedData, offSet, MAX_DECRYPT_BLOCK);
            } else {
                cache = cipher.doFinal(encryptedData, offSet, inputLen - offSet);
            }
            out.write(cache, 0, cache.length);
            i++;
            offSet = i * MAX_DECRYPT_BLOCK;
        }
        byte[] decryptedData = out.toByteArray();
        out.close();
        return decryptedData;
    }
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,817评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,329评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,354评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,498评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,600评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,829评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,979评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,722评论 0 266
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,189评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,519评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,654评论 1 340
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,329评论 4 330
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,940评论 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,762评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,993评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,382评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,543评论 2 349

推荐阅读更多精彩内容

  • 别人的总结不一定适合自己,所以尽量多做一些自己的总结,针对自己的薄弱点重点说明,适当的借鉴别人,少走一些弯路。最重...
    renkuo阅读 7,386评论 2 48
  • Swift1> Swift和OC的区别1.1> Swift没有地址/指针的概念1.2> 泛型1.3> 类型严谨 对...
    cosWriter阅读 11,092评论 1 32
  • 在一个方法内部定义的变量都存储在栈中,当这个函数运行结束后,其对应的栈就会被回收,此时,在其方法体中定义的变量将不...
    Y了个J阅读 4,413评论 1 14
  • 记录关于我和路过的狗狗们互动!
    郑灵悦阅读 256评论 16 3
  • 虽然有点力不从心,但蒲公英可不想放弃信念,以前他看到过妈妈在风雨中的倔犟。那时他还不懂生活的考验这么残酷。现在他要...
    小缘小愿阅读 195评论 1 3