《第一行代码》---Android 啃完,学习笔记

#Android 基础知识点总结

----------

##1.adb - android debug bridge

-adb start-server -----开启adb服务

-adb kill-server -----停止adb服务

-adb push 本地路径 手机路径 -----将文件导入手机

-adb pull 手机路径 本地路径 -----导出

-adb logcat -----查看Log

-adb install [-r] [-s] 全路径/xxx.apk

-r 重新安装该程序,保存数据

-s 安装在SD卡内,而不是设备内部存储

-adb uninstall [-k] 全路径/xxx.apk

-k 不删除程序运行所产生的数据和缓存目录

-adb remount -----重新挂载文件系统

-adb reboot -----重启手机

-adb reboot recovery -----重启到Recovery界面

-adb reboot bootloader -----重启到bootloader界面

-adb devices -----列出所有设备名称或IP

-adb -s IP install 全路径-apk

-adb shell -----挂载到Linux空间

-netstat -ano ---查看进程 (协议  本地地址 外部地址  状态   PID)

进入Linux空间后就好玩:

ls ---查看当前路径下的文件

ls -l ---查看文件的具体信息

ps ---查看当前运行进程

netstat -ano ---查看占用端口号的进程

monkey 1000;---猴子测试 测试整个系统;1000代表数量

monkey -p 包名 数量--- 测试某个应用程序运行数量后,会不会爆

cd .. ---返回上级目录

cat xxx.xml ---查看xml文件

sqlite3 xxx.db ---进入后

.help 查看帮助

.tables 查看所有表名

.quit 退出

**可以使用sql语句操作表

##2.UI(脸蛋)

1. 四种基本布局:

#LinearLayout#---线性布局

orientation:vertical竖直, horizontal 水平

gravity:指定子布局位置

layout_gravity:指定当前控件与父布局的相对位置

layout_weight:出去已经分配的具体屏幕大小,将剩余的屏幕大小按权重分配(自己的理解)

#RelativeLayout#---相对布局

初始位置:在屏幕左上角

容易忘记:layout_above="@id/xx"            在 xx控件上

layout_below="@id/xx"            在 xx控件下

layout_toRightOf="@id/xx"        在 xx控件右边

layout_alignParentRight="true"    在父窗体右边

layout_centerInParent="true"   在父窗体正中间

#FrameLayout#---帧布局、

特点:一层层覆盖

不好玩,用来被替换的布局,当使用碎片fragment来布局UI的时候,framelayout里面存放一个fragment

#TableLayout#---表格布局

基本不用

子控件 中无法指定宽度,使用 stretchColumns="1";

合并单元格: layout_span="2";

2. 常见控件:

#TextView#

textSize

textColor    #ARGB  a:透明度 r:red g:green b:blue

singleLine   true  单行显示

maxLines     多行显示

#Button#

四种点击事件:

第一种:通过给控件添加onclick属性,然后进到 activity中 去添加方法

添加方法时,方法的签名也是固定的.(google不推荐使用)

andorid:onclick="xxx"

public void xxx(View v){

}`

第二种:给控件添加id , 然后 在activity中拿到 控件,然后 给控件添加onclick时间的监听器

使用匿名内部类的写法:

`android:id="@+id/xxx"

xxx = (Button) findViewById(R.id.xxx);

xxx.setOnClickListener(new OnClickListener(){

public void onClick(View v){

}

});

第三种:实际上与第二种一样, 只是换成了 内部类的写法.写个类去 实现 Onclick接口

第四种:让activity类去实现 onclicklistner接口最终通过 switch...case去判断到底点击的是哪个控件.

`android:id="@+id/xxx"

xxx = (Button) findViewById(R.id.xxx);

xxx.setOnClickListener(this);

public void onClick(View v){

int id = v.getId();

switch(id){

case R.id.xxx:

break;

default:

break;

}

}

#EditText#

hint

maxLines

inputType

et_content =(EditText) findViewById(R.id.et_content);

String et_content = et_content.getText().toString.trim();

if(TextUtils.isEmpty(et_content)){

Toast.makeText("","",Toast.Length_SHORT).show();

return;

}

#imageView#

src

ImageView.setImageResource(R.drawable.xxx);

好玩的:实现图片轮播

自己见一个xml文件:

/animation-list>`

'ImageView rocketImage = (ImageView) findViewById(R.id.rocket_image);

rocketImage.setBackgroundResource(R.drawable.rocket_thrust);

rocketAnimation = (AnimationDrawable) rocketImage.getBackground()

;

rocketAnimation.start();

#RadioGroup#

eg:

android:id="@+id/rg"

android:layout_width="fill_parent"

android:layout_height="wrap_content"

android:orientation="horizontal">

android:layout_width="0dip"

android:layout_weight="1"

android:layout_height="wrap_content"

android:text="男"

android:id="@+id/rb_male"

/>

android:layout_width="0dip"

android:layout_weight="1"

android:layout_height="wrap_content"

android:text="女"

android:id="@+id/rb_female"

/>

获取属性值:

if(rg.getCheckedRadioButtonId() == R.id.rb_male){

sex = "male";

}else{

sex = "female";

}

'

#ProgressBar#

max

progress

重要设置样式:

style="?android:attr/progressBarStyleHorizontal"

#VideoView#          播放音频视频,底层是SurfaceView 和 mediaPlayer的结合。

vv = (VideoView) findViewById(R.id.vv);

// 设置要

vv.setVideoPath("/mnt/sdcard/lala.3gp");

//mediaController --- 媒体控制器

MediaController mc = new MediaController(this);

mc.setAnchorView(vv);

vv.setMediaController(mc);   // 成功的将MediaController与vv关联起来

vv.start();

#SurfaceView#

sv = (SurfaceView) findViewById(R.id.sv);

