期末,Android本学期学习总结

本人大二,这学期基本上大部分的课余时间都用来学编程了,其中学的最多的就是安卓了。有段时间几乎每天晚上,每个周末,每次没课的时候都在学安卓。。不过期末了,,要复习考试了,哎。。不然又挂了。。在这里总结一下这半年所学。不多。也不够详细。其中或有诸多讹误,大牛勿喷,望不吝赐教。

九月份

Xml与UI设计

布局是UI设计的基本组成单位。这里简单介绍下三种布局。布局layout,在这里可以称为parent,因为所有的部件必须存在于其之上。我喜欢把这个parent叫做母体。下面说母体就是指的具体的layout。这里我不会讲每一个组件,比如Button,TextView,EditText等等,这些东西你在每一本android的书上都有介绍,这里就不赘述了,主要讲一些共通的东西。

         值得一提的是,在xml文件中的一个小技巧。在每个xml文件开头都会有这么一句话,比如:

<LinearLayoutxmlns:android=http://schemas.android.com/apk/res/android

只要将上面的android换成其他的字母或单词,下面你就不用一遍一遍的写android:。。。了

<LinearLayoutxmlns:a="http://schemas.android.com/apk/res/android"

    a:layout_width="fill_parent"

    a:layout_height="fill_parent"

    a:orientation="vertical">

改成字母a后,会不会很方便

1.线性布局

线性布局是目前,我使用最多的布局,也是初学者最容易上手的布局。

<LinearLayout
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:padding="9dp"
android:background="#60000000"
>						

</LinearLayout>


所有的布局内的view组件都要写在两个LinearLayout标签之间。介绍几个常用参数。

layout_width,layout_height用来描述部件的长和宽。其值可为warp_content,或者match-parent/fill-parent。顾名思义,前者就是指的适配内容。后者就是与母体一样宽或高。fill_parent是最初版本里出现的,后来在android2.2之后出现了match_parent。貌似是叫法更加贴切了,目前也是鼓励使用这个,但是最新的sdk依旧是支持fil_parent的使用,这是为了旧设备上的一个兼容性。

orientation是指的布局上的组件的排布方向。horizontal是水平排布。vertical是垂直排布。

background就是背景喽。可以是颜色也可以是图片。颜色用#开头,后面就是通常的RGB颜色,用六位十六进制数字表示。000000为黑色。ffffff是白色。但是#后面是八位,因为后六位是rgb颜色,前两位是透明度。ff是不透明,00全透明。

pading是布局上的view组件的四周距离他的母体layout的长度。单位是dp。安卓布局里面有好多长度单位。px不鼓励使用了。sp是用来描述字体大小的。

再比如在这个布局之中添加两个图片按钮

 <ImageButton
	android:id="@+id/btn_blue"
	android:layout_width="0dp"
	android:layout_weight="1"
	android:layout_height="150dp"
	android:background="@drawable/blue"/>
<ImageButton
	android:id="@+id/btn_green"
	android:layout_width="0dp"
	android:layout_weight="1"
	android:layout_height="150dp"
	android:background="@drawable/green"/>

id属性是这个组件的标识。接下来会讲到。

还有一个常用的参数是layout_weight。表示的是所占比例。

上面就是说两个按钮的比例是1:1。在水平布局下,如果使用比例的话,layout_width属性就失效了,设置为0.同理,垂直布局时,layout_height应设置为0.

 

2.相对布局

         相对布局的产生是为了解决日趋严重的安卓设备碎片化问题,由于android的开放,不同的厂商生产了许许多多屏幕分辨率不同的产品,这对于一款应用的测试带来了很大的难度。相对布局使得这一现象得以缓解,注意,不是解决。

相对布局的使用,开始的时候是十分令人头疼的。但当你慢慢熟练之后,你会发现它的便利之处。在这里,根植于相对布局之上的部件,通过与母体layout或其他组件的相对位置来描述。如果你熟悉html的话,会很快上手。。当然我现在还不熟悉html=_=#

属性

描述

layout_alignParentTop, layout_alignParentBottom,

