BaseFramework 究竟是什么(BaseFramework 全解析)

BaseFramework 究竟是什么?

Github: https://github.com/kongzue/BaseFramework

BaseFramework 本质上就是一个对 Activity、Fragment、Adapter 等 Android 标准组件的一次封装,而我对这些组件进行封装的根本意义来源于对快速敏捷开发、高效管理的追求。

就像大家所知道的,Android 开发中总有很多令人头痛而官方未进行解决的问题,而这些问题都让我们在进行一次新项目的开始铺设时总要集成大量的工具类来辅助我们进行新工程的构架,类似于响应式适配、吐司提示、乃至于界面间跳转,都有很多令人诟病的地方,简单的来说,就是他们并不够好用。

真正好用的代码绝对是一句话能干完的事情绝对不拖泥带水的 BB 好几句,接下来我就从以下几个方面为大家介绍 BaseFramework 所做的一些努力。

举例

①我是一句简单的吐司

 Toast.makeText(MainActivity.this, "你好!", Toast.LENGTH_SHORT).show();

你看,他至少包含了以下几个不好用的地方:

  1. 是否有在主线程运行?你得自己加判断;
  2. 语句繁琐,在 Activity 中使用的话为什么还需要传 Context?自己 this 一下不好么?
  3. 繁琐+1,我们多半需要的简单提示都是短时间 LENGTH_SHORT 的,而非 LENGTH_LONG 的,能否默认一个 LENGTH_SHORT 而不要每次都要输入一遍时间参数?
  4. 新手总忘记 .show();

那么,这么一个吐司提示就这么复杂,每次在进行开发需要使用到 Toast 的时候总要输入这么一大段的话,十分繁琐,而我在 BaseFramework 的 BaseActivity 和 BaseFragment 等 UI 相关的封装类中都对 Toast 进行了精简,现在要显示一个简单的 Toast 你只需要:

 toast("你好!");

是不是简单的多了呢?

②我是一个日志

是否我们都有一个相同的需求,当 App 被编译为 Release 的时候我们需要把 Log 全部关闭?而每次调用 Log 的时候都需要使用 .i 、.e、 .d这些类型以及要设定 Tag 令人感到繁琐,我的日志能否用一句话完成?

来看看 BaseFramework 的 BaseActivity 和 BaseFragment 中如何快速的打一个日志吧?

 log("你好!");

就是这么简单。你还可以通过 BaseActivity 提供的总开关:BaseActivity.DEBUGMODE 来直接控制它是否可用,我们建议直接在您的 Application 初始化时将其与您的项目的编译模式进行绑定:

BaseActivity.DEBUGMODE = BuildConfig.DEBUG;

这样在编译为 Release 版本时就不会忘记关闭 log 输出了。

思想

你也许会说 BaseFramework 不就是把一些工具类的方法封装到了其中来使用嘛,虽然我就简单的举了两个例子,但 BaseFramework 可不仅仅如此,它其中除了带有一些简单的,诸如 toast、log 这样的工具类,还包含有响应式适配、权限管理与申请、字符串判空、高级跳转与回传、大到 Application 中所有 Activity 的生命周期管理、集成式属性工具类等,应有尽有,这些东西都是我们在日常开发实战中的总结,他可能带有一定的个人编程思想和习惯,但绝对不失为一个相当好用的开发工具。

BaseFramework

你还可以下载 Demo 来体验这个项目:


BaseFramework Demo

我们的努力

上述简单的介绍了两个工具在 BaseFramework 框架中的简化使用,可能看起来十分简单,但从我们一步步的简化过程中就能使我们的项目和构建变得更加高效,接下来我还会在这里介绍一些 BaseFramework 中更深层次的思想和行为,以帮助你更好地理解 BaseFramework 所能做的事,以及提升开发效率。

③ BaseActivity

  1. 约定

首先,我们使用了约定关键词 “me” 来代替 XXXActivity.this,只要你的 Activity 继承了 com.kongzue.baseframework.BaseActivity 那么在你的 Activity 生命周期中,你可以在任何需要传入 Context 的地方直接传 “me”,而无需再繁琐的使用 XXXActivity.this。

当你将自己的 Activity 继承了 com.kongzue.baseframework.BaseActivity 后你会发现,系统会强制你重写三个方法:

@Override
public void initViews() {
    //此处用于绑定组件(findViewById)
}

@Override
public void initDatas(JumpParameter parameter) {
    //此处用于加载数据,JumpParameter 的作用后边会有讲到
}

@Override
public void setEvents() {
    //此处用于编写组件事件
}