try {

mPlayer = new MediaPlayer();

mPlayer.reset();

mPlayer.setDataSource("/mnt/sdcard/lala.3gp");

mPlayer.prepare();

} catch (Exception e) {

e.printStackTrace();

}

sp = getSharedPreferences("config", 0);

// surfaceHodler --- 界面的持有器,持有者

sv.getHolder().addCallback(new Callback() {

//surface销毁了

@Override

public void surfaceDestroyed(SurfaceHolder holder) {

System.out.println("销毁了 ");

Editor editor = sp.edit();

editor.putInt("position", mPlayer.getCurrentPosition());

editor.commit();

mPlayer.stop();

}

//surface创建了

@Override

public void surfaceCreated(SurfaceHolder holder) {

System.out.println("创建了  ");

int position = sp.getInt("position", 0);

mPlayer.setDisplay(sv.getHolder());    //显示画面,必须设置

mPlayer.start();

mPlayer.seekTo(position);  // 直接跳到某个位置, 从这个位置开始播放

}

//surface变化了

@Override

public void surfaceChanged(SurfaceHolder holder, int format, int width,

int height) {

System.out.println("修改了  ");

}

});

#ScollView#  只能有一个子节点

#WebView#

webview = new WebView(this);

//设置WebView属性,能够执行Javascript脚本

webview.getSettings().setJavaScriptEnabled(true);

//加载需要显示的网页

webview.setWebViewClient(new WebViewClient());

webview.loadUrl("http://www.xxxx.com/");

//设置Web视图

setContentView(webview);

#Fragment#  碎片  类似Activity

-1. 静态添加碎片

1.自己布局一个fragment:

android:id="@+id/fm_left"        //id必须写,不然会报错

android:name="com.example.fragment.LeftFragment"// 添加name属性,包名必须写,此为加载LeftFragment 类

android:layout_weight="1"

android:layout_width="0dp"

android:layout_height="match_parent"/>

2.定义一个LeftFragment 继承 Fragment

public class LeftFragment extends Fragment {

@Override

public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {

//false 代表 不附着在parent布局中

return inflater.inflate(R.layout.left, container,false);

}

}

3.定义一个R.layout.left布局文件

-2. 动态添加碎片

核心代码



RightFragment rightFragment = new RightFragment();

FragmentManager fragmentManager = getFragmentManager();

FragmentTransaction beginTransaction = fragmentManager.beginTransaction();

beginTransaction.replace(R.id.fl, rightFragment);

beginTransaction.commit();

3. 四种对话框(不能用Application的上下文)

-1.取消对话框

AlertDialog.Builder builder = new AlertDialog.Builder(this);

builder.setTitle("please ");            // 下面都对话框都可以设置标题

builder.setMessage("继续撸代码吗?");        // 同上

builder.setPositiveButton("Yes", new DialogInterface.OnClickListener() {

@Override

public void onClick(DialogInterface dialog, int which) {

Toast.makeText(MainActivity.this, "lu", 0).show();

}

});        //确定按钮的点击事件

builder.setNegativeButton("No", null);    //取消按钮的点击事件

builder.show();

-2.单选对话框

Builder builder = new AlertDialog.Builder(this);

final String[] items = new String[]{"male","female"};

builder.setSingleChoiceItems(items, 0, new DialogInterface.OnClickListener() {

@Override

//which 是数组的 索引,下同

public void onClick(DialogInterface dialog, int which) {

Toast.makeText(MainActivity.this, "你选的是"+items[which], 0).show();

}

});

builder.show();

-3.多选对话框

AlertDialog.Builder builder = new AlertDialog.Builder(this);

final String[] items = new String[]{"跳楼","上吊","撸代码","滚回家"};

final boolean[] checkedItems = new boolean[]{false,false,false,false};

// fasle 代表默认不被选中

builder.setMultiChoiceItems(items, checkedItems, new DialogInterface.OnMultiChoiceClickListener() {

@Override

public void onClick(DialogInterface dialog, int which, boolean isChecked) {

Toast.makeText(MainActivity.this, "你选的是"+items[which], 0).show();

checkedItems[which] = isChecked;

}

});

builder.show();

-4.进度条对话框

final ProgressDialog progressDialog = ProgressDialog.show(MainActivity.this, "please wait", "加载loading。。。。。");

new Thread(){

public void run(){

progressDialog.setCancelable(false);//Back键不能取消掉

SystemClock.sleep(2000);

progressDialog.dismiss();            //设置 dismiss 退出

}

}.start();

***所有的控件都有的属性:visibility{visible(默认,可见),invisible(不可见占空间),gone(不可见也不占空间)}

4. 自定义控件

开源框架:SmartImageView(看源码)---- SmartImageView.setImageUrl(Url url){}

1 . 继承原生控件

2 . 重写构造方法

3 . 根据业务需求定义方法

5. ListView

-1.作用和方法:用来将数据显示到屏幕上的技术

google按照mvc的三层架构思想设计

m:model--- 模型--- 需要显示的数据

v:view ---视图---- 呈现的界面

c:controller--- 控制器--- Adapter(适配器)

1. lv.setSelection(random1); //该方法可以 ListView 定位到让指定位置(random1),就是让指定位置的条目位于当前界面第一行。非常准确。

2. lv.smoothScrollToPosition(xxx); //该方法可以让 ListView 平滑的滚动到指定位置(xxx),但是非常不准确。

3.  lv1.setOnScrollListener(OnScrollListener); //给ListView 设置监听器

4. //滑动时回调的方法

// scrollState 滚动状态有三种:0 静止,1 手指滑动,2惯性滚动

@Override

public void onScrollStateChanged(AbsListView view, int scrollState) { }

@Override

public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {}

-2.BaseAdapter    ---是ListAdapter的一个默认实现类

主要需要重写的2个方法:

public int getCount(){        //总共需要显示数据条数

return list.size();

}