layout_alignParentRight, layout_alignParentLeft

view与它的RelativeLayout容器对齐。上、下、左或者右对齐。

取值为boolean类型

layout_centerHorizontal, layout_centerVertical,

layout_centerInParent

设置居中,水平居中,垂直居中,在母体布局中居中

layout_alignTop, layout_alignBottom,

layout_alignRight, layout_alignLeft

同样是对齐,不过是和其他view组件对齐。后面的参数是相对的其他view组件的id

layout_alignBaseline

设置当前view组件的所有边和指定的组件对齐。参数是指定组件的id

layout_above, layout_below, layout_leftOf,

layout_rightOf

顾名思义,在其他view组件的上下左右。参数同样是指定组件的id

 

3.表格布局

         这个是比较简单的布局,如果你希望你的设计比较简单,并且规整的话,就可以使用这个布局。直接上代码。

Look:

<TableLayout
        a:layout_width="match_parent"
        a:layout_height="wrap_content"
        a:layout_marginTop="80dp">
        <TableRow
            a:id="@+id/tableRow1"
            a:layout_width="wrap_content"
            a:layout_height="wrap_content">
            <ImageButton
                a:id="@+id/btn_blue"
                a:layout_width="0dp"
                a:layout_weight="1"
                a:layout_height="150dp"
                a:background="@drawable/blue"/>
             <ImageButton
                a:id="@+id/btn_green"
                a:layout_width="0dp"
                a:layout_weight="1"
                a:layout_height="150dp"
                a:background="@drawable/green"/>
        </TableRow>
        <TableRow
            a:id="@+id/tableRow2"
            a:layout_width="wrap_content"
            a:layout_height="wrap_content">
             <ImageButton
               a:id="@+id/btn_red"
                a:layout_width="0dp"
                a:layout_weight="1"
                a:layout_height="150dp"
                a:background="@drawable/red"/>
             <ImageButton
                a:id="@+id/btn_yellow"
                a:layout_width="0dp"
                a:layout_weight="1"
                a:layout_height="150dp"
                a:background="@drawable/yellow"/>
        </TableRow>
</TableLayout>

TableLayout包含子组件,TableRow,用来指定每一行的布局。将每一行的view组件置于TableRow即可。

效果:

4.id

         id就是你可视化布局里面的资源的索引,类似于windows中的句柄。可以将理解为特殊的“指针”。由于界面逻辑与业务逻辑的分离,id成了打通两者的桥梁。当然在android中也是可以用java来写布局的。但是我不喜欢,这样耦合度太高,修改起来太麻烦。而使用xml布局时,界面逻辑与业务逻辑只通过id进行联系,那么以后你修改布局中某个组件的位置,背景颜色等等之类的属性的时候,Activity通常是不用做任何修改的。不过需要说明的时,在编译的时候xml时候先会转换成java,再转换成字节码文件,所以xml布局在效率上应该会差一点点。。

        其实这个id,你可以在R文件中可以看到,就是一个长整型。

Activity入门

         新建出工程以后,会自动生成activity的类,及其代码框架。一般来说,一个activity对应一个布局的xml文件,我一般将类名和布局名写成相同的或相似的,便于以后寻找修改。我们要做的是在这个类的开始部分(也可以是其他部分),声明出xml文件中出现的部件。一般不用初始化,但涉及其他变量最好应该初始化。

         然后必做的一件事情就是找到这个组件。连接视图与控制的桥梁——id。findViewById。

比如在Activity的类中,有一个红色按钮。

btnRed = (ImageButton)findViewById(R.id.btn_red);//R.id.btn_red就是你布局中指定id的按钮了。注意前面的强制类型转化哦,这个方法返回的是View对象。

手势与事件

        当然这只是让你找到了这个部件,你还要为他添加事件。比如一个按钮,添加一个【点击】事件。SetOnClickListenter(),直译就是设置点击事件监听器。参数是你要实现的点击事件接口,比如OnClickListener。每一种监听器的set方法都会有与之对应的监听器接口。注意,有时候你导入包的时候会出问题,比如dialog和view下面都有这个OnClickListener的接口。所以如果出现麻烦,请写成View.OnClickListener。你可以以内部类的形式实现此接口。也可以显示实现这个接口,然后再实例化你的类作为参数。

