Archive for the ‘一.Android基础篇’ Category
本讲内容:Service
1、Service的概念
2、Service的生命周期
3、实例:控制音乐播放的Service
本讲源代码:Android学习指南第十四讲源代码
一、Service的概念
Service是Android程序中四大基础组件之一,它和Activity一样都是Context的子类,只不过它没有UI界面,是在后台运行的组件。
二、Service的生命周期
Service对象不能自己启动,需要通过某个Activity、Service或者其他Context对象来启动。启动的方法有两种,Context.startService和Context.bindService()。两种方式的生命周期是不同的,具体如下所示。
Context.startService方式的生命周期:
启动时,startService –> onCreate() –> onStart()
停止时,stopService –> onDestroy()
Context.bindService方式的生命周期:
绑定时,bindService -> onCreate() –> onBind()
解绑定时,unbindService –>onUnbind() –> onDestory()
三、实例:控制音乐播放的Service
下面我们用一个可以控制在后台播放音乐的例子来演示刚才所学知识,同学们可以通过该例子可以明显看到通过绑定方式运行的Service在绑定对象被销毁后也被销毁了。
下面把代码分享如下:
1、建立一个新项目名字叫 Lesson14_HelloService,Activity起名叫MainHelloService.java
2、res/layout/main.xml中代码写成
01 |
< ? xml version = "1.0" encoding = "utf-8" ?>
|
02 |
< LINEARLAYOUT android:layout_height = "fill_parent" android:layout_width = "fill_parent" android:orientation = "vertical" xmlns:android = "http://schemas.android.com/apk/res/android" >
|
03 |
< TEXTVIEW android:layout_height = "wrap_content" android:layout_width = "fill_parent" android:layout_marginTop = "10dp" android:textSize = "25sp" android:text = "音乐播放服务" />
|
04 |
< BUTTON type = submit android:layout_height = "wrap_content" android:layout_width = "wrap_content" android:layout_marginTop = "10dp" android:textSize = "20sp" android:text = "开启音乐播放服务" android:id = "@+id/Button01" >
|
05 |
</ BUTTON >
|
06 |
< BUTTON type = submit android:layout_height = "wrap_content" android:layout_width = "wrap_content" android:layout_marginTop = "10dp" android:textSize = "20sp" android:text = "停止音乐播放服务" android:id = "@+id/Button02" >
|
07 |
</ BUTTON >
|
08 |
|
09 |
< BUTTON type = submit android:layout_height = "wrap_content" android:layout_width = "wrap_content" android:layout_marginTop = "10dp" android:textSize = "20sp" android:text = "绑定音乐播放服务" android:id = "@+id/Button03" >
|
10 |
</ BUTTON >
|
11 |
< BUTTON type = submit android:layout_height = "wrap_content" android:layout_width = "wrap_content" android:layout_marginTop = "10dp" android:textSize = "20sp" android:text = "解绑定音乐播放服务" android:id = "@+id/Button04" >
|
12 |
</ BUTTON >
|
13 |
</ LINEARLAYOUT >
|
2、在res目录中建立一个raw目录,并把一个音乐文件babayetu.mp3拷贝进来
3、在Activity的同目录新建一个service文件MusicService.java
01 |
package android.basic.lesson14;
|
02 |
|
03 |
import android.app.Service;
|
04 |
import android.content.Intent;
|
05 |
import android.media.MediaPlayer;
|
06 |
import android.os.IBinder;
|
07 |
import android.util.Log;
|
08 |
import android.widget.Toast;
|
09 |
|
10 |
public class MusicService extends Service {
|
11 |
|
12 |
//为日志工具设置标签
|
13 |
String tag = "MusicService" ;
|
14 |
|
15 |
//定义音乐播放器变量
|
16 |
MediaPlayer mPlayer;
|
17 |
|
18 |
//其他对象通过bindService方法通知该Service时该方法会被调用
|
19 |
@Override
|
20 |
public IBinder onBind(Intent intent) {
|
21 |
Toast.makeText( this , "MusicService onBind()" ,Toast.LENGTH_SHORT).show();
|
22 |
Log.i(tag, "MusicService onBind()" );
|
23 |
mPlayer.start();
|
24 |
return null ;
|
25 |
}
|
26 |
|
27 |
//其他对象通过unbindService方法通知该Service时该方法会被调用
|
28 |
@Override
|
29 |
public boolean onUnbind(Intent intent){
|
30 |
Toast.makeText( this , "MusicService onUnbind()" , Toast.LENGTH_SHORT).show();
|
31 |
Log.i(tag, "MusicService onUnbind()" );
|
32 |
mPlayer.stop();
|
33 |
return false ;
|
34 |
}
|
35 |
|
36 |
//该服务不存在需要被创建时被调用,不管startService()还是bindService()都会在启动时调用该方法
|
37 |
@Override
|
38 |
public void onCreate(){
|
39 |
Toast.makeText( this , "MusicService onCreate()" , Toast.LENGTH_SHORT).show();
|
40 |
//创建一个音乐播放器对象
|
41 |
mPlayer=MediaPlayer.create(getApplicationContext(), R.raw.babayetu);
|
42 |
//设置可以重复播放
|
43 |
mPlayer.setLooping( true );
|
44 |
Log.i(tag, "MusicService onCreate()" );
|
45 |
}
|
46 |
|
47 |
//用startService方法调用该服务时,在onCreate()方法调用之后,会调用改方法
|
48 |
@Override
|
49 |
public void onStart(Intent intent, int startid){
|
50 |
Toast.makeText( this , "MusicService onStart" ,Toast.LENGTH_SHORT).show();
|
51 |
Log.i(tag, "MusicService onStart()" );
|
52 |
mPlayer.start();
|
53 |
}
|
54 |
|
55 |
//该服务被销毁时调用该方法
|
56 |
@Override
|
57 |
public void onDestroy(){
|
58 |
Toast.makeText( this , "MusicService onDestroy()" , Toast.LENGTH_SHORT).show();
|
59 |
mPlayer.stop();
|
60 |
Log.i(tag, "MusicService onDestroy()" );
|
61 |
}
|
62 |
} |
4、MainHelloService.java中的代码:
01 |
package android.basic.lesson14;
|
02 |
|
03 |
import android.app.Activity;
|
04 |
import android.content.ComponentName;
|
05 |
import android.content.Context;
|
06 |
import android.content.Intent;
|
07 |
import android.content.ServiceConnection;
|
08 |
import android.os.Bundle;
|
09 |
import android.os.IBinder;
|
10 |
import android.util.Log;
|
11 |
import android.view.View;
|
12 |
import android.view.View.OnClickListener;
|
13 |
import android.widget.Button;
|
14 |
import android.widget.Toast;
|
15 |
|
16 |
public class MainHelloService extends Activity {
|
17 |
|
18 |
//为日志工具设置标签
|
19 |
String tag = "MusicService" ;
|
20 |
|
21 |
/** Called when the activity is first created. */
|
22 |
@Override
|
23 |
public void onCreate(Bundle savedInstanceState) {
|
24 |
super .onCreate(savedInstanceState);
|
25 |
setContentView(R.layout.main);
|
26 |
|
27 |
//输出Toast消息和日志记录
|
28 |
Toast.makeText(MainHelloService. this , "MainHelloService onCreate" , Toast.LENGTH_SHORT).show();
|
29 |
Log.i(tag, "MainHelloService onCreate" );
|
30 |
|
31 |
//定义组件对象
|
32 |
Button b1= (Button)findViewById(R.id.Button01);
|
33 |
Button b2= (Button)findViewById(R.id.Button02);
|
34 |
Button b3= (Button)findViewById(R.id.Button03);
|
35 |
Button b4= (Button)findViewById(R.id.Button04);
|
36 |
|
37 |
//定义服务链接对象
|
38 |
final ServiceConnection conn = new ServiceConnection(){
|
39 |
|
40 |
@Override
|
41 |
public void onServiceConnected(ComponentName name, IBinder service) {
|
42 |
Toast.makeText(MainHelloService. this , "ServiceConnection onServiceConnected" , Toast.LENGTH_SHORT).show();
|
43 |
Log.i(tag, "ServiceConnection onServiceConnected" );
|
44 |
|
45 |
}
|
46 |
|
47 |
@Override
|
48 |
public void onServiceDisconnected(ComponentName name) {
|
49 |
Toast.makeText(MainHelloService. this , "ServiceConnection onServiceDisconnected" , Toast.LENGTH_SHORT).show();
|
50 |
Log.i(tag, "ServiceConnection onServiceDisconnected" );
|
51 |
|
52 |
}};
|
53 |
|
54 |
//定义点击监听器
|
55 |
OnClickListener ocl= new OnClickListener(){
|
56 |
|
57 |
@Override
|
58 |
public void onClick(View v) {
|
59 |
//显示指定intent所指的对象是个Service
|
60 |
Intent intent = new Intent(MainHelloService. this ,android.basic.lesson14.MusicService. class );
|
61 |
switch (v.getId()){
|
62 |
case R.id.Button01:
|
63 |
//开始服务
|
64 |
startService(intent);
|
65 |
break ;
|
66 |
case R.id.Button02:
|
67 |
//停止服务
|
68 |
stopService(intent);
|
69 |
break ;
|
70 |
case R.id.Button03:
|
71 |
//绑定服务
|
72 |
bindService(intent,conn,Context.BIND_AUTO_CREATE);
|
73 |
break ;
|
74 |
case R.id.Button04:
|
75 |
//解除绑定
|
76 |
unbindService(conn);
|
77 |
break ;
|
78 |
}
|
79 |
}
|
80 |
};
|
81 |
|
82 |
//绑定点击监听器
|
83 |
b1.setOnClickListener(ocl);
|
84 |
b2.setOnClickListener(ocl);
|
85 |
b3.setOnClickListener(ocl);
|
86 |
b4.setOnClickListener(ocl);
|
87 |
|
88 |
}
|
89 |
|
90 |
@Override
|
91 |
public void onDestroy(){
|
92 |
super .onDestroy();
|
93 |
Toast.makeText(MainHelloService. this , "MainHelloService onDestroy" , Toast.LENGTH_SHORT).show();
|
94 |
Log.i(tag, "MainHelloService onDestroy" );
|
95 |
}
|
96 |
} |
好了,本讲就到这里。
第十三讲:用户界面 View(八)
本讲内容:Gallery,GridView
十五、Gallery 画廊
Gallery是一个内部元素可以水平滚动,并且可以把当前选择的子元素定位在它中心的布局组件。
我们还是直接看看例子的运行效果。
下面上代码,相关解释都放在代码里了。
1、建立一个新项目 HelloGallery
2、拷贝wallpaper_0.jpg…wallpaper_9.jpg 10个图片文件到res/drawable目录
3、res/layout/main.xml文件的内容如下:
1 |
<? xml version = "1.0" encoding = "utf-8" ?>
|
2 |
< FRAMELAYOUT android:layout_height = "fill_parent" android:layout_width = "fill_parent" xmlns:android = "http://schemas.android.com/apk/res/android" android:id = "@+id/FrameLayout01" >
|
3 |
< IMAGEVIEW android:layout_height = "fill_parent" android:layout_width = "fill_parent" android:id = "@+id/ImageView01" android:src = "@drawable/wallpaper_0" >
|
4 |
</ IMAGEVIEW >
|
5 |
|
6 |
< GALLERY android:layout_height = "wrap_content" android:layout_width = "fill_parent" xmlns:android = "http://schemas.android.com/apk/res/android" android:id = "@+id/Gallery01" android:spacing = "5dp" >
|
7 |
</ GALLERY >
|
8 |
</ FRAMELAYOUT >
|
其中我们使用FrameLayout来实现叠加效果,使用ImageView来显示大图,Gallery来展示画廊,android:spacing="5dp" 属性则是用来设置元素之间的间隔。
4、在res/values/目录中新建一个attrs.xml内容如下:
1 |
<? xml version = "1.0" encoding = "UTF-8" ?>
|
2 |
< RESOURCES >
|
3 |
< DECLARE name = "HelloGallery" -styleable>
|
4 |
< ATTR name = "android:galleryItemBackground" />
|
5 |
</ DECLARE >
|
6 |
</ RESOURCES >
|
5、在MainHelloGallery.java中的内容如下:
001 |
package android.basic.lesson13;
|
002 |
|
003 |
import android.app.Activity;
|
004 |
import android.content.Context;
|
005 |
import android.content.res.TypedArray;
|
006 |
import android.os.Bundle;
|
007 |
import android.view.View;
|
008 |
import android.view.ViewGroup;
|
009 |
|
010 |
import android.widget.AdapterView;
|
011 |
import android.widget.AdapterView.OnItemClickListener;
|
012 |
import android.widget.BaseAdapter;
|
013 |
import android.widget.Gallery;
|
014 |
import android.widget.ImageView;
|
015 |
import android.widget.Toast;
|
016 |
|
017 |
public class MainHelloGallery extends Activity {
|
018 |
|
019 |
/** Called when the activity is first created. */
|
020 |
@Override
|
021 |
public void onCreate(Bundle savedInstanceState) {
|
022 |
super .onCreate(savedInstanceState);
|
023 |
setContentView(R.layout.main);
|
024 |
|
025 |
//定义UI组件
|
026 |
final ImageView iv= (ImageView)findViewById(R.id.ImageView01);
|
027 |
Gallery g = (Gallery) findViewById(R.id.Gallery01);
|
028 |
|
029 |
//设置图片匹配器
|
030 |
g.setAdapter( new ImageAdapter( this ));
|
031 |
|
032 |
//设置AdapterView点击监听器,Gallery是AdapterView的子类
|
033 |
g.setOnItemClickListener( new OnItemClickListener() {
|
034 |
|
035 |
@Override
|
036 |
public void onItemClick(AdapterView<?> parent, View view,
|
037 |
int position, long id) {
|
038 |
//显示点击的是第几张图片
|
039 |
Toast.makeText(MainHelloGallery. this , "" + position,
|
040 |
Toast.LENGTH_LONG).show();
|
041 |
//设置背景部分的ImageView显示当前Item的图片
|
042 |
iv.setImageResource(((ImageView)view).getId());
|
043 |
}
|
044 |
});
|
045 |
}
|
046 |
|
047 |
//定义继承BaseAdapter的匹配器
|
048 |
public class ImageAdapter extends BaseAdapter {
|
049 |
|
050 |
//Item的修饰背景
|
051 |
int mGalleryItemBackground;
|
052 |
|
053 |
//上下文对象
|
054 |
private Context mContext;
|
055 |
|
056 |
//图片数组
|
057 |
private Integer[] mImageIds = { R.drawable.wallpaper_0,
|
058 |
R.drawable.wallpaper_1, R.drawable.wallpaper_2,
|
059 |
R.drawable.wallpaper_3, R.drawable.wallpaper_4,
|
060 |
R.drawable.wallpaper_5, R.drawable.wallpaper_6,
|
061 |
R.drawable.wallpaper_7, R.drawable.wallpaper_8,
|
062 |
R.drawable.wallpaper_9 };
|
063 |
|
064 |
//构造方法
|
065 |
public ImageAdapter(Context c){
|
066 |
mContext = c;
|
067 |
//读取styleable资源
|
068 |
TypedArray a = obtainStyledAttributes(R.styleable.HelloGallery);
|
069 |
mGalleryItemBackground = a.getResourceId(
|
070 |
R.styleable.HelloGallery_android_galleryItemBackground, 0 );
|
071 |
a.recycle();
|
072 |
|
073 |
}
|
074 |
|
075 |
//返回项目数量
|
076 |
@Override
|
077 |
public int getCount() {
|
078 |
return mImageIds.length;
|
079 |
}
|
080 |
|
081 |
//返回项目
|
082 |
@Override
|
083 |
public Object getItem( int position) {
|
084 |
return position;
|
085 |
}
|
086 |
|
087 |
//返回项目Id
|
088 |
@Override
|
089 |
public long getItemId( int position) {
|
090 |
return position;
|
091 |
}
|
092 |
|
093 |
//返回视图
|
094 |
@Override
|
095 |
public View getView( int position, View convertView, ViewGroup parent) {
|
096 |
|
097 |
ImageView iv = new ImageView(mContext);
|
098 |
iv.setImageResource(mImageIds[position]);
|
099 |
//给生成的ImageView设置Id,不设置的话Id都是-1
|
100 |
iv.setId(mImageIds[position]);
|
101 |
iv.setLayoutParams( new Gallery.LayoutParams( 120 , 160 ));
|
102 |
iv.setScaleType(ImageView.ScaleType.FIT_XY);
|
103 |
iv.setBackgroundResource(mGalleryItemBackground);
|
104 |
return iv;
|
105 |
}
|
106 |
|
107 |
}
|
108 |
} |
我们点击某一张图片,会把该子元素的图片显示在放在后面一层的ImageView组件中。
有兴趣的同学可以了解一下AdapterView的继承关系:
十六、GridView 网格组件