public public View getView(int position, View convertView, ViewGroup parent) {

//三种填充View的方法,本质是一样的

//第一种

View view = View.inflate(MainActivity.this,R,layout.item_main,null);

//第二种

View view = LayoutInflater.from(MainMainActivity.this).inflate(R,layout.item_main,null);

//第三种

LayoutInflater ll = (LayoutInflater)getSystemService("LAYOUT_INFLATER_SERVICE");

View view = ll.inflate(R,layout.item_main,null);

}

-3.ArrayAdapter

listView.setAdapter(new ArrayAdapter(MainActivity.this,R.layout.item,T[] Objects));

最后一个参数也可以是集合。

-4.SimpleAdapter

待完善

-5.优化

(1)//删除之前已经显示的数据 ,然后再次重新加载进来,这样避免重复显示

if(myadapter ==null){

myadapter = new MyAdapter();

lv.setAdapter(myadapter);

}else{

//要通知适配器去更新一下数据

myadapter.notifyDataSetChanged();

}

(2)防止OOM(Out Of Memory)异常

View view;

if(convertView==null){

view = View.inflate(MainActivity.this, R.layout.item, null);

}else{

view =convertView;

}

if(convertView==null){

convertView = View.inflate(MainActivity.this, R.layout.item, null);

}

(3)faster(用Holder)持有器

