Appium学习(二)--工具介绍、服务关键字、元素定位

一、使用Android工具查找包名和activity

以下有三种查看应用包名package和入口activity名称的方法:
方法一:使用aapt //aapt是sdk自带的一个工具,在sdk\builds-tools\目录下
1.以ES文件浏览器为例,命令行中切换到aapt.exe目录执行:aapt dump badging E:\apk\es3.apk | findstr "package launchable-activity"
2.运行后的结果中以下两行分别是应用包名package和入口activity名称
package: name=’com.estrongs.android.pop’
launchable-activity: name=’com.estrongs.android.pop.view.FileExplorerActivity’
注:在android sdk目录搜索可以找到aapt.exe,如果没有可以下载apktool。

查看当前的activity:
adb shell dumpsys window windows|findstr “mCurrentFocus”


image.png

方法二:查看AndroidManifest.xml
1.使用apktool反编译app:apktool.bat d es3.apk E:\apk\es
2.打开AndroidManifest.xml
manifest节点的package属性值是应用的包名:<manifest package=”com.estrongs.android.pop”>
查找android.intent.action.MAIN和android.intent.category.LAUNCHER对应的activity,该activity对应的android:name属性既是入口activity名称,如下:
<activity android:theme=”@*android tyle/Theme.NoTitleBar” android:label=”@string/app_name” android:name=”com.estrongs.android.pop.view.FileExplorerActivity”>
<intent-filter>
<action android:name=”android.intent.action.MAIN” />
<category android:name=”android.intent.category.LAUNCHER” />
</intent-filter>
</activity>
android.intent.action.MAIN决定应用程序最先启动的Activity
android.intent.category.LAUNCHER决定应用程序是否显示在程序列表里

方法三:通过hierarchyviewer.bat查看包名和activity
比如在设备上打开一个计算器,然后进入adt目录的sdk\tools中打开hierarchyviewer.bat
D:\adt-bundle-windows-x86_64-20140702\sdk\tools\hierarchyviewer.bat
就能看到包名和activity

image.png

注:也可以问Android开发包名和activity,执行appium自动化测试安装app时在appium图形化视图中看日志也可以查看到包名和activity。

二、使用Android工具查找元素

进入sdk\tools\uiautomatorview.bat 打开这个工具


image.png

monitor.bat和hierarchyviewer.bat工具也能查看元素,只能看到元素的坐标,没有uiautomatorview.bat好用。

三、扩展学习adb命令

参考:https://www.jianshu.com/p/fb4462919015

四、appium服务关键字

1.deviceName
手机设备的名字或者模拟器名字(虽然是必须项,但在安卓平台上这个值写啥都行,不影响脚本)
2.app
App(.ipa/.apk)安装文件所在路径,本地路径或者远程路径,或者是一个包含app安装文件的.zip文件。Appium将先尝试安装应用到设备上。注意,如果在安卓上你指定了应用的appPackage和appActivity那么app这个参数就不是必须的。这个参数与browserName冲突,例如:/abs/path/to/my.apk or http://myapp.com/app.ipa
3.browserName
手机web自动化的浏览器的名字。如果是做一个app的自动化这个参数值为空(但是实际上这个参数直接不写就可以了)
例如:'Safari' for iOS and 'Chrome', 'Chromium', or 'Browser' for Android
4.newCommandTimeout
等待新命令超时时间,单位是秒,默认是60秒。当appium服务在指定的超时时间内未接收到任何来自客户端的指令,appium服务端与客户端会主动断开链接
一般可以设置时间为600秒,即10分钟,这样不影响自动化在执行过程中等待元素的加载。
5.autoLaunch
设置appiun启动后是否自动安装和启动app应用。默认值是true(当这个值是true时,代码里则不能使用driver.launchApp方法)
6.udid
链接物理指定设备的唯一标识(安卓上就是通过adb devices看到的设备名称)
7.noReset
不重置应用数据,默认是false
8.fullReset
(iOS) 删除整个虚拟目录,(Android) 通过卸载app来重置应用默认是false
9.appActivity
应用的起始启动
10.appPackage
应用的包名(当以安装模式执行脚本的时候,appPackage和appActivity可以不写)
11.appWaitActivity
启动后等待的activity
12.deviceReadyTimeout
等待设备连接正常的超时时间
13.unicodeKeyboard
使用Unicode input输入法, true
14.resetKeyboard
在运行了unicodeKeyboard完成测试后将输入法重置为原有状态,如果单独使用该参数将被忽略,true(该参数好像没用,测试前需要把Android的输入法改成appium Android input manager for unicode,测试时才可以输入中文。如果测试前没有修改为这个输入法,测试时中文输入会失败)
15.noSign
在uiautomator模式下跳过检查和应用的重签名, true