class MyClickListener implements OnClickListener{

		@Override
		public void onClick(View arg0) {//onClick是要实现的方法
			// TODO Auto-generated method stub
			}
		
	}

 此时,设置监听可以:

btnRed.setOnClickListener(new MyClickListener());   

基本上,每种手势事件与监听器有一一对应的关系,不过Click和Touch是比较像的手势,但却不同。。。下面给出android4.3的支持的七种手势。

注意,android4.4支持的手势发生了变化,变成了八种手势。具体请看这里。http://developer.android.com/design/patterns/gestures.html

Intent

         Intent有时候翻译为“意图”,确实它代表着你要跳转的页面的一个意图。。一个冲动。。Android之中的activity是堆在一个类似“栈”的容器中。一层一层,如果你finish掉当前的activity,那么他下面的activity就会显示出来。也可以通过当前的activity,向新的activity的跳转,从而使新的activity叠加到原先的activity上面,而这一实现便是通过Intent。我喜欢把Intent看成一艘船,两个activity是此岸和彼岸。微笑

Intent myIntent = new Intent();
myIntent.setClass(A.this,B.class);
A.this.startActivity(myIntent);
A.this.finish();

此时的A.this便是指的当前的activity,B.class是你要跳转的activity。。

finish方法会从activity的栈中移除当前的activity,也就是说你从新的activity中按返回键的时候是无法返回上一个activity的。如果要返回就不需要写finish方法。

不过,请原谅我没告诉你更简洁的构造方法

Intent myIntent = new Intent(A.this,B.class);

 如果在两个activity之间传递数据怎么办?Intent也可以实现。 那就让这艘船装点货吧。

Bunlde

 putExtra方法,这个方法的参数有两个值,name,value,这是一个映射。name是一个字符串,value可以是任何类型。

比如。myIntent.putExtra("mode", "admin");这个就可以传递一个映射。

如果要传递大量的数据这样就不好了

putExtras方法:IntentputExtras(Bundle extras)。它的参数是一个Bundle类型。Bundle封装映射。

Bundle bundle = new Bundle();          //创建Bundle对象   
bundle.putString("mode", "admin");     //装入数据   
intent.putExtras(bundle);

putExtras方法还有一个重载方法。

IntentputExtras(Intent src)。参数就是另外一个封装好的Intent对象。

然后在新的activity中要获得数据,就是在onCreate方法中

Intent myintent = getIntent();
Bundle bundle = myintent.getExtras();
String mode  = bundle.getString("mode"); 

十月份

SQLite数据库

SQLite数据库是一个开源数据库。它内部只支持NULL,INTEGER,REAL,TEXT,BLOB这五种数据类型。但它也可以接受varchar,char等类型。只不过会在执行的时候转化为上述类型。所以你会发现,SQLite中在定义字段的时候你写varchar(10)和varchar其实没有什么不同。

在学习android数据库的时候,最好下载一个SQLite Expert这个可视化软件。你从模拟器中导出的表通过这个软件得以方便查看。原来貌似是收费软件但是现在有了person版,个人版即免费版。

要引起注意的是,由于android原生的模拟的慢的令人无法忍受,我相信很多人和我一样在使用Genymotion模拟器。确实快很多。但是问题是这个模拟器中产生的表,是无法通过eclipse查看并导出的。开始的时候怎么都找不到模拟器中的表,这个问题曾经折磨我很长时间,一段怀疑自己的代码,甚至怀疑开始怀疑人生。后来才发现是Genymotion并不能查看表。所以如果你要导出表的话,最好还是使用原生的模拟器。毕竟他才是亲儿子。

SQLiteDBHelper