static class ViewHolder {

TextView tv;

ImageView iv;

View Holder holder;

if (convertView == null) {

convertView=View.inflate(MainActivity.this,R.layout.item,null);

holder = new ViewHolder();

holder.iv = (ImageView) convertView.findViewById(R.id.iv);

holder.tv_title = (TextView) convertView.findViewById(R.id.tv);

convertView.setTag(holder);

}else{

holder = (ViewHolder) convertView.getTag();

##3.数据存储和解析

1. 文件存储

-1.基本输入输出流文件

-2.Context提供了两个方法来打开数据文件里的文件IO流,存储路径:/data/data//files

openFileInput("xxx.txt"); //读取

openFileOutput("xxx.txt",Context.MODE_PRIVATE);    //保存

//MODE_PRIVATE:为默认操作模式,代表该文件是私有数据,只能被应用本身访问,在该模式下,写入的内容会覆盖原文件的内容

//MODE_APPEND:模式会检查文件是否存在,存在就往文件追加内容,否则就创建新文件;

//后面的两种模式在Android4.2被废弃,不安全

//MODE_WORLD_READABLE:表示当前文件可以被其他应用读取;

//MODE_WORLD_WRITEABLE:表示当前文件可以被其他应用写入。

deleteFile("xxx.txt");    //删除

File getFilesDir():获取该应用程序的数据文件夹得绝对路径

File getCacheDir():缓存区

String[] fileList():返回该应用数据文件夹的全部文件

-3.Sdcard 存储

1、调用Environment的getExternalStorageState()方法判断手机上是否插了sd卡,且应用程序具有读写SD卡的权限,如下代码将返回true

Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)

2、调用Environment.getExternalStorageDirectory()方法来获取外部存储器,也就是SD卡的目录,或者使用"/mnt/sdcard/"目录

File file = new File(Environment.getExternalStorageDirectory(),filename);

3、使用IO流操作SD卡上的文件

注意点:

手机应该已插入SD卡,对于模拟器而言,可通过mksdcard命令来创建虚拟存储卡

必须在AndroidManifest.xml上配置读写SD卡的权限

2. SharedPreferences存储 --- 单例,一般不会发生并发冲突

1.创建文件:    SharedPreferences sp = context.getSharedPreferences("xxx",int mode);//创建的xxx一定是xml文件,存储在/data/data//shared_prefs,参数mode 和上面一样。

2.存储数据:    Editor edit = sp.edit();

edit.putXxx(String key,Xxx value);

edit.commit(); || edit.apply();

区别: 1. apply没有返回值apply方法不会提示任何失败的提示。而commit返回boolean表明修改是否提交成功

2. apply异步提交数据,commit同步。

3.读取数据: sp.getXxx(String key,XxxDefault);// 如果没有读取到,就返回第二参数。

4.删除数据: sp.remove(String key);//删除key字段

sp.clear();//清空文件

3. SQLite数据库存储

-1.创建表

db.execSQL("create table tablename(_id integer primary key autoincrement,name varchar(30),age Integer)");

-2.Insert

第一种:存在sql注入问题

db.execSQL("insert into tablename(name,age) values('Myth',2)");

第二种:通过占位符? 解决sql注入

db.execSQL("insert into tablename(name,age) values(?,?)",new Object[]{"Myth",2});

第三种:底层还是通过拼接字符串得到sql语句

ContentValues values = new ContentValues();

values.put("name","Myth");

values.put("age",2);

db.insert("tablename",columnNull,values);//第二个参数:sql表不允许插入空,所以就会用null当作该值插入表。

-3.Delete

db.execSQL("delete from tablename where _id = 2");

db.execSQL("delete from tablename where _id = ?",new Object[]{2});

db.delete("tablename","_id = ?",new String[]{String.valueOf(id)});

-4.Update

db.execSQL("update tablename set name = 'Myth' where age = 2");

db.execSQL("update tablename set name=?,age =? where _id=?",new Objecet[]{"Myth",2,2});

ContentValues values = new ContentValues();

values.put("name","Myth");

values.put("age",2);

db.update("tablename",values,"_id=?",new String[]{String.valueOf(id)});

-5.Query

Cursor cursor = db.rawQuery("select * from tablename where _id=2");

Cursor cursor = db.rawQuery("select * from tablename where _id=?",new Object[]{2});

Cursor cursor = db.query(table, columns, selection, selectionArgs, groupBy, having, orderBy, limit);

if(cursor.moveToFirst){

while(cursor.moveToNext){

}

}

4. SQLite实践

-1.事务操作  --- 同时处理多条数据时,保证数据安全性

db.beginTransaction();

try {

db.setTransactionSuccessful();//执行提交

} finally {

db.endTransaction();

}

-2.数据库升级操作 --- 保证版本更新成功

switch (oldVersion) {

case 1:        //代表每个版本的操作 该case 是1~2的升级操作

case 2:        //case 一直到最新版本

default:

}

-3.注意:getWritableDatabase()和getReadableDatabase()方法的区别:

getWriteableDatabase()方法以读写方式打开数据库一旦数据库的磁盘空间满了,数据库就只能读而不能写,倘若使用getWritableDatabase()打开数据库就会出错。

getReadableDatabase()方法先以读写方式打开数据库,如果数据库的磁盘空间满了,就会打开失败,当打开失败后会继续尝试以只读的方式打开数据库.

5. XML生成器:XmlSerializer和 XML解析器:XmlPullParser

-1.XmlSerializer:(字符串拼接Xm文件存在语句注入问题,so用XmlSerializer)

XmlSerializer serializer = Xml.newSerializer();

serializer.setOutput(fos,"UTF-8");

serializer.startDocument("UTF-8",true);// "

serializer.startTag(null, "smses");

for (int i = 0; i < 50; i++) {

serializer.startTag(null, "sms"); //第一个参数为命名空间

serializer.startTag(null, "body");

serializer.text("我是内容<>" + i);

serializer.endTag(null, "body");

serializer.endTag(null, "sms");

}

serializer.endTag(null, "smses");

serializer.endDocument;

fos.close;

-2.XmlPullParser:

XmlPullParser pullparser = Xml.newPullParser();

pullparser.setInput(fis,"UTF-8");

int eventType = pullparser.next();

while(eventType != XmlPullParser.END_DOCUMENT){

switch(eventType){

case XmlPullParser.START_TAG:

if ("smses".equals(tagName)) {

list = new ArrayList();

}else if ("sms".equals(tagName)) {

sms = new Sms();

}else if ("body".equals(tagName)) {

sms.setBody(parser.nextText());

}

break;

case XmlPullParser.END_TAG:

if ("sms".equals(tagName)) {

list.add(sms);

}

break;

}

eventType = pullparser.next();

}

##4.Activity

-1.自定义的Activity

-1.创建一个MyActivtiy实现Activity

public class MyActivity extends Activity {

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

}

-2.新建一个布局文件 xxx.xml

-3.在onCreate()方法里写 上 setContentView(R.layout.xxx);

-4.最重要的一步:在清单文件中注册一个自己的Activity

android:name="com.example.MyActivity"    //可以缩写.MyActivity

android:label="@string/app_name" >        //标题栏的内容

-5.非必须的一步:隐藏标题栏 ---在onCreate()方法内写

requestWindowFeature(Window.FEATURE_NO_TITLE);

-2.生命周期

OnCreate()        创建界面

OnStart()        不可见---->可见  时候会调用

OnResume()        获得焦点,此时活动在栈顶,处于运行状态

OnPause()        失去焦点,不在栈顶但可见,处于暂停状态

OnStop()        可见---->不可见  时候会调用,处于停止状态

OnDestroy()        销毁

**注意:要使得Activity不可见,可以再建一个项目同时运行,将其Application的theme属性改成透明状态:

android:theme="@android:style/Theme.Translucent">

-3.活动的四种启动模式(android:launchMode="")

-1.standard:每次启动acitvity组件时, 都会新创建activity 实例,是活动默认的启动模式

-2.singleTop:如果某个activity设置单一顶部模式, 那么当发现当前的activity就在当前任务栈的顶部, 那么就不再新创建当前的activity的实例.例如:系统的短信

-3.singleTask:如果当前任务栈中已经有当前activity 的实例, 那么就将当前activity 的实例直接拿过来用, 用的时候,如果当前activity 不在栈顶,那么将在当前activity 之上的其他的activity的实例 给干掉... 然后再处于栈顶了一般情况下, 当某个activity  启动的时候, 要占用的内存比较大, 而手机上的内存又是有限的, 那么这个时候, 就推荐将这个activity的启动模式设置为单一任务栈模式.例如:系统的浏览器

-4.singleInstance:如果某个activity 的启动模式设置为单一实例模式, 那么系统会为这个activity 单独的去开辟一个任务栈,这个任务栈中,只放这个 activity的实例.这样确保了整个操作系统中,只有一个这个activity的实例了.如果某个activity在整个系统中就只需要有一个实例, 并且永远不会更改, 那么就推荐使用这种模式.例如:系统的电话

-4.使用intent在活动之间传输数据

-1.显示意图:是明显的指定要激活哪个组件 ..一般建议用在应用程序内部 .

Intent intent = new Intent();

//    intent.setClass(this, SecondActivity.class);

//    intent.setClassName("com.itheima.exactintent", "com.itheima.exactintent.SecondActivity");

intent.setClassName(this,  "com.itheima.exactintent.SecondActivity");

startActivity(intent);

-2.隐式意图:是指不明确到底哪个组件可以响应你的意图,你只需要将意图发出去就可以了.当存在满足你的意图的组件时,

这个时候,系统就会将组件给激活起来...一般用在不同应用程序之间 激活组件 ..

Intent intent = new Intent();

//必须和Activity下的intent-filter内容匹配

intent.setAction("com.itheima.nu");

//    intent.addCategory("android.intent.category.DEFAULT");

intent.addCategory(Intent.CATEGORY_DEFAULT);

startActivity(intent);

****1. 每个应用中的组件是可以配置多个Intent-filter,可以配置多个隐式意图去激活这个组件

2. 如果想要激活这个组件, 只需要发送对应的隐式意图就可以了.

-3.intent中setData:接收一个Uri对象,例如:intent.setData(Uri.parse("www.baidu.com"));

与其对应,可以在标签中配置一个子标签。

属性:scheme(http),host(www.baidu.com),port(8080),path(指定端口号后面的部分),mimeType(指定可以处理的数据类型,允许使用通配符)

-4.intent携带数据,实现短信分享:

Intent intent = new Intent();

//短信有很多intent-filter,只需要其中匹配上一个即可

intent.setAction("android.intent.action.SENDTO");

intent.addCategory("android.intent.category.DEFAULT");

intent.addCategory("android.intent.category.BROWSABLE");

intent.setData(Uri.parse("smsto://xxxxx"));

intent.putExtra("sms_body", "xxxxxxxxxx");

startActivity(intent);

//Activity接收数据

Intent intent = getIntent();

String data = intent.getExtraString("sms_body");

-5.带有返回结果的intent

主Activity:

Intent intent2 = new Intent();

intent2.setClass(this, xxx.class);

startActivityForResult(intent2, 2);

//接收其他Activity回传的结果数据

protected void onActivityResult(int requestCode, int resultCode, Intent data) {

System.out.println("结果返回到这里了 ....");

if(requestCode==1){

}else if(requestCode==2){

}

super.onActivityResult(requestCode, resultCode, data);

}

xxxActivity:

Intent data = new Intent();

data.putExtra("contact", contact);

setResult(0, data);

finish();        // 关闭该xxxActivity

-6.其他

intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);重新启动Activity