之所以这么做,就是为了将我们的代码规范化,一般一个 Activity 启动后需要执行的三步流程即是如此,按照相应的规范行事能够减少很多错误,提升程序的可靠性。

  1. 使用注解构建模式

可能有人注意到了,onCreate 方法在这里不再推荐重写使用,您可以直接在您的 Acttivity 上使用注解完成 Layout 的绑定:

@Layout(R.layout.activity_adapter_test)
public class AdapterTestActivity extends BaseActivity {
    //...
}

之所以这么做,也是方便当我们的工程开发到后期时,避免当代码复杂繁乱的时,想在其中确定 onCreate 的位置以及寻找 Activity 界面布局 layout 的位置十分麻烦的情况。
除此之外,BaseActivity 还支持的注解有:

@Layout(R.layout.xxx)                      //绑定布局
@DarkStatusBarTheme(true)           //开启顶部状态栏图标、文字暗色模式
@DarkNavigationBarTheme(true)       //开启底部导航栏按钮暗色模式
@NavigationBarBackgroundColor(a = 255,r = 255,g = 255,b = 255)      //设置底部导航栏背景颜色(a = 0,r = 0,g = 0,b = 0可透明)

//也可从代码中进行控制:
setDarkStatusBarTheme(true);            //开启顶部状态栏图标、文字暗色模式
setDarkNavigationBarTheme(true);        //开启底部导航栏按钮暗色模式
setNavigationBarBackgroundColor(Color.argb(255,255,255,255));       //设置底部导航栏背景颜色(a = 0,r = 0,g = 0,b = 0可透明)
  1. 如何进行沉浸式适配?

BaseActivity 默认就会支持沉浸式适配,最低支持到安卓4.4,您还需要单独做的只有在您的布局中使用 android:fitsSystemWindows="true" 构建安全区:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <!--背景部分-->
        
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:fitsSystemWindows="true"
        android:orientation="horizontal">

        <!--内容部分-->

    </LinearLayout>
</LinearLayout>

具体的范例可以参考:

《Android基础 - 如何做鲁棒性更高的布局》 https://www.jianshu.com/p/174bcb6c0d93

  1. 如何快速方便的使用权限?

以往的权限申请与管理需要我们手动申请,并在 onRequestPermissionsResult 中进行处理,十分繁琐。BaseActivity 则对其进行了进一步的封装,并设立了回调监听器,一句话即可以使用:

requestPermission(new String[]{Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION}, new OnPermissionResponseListener() {
    @Override
    public void onSuccess(String[] permissions) {
        toast("申请权限成功");
    }
    @Override
    public void onFail() {
        toast("申请权限失败");
    }
});
  1. 其他
    BaseActivity 支持的工具类如下,都可以随意简单的进行使用:
toast(Obj); //简易吐司:

log(Obj);  //简易Log打印日志(可通过BaseActivity.DEBUGMODE = false关闭):

setIMMStatus(boolean show, EditText editText);    //软键盘开关:

moveAnimation(Object obj, String perference, float aimValue, long time, long delay);    //属性动画:

isNull(String);    //数据判空(适合网络返回值判断处理,即便为字符串“null”也为空):

jumpAnim(int enterAnim, int exitAnim);    //跳转动画(参数为您的动画资源id)

copy(String);    //直接复制文字

//适配相关:
dip2px(Context context, float dpValue);    //dip与像素px转换:

dip2px(Context context, float dpValue);    //像素px与dip转换:

getNavbarHeight();    //获取底部导航栏高度

getDisPlayHeight();    //获取屏幕高度

getDisPlayWidth();    //获取屏幕宽度

getStatusBarHeight();    //获取顶部状态栏高度

③ BaseFragment

Fragment 也是常用的布局元素之一,但它先天创建过程较为繁琐,且绑定组件的过程也并不友好,为与 BaseActivity 保持一致的规范化,在 BaseFragment 中我采用了相同的构建方式:

@Layout(R.layout.fragment_demo)
public class FragmentDemo extends BaseFragment {
    private TextView info;
    private Button btnHide;
    @Override
    public void initViews() {
        //此处加载组件
        info = findViewById(R.id.info);
        btnHide = findViewById(R.id.btn_hide);
    }
    
    @Override
    public void initDatas() {
        //此处编写初始化代码
    }
    @Override
    public void setEvents() {
        //此处为组件绑定事件
       
    }
}

使用时同样可以直接使用 @Layout 注解绑定布局,默认强制重写 initViews、initDatas和setEvents三个方法,并且在 BaseFragment 中,您可以直接使用 findViewById 来绑定组件,并且 BaseFragment 同样支持 BaseActivity 的一些工具方法。