用于操纵数据库,所有具体的增删查改操作,都应该在继承此类的子类中实现。不要臃肿的堆在ui类中。

         新建一个继承SQLiteOpenHelper的类的时候,会自动生成两个重写的方法:onCreate和onUpgrade。在app运行的时候,若调用该类,如果没有相应的表,则会默认调用onCreate方法去创建一个表,所以你必须要补充完整此方法。这也是一个典型的回调机制的运用。而onUpgrade一般可不做任何处理。

 

SQL语句的执行

比如一个数据库member,表名为contacts的表。我习惯在类的开头将所有的字段定义成字符串常量。         

public class MemberHelper extends SQLiteOpenHelper {
         public static final String DB_NAME = "member";	                                                                	public static final String TABLE_NAME = "contacts";		
	public static final String ID="_id";
	public static final String NAME="name";	
	public static final String PHONE="phone";
	public static final String NO="no";	
	public static final String QQ="qq";
	public static final String BANJI="banji"; 
	public static final String STU_NO="stu_no";
	public static final String PWD="pwd"; //省略

(数据库的字段名都是字符串,不要搞混,比如_id字段名是字符串类型,但存储的数据的类型定义的是整型)

与jdbc不同,在执行SQL语句时,可以直接由数据库的对象来执行,而不需要jdbc那样,搞一个Statement对象出来用于专门执行sql语句。

Android使用的数据库为SQLite数据库,对应的类为SQLiteDataBase。

比如一个对象SQLiteDataBase db,可以这样执行sql。

                   db.execSQL("create table"+TABLE_NAME+" (" //调用execSQL方法创建表

                                     + ID+ " integer primary key," 

                                + NO + " varchar,"

                                     + NAME+ " varchar,"

                                     + STU_NO+ " varchar,"

                                     + PHONE+ " varchar,"

                                     + QQ+ " varchar,"

                                     + BANJI+ " varchar,"

                                     + PWD+ " varchar)");

【用于onCreate方法中创建表】

而有些时候对于表的操作时包含变量的,比如插入操作。在MemberHelper方法中增加一个增加联系人的方法。

public void add(SQLiteDatabase db,String no,String name,String stu_no,String phone,String qq,String banji,String pwd){
	db.execSQL("insert into contacts(no,name,stu_no,phone,qq,banji ,pwd)values(?,?,?,?,?,?,?)",
	new String[]{no,name,stu_no,phone,qq,banji,pwd});
	}

在方法中的sql语句处使用?来代替后面的变量。?就是占位符,类似于C语言中的%d吧,呵呵。Sql语句后面,所有的参数都要放进一个String类型的数组中。注意,如果插入数据中有数整型数据,要转换为字符串。比如整型数据id。Id+””就变成了字符串。

Cursor游标

        Cursor的概念类似于jdbc中的ResultSet结果集,就是说当你执行查询语句的时候,可能返回的结果不只一个,此时返回的是所有结果的集合(确切的说是指向这个集合的第一条记录)。

 Cursor cursor = db.query(TABLE_NAME, null, null, null, null, null, null);

         通过游标获得某个字段的内容就是具体类型的get方法,比如如果字段是String,那么就是getString(),括号里的参数是字段也就是列的下标(index),从0开始。但是这样容易记混顺序从而引发问题,所以可以通过一个提供的方法获得这个index

 String no = cursor.getString(cursor.getColumnIndex(NO));

         getColumnIndex方法的参数是字段名,为String类型,注意,上例中的NO是前面定义的静态字符串变量。

如果要遍历查询结果中的每条记录,可以使用循环语句,不过请注意,whiledo while的不同,这个小差别,在遍历的时候有时会非常重要。

if(cursor.moveToFirst()){
			do{
	String number = cursor.getString(cursor.getColumnIndex(NO));
				if(no.equals(number))
					return cursor.getInt(cursor.getColumnIndex(ID));
			}while(cursor.moveToNext());
		}

如果直接用while的话,第一条记录是不会执行循环体的,从而产生问题。这是小细节,但也层让我百般调试,细节决定成败。

 

listVIew与数据库

         ListVIew是我们可以经常看到的组件(比如你的电话薄),它用来在屏幕上显示一条一条的数据(Item),是一个非常强大的view组件。ListVIew充分利用了MVC的设计思想。我喜欢简称他为LV。