intent.addAction("android.media.action.IMAGE_CAPTURE");启动相机

##5.BroadcastReceiver(买收音机,装电池,调频道)

-1.动态注册广播接收者

(1)监听手机屏幕开关

receiver = new ScreenStateReceiver();//继承BroadcastReceiver

IntentFilter filter = new IntentFilter();

filter.addAction("android.intent.action.SCREEN_OFF");

filter.addAction("android.intent.action.SCREEN_ON");

registerReceiver(receiver, filter);//注册 广播接收者

public void onReceive(Context context, Intent intent) {

String action = intent.getAction();

if("android.intent.action.SCREEN_OFF.equals(action)){

System.out.println("发现用户屏幕关了  ");

abortBroadcast();//拦截广播

}else if("android.intent.action.SCREEN_ON".equals(action)){

System.out.println("发现用户屏幕开启 ");

}

}

(2)监听网络变化

receiver = new NetworkChangeReceiver();//继承BroadcastReceiver

IntentFilter filter = new IntentFilter();

filter.addAction("android.intent.action.CONNECTIVITY_CHANGE");

registerReceiver(receiver, filter);//注册 广播接收者

(3)需要手动取消注册

在onDestroy()中调用 unregisterReceiver(receiver);

-2.静态注册一个广播接收者 --- 监听开机启动

//优先级-1000~10000,优先接收广播

-3.发送无序广播

// 定义 intent , intent 设置 必要的信息就可以 了

Intent intent = new Intent();

intent.setAction("com.example.xxx");

// 发送 广播

sendBroadcast(intent);

-4.发送有序广播

// 定义 intent , intent 设置 必要的信息就可以 了

Intent intent = new Intent();

intent.setAction("com.example.xxx");

// 发送 广播

sendOrderBroadcast(intent,null);//第二个参数是一个与权限有关的字符串

##6.Server

-1.Handler异步消息处理机制(IPC 线程通信,避免发生ANR 问题,在Activity中 耗时操作 5s 产生ANR。)

-1.Message:线程间传递消息,

//获得消息对象,两种方式本质一样

Message msg = handler.obtainMessage();

Message msg = Message.obtain();

msg.sendToTarget(); //该msg需要绑定handler,从handler.obtainMessage()获取

-2.Handler:用于发送和处理消息

handler.sendMessage();

handler.sendEmptyMessage();//发送无效消息

private Handler handler = new Handler(){

public void handleMessage(android.os.Message msg) {

switch (msg.what) {

case SUCCESS:

Xxx data = (Xxx)msg.obj;

Toast.makeText(MainActivity.this, "成功", 0).show();

break;

case FAILURE:

Toast.makeText(MainActivity.this, "失败", 0).show();

break;

}

}

};

-3.MessageQueue :消息队列,存放将要被处理的消息,每个线程只有一个MessageQueue

-4.Looper:调用loop(),进入一个无线循环中,不断取出MessageQueue中的消息传递到handleMessage()中,每个线程也只有一个Looper对象。

-5.发送消息代码

Message msg = Message.obtain();

msg.what = SUCCESS;        // what 区分发送的消息类型

msg.obj = data;            // obj 携带任意消息

handler.sendMessage(msg);

-2.AsyncTask ---Android已经封装好,方便在子线程中对UI进行操作。(待更新)

-3.进程优先级分类:

-1.前台进程: 就是正在与用户进程交互的引用程序

-2.可视进程: 用户看得见的,但是摸不着的

-3.服务进程: 在服务中运行, 在后台运行着

-4.后台进程: 在后台一直运行着,不是运行在service ,是运行在activity中

-5.空进程:  引用程序已经退出了,没有activity,没有service .

-6.总结:前台进程> 可视进程> 服务进程> 后台进程> 空进程   当系统内存不够用的时候, 就会去尝试回收进程,来重新分配内存. 会按照如上优先级分类杀掉进程.

-4.自定义服务

public class MyService extends Service {

@Override

public IBinder onBind(Intent intent) {

return new MyBinder();

}

}

// 必须清单文件注册

-5.开启服务的生命周期

onCreate()            //服务创建时调用

onStartComment()    //每次服务启动时调用,废弃了onStart()

onDestroy()            //服务销毁时调用

-1.开启服务

Intent intent = new Intent();

intent.setClass(MainActivity.this,MyService.class);

startService(intent);

-2.停止服务

Intent intent = new Intent();

intent.setClass(MainActivity.this,MyService.class);

stopService(intent);

-6.绑定服务的生命周期

onCreate()        //创建服务

onBind()         //绑定服务

onUnbind()         //解除绑定服务

onDestroy()     //销毁服务

(1)绑定服务时, onStartCommand并不会执行,