④高级跳转

在 BaseFramework 中,您依然可以使用 startActivity(new Intent(context, class)); 的代码形式来进行界面间的跳转,但我们都知道的是,Intent 只支持携带基本的数据类型格式而不支持自定义的数据类型,例如,我们需要在两个界面间传输一张图像(Bitmap),这是一个很常见的业务需求,但 Intent 并不能办到,也许你会使用诸如 EventBus 之类的工具来完成,但我相信依然有更加的简单的方式实现它,在这里,BaseFramework 提供了一个简单的跳转指令来完成它:

jump(JumpActivity.class);        //这是它的基本使用方式

本质上,jump 依然是调用了 startActivity 方法来完成操作,但它更加简单易用,一句话即可。

如上边的例子所述,有时我们可能需要携带一个自定义类型的参数,例如 bitmap 到下一界面,那么可以这样:

Bitmap bmp = xxx;
jump(JumpActivity.class, new JumpParameter()
        .put("参数1", "这是一段文字参数")
        .put("参数2", bmp)
);

而在下一个界面中,您可以在已经重写的 initDatas 方法中直接获取到相应的参数:

@Override
public void initDatas(JumpParameter parameter) {
    if(parameter!=null){
        //此处可以使用 parameter.get(key); 方法获取数据
    }
}

*从 BaseFramework 6.5.0 版本起,跳转参数 JumpParameter 新增 getBoolean、getInt 和 getString 三个基础方法,从此方法获取数据不需要判断是否为空(null)以及进行强转类型;

我们在实际开发中还可能遇到一种跳转到下一界面后需要下一界面在关闭时回传数据的情况,一般我们得重写 onActivityResult 来进行处理,相当繁琐,在 jump 方式中我们提供了回调函数来回传数据,此方法会在回到当前界面后执行:
跳转代码范例:

jump(ResponseActivity.class, new OnResponseListener() {
    @Override
    public void OnResponse(JumpParameter parameter) {
        if (parameter == null) {
            toast("未返回任何数据");
        } else {
            toast("收到返回数据,参数“返回数据1”中的值为:" + parameter.get("返回数据1"));
        }
    }
});

亦可选用同时带参数+返回值的跳转:

jump(ResponseActivity.class,new JumpParameter()
                .put("参数1", "这是一段文字参数")
                .put("参数2", "这是一段文字参数")
        , new OnResponseListener() {
    @Override
    public void OnResponse(JumpParameter parameter) {
        if (parameter==null){
            toast("未返回任何数据");
        }else{
            toast("收到返回数据,参数“返回数据1”中的值为:" + parameter.get("返回数据1"));
        }
    }
});

而要返回数据的界面可以通过以下方法直接设置要返回的数据:

setResponse(new JumpParameter().put("返回数据1", "测试成功"));

④Activity 总管

当我们的界面中拥有大量的 Activity 后,这些 Activity 的管理就会相当的麻烦,例如我们需要关闭程序,已知的 killProcess、System.exit 之类的方法只会让 App 重启,那么如何有效的对 Activity 进行控制呢?

AppManager 是 BaseActivity 的管理工具类,更适合 BaseActivity 的管理工作。

提供如下方法:

killActivity(baseActivity)      //结束指定BaseActivity
killAllActivity()               //结束所有BaseActivity
AppExit()                       //退出App

其他方法,例如 pushActivity 添加Activity到堆栈,都是自动执行的,不需要手动调用。

⑤万能适配器BaseAdapter

通常由系统提供的的 android.widget.BaseAdapter 和 android.widget.SimpleAdapter 虽然都能满足我们的日常所需,但面对各种自定义需求以及多种布局的需求时往往捉襟见肘,以至于我们不得不重写 Adapter 来满足需求。

但再重写过程中实际上是有很多重复性的代码,导致我们的项目臃肿不堪,从 v6.4.8 版本起,新增了 BaseAdapter 来实现各种自定义布局适配器的需求:

  1. JavaBean 适配方式

使用此方式需要先创建继承自 BaseAdapter.BaseDataBean 的 JavaBean 数据集合来封装数据,例如在我们 Demo 中的:

List<CustomDatas> datas = new ArrayList();
datas.add(new CustomDatas().setTitle("我是布局1"));
datas.add(new CustomDatas().setTitle("我是布局2"));
datas.add(new CustomDatas().setTitle("我是布局3"));

其中 CustomDatas 的具体代码为:

private class CustomDatas extends BaseAdapter.BaseDataBean {
    String title;

    @Override
    public CustomDatas setType(int type) {
        this.type = type;
        return this;
    }