         说几句“废话”。问:如果你要显示的Item条目有成千上万那么ListView在加载的时候会不会很耗时呢?答:不会。因为ListVIew不会全部加载所有的Item条目,而是只加载一部分,比一屏幕的要显示的条目要多些。反正能铺满一屏幕,然后当你滚动屏幕,每次有新的Item要进入屏幕的时候,就会有一个旧的Item被垃圾回收器销毁掉,从而节约内存。

         要充分利用数据库就要和ListVIew相联系,利用ListView来显示数据库中的数据。当你定义好了ListView对象,下面要显示数据就要设置适配器,setAdapter( );这个方法的参数是各种适配器类型的对象。比如BaseAdapter,SimpleAdapter。这些都是抽象类,实现起来总有麻烦。这里说一个简单的适配器类SimpleCursorAdapter。看到游标Cursor了吧。要实现显示数据库数据简单了不少。

SimpleCursorAdapter(Context context,intlayout,Cursor c,String[] from,int[] to,int flags)

         它有六个参数。Contextcontext是你的Activity对象,传入this就行了。int layout是你定义的一个item的布局,就是ListVIew中每一条item的xml布局。这里传入的就是他的id喽。Cursor c是你定义好了的一个游标对象,它必须是返回了你数据库查询结果的游标哦。接下来的String[] from和 int[] to有一个一一对应的关系,也就是形成映射。int[] to是你item布局里要显示的子组件的id,比如多个TextView。而String[] from中就是你要显示的数据库中的字段名了。至于int flags呢,官方的API的说法是定义默认的适配器行为。具体什么行为我也没研究,默认就是0就行了。

adapter = new SimpleCursorAdapter(this,
			R.layout.listitem,cursor,
			newString[]{"no","name","stu_no"},
			newint[]{R.id.txNO,R.id.txName,R.id.txStu},
			0);
lv.setAdapter(adapter);

//其中adapter是SimpleCursorAdapter类型,lv是LiseVIew类型。