(2)绑定服务,服务何时销毁呢, 在应用程序退出的时候就销毁了

(3)绑定服务, 服务不会在后台运行 ---在apps中running 中是看不到的

(4)开启服务, 服务会在后台运行--- 可以在apps中 runnning 中看到

(5)绑定服务, 服务只会创建一次, 如果再次绑定服务, 那么服务是不会重新再次创建的.

(6)如果没有绑定服务, 就直接去解绑服务, 那么会抛异常.so 需要做如下判断,避免异常

if(conn != null){

unbindService(conn);

conn = null;

mybinder = null;

}

-7.绑定服务实现流程(活动和服务间通信,进程间通信使用 IBinder)

-1.绑定服务

Intent intent = new Intent();

intent.setClass(this, MyService.class);

if(conn == null){

conn = new MyConnection();

}

bindService(intent, new MyConnection(), BIND_AUTO_CREATE);

-2.编写MyBinder 继承 Binder 实现 IService  // IService 接口中是将要调用的方法

private class MyBinder extends Binder implements IService{

@Override

// 服务中对外提供的方法封装在接口中,重写接口中的方法

public void call(String name, String service) {

calle(name, service);

}

//该方法为不对外提供的方法,只对内访问

public void callee(String name, String service) {

System.out.println("呼叫前台");

}

}

-3.向Activity传回MyBinder对象

@Override

public IBinder onBind(Intent intent) {

return new MyBinder();

}

-4.实现MyConnection 实现 ServiceConnection接口

private class MyConnection implements ServiceConnection{

@Override

//当与服务建立联系的时候被回调

public void onServiceConnected(ComponentName name, IBinder service) {

if(mybinder == null){

mybinder = (IService) service;

//    mybinder = IService.Stub.asInterface(service);

}

}

@Override

//当与服务断开联系的时候被回调

public void onServiceDisconnected(ComponentName name) {

mybinder = null;

}

}

-5.调用服务中的方法

mybinder.call("xxxx", "xxxx");

-8.不同Activity之间通信

主要编写.aidl

(1)语法与接口很类似,直接修改接口文件后缀名即可。但是.aidl文件中不能写权限修饰符

(2).aidl文件接受参数和返回值类型是8种基本数据类型,如果是引用数据类型,必须实现Parceable接口

(3)不能修改.aidl文件生成的.java类

-1 Parceable 比 Serializable 性能高

-2 Serializable 使用时会产生大量临时变量,引起频繁的GC

-3 Parceable 不能保证数据的持续性,不能使用数据保存在磁盘上

-9.混合开发服务

(1)开启服务:只能在后台运行

(2)绑定服务:只能调用服务中的方法

想在后台运行,但是同时又想 调用服务中 方法, 那么 就需要混合开启服务了.

以后,如果需要在后台运行, 并且又需要调用服务中的方法时,请严格按照 如下的顺序去 实现程序的逻辑, 否则 就容易出现问题..

混合开启:

开启服务

绑定服务

调用服务中的方法

解除绑定

停止服务

例如:QQ 在退出的时候,选择退出后, 仍然可以接受消息, 就可以使用混合开启服务的方式...

-10.Android提供一个IntentService类:是一个异步的,会自动停止的服务,可以避免ANR问题(服务在主线程中耗时操作 10s 产生ANR 问题)

public class MyIntentService extends IntentService {

public MyIntentService() {

super("MyIntentService");

}

@Override

protected void onHandleIntent(Intent intent) {

}        //处理具体的逻辑

}

// 清单文件注册

##7.ContentProvider

-1.自定义内容提供者  ---ContentProvider

public class MyContentProvider extends ContentProvider {

private static final int SUCCESS = 0;

private static UriMatcher matcher;

//匹配器 -- UriMatcher

//是固定的写法

//这里的no_match是用来指定当匹配不成功时,返回的值

static {

matcher = new UriMatcher(UriMatcher.NO_MATCH);

// com.itheima.xxx 公开的名称, table就是暗号了, SUCCESS指匹配成功后返回的结果

matcher.addURI("com.example.xxx", "table", SUCCESS);

}

@Override

public boolean onCreate() {

return false;

}

@Override

public Cursor query(Uri uri, String[] projection, String selection,

String[] selectionArgs, String sortOrder) {

BankOpenHelper myOpenHelper = new MyOpenHelper(getContext());

SQLiteDatabase db = myOpenHelper.getWritableDatabase();

if (matcher.match(uri) == SUCCESS) {

return db.query("table", projection, selection, selectionArgs, null,

null, sortOrder);

}

return null;

}

@Override

public String getType(Uri uri) {

return null;

}

@Override

public Uri insert(Uri uri, ContentValues values) {

BankOpenHelper myOpenHelper = new MyOpenHelper(getContext());

SQLiteDatabase db = myOpenHelper.getWritableDatabase();

// TODO Auto-generated method stub

if (matcher.match(uri) == SUCCESS) {

db.insert("table", null, values);

} else {

try {

throw new Exception("暗号错误。");

} catch (Exception e) {

e.printStackTrace();

}

}

return null;

}

@Override

public int delete(Uri uri, String selection, String[] selectionArgs) {

BankOpenHelper myOpenHelper = new MyOpenHelper(getContext());

SQLiteDatabase db = myOpenHelper.getWritableDatabase();

if (matcher.match(uri) == SUCCESS) {

return db.delete("account", selection, selectionArgs);

}

return 0;

}

@Override

public int update(Uri uri, ContentValues values, String selection,

String[] selectionArgs) {

BankOpenHelper myOpenHelper = new MyOpenHelper(getContext());

SQLiteDatabase db = myOpenHelper.getWritableDatabase();

if (matcher.match(uri) == SUCCESS) {

return db.update("account", values, selection, selectionArgs);

}

return 0;

}

}

##清单文件注册:

-2. 内容解析者 ---- ContentResolver (类似SQLite操作)

