一、Android的布局管理
1、线性布局(LinearLayout)
是Android应用中最简单的布局方式,有水平和竖直两种排列方式。
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="左下"
/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="右下"
/>
</LinearLayout>
2、表格布局(TableLayout)
是以行列的形式来管理子控件,每一行可以是一个View控件或者是一个TableRow控件。
<?xml version="1.0" encoding="utf-8"?>
<TableLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<TableRow
android:gravity="center"
>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="第0列"
>
</TextView>
</TableRow>
<TableRow
android:gravity="center"
>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="按钮1"
/>
</TableRow>
</TableLayout>
3、相对布局(RelativeLayout)
子控件是根据设置的参照控件来进行布局的,设置的参照控件可以是父控件,也可以是子控件。
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<Button
android:id="@+id/button1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="中间的按钮,很长很长很长"
android:layout_centerInParent="true"
>
</Button>
<Button
android:id="@+id/button2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="上面的按钮"
android:layout_above="@id/button1"
android:layout_alignLeft="@id/button1"
>
</Button>
</RelativeLayout>
4、单帧布局(FrameLayout)
布局中的所有控件都放置在布局的左上角,相互叠加。
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/big"
>
</ImageView>
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/center"
>
</ImageView>
</FrameLayout>
5、网格布局(GradLayout)
所有控件的位置是排列在一个指定的网格中的,使用虚细线将布局划分为行、列、和单元格。也支持一个控件在行列上交错排列。
<GridLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/GridLayout1"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:columnCount="4"
android:orientation="horizontal"
android:rowCount="6">
<TextView
android:layout_columnSpan="4"
android:layout_gravity="fill"
android:layout_marginLeft="5dp"
android:layout_marginRight="5dp"
android:background="#FFCCCC"
android:text="0"
android:textSize="50sp" />
<Button
android:layout_columnSpan="2"
android:layout_gravity="fill"
android:text="回退" />
<Button
android:layout_columnSpan="2"
android:layout_gravity="fill"
android:text="清空" />
<Button
android:layout_gravity="fill"
android:text="+" />
<Button
android:layout_gravity="fill"
android:text="1" />
<Button
android:layout_gravity="fill"
android:text="2" />
</GridLayout>
6、绝对布局(AbsoluteLayout)
通过指定控件的位置对其进行放置。
<?xml version="1.0" encoding="utf-8"?>
<AbsoluteLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="用户名:"
android:layout_x="10px"
android:layout_y="20px"
>
</TextView>
<EditText
android:layout_width="90px"
android:layout_height="wrap_content"
android:layout_x="70px"
android:layout_y="10px"
>
</EditText>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="密 码:"
android:layout_x="10px"
android:layout_y="75px"
>
</TextView>
<EditText
android:layout_width="90px"
android:layout_height="wrap_content"
android:layout_x="70px"
android:layout_y="60px"
>
</EditText>
</AbsoluteLayout>
二、图形和动画在Adroid中的实现
1、自定义图形控件
package myview;
import android.content.Context;//引入相关的包
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.view.View;
public class MyView extends View{
public MyView(Context context, AttributeSet attrs) {//构造器
super(context, attrs);
}
protected void onDraw(Canvas canvas) {//重写的绘制方法
super.onDraw(canvas);
canvas.drawColor(Color.BLACK);//绘制黑色背景
Paint paint = new Paint();//创建画笔
paint.setColor(Color.RED);//设置画笔颜色为红色
canvas.drawRect(10, 10, 110, 110, paint);//绘制矩形
canvas.drawText("这是字符串", 10, 130, paint);//字符串,以字符串下面为基准
RectF rf1 = new RectF(10, 130, 110, 230);//定义一个矩形
canvas.drawArc(rf1, 0, 45, true, paint);//根据矩形画弧,顺时针
canvas.drawLine(150, 10, 250, 110, paint);//画线
RectF rf2 = new RectF(200, 10, 250, 230);//定义一个矩形,参数分别是和父控件的距离
canvas.drawOval(rf2, paint);//根据矩形画圆
}
}
2、贴图
package myview;
import android.content.Context;//引入相关的类
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.view.View;
import com.my_application.R;
public class MyView extends View{//继承自View
Bitmap myBitmap;//图片的引用
Paint paint;//画笔的引用
public MyView(Context context, AttributeSet attrs) {//构造器
super(context, attrs);
// TODO Auto-generated constructor stub
this.initBitmap();
}
public void initBitmap(){
paint = new Paint();//创建一个画笔
myBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.img);//获得图片资源
}
@Override
protected void onDraw(Canvas canvas) {//重写的绘制方法
super.onDraw(canvas);
paint.setAntiAlias(true);//打开抗锯齿
paint.setColor(Color.WHITE);//设置画笔的颜色
paint.setTextSize(15);
canvas.drawBitmap(myBitmap, 10, 10, paint);//绘制图片
canvas.save(); // 保存桌布状态
Matrix m1=new Matrix();
m1.setTranslate(500, 10); //平移矩阵
Matrix m2=new Matrix();
m2.setRotate(15); //旋转矩阵
Matrix m3=new Matrix();
m3.setConcat(m1, m2); //将两个矩阵对象连接起来
m1.setScale(0.8f, 0.8f); //缩放矩阵
m2.setConcat(m3, m1);
canvas.drawBitmap(myBitmap, m2, paint); //绘制图片
canvas.restore(); //恢复画布状态
canvas.save();
paint.setAlpha(180); //设置透明度
m1.setTranslate(200,100);
m2.setScale(1.3f, 1.3f);
m3.setConcat(m1, m2);
canvas.drawBitmap(myBitmap, m3, paint); //绘制图片
paint.reset(); //恢复画笔设置
canvas.restore();
paint.setTextSize(40);
paint.setColor(0xffFFFFFF);
canvas.drawText("图片的宽度: "+myBitmap.getWidth(), 20, 380, paint);//绘制字符串,图片的宽度
canvas.drawText("图片的高度: "+myBitmap.getHeight(), 20, 430, paint);//绘制字符串,图片的高度
paint.reset(); // 恢复画笔设置
}
}
3、自定义动画播放
设置一段动画效果:res/anim/myanim.xml
<?xml version="1.0" encoding="utf-8"?><!-- XML的版本以及编码方式 -->
<set xmlns:android="http://schemas.android.com/apk/res/android">
<alpha
android:fromAlpha="0.1"
android:toAlpha="1.0"
android:duration="8000"
/> <!-- 透明度的变换 -->
<scale
android:interpolator= "@android:anim/accelerate_decelerate_interpolator"
android:fromXScale="0.0"
android:toXScale="1.4"
android:fromYScale="0.0"
android:toYScale="1.4"
android:pivotX="50%"
android:pivotY="50%"
android:fillAfter="false"
android:duration="10000"
/> <!-- 尺寸的变换 -->
<translate
android:fromXDelta="30"
android:toXDelta="0"
android:fromYDelta="30"
android:toYDelta="50"
android:duration="10000"
/> <!-- 尺位置的变换 -->
<rotate
android:interpolator="@android:anim/accelerate_decelerate_interpolator"
android:fromDegrees="0"
android:toDegrees="+350"
android:pivotX="50%"
android:pivotY="50%"
android:duration="10000"
/> <!-- 旋转变换 -->
</set>
主界面:activity_main.xml
用于展示布局——
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<ImageView
android:id="@+id/myView"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:src="@drawable/img"
/>
</LinearLayout>
java控制代码:
import android.app.Activity; //引入Activity类
import android.os.Bundle;//引入Bundle类
import android.view.animation.Animation;//引入Animation类
import android.view.animation.AnimationUtils;//引入AnimationUtils类
import android.widget.ImageView;//引入ImageView类
public class MainActivity extends Activity {
Animation myAnimation;//动画的引用
ImageView myImageView;//ImageView的引用
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {//重写的onCreate回调方法
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);//设置当前显示的View
myAnimation= AnimationUtils.loadAnimation(this,R.anim.myanim);//加载动画
myImageView = (ImageView) this.findViewById(R.id.myView);//得到ImageView的引用
myImageView.startAnimation(myAnimation);//启动动画
}
}
三、音频的播放
Android中关于音频播放的方式有两种:
SoundPool
:适合短促但相对反映速度要求较高的情况(游戏中的爆炸声)MediaPlayer
:适合时间长,对时间要求不高的情况
关于音频播放的代码示例:
布局文件:main.xml
<?xml version="1.0" encoding="utf-8"?><!-- XML的版本以及编码方式 -->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
><!--定义一个垂直的线性布局 -->
<TextView
android:id="@+id/textView"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="没有播放任何声音"
/><!-- 向线性布局中添加一个TextView控件 -->
<Button
android:id="@+id/button1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="使用MediaPlayer播放声音"
/><!-- 向线性布局中添加一个Button控件 -->
<Button
android:id="@+id/button2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="暂停MediaPlayer声音"
/><!-- 向线性布局中添加一个Button控件 -->
<Button
android:id="@+id/button3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="使用SoundPool播放声音"
/><!-- 向线性布局中添加一个Button控件 -->
<Button
android:id="@+id/button4"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="暂停SoundPool声音"
/><!-- 向线性布局中添加一个Button控件 -->
</LinearLayout>
java控制文件:
public void initSounds(){//初始化声音的方法
mMediaPlayer = MediaPlayer.create(this, R.raw.backsound);//初始化MediaPlayer
soundPool = new SoundPool.Builder().setMaxStreams(4).build();
soundPoolMap = new HashMap<Integer, Integer>();
soundPoolMap.put(1, soundPool.load(this, R.raw.dingdong, 1));
}
public void playSound(int sound, int loop) {//用SoundPoll播放声音的方法
AudioManager mgr = (AudioManager)this.getSystemService(Context.AUDIO_SERVICE);
float streamVolumeCurrent = mgr.getStreamVolume(AudioManager.STREAM_MUSIC);
float streamVolumeMax = mgr.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
float volume = streamVolumeCurrent/streamVolumeMax;
soundPool.play(soundPoolMap.get(sound), volume, volume, 1, loop, 1f);//播放声音
}
public void onClick(View v) {//实现接口中的方法
// TODO Auto-generated method stub
if(v == button1){//点击了使用MediaPlayer播放声音按钮
textView.setText("使用MediaPlayer播放声音");
if(!mMediaPlayer.isPlaying()){
mMediaPlayer.start();//播放声音
}
}
else if(v == button2){//点击了暂停MediaPlayer声音按钮
textView.setText("暂停了MediaPlayer播放的声音");
if(mMediaPlayer.isPlaying()){
mMediaPlayer.pause();//暂停声音
}
}
else if(v == button3){//点击了使用SoundPool播放声音按钮
textView.setText("使用SoundPool播放声音");
this.playSound(1, 0);
}
else if(v == button4){//点击了暂停SoundPool声音按钮
textView.setText("暂停了SoundPool播放的声音");
soundPool.pause(1);//暂停SoundPool的声音
}
}
四、Android应用中的基本组件
1、Activity组件
是最常见的一种Android组件,每一个Activity相当于一个屏幕,为用户提供了可视界面。这些Activity都继承自android.app
下的Activity类
。
1.1 Activity生命周期
- 运行态:显示在屏幕前台,并且具有焦点
- 暂停态:失去了焦点,被其他Activity取代在屏幕前台。对用户可见,但是不可以交互。当资源匮乏时,会被终止
- 停止态:没有焦点,完全不可见,但是还保留状态和成员等信息,会在需要时被结束。
生命周期图:
1.启动Activity:系统会先调用onCreate方法,然后调用onStart方法,最后调用onResume,Activity进入运行状态。
2.当前Activity被其他Activity覆盖其上或被锁屏:系统会调用onPause方法,暂停当前Activity的执行。
3.当前Activity由被覆盖状态回到前台或解锁屏:系统会调用onResume方法,再次进入运行状态。
4.当前Activity转到新的Activity界面或按Home键回到主屏,自身退居后台:系统会先调用onPause方法,然后调用onStop方法,进入停滞状态。
5.用户后退回到此Activity:系统会先调用onRestart方法,然后调用onStart方法,最后调用onResume方法,再次进入运行状态。
6.当前Activity处于被覆盖状态或者后台不可见状态,即第2步和第4步,系统内存不足,杀死当前Activity,而后用户退回当前Activity:再次调用onCreate方法、onStart方法、onResume方法,进入运行状态。
7.用户退出当前Activity:系统先调用onPause方法,然后调用onStop方法,最后调用onDestory方法,结束当前Activity。
1.2 Activity显示的内容的方式
- 通过XML配置文件声明
在xml布局文件中声明显示内容,然后通过setContentView()
方法设置对应布局文件。 - 通过View的子类对象声明
通过继承View类
来开发想要的用户界面。参照前面自定义图形控件。
2、Service组件
与Activity不同的是,Service组件没有与用户交互的表示层,是运行在后台的一种组件。每个Service组件都继承自android.app包下的 Service类
。
2.1 Service的启动
- 通过
startService()
启动。当调用该方法时,如果Service还未启动,则会依次调用onCreate
和onStart
方法来启动。当其他Context对象调用stopService
、Service自身调用stopSelf
和stopService
时停止Service的执行。 - 通过
bindService()
启动。当调用该方法时,如果Service还未启动,则会调用onCreate
完成初始化,然后会将该Service和Context对象(如Activity)进行绑定,当绑定的对象销毁时,对应Service也会被销毁。
2.2 Service的生命周期
3、BroadCast Receiver组件
不提供用于交互的表示层,是一种负责接收广播消息并对消息做出反应的组件。应用程序如果要响应一个广播消息,则需要注册对应的BroadCastReceiver
对象。
3.1 BroadCastReceiver发布广播的方式
创建一个Intent
对象,将信息的内容和用于过滤的信息封装起来,通过调用Context.sendBroadcast()
、Context.sendOrderdBroadcast()
、Context.sendStickyBroadcast()
将intent
对象发送出去。
三种发布广播的方式的区别:
Context.sendOrderdBroadcast()
是有序广播,所有的receiver依次执行。Context.sendBroadcast()
是普通无序的广播,可以通过在intent-filter中设置android:priority属性来设置receiver的优先级。优先级相同的receiver其执行顺序不确定。效率高。Context.sendStickyBroadcast()
无序广播,发出的广播会一直存在,以便新注册的Receiver能尽快的收到这条广播。其他功能与普通广播相同。但是使用sendStickyBroadcast
发送广播需要获得BROADCAST_STICKY
权限。
3.2 BroadCastReceiver接收广播的方式
- 在
androidmanifest.xml
中注册,注册信息包含在<receiver></receiver>
标签中,并在<intent-filter>
中设定过滤规则。 - 在代码中创建
IntentFilter
对象。该对象包含了对广播的过滤规则,然后在需要的地方调用Context.registerReceiver
方法和Context.unregisterReceiver
方法进行注册和取消注册。采用这种方法,Context对象被销毁时,BroadCastReceiver
也不存在了。
4、Content Provider组件
主要用于不同应用程序之间进行数据共享。每一个ContentProvider都继承自android.content
包下的ContentProvider
类,提供数据以及访问数据的接口,真正访问的ContentResolver
对象,该对象可以与ContentProvider
进行通信,达到共享数据的目的。
五、通信
1、应用程序的内部通信
消息的处理者:Handler
类简介
用于应用程序的主线程同用户自己创建的线程进行通信,将消息封装到Message
对象中,Message
对象有以下几个字段:
字段 | 说明 |
---|---|
arg1 | int类型,当传入的消息只包含整数时,可用该字段降低成本。 |
arg2 | int类型,当传入的消息只包含整数时,可用该字段降低成本。 |
obj | Obj类型,可以为任意类型 |
what | int类型,由用户定义的消息类型码,接收方可根据字段确定收到的消息是关于什么 |
不同的发送消息的方法说明:
myHandler.sendEmptyMessage(int what); //发送一个空消息,只指定Message的what字段
myHandler.sendMessage(Message message); //发送一个消息
myHandler.sendMessageAtTime(Message message,long time); //在指定时间之前发送一个消息
myHandler.sendMessageDelayed(Message message,long time);//在指定时间间隔以后发送一个消息
代码示例:
public class MainActivity extends Activity {
public static final int UPDATE_DATA = 0;//常量,代表更新数据
public static final int UPDATE_COMPLETED = 1;//常量,代表更新数据
TextView tv;//TextView对象的引用
Button btnStart;//Button对象的引用
//主线程的Handler对象
Handler myHandler = new Handler(){
@Override
public void handleMessage(Message msg) {//重写处理消息方法
switch(msg.what){//判断消息类别
case UPDATE_DATA://消息为更新的数据
tv.setText("正在更新来自线程的数据:"+msg.arg1+"%...");
break;
case UPDATE_COMPLETED://消息为更新完毕
tv.setText("已完成来自线程的更新数据!");
break;
}
}
};
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);//设置当前屏幕为R.layout.main布局文件
tv = (TextView)findViewById(R.id.tv);//获得屏幕中TextView对象引用
btnStart = (Button)findViewById(R.id.btnStart);//获得屏幕中Button对象引用
btnStart.setOnClickListener(new View.OnClickListener() {//为Button添加点击事件监听器
@Override
public void onClick(View v) {
new Thread(){//启动一个新线程
public void run(){
for(int i=0;i<100;i++){
try{//睡眠一段时间
Thread.sleep(150);
}
catch(Exception e){
e.printStackTrace();
}
Message m = myHandler.obtainMessage();//创建Message对象
m.what = UPDATE_DATA;//为what字段赋值
m.arg1=i+1;//为arg1字段赋值
myHandler.sendMessage(m);//发出Message对象
}
myHandler.sendEmptyMessage(UPDATE_COMPLETED);//发出更新完毕消息
}
}.start();
}
});
}
}
2、应用组件之间通信
例子:一个Service和一个Activity之间通过Intent和Broadcast Receiver 组件通信。
// MainActivity.java
public class MainActivity extends Activity {
public static final int CMD_STOP_SERVICE = 0;
Button btnStart;//开始服务Button对象应用
Button btnStop;//停止服务Button对象应用
TextView tv;//TextView对象应用
DataReceiver dataReceiver;//BroadcastReceiver对象
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
btnStart = (Button)findViewById(R.id.btnStart);
btnStop = (Button)findViewById(R.id.btnStop);
tv = (TextView)findViewById(R.id.tv);
btnStart.setOnClickListener(new OnClickListener() {//为按钮添加点击事件监听
@Override
public void onClick(View v) {
Intent myIntent = new Intent(MainActivity.this, com.my_application.MyService.class);
MainActivity.this.startService(myIntent);//发送Intent启动Service
}
});
btnStop.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
Intent myIntent = new Intent();//创建Intent对象
myIntent.setAction("com.my_application.MyService");
myIntent.putExtra("cmd", CMD_STOP_SERVICE);
sendBroadcast(myIntent);//发送广播
}
});
}
//服务启动后会向Activity发送Intent,所以Activity也必须注册一个BroadcastReceiver组件用于接收
private class DataReceiver extends BroadcastReceiver{//继承自BroadcastReceiver的子类
@Override
public void onReceive(Context context, Intent intent) {//重写onReceive方法
double data = intent.getDoubleExtra("data", 0);
tv.setText("Service的数据为:"+data);
}
}
@Override
protected void onStart() {//重写onStart方法
dataReceiver = new DataReceiver();
IntentFilter filter = new IntentFilter();//创建IntentFilter对象
filter.addAction("com.my_application.MainActivity");
registerReceiver(dataReceiver, filter);//注册Broadcast Receiver
super.onStart();
}
@Override
protected void onStop() {//重写onStop方法
unregisterReceiver(dataReceiver);//取消注册Broadcast Receiver
super.onStop();
}
}
Activity的运行过程:
- Activity依次执行
oncreate
方法和onstart
方法进入运行态。 - 在
oncreate
方法中依次注册了两个按钮组件,并实现了点击事件——启动Service和发送广播。 onstart
方法方法中注册BroadcastReceiver组件用于接收广播。- Activity终止时,执行
onstop
方法,取消注册BroadcastReceiver组件。
// Myservice.java
public class MyService extends Service {
CommandReceiver cmdReceiver;
boolean flag;
@Override
public void onCreate() {//重写onCreate方法
flag = true;
cmdReceiver = new CommandReceiver(); //注册一个BroadcastReceiver接收广播
super.onCreate();
}
@Override
public IBinder onBind(Intent intent) {//重写onBind方法
// TODO Auto-generated method stub
return null;
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {//重写onStartCommand方法
IntentFilter filter = new IntentFilter();//创建IntentFilter对象
filter.addAction("com.my_application.MyService");
registerReceiver(cmdReceiver, filter);//注册Broadcast Receiver
doJob();//调用方法启动线程
return super.onStartCommand(intent, flags, startId);
}
//方法:
public void doJob(){
new Thread(){
public void run(){
while(flag){
try{//睡眠一段时间
Thread.sleep(1000);
}
catch(Exception e){
e.printStackTrace();
}
Intent intent = new Intent();//创建Intent对象
intent.setAction("com.my_application.MainActivity");
intent.putExtra("data", Math.random());
sendBroadcast(intent);//发送广播
}
}
}.start();
}
private class CommandReceiver extends BroadcastReceiver {//继承自BroadcastReceiver的子类
@Override
public void onReceive(Context context, Intent intent) {//重写onReceive方法
int cmd = intent.getIntExtra("cmd", -1);//获取Extra信息
if(cmd == MainActivity.CMD_STOP_SERVICE){//如果发来的消息是停止服务
flag = false;//停止线程
stopSelf();//停止服务
}
}
}
@Override
public void onDestroy() {//重写onDestroy方法
this.unregisterReceiver(cmdReceiver);//取消注册的CommandReceiver
super.onDestroy();
}
}
// 第一个参数为键名,第二个参数为键对应的值,一个发送一个接收
intent.putExtra(“data”, Math.random());
intent.getDoubleExtra(“data”, 0);
Service的运行过程:
- Service依次执行
onCreate
方法和onStartCommand
方法进入运行态。 - 在
onCreate
方法注册一个BroadcastReceiver来接收广播。 - 在
onStartCommand
方法中启动一个线程执行相关服务并发送广播。 - Service销毁时,执行
onDestroy
方法,取消注册BroadcastReceiver。
3、与系统组件之间的通信
在AndroidManifest.xml中注册filter:
<intent-filter>
<action android:name="android.intent.action.CALL_BUTTON" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
在java文件中调用系统拨电话组件:
btnDial.setOnClickListener(new Button.OnClickListener(){//为按钮添加点击事件的监听器
@Override
public void onClick(View v) {//重写onClick方法
Intent myIntent = new Intent(Intent.ACTION_DIAL);//创建Intent对象
Sample_3_5.this.startActivity(myIntent);//启动Android内置的拨号程序
}
});
六、存储
1、私有文件夹文件的I/O操作
私有文件夹位于Android系统的/data/data/<应用程序包名>目录下,仅这个程序可以对其进行访问。
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);//设置当前屏幕
writeFileData(fileName, message);//创建文件并写入数据
String result = readFileData(fileName);//获得从文件读入的数据
tv = (TextView)findViewById(R.id.tv);//根据id获取屏幕中TextView对象的引用
tv.setText(result);//设置TextView的内容
}
//方法:向指定文件中写入指定的数据
public void writeFileData(String fileName,String message){
try{
FileOutputStream fout = openFileOutput(fileName, MODE_PRIVATE);//获得FileOutputStream对象
byte [] bytes = message.getBytes();//将要写入的字符串转换为byte数组
fout.write(bytes);//将byte数组写入文件
fout.close();//关闭FileOutputStream对象
}
catch(Exception e){
e.printStackTrace();//捕获异常并打印
}
}
//方法:打开指定文件,读取其数据,返回字符串对象
public String readFileData(String fileName){
String result="";
try{
FileInputStream fin = openFileInput(fileName);//获得FileInputStream对象
int length = fin.available();//获取文件长度
byte [] buffer = new byte[length];//创建byte数组用于读入数据
fin.read(buffer);//将文件内容读入到byte数组中
// result = EncodingUtils.getString(buffer, ENCODING);//将byte数组转换成指定格式的字符串
result = new String(buffer);
fin.close(); //关闭文件输入流
}
catch(Exception e){
e.printStackTrace();//捕获异常并打印
}
return result;//返回读到的数据字符串
}
2、读取Resorces和Assets文件中的文件
Resorces和Assets中的文件(在res/raw和assets目录下)会在编译的时候一起被打包,只能进行读取而不能进行写操作。
//方法:从resource中的raw文件夹中获取文件并读取数据
public String getFromRaw(String fileName){
String result = "";
try{
InputStream in = getResources().openRawResource(R.raw.test1); //从Resources中raw中的文件获取输入流
int length = in.available(); //获取文件的字节数
byte [] buffer = new byte[length]; //创建byte数组
in.read(buffer); //将文件中的数据读取到byte数组中
result = EncodingUtils.getString(buffer, ENCODING); //将byte数组转换成指定格式的字符串
in.close(); //关闭输入流
}
catch(Exception e){
e.printStackTrace(); //捕获异常并打印
}
return result;
}
//方法:从asset中获取文件并读取数据
public String getFromAsset(String fileName){
String result="";
try{
InputStream in = getResources().getAssets().open(fileName); //从Assets中的文件获取输入流
int length = in.available(); //获取文件的字节数
byte [] buffer = new byte[length]; //创建byte数组
in.read(buffer); //将文件中的数据读取到byte数组中
result = EncodingUtils.getString(buffer, ENCODING); //将byte数组转换成指定格式的字符串
}
catch(Exception e){
e.printStackTrace(); //捕获异常并打印
}
return result;
}
3、轻量级数据库SQLite
使用数据库的辅助类创建和打开数据库:
//继承自SQLiteOpenHelper的子类
public class MySQLiteHelper extends SQLiteOpenHelper{
public MySQLiteHelper(Context context, String name, CursorFactory factory,
int version) {
super(context, name, factory, version); //调用父类的构造器
}
@Override
public void onCreate(SQLiteDatabase db) { //重写onCreate方法
db.execSQL("create table if not exists hero_info(" //调用execSQL方法创建表
+ "id integer primary key,"
+ "name varchar,"
+ "level integer)");
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
}
}
数据库的增删改查操作:
//继承自Activity的子类
public class MainActivity extends Activity {
MySQLiteHelper myHelper; //数据库辅助类对象的引用
TextView tv; //TextView对象的引用
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);//设置显示的屏幕
tv = (TextView)findViewById(R.id.tv); //获得TextView对象的引用
myHelper = new MySQLiteHelper(this, "my.db", null, 1); //创建MySQLiteOpenHelper辅助类对象
insertAndUpdateData(myHelper); //向数据库中插入和更新数据
String result = queryData(myHelper); //向数据库中查询数据
tv.setText("名字\t等级\n"+result); //将查询到的数据显示到屏幕上
}
//方法:向数据库中的表中插入和更新数据
public void insertAndUpdateData(MySQLiteHelper myHelper){
SQLiteDatabase db = myHelper.getWritableDatabase(); //获取数据库对象
//使用execSQL方法向表中插入数据
db.execSQL("insert into hero_info(name,level) values('Hero1',1)");
//使用insert方法向表中插入数据
ContentValues values = new ContentValues(); //创建ContentValues对象存储“列名-列值”映射
values.put("name", "hero2");
values.put("level", 2);
db.insert("hero_info", "id", values); //调用方法插入数据,第二个参数表示如果没有对应值,则为null
//使用update方法更新表中的数据
values.clear(); //清空ContentValues对象
values.put("name", "hero2");
values.put("level", 3);
db.update("hero_info", values, "level = 2", null); //更新level=2的那行数据,第四个参数用来替换第三参数中的?
db.close(); //关闭SQLiteDatabase对象
}
//方法:从数据库中查询数据
public String queryData(MySQLiteHelper myHelper){
String result="";
SQLiteDatabase db = myHelper.getReadableDatabase(); //获得数据库对象
Cursor cursor = db.query("hero_info", null, null, null, null, null, "id asc"); //查询表中数据
int nameIndex = cursor.getColumnIndex("name"); //获取name列的索引
int levelIndex = cursor.getColumnIndex("level"); //获取level列的索引
for(cursor.moveToFirst();!(cursor.isAfterLast());cursor.moveToNext()){ //遍历查询结果集,将数据提取出来
result = result + cursor.getString(nameIndex)+" ";
result = result + cursor.getInt(levelIndex)+" \n";
}
cursor.close(); //关闭结果集
db.close(); //关闭数据库对象
return result;
}
@Override
protected void onDestroy() { //程序退出时执行
SQLiteDatabase db = myHelper.getWritableDatabase(); //获取数据库对象
db.delete("hero_info", "1", null); //1表示删除hero_info表中的所有数据
super.onDestroy();
}
}
4、数据共享者——Content Provider
是应用程序之间唯一的共享数据的途径。主要功能是存储并检索数据以及向其他应用程序提供访问数据的接口。
让自己的数据共享的方式有两种:
1、创建ContentProvider的子类;
2、将自己的数据(保持数据类型相同)添加到已有的Content Provider中去,且需要有写权限
ContentProvider的数据模型(data model)和URI:
数据模型:是以数据表的方式将存储数据提供给访问者,数据表中每一行为一条记录,每一个记录都包括唯一字段“_ID”
URI:每一个ContentProvider都对外提供一个URI标识自己的数据集,以“content://”开头。
URI的格式如下: [scheme:][//host:port][path][?query]
单单看这个可能我们并不知道是什么意思,下面来举个栗子就一目了然了
URI:http://www.baidu.com:8080/wenku/jiatiao.html?id=123456&name=jack
scheme:根据格式我们很容易看出来scheme为http host:www.baidu.com
port:就是主机名后面path前面的部分为8080 path:在port后面?的前面为wenku/jiatiao.html
query:?之后的都是query部分为 id=123456$name=jack
uri的各个部分在安卓中都是可以通过代码获取的,下面我们就以上面这个uri为例来说下获取各个部分的方法: getScheme()
:获取Uri中的scheme字符串部分,在这里是http getHost():获取Authority中的Host字符串,即
www.baidu.com getPost():获取Authority中的Port字符串,即 8080
getPath():获取Uri中path部分,即 wenku/jiatiao.html
getQuery():获取Uri中的query部分,即 id=15&name=du
ContentProvider是一个抽象类,如果我们需要开发自己的内容提供者我们就需要继承这个类并复写其方法,需要实现的主要方法如下:
public boolean onCreate()
在创建ContentProvider时调用
public Cursor query()
用于查询指定uri的数据返回一个Cursor
public Uri insert()
用于向指定uri的ContentProvider中添加数据
public int delete()
用于删除指定uri的数据
public int update()
用户更新指定uri的数据
public String getType()
用于返回指定的Uri中的数据MIME类型
ContentProvider创建的过程较复杂参考:https://www.jianshu.com/p/5e13d1fec9c9
5、简单的数据存储——Preferences的使用
是一种轻量级的数据存储方案,存储方式以键值对的方式存放在应用程序的私有文件夹下。获取Preferences的方式有如下两种:
- 调用Context对象的
getSharedPreferences
方法获得,需要传入名称和打开模式,打开模式有:PRIVATE
、MODE_WORLD_READABLE
和MODE_WORLD_WRITEABLE
其中之一。该方式获取的对象,只能被同一应用程序组件共享。 - 调用Activity对象的
getPreferences
方法获得,只能在相应的Activity中使用。
保存EditText内容的代码示例:
//继承自Activity的子类
public class MainActivity extends Activity {
EditText etPre; //EditText对象的引用
SharedPreferences sp; //SharedPreference对象的引用
public final String EDIT_TEXT_KEY = "EDIT_TEXT"; //Preferences文件中的键
@Override
public void onCreate(Bundle savedInstanceState) { //重写onCreate方法
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
etPre = (EditText)findViewById(R.id.et); //获得屏幕中EditText对象引用
sp = getPreferences(MODE_PRIVATE); //获得SharedPreferences对象,Activity下的方法创建,文件名为Activity的名称
String result = sp.getString(EDIT_TEXT_KEY, null); //表示EDIT_TEXT_KEY不存在则返回null
if(result != null){ //如果获取的值不为空
etPre.setText(result); //EditText对象显示的内容设置为读取的数据
}
}
@Override
protected void onDestroy() { //重写onDestroy方法
SharedPreferences.Editor editor = sp.edit(); //获得SharedPreferences的Editor对象
editor.putString(EDIT_TEXT_KEY, String.valueOf(etPre.getText())); //修改数据
editor.commit(); //必须调用该方法以提交修改
super.onDestroy();
}
}
七、网络编程
1、基于Socket套接字的网络编程
服务器端的开发(运行在java端):
public class Server{
public static void main(String[] args){//主方法
ServerSocket ss = null;//ServerSocket的引用
Socket s = null;//Socket的引用
DataInputStream din = null;
DataOutputStream dout = null;
try{
ss = new ServerSocket(8888);//监听到8888端口
System.out.println("已监听到8888端口!");
}
catch(Exception e){
e.printStackTrace();//打印异常信息
}
while(true){
try{
s = ss.accept();//等待客户端连接
din = new DataInputStream(s.getInputStream());
dout = new DataOutputStream(s.getOutputStream());//得到输入输出流
String msg = din.readUTF();//读一个字符串
System.out.println("ip: " + s.getInetAddress());//打印客户端IP
System.out.println("msg: " + msg);//打印客户端发来的消息
System.out.println("====================");
dout.writeUTF("Hello Client!");//向客户端发送消息
}
catch(Exception e){
e.printStackTrace();//打印异常信息
}
finally{
try{
if(dout != null){
dout.close();//关闭输出流
}
if(din != null){
din.close();//关闭输入流
}
if(s != null){
s.close();//关闭Socket连接
}
}
catch(Exception e){
e.printStackTrace();//打印异常信息
}
}
}
}
}
客户端的开发(运行在Android客户端):
public class MainActivity extends Activity implements OnClickListener{
Button button1;//按钮的引用
EditText editText;//文本框的引用
TextView textView;//文本的引用
public void onCreate(Bundle savedInstanceState) {//重写的onCreate回调方法
super.onCreate(savedInstanceState);
setContentView(R.layout.main);//设置当前的用户界面
button1 = (Button) findViewById(R.id.button1);//得到布局中的按钮引用
editText = (EditText) findViewById(R.id.editText);
textView = (TextView) findViewById(R.id.textView);
button1.setOnClickListener(this);//添加监听
}
public void onClick(View v) {
Socket s = null;
DataOutputStream dout = null;
DataInputStream din = null;
if(v == button1){//点击的是按钮
try {
s = new Socket("192.168.9.102", 8888);//连接服务器
dout = new DataOutputStream(s.getOutputStream());//得到输出流
din = new DataInputStream(s.getInputStream());//得到输入流
dout.writeUTF(editText.getText().toString());//向服务器发送消息
textView.setText("服务器发来的消息:" + din.readUTF());//接收服务器发来的消息
} catch (Exception e) {
e.printStackTrace();//打印异常信息
} finally {
try{
if(dout != null){
dout.close();//关闭输入流
}
if(din != null){
din.close();//关闭输入流
}
if(s != null){
s.close();//关闭Socket连接
}
}
catch(Exception e){
e.printStackTrace();//打印异常信息
}
}
}
}
}
2、基于HTTP协议的网络编程
在AndroidManifes.xml中注册网络权限:
<uses-permission android:name="android.permission.INTERNET"/>
执行网络操作是一件比较耗时的操作,所以我们应该开启一个新的线程去单独执行耗时的网络操作。但是UI设置必须是在主线程中!
public class MainActivity extends Activity {
TextView textView = null;//声明一个TextView的引用
ScrollView scrollView = null;//声明一个ScrollView的引用
public void onCreate(Bundle savedInstanceState) {//重写的onCreate方法
super.onCreate(savedInstanceState);
scrollView = new ScrollView(this);
textView = new TextView(this);
new Thread(new Runnable() {
@Override
public void run() {
HttpURLConnection connection = null;
BufferedReader reader = null;
//第一步建立httpurlconnection实例
try {
URL url = new URL("http://www.baidu.com");
connection = (HttpURLConnection) url.openConnection();
//自由定制connection
connection.setRequestMethod("GET");
connection.setConnectTimeout(800);
connection.setReadTimeout(80);
//使用getInputStream获取服务器返回的输入流
InputStream stresm = connection.getInputStream();
//对返回的输入流进行读取
reader = new BufferedReader(new InputStreamReader(stresm));
StringBuilder response = new StringBuilder();
String line;
while ((line=reader.readLine()) != null){
response.append(line);
}
//在UI中展示数据,注意UI展示进程必须是主线程
showdata(response.toString());
} catch (java.io.IOException e) {
e.printStackTrace();
}finally {
if (reader!=null){
try {
reader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (connection != null){
connection.disconnect();
}
}
}
}).start();
}
private void showdata(final String message){
runOnUiThread(new Runnable() {
@Override
public void run() {
textView.setText(message);
scrollView.addView(textView);//将textView添加到scrollView中
MainActivity.this.setContentView(scrollView);//设置当前显示的用户界面为scrollView
}
});
}
}