         然后呢,你可以给你的ListVIew对象设置事件监听器了。常用的比如setOnItemClickListener、setOnItemLongClickListener方法。顾名思义,(=_=#,好多顾名思义)就是点击某一item条目的事件,和长按某一item条目的事件。这两个实现起来基本一致,以前者为例。要实现的OnItemClickListener 接口中的为实现方法为

public void onItemClick(AdapterView<?> arg0,View arg1, int position,long id)

    position就是你点击的是第几个条目,从0算起。这个位置也是该条记录在数据库中的位置。所以当你想实现点击item显示该条记录的详情的时候。就需要通过游标或许相应的_id(int类型)。

cursor.moveToPosition(position);
_id=cursor.getInt(0);

然后在跳转的新的activity(也就是详情页面),显示该条记录的全部信息。只需要需要用Intent把这个_id传过去。再去通过查询_id对应的记录,然后获取各个字段的数据就行了

网络编程

恼人的socket与线程

         关于socket通信的实现,这里不具体介绍了,是java网络编程中的。android中和java中基本一致的。就是客户端类Socket要指定,ip和port端口。服务器端的ServerSocket指定端口就行了。注意客户端socket指定的端口要与此一致。port的范围在1025到65535之间。

         在android4.0以后,不允许在UI线程里,进行可能会产生阻塞的操作了,比如网络通信。所以你在主线程里进行socket连接,在android4.0以后会崩溃。当初刚学的时候,书上还写得是老版本的写法,网上好多也是,然后就是一路的崩溃。。

         其实,解决方法不难,只不过当初自己对于线程还没深的认知。线程大家都知道了,实现以来有两种方法,一是继承Thread类,二是实现Runnable接口。当你的线程的对象开始执行start方法的时候,默认的会调用你自己写的run方法。具体实现途径好多了。比如可以

private Runnable mRunnable = new Runnable() {
	@Override
	public void run() {
		// TODO Auto-generated method stub
		while (true) {
			try {
				socket = new Socket(IP, PORT);
			//shenglve
				};
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}
};

然后在主线程的onCreate方法中

mThread = new Thread(mRunnable);
mThread.start();

mThread是activity的类中声明的私有成员,
private Thread mThread=null;

 

十一月份

继续网络编程

HandlerUI更新

        前面已经说了,在UI线程里不能进行耗时的操作,所以网络通信放在一个子线程中,但如果子线程涉及到UI更新,Android主线程是线程不安全的,也就是说,更新UI只能在主线程中更新,子线程中操作是危险的。Handler就出现了来解决这个问题,由于Handler运行在主线程中(UI线程中),它与子线程可以通过Message对象来传递数据,Handler把这些消息放入主线程队列中,配合主线程进行更新UI。Bundle封装了数据,然后传给Message来搬运数据,好比Bundle传给Intent来搬运数据一样。

private Handler myHandler = new Handler(){
	@Override
	public void handleMessage(Message msg) {
		// TODO Auto-generated method stub
		super.handleMessage(msg);
		
		Bundle b = msg.getData();
		char flag = b.getChar("color");
		switch(flag)
		{
		case 'R':img.setBackgroundResource(R.drawable.red);break;
		case 'G':img.setBackgroundResource(R.drawable.green);break;
		case 'B':img.setBackgroundResource(R.drawable.blue);break;
		case 'Y':img.setBackgroundResource(R.drawable.yellow);break;
		}
	}
};

        这个例子是通过网络传送过来的不同字符,来改变img当前的图片。有get就有set,上面只是获取Handler中的数据,那么如何放置数据呢?其实只不过是逆过程罢了。

Message msg = new Message();
Bundle b = new Bundle();
b.putChar("color", flag);
msg.setData(b);
myHandler.sendMessage(msg);
注意这些操作都要放在子线程中哦。

说一点IO

        说到网络编程不能不提IOandroidjava的基础上并无变化。IO是一个比较复杂的东西,并且我自己也不敢说会IO,简单说一下,IO分为两类,字符流、字节流。记忆起来就是后缀是Stream的都是字节流,比如InputStreamOutputStream,而ReaderWriter是字符流的输入输出。IO类是一个庞大的体系,要分析的话就要记好后缀。就像汉语里,牛奶和奶牛的区别,一个偏正短语后面字(词)的才是中心语,很好懂吧。那InputStreamReader()可以理解了,它是一个Reader类型,但它的构造方法的参数是InputStream类型。它的作用是将字节流转化为字符流。

        从Socket获取的IO是字节流。比如一个Socket对象socket。在建立了连接后,可以通过socket.getInputStream()socket.getOutputStream()来获取IO流。然后都需要对获得的IO流进行加工。比如转化,将字节流转化为字符流,利用InputStreamReader()。或者包装,将字节流包装成过滤流(继承自FilterInputSteam/FilterOutputSteam抽象类)——DataInputStream(DataOutputStream)BufferedInputStream(BufferedOutputStream)。前者比较常用因为它提供了针对基本数据类型的读/写方法。比如readInt()readChar()readByte()readDouble()等等,需要提一下针对字符串的方法是readUTF而不是readString。这些read方法都是针对输入流DataInputStream的,同理,在DataOutputStream类中有相应的write方法。另外还有一个单独的read方法——int read(byte[]),就是将流中的字节存储到字节数组b中。返回值int是调用一次方法,所存储的字节的数目,如果到了末尾,没有存储字节就返回-1。重载方法int read(byte[] b,int off, int len)。就是可以指定存储位置,off是要存入字节数组b中的起始位置,len就是存储的长度了,也就是说你可以不从b[0]开始存入数据。

        回过头再谈一下字节流字符流的转化。

InputStream input = socket.getInputStream();

InputStreamReader isr = new InputStreamReader(input);

BufferedReader buffer = new BufferedReader(isr);

String inputLine=null;

while((inputLine=buffer.readLine())!=null){......}

先转化成字符流,再包装成字符流的缓冲流,目的是实现一行一行的读取数据,提高效率。所谓一行一行就是当字符流中出现\r\n字符时,就认作行结束了,然后返回该行字符串。注意是\r\n不会被返回的。

蓝牙通信小解

       原来有个误区就是所谓的网络编程只是TCPUDP那种互联网的通信编程才叫网络编程。其实蓝牙,Wifi等等也都在网络编程的范畴里。学蓝牙是很淡疼的,能找到的资料不是很多,原因大概是蓝牙是涉及硬件的,所以有些在模拟器上学习android编程的人,就束手无策,其次,一定要有至少两个蓝牙设备才能测试,也这也抬高了学习蓝牙的门槛。从搜集到的断简残篇里我学到了一点东西。

         同样是通信,同样是socket。不过是BluetoothServerSocketBluetoothSocket。其实关键的不同之处就是在于如何连接两个蓝牙的过程。在连接之后的操作和一般的socket编程无异。

         还要介绍两个类BluetoothAdapterBluetoothDevice。前者代表本地的蓝牙适配器,后者代表要与本地连接的其他的蓝牙设备。通过BluetoothAdapter静态方法getDefaultAdapter()可以获得本地蓝牙适配器的对象。

private BluetoothAdapter myAdapter = BluetoothAdapter.getDefaultAdapter();

同样是C/S模式的通信我们先看客户端:

首先要获得远程蓝牙设备的对象需要本地适配器对象执行getRemoteDevice方法。

public BluetoothDevice getRemoteDevice (String address)

        参数是远程蓝牙设备的地址,不是本地蓝牙的地址。就好像一般socket编程中要指定的ip一样。地址就是MAC地址,也就是物理地址,包括互联网上的每一台电脑的网卡都有一个物理地址。蓝牙也有MAC地址。这个地址在哪里找呢。首先打开手机的蓝牙,然后设置其可见。接着依次打开手机里的设置——关于手机——状态信息就能看到了。

        你获得了BluetoothDevice对象,接着要获得BluetoothSocket对象。执行下面的方法

public BluetoothSocket createRfcommSocketToServiceRecord (UUID uuid)

        方法中的有个单词RFCOMM,就是蓝牙通信的协议。参数UUID叫做通用唯一识别码,这里简单说就是代表着你的蓝牙通信要进行哪种服务,不同的服务有不同的UUID识别码。具体看这位同学的博客吧——传送门。一般的通信就用这个吧00001101-0000-1000-8000-00805F9B34FB

socket = device.createRfcommSocketToServiceRecord(UUID.fromString("

00001101-0000-1000-8000-00805F9B34FB"));

UUID的静态方法fromString就是从字符串创建UUID对象喽。

再看看服务端:

         服务端的BluetoothServerSocket对象必然要开启对于客户端连接请求的监听。使用listenUsingRfcommWithServiceRecord方法。

public BluetoothServerSocket listenUsingRfcommWithServiceRecord (String name, UUID uuid)

参数字符串name是服务器名,随便起个名字就好了。UUID就不再解释了。

再说一下,打开蓝牙

         蓝牙适配器类有isEnable方法来判断本地蓝牙是否已经打开。有两种方法。第一种是直接调用适配器类的enable方法打开。第二种调用系统API,弹出一个小对话框,让你选择是否打开。

//第一种打开方法: 

boolean result = mBluetoothAdapter.enable();

 

//第二种打开方法,调用系统API去打开蓝牙

if (!mBluetoothAdapter.isEnabled()){

Intent intent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);

startActivityForResult(intent,1);

}

看一下startActivityForResult方法。

public void startActivityForResult (Intent intent, int requestCode)

Intent intent 就是前面定义的Intent对象,一个系统定义好的打开蓝牙请求对话框的意图。

int requestCode是请求返回码,大于等于0即可。这个返回码会在onActivityResult()中得到处理。

onActivityResult()Activity类中的方法)

         蓝牙的东西太多了。这里只是很小很小的一部分,只是当做给大家学习蓝牙的一个开胃小菜。好多东西都没有讲到,大家自己去探索吧。

 

 

posted on 2013-12-10 14:09  果冻虾仁  阅读(1095)  评论(0编辑  收藏  举报

导航