1 insert

ContentResolver resolver = getContentResolver();

Uri url = Uri.parse("content://com.example.xxx/table");

ContentValues values = new ContentValues();

values.put("key", "value");

resolver.insert(url, values);

2.delete

ContentResolver resolver = getContentResolver();

Uri url = Uri.parse("content://com.example.xxx/table");

resolver.delete(url, "key=?", new String[]{"value"});

3.update

ContentResolver resolver = getContentResolver();

Uri url = Uri.parse("content://com.example.xxx/table");

ContentValues values = new ContentValues();

values.put("key1", "value1");

resolver.update(url, values, "key=?", new String[]{"value"});

4.query

ContentResolver resolver = getContentResolver();

Uri url = Uri.parse("content://com.example.xxx/table");

Cursor cursor = resolver.query(url, null, "key=?", new String[]{"value"}, null);

while(cursor.moveToNext()){

}

-3.内容观察者 --- ContentObserver

(1)ContentResolver resolver = getContentResolver();

//注册一个内容观察者

//如果notifyForDescendents参数设为true,假如Uri为content://abc,那么Uri为content://abc/xyz, content://abc/xyz/foo的数据改变时也会触发该监听器,如果参数为false,那么只有content://abc的数据改变时会触发该监听器

resolver.registerContentObserver(uri, boolean notifyForDescendents, new ContentObserver(null) {

// selfChange : 数据的变化是否是来自于自己

@Override

public void onChange(boolean selfChange) {

System.out.println("数据发生变化了 ....");

}

});

(2)在不需要时,需要手动的调用

unregisterContentObserver()去取消注册。

(3)ContentObserver类介绍

构造方法 public void ContentObserver(Handler handler) {}

说明:所有 ContentObserver的派生类都需要调用该构造方法

参数: handler  Handler对象。可以是主线程Handler(这时候可以更新UI了),也可以是任何Handler对象。

##8.手机多媒体

-1.通知的使用(NotificationManager)

-1.第一种

//拿到通知管理器

NotificationManager nm = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);

Notification nf = new Notification(R.drawable.ic_launcher,"This is ticker Text ",System.currentTimeMillis());

// 第四个参数为延期意图(能跳转干你想干的事)

nf.setLatestEventInfo(context, "This is content title", "This is content text", null);

//显示通知,第一个参数是每一个通知的id(保证不同)

nm.notify(1,nf);

-2.第二种

//基于Notification是builer模式构建,可以链式编程创建

Notification nf = new Notification.Builder(this)

.setContentTitle("你有消息")            //标题

.setContentText("你mama 喊你回家吃饭")    //文本

.setSmallIcon(R.drawable.ic_launcher)    //小图标

.setLargeIcon(BitmapFactory.decodeResource(getResources(),

R.drawable.ic_launcher))     //大图标

.build();

NotificationManager nm = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);

nm.notify(1,nf);

-3.通过pending(延期意图)拨打电话

Notification nf = new Notification(R.drawable.ic_launcher, "你好一有消息", System.currentTimeMillis());

Intent intent = new Intent();

intent.setAction(Intent.ACTION_CALL);

intent.setData(Uri.parse("tel:"+5556));

// pendingIntent 可以getActivity()  getService()  getBroadcast()

PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, intent, 0);

nf.setLatestEventInfo(this, "ai", "huijiachifan", pendingIntent);

//权限不能少,打电话权限

-2.短信的接收和发送(SmsManger)

-1.接收短信:监听 android.provider.Telephony.SMS_RECEIVED的广播

public class MessageReceiver extends BroadcastReceiver {

@Override

public void onReceive(Context context, Intent intent) {

Bundle bundle = intent.getExtras();

//通过密钥"pdus"获取SMS pdu 数组,每一个pdu是一条短信

Object[] objects = (Object[])bundle.get("pdus");

for (Object object : objects) {

//将每一个pdu字节数组转换SmsMessage对象

SmsMessage smsMessage = SmsMessage.createFromPdu((byte[])object);

//获取短信的发送方号码

if(smsMessage.getOriginatingAddress().equals("55666")){

//获取短信

smsMessage.getMessageBody();

}

}

}

}

//接收短信权限

-2.发送短信

SmsManager smsManager = SmsManager.getDefault();

//发送很长很长的短信需要切割短信存到集合中,才能一条一次性发出去

ArrayList divideMessage = smsManager.divideMessage("");

smsManager.sendMultipartTextMessage("phone", null, divideMessage, null, null);

//接收短信权限

-3.监听手机状态(TelephonyManager):开启录音功能(MediaRecoder)

TelephonyManager manager = (TelephonyManager) getSystemService(TELEPHONY_SERVICE);

manager.listen(new myPhoneStateListener(), PhoneStateListener.LISTEN_CALL_STATE);

private class myPhoneStateListener extends PhoneStateListener{

@Override

public void onCallStateChanged(int state, String incomingNumber) {

// TODO Auto-generated method stub

super.onCallStateChanged(state, incomingNumber);

switch (state) {

if(mRecorder != null){

stopRecording();

}

break;

case TelephonyManager.CALL_STATE_OFFHOOK:

startRecording();

break;

case TelephonyManager.CALL_STATE_RINGING:

break;

default:

break;

}

}

}

MediaRecorder mRecorder;

private void startRecording() {

//下面是模版,可以录音文件的格式有:3gp,mp3。。。

mRecorder = new MediaRecorder();

mRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);

mRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);

mRecorder.setOutputFile("/mnt/sdcard/jianting.3gp");

mRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);

try {

mRecorder.prepare();

} catch (IOException e) {

//    Log.e(LOG_TAG, "prepare() failed");

}

mRecorder.start();

}

private void stopRecording() {

mRecorder.stop();

mRecorder.release();

mRecorder = null;

}

//录音权限,读手机状态权限

-4 播放音视频(MediaPlayer)