在Androidsetting方法中会用到上面讲到的关键字,代码如下:

public void AndroidSettings() throws MalformedURLException {
        //获取当前根目录下的文件,当前根目录就是D:\Springboot\AppiumTest
        File classpathRoot = new File(System.getProperty("user.dir"));
        //获取apps目录下的文件
        File appDir = new File(classpathRoot, "/apps");
        //需要提前把该apk放到apps文件夹下
        File app = new File(appDir, "meituan-9.12.801.apk");

        //创建设备属性对象
        DesiredCapabilities capabilities = new DesiredCapabilities();
        //设置设备系统为Android
        capabilities.setCapability("platformName", "Android");
        //设置设备名称为edaed87d,可以通过usb连接手机,在cmd中输入命令,adb devices 看到设备名称
        // (这个名称写什么都行,通常情况下测试时只会连一台设备,appium会直接去连,如果有多台用udid参数指定设备号)
        capabilities.setCapability("deviceName","edaed87d");
        //udid指定设备信息,如果有多台设备这个值会有用
//        capabilities.setCapability("udid","edaed87d");
        //设置设备安卓系统,可以在手机的设置中查看系统号
        capabilities.setCapability("platformVersion", "6.0.1");

        //获取app的绝对路径
//        capabilities.setCapability("app", app.getAbsolutePath());
        //设置app的包名,在cmd中进入aapt.exe的目录输入命令aapt dump badging E:\apk\es3.apk | findstr "package launchable-activity" 查看包名和activity
        capabilities.setCapability("appPackage", "com.sankuai.meituan");
        //设置app的入口activity
        capabilities.setCapability("appActivity", "com.meituan.android.pt.homepage.activity.MainActivity");
        //设置每次运行不清空app的数据
        capabilities.setCapability("noReset","true");
//        //设置每次运行不重签名app
//        capabilities.setCapability("noSign","true");
        //设置超时时间,超过10分钟,客户端与服务器断开链接
//        capabilities.setCapability("newCommandTimeout",600);
        //在Android4.2版本以下需要指定这个Selendroid
//        capabilities.setCapability("automationName","Selendroid");


        //使用Unicode编码方式发送字符串
        capabilities.setCapability("unicodeKeyboard","True");
        //恢复键盘输入法,也可以说是隐藏键盘(要输入中文,需要先把手机上的输入法调整成appium android input manager for unicode)
        //如果需要把键盘调出来只能手工的去改输入法
        capabilities.setCapability("resetKeyboard","True");

        //设置测试机与idea的通讯协议
        driver = new AndroidDriver<WebElement>(new URL("http://127.0.0.1:4723/wd/hub"), capabilities);
        driver.manage().timeouts().implicitlyWait(15, TimeUnit.SECONDS);
        System.out.println("App is launched!");
}

五、定位

5.1 Xpath定位:
Xpath定位中都是用classname定位的。

        WebElement element = driver.findElement(By.xpath("//android.support.v7.widget.RecyclerView/child::android.view.ViewGroup[1]/android.widget.FrameLayout"));
        element.click();
image.png

要定位到红框里的元素,首先找到它的父级目录,父级目录的classname是android.support.v7.widget.RecyclerView
红框里的元素的classname是android.view.ViewGroup,他是第1个元素,所以
写android.view.ViewGroup[1],如果是最后一个就可以写[last()],如果要定位倒数第2个就写[last()-1]


image.png

android.view.ViewGroup[1]下面还有元素,我们要定位到以上图中的红框的元素,找到该元素的classname是android.widget.FrameLayout

该xpath中用到一个child:: 就是存在一个父级和子级的关系:
driver.findElement(By.xpath("//android.support.v7.widget.RecyclerView/child::android.view.ViewGroup[1]/android.widget.FrameLayout"));

如果父级跟目录下有多个级别元素,可以通过descendant:: 定位子孙元素:


image.png