    public String getTitle() {
        return title;
    }

    public CustomDatas setTitle(String title) {
        this.title = title;
        return this;
    }
}

它是一个典型的 JavaBean,其中所有属性请根据实际业务需求添加,并建议生成相应的 get、set 方法。

接下来创建适配器并绑定在相应组件上:

baseAdapter = new BaseAdapter(me, datas, R.layout.item_list_layout1, new SimpleAdapterSettings() {
    @Override
    public Object setViewHolder(View convertView) {
        ViewHolder1 viewHolder1 = new ViewHolder1();
        viewHolder1.txtTitle = convertView.findViewById(R.id.txt_title);
        return viewHolder1;
    }

    @Override
    public void setData(Object viewHolder, BaseAdapter.BaseDataBean dataBean) {
        CustomDatas data = (CustomDatas) dataBean;
        ViewHolder1 viewHolder1 = (ViewHolder1) viewHolder;
        viewHolder1.txtTitle.setText(data.getTitle());
    }
});
list.setAdapter(baseAdapter);

SimpleAdapterSettings 是一个适配器控制器的回调接口,在其中重写 setViewHolder 和 setData方法,其中 setViewHolder 需要您在此处根据父布局 convertView 创建布局管理组件 ViewHolder,并回传您的 ViewHolder。接下来会在 setData 中将 ViewHolder 和 相对应的数据 dataBean给出,请在此方法中对组件进行赋值和事件绑定。
注意在此方法中您可以将 dataBean 强转为您的 JavaBean 类,viewHolder 也可以强转为您的 ViewHolder。

  1. Map 适配方式

应对复杂多变的数据我们可能会选择使用 Map 来存储我们的需要展现的数据,BaseAdapter 亦支持此方式的数据,与上述方法类似,您可以轻松完成数据的绑定和组件的展现:

List<Map<String, Object>> datas = new ArrayList<>();
Map<String, Object> map = new HashMap<>();
map.put("title", "我是布局1");
datas.add(map);
map = new HashMap<>();
map.put("title", "我是布局2");
datas.add(map);
map = new HashMap<>();
map.put("title", "我是布局3");
datas.add(map);
baseAdapter = new BaseAdapter(me, datas, R.layout.item_list_layout1, new SimpleMapAdapterSettings() {
    @Override
    public Object setViewHolder(View convertView) {
        ViewHolder1 viewHolder1 = new ViewHolder1();
        viewHolder1.txtTitle = convertView.findViewById(R.id.txt_title);
        return viewHolder1;
    }

    @Override
    public void setData(Object viewHolder, Map<String, Object> data) {
        ViewHolder1 viewHolder1 = (ViewHolder1) viewHolder;
        viewHolder1.txtTitle.setText(data.get("title") + "");
    }
});
list.setAdapter(baseAdapter);
  1. 多种布局的绑定方式

根据实际业务需求,我们可能需要在一个组件中展现多种布局,此时您首先需要对您的布局进行编号,从0开始,依次往后,并将他们添加为一个 Map 集合,其中键值对:id对应布局资源id(LayoutResId):

Map<Integer, Integer> layoutResIdMap = new HashMap<>();
layoutResIdMap.put(0, R.layout.item_list_layout1);
layoutResIdMap.put(1, R.layout.item_list_layout2);
layoutResIdMap.put(2, R.layout.item_list_layout3);

接下来,您需要将数据存储为一个集合,此处展示的是 JavaBean 形式的存储方式,您亦可以使用 Map 作为数据的存储器,最后将它打包为一个 List即可:

List<CustomDatas> datas = new ArrayList();
datas.add(new CustomDatas().setTitle("我是布局1").setType(0));
datas.add(new CustomDatas().setTitle("我是布局2").setType(1));
datas.add(new CustomDatas().setTitle("我是布局3").setType(2));

需要注意的是,之前提到过,您的 JavaBean(CustomDatas)需要继承自 BaseAdapter.BaseDataBean,而在 BaseAdapter.BaseDataBean 中,我们默认实现了一个属性“type”,它是 int 整数型,用于存储与布局对应的编号 id。

如果您默认使用 Map 的方式存储数据,您需要手动 put("type", 对应布局编号id ) 以保证能够和布局资源相匹配。

最后,创建适配器和绑定展示组件:

baseAdapter = new BaseAdapter(me, datas, layoutResIdMap, new MultipleAdapterSettings() {
    @Override
    public Object setViewHolder(int type, View convertView) {
        switch (type) {
            case 0:
                ViewHolder1 viewHolder1 = new ViewHolder1();
                viewHolder1.txtTitle = convertView.findViewById(R.id.txt_title);
                return viewHolder1;
            case 1:
                ViewHolder2 viewHolder2 = new ViewHolder2();
                viewHolder2.txtTitle = convertView.findViewById(R.id.txt_title);
                return viewHolder2;
            case 2:
                ViewHolder3 viewHolder3 = new ViewHolder3();
                viewHolder3.txtTitle = convertView.findViewById(R.id.txt_title);
                return viewHolder3;
            default:
                return null;
        }
    }

    @Override
    public void setData(int type, Object viewHolder, BaseAdapter.BaseDataBean dataBean) {
        CustomDatas data = (CustomDatas) dataBean;
        switch (type) {
            case 0:
                ViewHolder1 viewHolder1 = (ViewHolder1) viewHolder;
                viewHolder1.txtTitle.setText(data.getTitle());
                break;
            case 1:
                ViewHolder2 viewHolder2 = (ViewHolder2) viewHolder;
                viewHolder2.txtTitle.setText(data.getTitle());
                break;
            case 2:
                ViewHolder3 viewHolder3 = (ViewHolder3) viewHolder;
                viewHolder3.txtTitle.setText(data.getTitle());
                break;
        }
    }
});
list.setAdapter(baseAdapter);

从上述代码中可以看到,回调函数中出现了一个 type 的值,在这里您可以根据不同的值绑定不同的布局,设置不同的数据和事件。

以上就是关于 BaseAdapter 的简单介绍了。您还可以通过文档前半部分的二维码下载 Demo ,其中会为您展现关于 BaseAdapter 全部的绑定方式。

⑥Preferences

Preferences是SharedPreferences的简易封装。

每次手写SharedPreferences过于繁琐,因此封装了一个简易的属性记录读取类。 通过对属性的常见数据类型进行封装,使属性读取写入更方便,同时提供一些属性管理方法。

//读取属性为String类型
//参数:context上下文索引,path路径,preferencesName属性名
getString(context, path, preferencesName)
//类似的,提供读取为Boolean的方法:
getBoolean(context, path, preferencesName)
//提供读取为Int的方法:
getInt(context, path, preferencesName)

//写入属性方法是统一的
//参数:context上下文索引,path路径,preferencesName属性名,value根据属性数据类型定义
set(context, path, preferencesName, ?)

//提供清除(清空)所有属性的方法
cleanAll();

引入方法

Maven仓库:

<dependency>
  <groupId>com.kongzue.baseframework</groupId>
  <artifactId>baseframework</artifactId>
  <version>6.5.0</version>
  <type>pom</type>
</dependency>

Gradle:
在dependencies{}中添加引用:

implementation 'com.kongzue.baseframework:baseframework:6.5.0'

总结

BaseFramework 旨在帮助开发者快速顺利的铺设一个新的项目,迅速完成项目的构建以及开发,在 BaseFramework 中提供了丰富的工具类可以轻松使用,且无需担心额外的负担,BaseFramework 是降本增效的典型框架,也是我对于开发过程中所遇到的问题、麻烦的统一解决方案,同时,以此基础上我还有提供 基础网络请求框架以及基础对话框框架等实用框架,以此为起点构建安卓应用程序会变得更加简单。

BaseFramework 框架的 Github 地址 https://github.com/kongzue/BaseFramework
更多参考框架请访问 https://github.com/kongzue
或前往 https://kongzue.github.io 查看我的开发指导意见

喜欢的话麻烦给个赞,我还会不断完善 BaseFramework,您可以 Fork&Star BaseFramework 的 Repositories 来订阅更新。

开源协议

   Copyright BaseFragment

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

     http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,015评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,262评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,727评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,986评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,363评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,610评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,871评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,582评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,297评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,551评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,053评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,385评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,035评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,079评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,841评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,648评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,550评论 2 270

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 170,569评论 25 707
  • Java基础 什么是重载,什么是重写?有什么区别?重载(Overload):(1)Overloading是一个类中...
    勤息嘻嘻嘻阅读 598评论 0 1
  • 分离仿似烈酒,初品,刺喉,如跌宕起伏的暖流,缓缓归入心头,挥不尽,散不去。但不可久尝,久尝,麻木。一口,直入胃。升...
    moon_神阅读 137评论 0 0
  • 神话_shenhua阅读 141评论 0 1
  • 没有什么比生活更好糊弄,也没有什么比内心更难敷衍,接受和认同,才能跟这荒谬又精彩的人生冰释前嫌。 ​​​ 《观虫》...
    神奇小瘪斯阅读 476评论 2 0