setDataSource() // 设置要播放

seekTo()//    从指定位置播放

isPlaying()//当前是否播放

getDuration// 获取音频时长

// 开始播放Button

public void start(View v){

//播放的地址

String dizhi = et_shu.getText().toString().trim();

if(TextUtils.isEmpty(dizhi)){

Toast.makeText(this, "不能为空", Toast.LENGTH_SHORT).show();

return;

}

player = new MediaPlayer();

player.reset();

try {

player.setDataSource(dizhi);

player.prepare();

//异步准备

/*player.prepareAsync();

player.setOnPreparedListener(new OnPreparedListener() {

@Override

public void onPrepared(MediaPlayer mp) {

// TODO Auto-generated method stub

player.start();

}

});*/

player.start();

} catch (Exception e) {

e.printStackTrace();

}

}

// 暂停Button

public void pause(View v){

if(player!= null && player.isPlaying()){

player.pause();

return;

}

if(player!= null){

player.start();

}

}

// 停止Button

public void stop(View v){

if(player!= null){

player.stop();

player.release();

player = null;

}

}

-5.传感器的使用(SensorManager)

SensorManager sm = (SensorManager) getSystemService(Context.SENSOR_SERVICE);

// Sensor.TYPE_XXX 可以获取

Sensor lightSensor = sm.getDefaultSensor(Sensor.TYPE_XXX);

MySensorEventListener myListenser = new MySensorEventListener();

// 必须注册监听事件,第二个参数 是Senor实例,第三个参数是 传感器输出信息的更新速率

有4个值:SENSOR_DELAY_UI  SENSOR_DELAY_NORMAL  SENSOR_DELAY_GAME  SENSOR_DELAY_FASTEST   越来越快,耗电

sm.registerListener(myListenser, XXXSensor, SensorManager.SENSOR_DELAY_NORMAL);

}

// 不使用的时候取消监听,省电

@Override

protected void onDestroy() {

super.onDestroy();

if(myListenser!=null){

sm.unregisterListener(myListenser);

}

}

private class MySensorEventListener implements SensorEventListener{

// sensor获得周围环境数据发生变化时 ,会被调用

@Override

public void onSensorChanged(SensorEvent event) {

//编写逻辑事件

}

// sensor获得周围环境数据发生变化时,精确度发生变化时会被调用

@Override

public void onAccuracyChanged(Sensor sensor, int accuracy) {

}

}

// 获取Sensor.TYPE_ALL

List list = sm.getSensorList(Sensor.TYPE_ALL);

System.out.println("数量 : " + list.size());

for (Sensor sensor : list) {

System.out.println(sensor.getName() +" , 类型 值 : " + sensor.getType());

}

##9.网络编程

-1.HttpURLConnection

1.get请求(拼接URL。。)

//防止乱码  URL编码

String username = URLEncoder.encode(username, "UTF-8");

String password = URLEncoder.encode(password, "UTF-8");

URL url = new URL(path+"?username="+username+"&password="+password);

HttpURLConnection conn = (HttpURLConnection) url.openConnection();

conn.setRequestMethod("GET");

//伪装设置成windows端查看网页

//conn.setRequestProperty("User-Agent",

"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2486.0 Safari/537.36 Edge/13.10586");

//设置连接超时时间

conn.setConnectTimeout(5000);

//获取响应码

int code = conn.getResponseCode();

2.post请求

URL url = new URL(path);

String params = "?username="+URLEncoder.encode(username, "UTF-8")+"&password="+URLEncoder.encode(password, "UTF-8");

HttpURLConnection conn = (HttpURLConnection) url.openConnection();

conn.setRequestMethod("POST");

//设置连接超时时间

conn.setConnectTimeout(5000);

//获取响应码

conn.setDoOutput(true);

conn.getOutputStream().write(params.getBytes());

int code = conn.getResponseCode();

-2.HttpClient(Apache) ---android 6.0 废弃

1.get请求

username = URLEncoder.encode(username, "UTF-8");

String path = "http://169.254.26.249:8080/qqlogin/servlet/login?username="+username+"&password="+password;

DefaultHttpClient client = new DefaultHttpClient();

HttpGet get = new HttpGet(path);

//执行请求,拿到响应对象

HttpResponse response = client.execute(get);

int code = response.getStatusLine().getStatusCode();

if(code == 200){

//拿到服务端响应的输入流

InputStream is = response.getEntity().getContent();

String login = StreamTool.decodeStream(is);

2.post请求

String path = "http://169.254.26.249:8080/qqlogin/servlet/login";

DefaultHttpClient client = new DefaultHttpClient();

HttpPost post = new HttpPost(path);

//通过一个NameValuePair集合来存放待提交的数据

List list = new ArrayList();

list.add(new BasicNameValuePair("username", username));

list.add(new BasicNameValuePair("password", password));

//防止乱码

post.setEntity(new UrlEncodedFormEntity(list , "UTF-8"));

//执行请求,拿到响应对象

HttpResponse response = client.execute(post);

int code = response.getStatusLine().getStatusCode();

if(code == 200){

InputStream is = response.getEntity().getContent();

String login = StreamTool.decodeStream(is);

-3.AsyncHttpClient---post请求

AsyncHttpClient client = new AsyncHttpClient();

RequestParams params = new RequestParams();

params.add("username", username);

params.add("password", password);

client.post(path, params, new AsyncHttpResponseHandler() {

@Override

//statusCode 是 响应码        headers响应头   responseBody 响应体

public void onSuccess(int statusCode, Header[] headers, byte[] responseBody) {

Toast.makeText(MainActivity.this,"登录结果为"+new String(responseBody), 0).show();

}

@Override

public void onFailure(int statusCode, Header[] headers, byte[] responseBody, Throwable error) {

Toast.makeText(MainActivity.this,"登录结果为"+new String(responseBody), 0).show();

}

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

推荐阅读更多精彩内容