通过父级去找:

        driver.findElement(By.xpath("//android.widget.FrameLayout{contains(@resource-id,'tv.danmaku.bili:id/cover_layout')}/parent::android.view.ViewGroup/fowlling:sibling::android.view.ViewGroup[2]/android.widget.FrameLayout/android.widget.ImageView")).click();

通过parent找到上一级,然后再找同一目录下的其他元素。

一般通过定位classname和id就能定位到元素,Xpath会有点繁琐,用findElementbyId和findElementbyClassname实现上面的定位
代码如下:

        //找到第一个元素点击,默认是第一个元素
//        driver.findElement(By.id("tv.danmaku.bili:id/cover")).click();
        //找到第二个元素点击,因为id都一样,所有通过list来接收,通过elements.get(1).click();点击第二个元素
        List<AndroidElement> elements = driver.findElementsById("tv.danmaku.bili:id/cover");
        elements.get(1).click();
        //点击屏幕
        driver.findElementById("tv.danmaku.bili:id/video_view_biliplayer").click();
        //点击右上角返回键
        driver.findElementByClassName("android.widget.ImageButton").click();

以上代码要定位到第二个视频,打开后再返回


image.png

xpath内容补充:
一、XPath

from lxml import etree

html = etree.parse('html源代码',etree.HTMLPaser())

1.节点的获取

a.html.xpath('//*') #匹配HTML中的所有节点

b.html.xapth('//li') #匹配所有的li节点

c.// 获取所有的子孙节点 ‘/’ 获取子节点(区别好 '//' 与 '/')

d.html.xapth('//li[@class='item-0']') #根据属性匹配

e.html.xapth('//li[contains(@class,'li')]) #属性多值匹配 contains()方法,第一个参数传入属性名称,第二个参数传入属性值,只要此属性包含所传入的属性值就可以完成匹配

f.多属性匹配 .html.xapth('//li[contains(@class,'li') and @name='item'])

g.按顺序选择 html.xapth('//li[last()]') 最后一个li节点

html.xapth('//li[position()<3]') 位置小于3的li节点

html.xapth('//li[1]') 选择第一个li节点

h.节点轴选择 html.xapth('//li/child::') 直接子节点

html.xapth('//li/ancestor::') 所有祖先节点

html.xapth('//li'/attribute::) 获取所有的属性值

html.xapth('//li/decendant::') 获取所有的子孙节点

2.属性,文本获取

(1).文本获取

html.xapth('//li/text()')

(2).属性获取

html.xapth('//li/@class') #获取li标签的class的属性值

5.2 滑动操作

image.png

Appium处理滑动方法是swipe

滑动API:Swipe(int start x,int start y,int end x,int y,duration)
解释:
int start x-开始滑动的x坐标;
int start y -开始滑动的y坐标 ;
int end x -结束点x坐标;
int end y -结束点y坐标;
duration 滑动时间(默认5毫秒)。

屏幕左上角为起点,坐标为(0,0),起点往右为Y轴,起点以下为X轴
微信来说,登录按钮起点和终点坐标分别为(63,1716)和(432,1860)


image.png

封装的代码:

package com.yin.Appiumdemo;

import io.appium.java_client.AppiumDriver;
import org.openqa.selenium.WebElement;

public class SwipeCenter {

    /*
    方法参数介绍:
    driver(就是把appiumdriver对象传进来)
    during(这里是填写毫秒数,这里的 毫秒数越小 滑动的速度越快~ 一般设定在500~1000,如果你想快速滑动 那就可以设置的更加小)
    num(是只滑动的次数,如相册多张图片翻页测试什么的滑动或者滑动到列表底部。就直接输入次数就行了)
     */

    //上滑 swipeToUp
    public static void swipeToUp(AppiumDriver driver,int during,int num) throws InterruptedException {
        //先获取屏幕的分辨率
        int width = driver.manage().window().getSize().width;
        int height = driver.manage().window().getSize().height;
        Thread.sleep(2000);
        for (int i=0;i<num;i++){
            driver.swipe(width/2,height*3/4,width/2,height/4,during);
            Thread.sleep(2000);
        }

    }
    //下拉 swipeToDown
    public static void swipeToDown(AppiumDriver<WebElement> driver,int during,int num) throws InterruptedException {
        //先获取屏幕的分辨率
        int width = driver.manage().window().getSize().width;
        int height = driver.manage().window().getSize().height;
        //等待2秒
        Thread.sleep(2000);
        for (int i=0;i<num;i++){
            driver.swipe(width/2,height/4,width/2,height*3/4,during);
            //等待2秒
            Thread.sleep(2000);
        }

    }
    //向左滑动 swipeToLeft
    public static void swipeToLeft(AppiumDriver<WebElement> driver,int during,int num) throws InterruptedException {
        //先获取屏幕的分辨率
        int width = driver.manage().window().getSize().width;
        int height = driver.manage().window().getSize().height;
        //等待2秒
        Thread.sleep(2000);
        for (int i=0;i<num;i++){
            driver.swipe(width*3/4,height/2,width/4,height/2,during);
            //等待2秒查看界面变动
            Thread.sleep(2000);
        }

    }
    //向右滑动 swipeToRight
    public static void swipeToRight(AppiumDriver<WebElement> driver,int during,int num) throws InterruptedException {
        //先获取屏幕的分辨率
        int width = driver.manage().window().getSize().width;
        int height = driver.manage().window().getSize().height;
        //等待2秒
        Thread.sleep(2000);
        for (int i=0;i<num;i++){
            driver.swipe(width/4,height/2,width*3/4,height/2,during);
            //等待2秒查看界面变动
            Thread.sleep(2000);
        }

    }

}

调用的代码:

    @Test(description = "滑动操作")
    public void testswip() throws InterruptedException {

        //调取SwipeCenter类中的方法,实现向上、向下、向左、向右滑动
        //向上滑动
        SwipeCenter.swipeToUp(driver,500,2);
        //向下滑动
        SwipeCenter.swipeToDown(driver,500,1);
        //向左滑动
        SwipeCenter.swipeToLeft(driver,500,1);
        //向右滑动
        SwipeCenter.swipeToRight(driver,500,1);
}

还可以利用try、catch,先去找元素,找不到滑动页面再找

        WebElement element = null;
        try {
            //先去找元素
            element = driver.findElementByName("百万播放");
        }catch(Exception e){
            e.printStackTrace();
            //找不到向左滑动
            SwipeCenter.swipeToLeft(driver,500,1);
            element = driver.findElementByName("百万播放");
            //找到后再点击click
            element.click();
        }

5.3 location坐标定位

    @Test(description = "location坐标定位")
    public void testlocation() throws InterruptedException {
        //tap模拟手指点击
        //先等待2秒,等待元素出来
        Thread.sleep(2000);
        //使用tap方法,第一个参数是模拟一个手指去点,第二个参数是X点,第三个是y点,第四个是持续点击0.5秒
        //通过Ui Automator viewer定位看到坐标点是[846,362][1080,482],取X值在846~1080之间,取Y值在362~482之间
        driver.tap(1,900,420,500);
        Thread.sleep(2000);
        System.out.println("坐标定位成功");
    }
image.png

5.4 混合应用定位获取webview页面的元素
混合应用就是在app中嵌套了H5的页面,H5页面的元素是webview的。
步骤都在代码中有些注释:

    @Test(description = "获取webview页面元素")
    public void testwebview(){

        //获取driver中的handles,利用foreach循环查找如果是webview的页面就进入webview页面,转换成html在浏览器中打开再去定位到某个元素
        //定位成功之后再转回到NATIVE_APP 页面
        Set<String> contextset = driver.getContextHandles();
        for (String context : contextset ){
            System.out.println("页面上context是:"+context);


            if (context.toLowerCase().contains("webview")){
                driver.context(context);//进入webview页面
                System.out.println("进入成功");
                break;
            }
        }
        //通过封装的方法把webview源码转换成html,然后在浏览器中打开去定位到元素
        //封装的方法还没有实现,如果找到webview页面,获取到源码使用chrome-development tool 来定位元素
        //使用步骤参考:https://blog.csdn.net/wanglin_lin/article/details/77100028
//        PageResourceGetter.initialize("uers/dir/prg.html",driver.getPageSource());
        //定位到元素
       driver.findElement(By.id("username")).sendKeys("appium.test@gmail.com");
       //再回到原生app
        driver.switchTo().window("NATIVE_APP");

    }

参考地址:https://blog.csdn.net/wanglin_lin/article/details/77100028
一般情况下通过Ui Automator viewer也可以看到webview中的控件,如果看不到就把driver.getPageSource()的内容打印出来,然后分析里面的控件再定位也可以。

appium的api

image.png

手机坐标、首页导航滑动前面已经讲过,省略。

5.5 截屏操作
先创建一个截屏类:

package com.yin.Appiumdemo;
/*
截图操作
 */

import io.appium.java_client.AppiumDriver;
import io.appium.java_client.android.AndroidDriver;
import org.apache.commons.io.FileUtils;
import org.openqa.selenium.OutputType;

import java.io.File;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;

public class Screenshot {
    public static void Screenshot(AppiumDriver driver, String ScreenName) throws IOException {
        //设置时间格式,截图的文件要加上时间戳这样不会重复
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss");
        //生成时间戳
        String dataString = simpleDateFormat.format(new Date());
        //设置存放图片目录
        String dir_name = System.getProperty("user.dir")+"\\异常图片";
        System.out.println("异常图片目录:"+dir_name);
        //防止出现图片目录被删除,所以先判断目录是否存在,如果不存在就新建一个目录
        if (!(new File(dir_name).isDirectory())){
            //如果不存在就创建一个目录
            new File(dir_name).mkdir();
        }
        //调用方法获取页面截屏
        File screenfile = driver.getScreenshotAs(OutputType.FILE);
        //复制图片到指定目录,放到dir_name目录下,命名是时间戳+测试用例名称
        FileUtils.copyFile(screenfile,new File(dir_name+"\\"+dataString+ScreenName+".jpg"));
        //打印图片名称,
        System.out.println("异常图片名称:"+dir_name+"\\"+dataString+ScreenName+".jpg");

    }
}

在测试方法中调用:

    @Test(description = "截屏")
    public void testscreenshot() throws InterruptedException, IOException {
        //正常请求的截图
        Thread.sleep(1000);
        Screenshot.Screenshot(driver,"test001");
        Thread.sleep(1000);

        //异常的截图放到catch中
        try{
            driver.findElementByName("百万播放");
        }catch (Exception e){
            Screenshot.Screenshot(driver,"test001");
        }


    }

生成目录时可能会遇到目录出现乱码的情况,进入idea的设置中找到File Encoding 都设置成UTF-8,然后再执行测试类就可以了


image.png

5.6 元素之间的滑动

image.png

image.png

image.png
image.png

控件的滑动可以用两种方式实现,第一种是TouchAction,第二种是swipe
封装的方法如下:

package com.yin.Appiumdemo;

import io.appium.java_client.AppiumDriver;
import io.appium.java_client.TouchAction;
import org.openqa.selenium.WebElement;

/*
封装方法,实现控件的滑动
 */
public class ElementHua {
    //利用TouchAction 拖动控件
    public static void Element_HuaWithTouchAction(AppiumDriver driver,WebElement element, String swipeway) throws InterruptedException {
        //先获取控件的起点x坐标值
        int xStartPoint = element.getLocation().getX();
        //获取控件的宽度,计算出控件的x终点坐标值
        int xEndPoint = xStartPoint+element.getSize().getWidth();
        //获取控件的Y起点坐标值
        int yStartPoint = element.getLocation().getY();
        System.out.println("X起点坐标是"+xStartPoint);
        System.out.println("X终点坐标是"+xEndPoint);
        System.out.println("Y起点坐标是"+yStartPoint);

        //利用TouchAction类实现拖动
        TouchAction action = new TouchAction(driver);

        if (swipeway.equals("左滑")){
            Thread.sleep(1000);
            //可以根据控件的宽度调整滑动多宽,可以调整20为30、40等等,以下方法类似
            action.press(xEndPoint-20,yStartPoint+20).waitAction(1000).moveTo(xStartPoint,yStartPoint+20).release().perform();
            Thread.sleep(1000);

        }
        if (swipeway.equals("右滑")){
            Thread.sleep(1000);
            action.press(xStartPoint+20,yStartPoint+20).waitAction(1000).moveTo(xEndPoint,yStartPoint+20).release().perform();
            Thread.sleep(1000);
        }

    }
    //利用滑动 滑动控件
    public static void Element_HuaWithSwipe(AppiumDriver driver,WebElement element, String swipeway) throws InterruptedException {
        //先获取控件的起点x坐标值
        int xStartPoint = element.getLocation().getX();
        //获取控件的宽度,计算出控件的x终点坐标值
        int xEndPoint = xStartPoint+element.getSize().getWidth();
        //获取控件的Y起点坐标值
        int yStartPoint = element.getLocation().getY();
        System.out.println("X起点坐标是"+xStartPoint);
        System.out.println("X终点坐标是"+xEndPoint);
        System.out.println("Y起点坐标是"+yStartPoint);


        if (swipeway.equals("左滑")){
            Thread.sleep(1000);
            driver.swipe(xEndPoint-20,yStartPoint+20,xStartPoint+10,yStartPoint+20,500);
            Thread.sleep(1000);

        }
        if (swipeway.equals("右滑")){
            Thread.sleep(1000);
            driver.swipe(xStartPoint+10,yStartPoint+20,xEndPoint-20,yStartPoint+20,500);
            Thread.sleep(1000);
        }

    }
}

调用的方法是:

    @Test(description = "控件滑动")
    public void testactionswipe() throws InterruptedException {
        //调用封装的方法实现控件滑动

        driver.findElementByName("追番").click();
        driver.findElementByName("时间表").click();
        List<WebElement> elements = driver.findElementsById("tv.danmaku.bili:id/date_item");
        WebElement element = elements.get(2);

        ElementHua.Element_HuaWithTouchAction(driver,element,"左滑");
        ElementHua.Element_HuaWithTouchAction(driver,element,"右滑");

        ElementHua.Element_HuaWithSwipe(driver,element,"左滑");
        ElementHua.Element_HuaWithSwipe(driver,element,"右滑");
    }

5.7 控件点击操作
很简单,就用click()方法

image.png

控件的输入
用到sendkeys()

image.png

    @Test(description = "控件的输入")
    public void testsendkeys() throws InterruptedException {
        Thread.sleep(1000);
        driver.findElementById("tv.danmaku.bili:id/expand_search").click();
        Thread.sleep(1000);
        driver.findElementById("tv.danmaku.bili:id/search_src_text").sendKeys("test");
        Thread.sleep(1000);

    }

5.8 控件长按

image.png

    @Test(description = "控件的长按")
    public void testlongpress(){
        //获取控件的X、Y坐标点,通过坐标点长按

        WebElement element = driver.findElementByName("追番");
        int X = element.getLocation().getX();
        int Y = element.getLocation().getY();

        TouchAction action = new TouchAction(driver);
        //通过坐标点长按
        action.longPress(X,Y).perform();
        //通过定位到元素长按
        action.longPress(driver.findElementByName("时间表")).perform();
    }

5.9 控件的点击
TouchAction action = new TouchAction(driver);
//通过坐标点点击控件,
action.tap(X,Y).release().perform();
//通过输入控件的element点击控件
action.tap(element).release().perform();

tap比click点击准确,会点击控件的中间位置,click会存在点击不准确的情况。

5.10 Appium调起手机端chrome app浏览器
appium测试手机web的方法。
请参考https://www.jianshu.com/p/b46d3687fcfc

5.11 CSS定位
适用与webview的页面。
基础表达式部分:

image.png

image.png

高级表达式部分:


image.png
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,590评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 86,808评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,151评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,779评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,773评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,656评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,022评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,678评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,038评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,659评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,756评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,411评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,005评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,973评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,053评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,495评论 2 343

推荐阅读更多精彩内容

  • ¥开启¥ 【iAPP实现进入界面执行逐一显】 〖2017-08-25 15:22:14〗 《//首先开一个线程,因...
    小菜c阅读 6,357评论 0 17
  • Swift1> Swift和OC的区别1.1> Swift没有地址/指针的概念1.2> 泛型1.3> 类型严谨 对...
    cosWriter阅读 11,084评论 1 32
  • 发现 关注 消息 iOS 第三方库、插件、知名博客总结 作者大灰狼的小绵羊哥哥关注 2017.06.26 09:4...
    肇东周阅读 12,019评论 4 62
  • 莎士比亚:上大学还是不上大学,这是一个值得思考的问题。 苏轼:月有阴晴圆缺,学有大小之分,此事古难全,但愿人长久,...
    海红果阅读 190评论 0 4
  • 我抬笔将冷暖世态隔绝 究竟抵不过 所钟情之人赠我凉薄 早春暮春 酒暖花深 酒自飘香 花自飘落 千千心结 付与谁知 ...
    曾经美得像花儿阅读 455评论 0 1