android音乐播放器(Service+ContentProvider+Broadcast+Activity四大组件完成)

1、获取音乐

  1-1:获取手机中的音乐(用ContentProvider内容提供者来完成):

 1 package com.firefly.util;
 2 
 3 import java.util.ArrayList;
 4 import java.util.HashMap;
 5 import java.util.Iterator;
 6 import java.util.List;
 7 
 8 import android.content.Context;
 9 import android.database.Cursor;
10 import android.provider.MediaStore;
11 import android.util.Log;
12 
13 public class MediaUtil {
14     /**
15      * 用于从数据库中查询歌曲的信息,保存在List当中
16      * 
17      * @return
18      */
19     public static List<Mp3Info> getMp3Infos(Context context) {
20         Cursor cursor = context.getContentResolver().query(
21                 MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, null, null, null,
22                 MediaStore.Audio.Media.DEFAULT_SORT_ORDER);
23         List<Mp3Info> mp3Infos = new ArrayList<Mp3Info>();
24 
25         for (int i = 0; i < cursor.getCount(); i++) {
26             cursor.moveToNext();
27             Mp3Info mp3Info = new Mp3Info();
28             long id = cursor.getLong(cursor
29                     .getColumnIndex(MediaStore.Audio.Media._ID)); // 音乐id
30             String title = cursor.getString((cursor
31                     .getColumnIndex(MediaStore.Audio.Media.TITLE))); // 音乐标题
32             String artist = cursor.getString(cursor
33                     .getColumnIndex(MediaStore.Audio.Media.ARTIST)); // 艺术家
34             long duration = cursor.getLong(cursor
35                     .getColumnIndex(MediaStore.Audio.Media.DURATION)); // 时长
36             long size = cursor.getLong(cursor
37                     .getColumnIndex(MediaStore.Audio.Media.SIZE)); // 文件大小
38             String url = cursor.getString(cursor
39                     .getColumnIndex(MediaStore.Audio.Media.DATA)); // 文件路径
40             int isMusic = cursor.getInt(cursor
41                     .getColumnIndex(MediaStore.Audio.Media.IS_MUSIC)); // 是否为音乐
42             if (isMusic != 0) { // 只把音乐添加到集合当中
43 
44                 mp3Info.setId(id);
45                 mp3Info.setTitle(title);
46                 mp3Info.setArtist(artist);
47                 mp3Info.setDuration(duration);
48                 mp3Info.setSize(size);
49                 mp3Info.setUrl(url);
50 
51                 mp3Infos.add(mp3Info);
52             }
53         }
54         return mp3Infos;
55     }
56 
57     /**
58      * 格式化时间,将毫秒转换为分:秒格式
59      * 
60      * @param time
61      * @return
62      */
63     public static String formatTime(long time) {
64         String min = time / (1000 * 60) + "";
65         String sec = time % (1000 * 60) + "";
66         if (min.length() < 2) {
67             min = "0" + time / (1000 * 60) + "";
68         } else {
69             min = time / (1000 * 60) + "";
70         }
71         if (sec.length() == 4) {
72             sec = "0" + (time % (1000 * 60)) + "";
73         } else if (sec.length() == 3) {
74             sec = "00" + (time % (1000 * 60)) + "";
75         } else if (sec.length() == 2) {
76             sec = "000" + (time % (1000 * 60)) + "";
77         } else if (sec.length() == 1) {
78             sec = "0000" + (time % (1000 * 60)) + "";
79         }
80         return min + ":" + sec.trim().substring(0, 2);
81     }
82 }
MediaUtil

  1-2:写实体类来存放获取的音乐数据

 1 package com.firefly.util;
 2 
 3 public class Mp3Info {
 4     public long id; // 音乐id
 5     public String title;// 音乐标题
 6     public String artist;// 艺术家
 7     public long duration; // 时长
 8     public long size;// 文件大小
 9     public String url;// 文件路径
10     public int isMusic;// 是否为音乐
11 
12     public long getId() {
13         return id;
14     }
15 
16     public void setId(long id) {
17         this.id = id;
18     }
19 
20     public String getTitle() {
21         return title;
22     }
23 
24     public void setTitle(String title) {
25         this.title = title;
26     }
27 
28     public String getArtist() {
29         return artist;
30     }
31 
32     public void setArtist(String artist) {
33         this.artist = artist;
34     }
35 
36     public long getDuration() {
37         return duration;
38     }
39 
40     public void setDuration(long duration) {
41         this.duration = duration;
42     }
43 
44     public long getSize() {
45         return size;
46     }
47 
48     public void setSize(long size) {
49         this.size = size;
50     }
51 
52     public String getUrl() {
53         return url;
54     }
55 
56     public void setUrl(String url) {
57         this.url = url;
58     }
59 
60     public int getIsMusic() {
61         return isMusic;
62     }
63 
64     public void setIsMusic(int isMusic) {
65         this.isMusic = isMusic;
66     }
67 
68 }
Mp3Info

  1-3:写适配器来将音乐数据初始化

 1 package com.firefly.util;
 2 
 3 import java.util.List;
 4 
 5 import com.firefly.beautifulmusic.R;
 6 
 7 import android.content.Context;
 8 import android.graphics.Color;
 9 import android.view.LayoutInflater;
10 import android.view.View;
11 import android.view.ViewGroup;
12 import android.widget.BaseAdapter;
13 import android.widget.TextView;
14 
15 public class MusicAdapter extends BaseAdapter {
16     List<Mp3Info> datas;
17     LayoutInflater inflater;
18     int index = -1;
19 
20     // 把当前位置传过来
21     public void setIndex(int index) {
22         this.index = index;
23     }
24 
25     public MusicAdapter(Context context, List<Mp3Info> datas) {
26         this.inflater = LayoutInflater.from(context);
27         this.datas = datas;
28     }
29 
30     @Override
31     public int getCount() {
32         // TODO Auto-generated method stub
33         return datas.size();
34     }
35 
36     @Override
37     public Object getItem(int position) {
38         // TODO Auto-generated method stub
39         return datas.get(position);
40     }
41 
42     @Override
43     public long getItemId(int position) {
44         // TODO Auto-generated method stub
45         return position;
46     }
47 
48     @Override
49     public View getView(int position, View convertView, ViewGroup parent) {
50         ViewHolder holder;
51         if (convertView == null) {
52             convertView = inflater.inflate(R.layout.item_music_list, null);
53             holder = new ViewHolder();
54             holder.title = (TextView) convertView.findViewById(R.id.item_title);
55             holder.duration = (TextView) convertView
56                     .findViewById(R.id.item_duration);
57             holder.artist = (TextView) convertView
58                     .findViewById(R.id.item_artist);
59             convertView.setTag(holder);
60         } else {
61             holder = (ViewHolder) convertView.getTag();
62         }
63         Mp3Info music = datas.get(position);
64         holder.title.setTag(music);
65         holder.title.setText(music.getTitle());
66         holder.artist.setText(music.getArtist());
67         // 获取时间并改变它的格式
68         Long time = music.getDuration();
69         long min = (time / 1000) / 60;
70         long second = (time / 1000) % 60;
71         holder.duration.setText(min + ":" + second);
72         if (position == index) {
73             // 如果正在播放当前项,就改其颜色
74             holder.title.setTextColor(Color.parseColor("#E34C36"));
75             holder.duration.setTextColor(Color.parseColor("#E34C36"));
76             holder.artist.setTextColor(Color.parseColor("#E34C36"));
77         } else {
78             // 否则的话则回到原来的状态
79             holder.title.setTextColor(Color.BLACK);
80             holder.duration.setTextColor(Color.BLACK);
81             holder.artist.setTextColor(Color.BLACK);
82         }
83         return convertView;
84     }
85 
86     static class ViewHolder {
87         public TextView title;
88         public TextView duration;
89         public TextView artist;
90     }
91 
92 }
MusicAdapter

  1-4:(可不参考)用来适配的素材xml

 1 <?xml version="1.0" encoding="utf-8"?>
 2 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
 3     android:layout_width="match_parent"
 4     android:layout_height="match_parent"
 5     android:orientation="horizontal" >
 6 
 7     <LinearLayout
 8         android:layout_width="match_parent"
 9         android:layout_height="match_parent"
10         android:layout_weight="1"
11         android:orientation="vertical" >
12 
13         <TextView
14             android:id="@+id/item_title"
15             android:layout_width="match_parent"
16             android:layout_height="wrap_content"
17             android:text="歌名" />
18 
19         <TextView
20             android:id="@+id/item_artist"
21             android:layout_width="match_parent"
22             android:layout_height="wrap_content"
23             android:text="歌手" />
24     </LinearLayout>
25 
26     <TextView
27         android:id="@+id/item_duration"
28         android:layout_width="match_parent"
29         android:layout_height="match_parent"
30         android:layout_weight="4"
31         android:text="时长" />
32 
33 </LinearLayout>
item_music_list

2、在主页面中进行初始化数据

  2-1:xml页面布局

 1 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
 2     xmlns:tools="http://schemas.android.com/tools"
 3     android:layout_width="match_parent"
 4     android:layout_height="match_parent"
 5     android:orientation="vertical"
 6     tools:context="${relativePackage}.${activityClass}" >
 7 
 8     <ListView
 9         android:id="@+id/lv_musicList"
10         android:layout_width="match_parent"
11         android:layout_height="match_parent"
12         android:layout_weight="1" >
13     </ListView>
14 
15     <LinearLayout
16         android:layout_width="match_parent"
17         android:layout_height="wrap_content"
18         android:gravity="center"
19         android:orientation="horizontal" >
20 
21         <Button
22             android:id="@+id/btnUp"
23             android:layout_width="50dp"
24             android:layout_height="50dp"
25             android:background="@drawable/up"
26             android:onClick="playMusic" />
27 
28         <Button
29             android:id="@+id/btnPlay"
30             android:layout_width="50dp"
31             android:layout_height="50dp"
32             android:layout_marginLeft="20dp"
33             android:layout_marginRight="20dp"
34             android:background="@drawable/pause"
35             android:onClick="playMusic" />
36 
37         <Button
38             android:id="@+id/btnDown"
39             android:layout_width="50dp"
40             android:layout_height="50dp"
41             android:background="@drawable/down"
42             android:onClick="playMusic" />
43     </LinearLayout>
44 
45     <SeekBar
46         android:id="@+id/sb"
47         android:layout_width="match_parent"
48         android:layout_height="wrap_content" />
49 
50 </LinearLayout>
activity_main

  2-2:在其相对应的Activiity文件中初始化数据

  1 package com.firefly.beautifulmusic;
  2 
  3 import java.io.File;
  4 import java.util.List;
  5 
  6 import android.app.Activity;
  7 import android.content.BroadcastReceiver;
  8 import android.content.ComponentName;
  9 import android.content.Context;
 10 import android.content.Intent;
 11 import android.content.IntentFilter;
 12 import android.content.ServiceConnection;
 13 import android.graphics.Color;
 14 import android.os.Bundle;
 15 import android.os.IBinder;
 16 import android.telephony.TelephonyManager;
 17 import android.util.Log;
 18 import android.view.View;
 19 import android.widget.AdapterView;
 20 import android.widget.AdapterView.OnItemClickListener;
 21 import android.widget.Button;
 22 import android.widget.ListView;
 23 import android.widget.SeekBar;
 24 import android.widget.TextView;
 25 import android.widget.Toast;
 26 
 27 import com.firefly.service.MusicService;
 28 import com.firefly.service.MusicService.MyBinder;
 29 import com.firefly.util.MediaUtil;
 30 import com.firefly.util.Mp3Info;
 31 import com.firefly.util.MusicAdapter;
 32 
 33 public class MainActivity extends Activity {
 34     ServiceConnection conn;
 35     MusicService ms;
 36     String music_name;
 37     File f;
 38     Boolean isPlay = false;
 39     ListView lv;
 40     MusicAdapter adapter;
 41     List<Mp3Info> datas;
 42     Button btn;
 43     int current;
 44     SeekBar sb;
 45 
 46     @Override
 47     protected void onCreate(Bundle savedInstanceState) {
 48         super.onCreate(savedInstanceState);
 49         setContentView(R.layout.activity_main);
 50         //初始化数据
 51         init();
 52     }
 53 
 54     private void init() {
 55         btn = (Button) findViewById(R.id.btnPlay);
 56         lv = (ListView) findViewById(R.id.lv_musicList);
 57         //获取手机中 的音乐数据
 58         datas = MediaUtil.getMp3Infos(MainActivity.this);
 59         sb = (SeekBar) findViewById(R.id.sb);
 60         adapter = new MusicAdapter(MainActivity.this, datas);
 61         lv.setAdapter(adapter);
 62         lv.setOnItemClickListener(new OnItemClickListener() {
 63             @Override
 64             public void onItemClick(AdapterView<?> parent, View view,
 65                     int position, long id) {
 66                 //点击哪一项就播放哪一项(前提条件是它是音乐文件)
 67                 current = position;
 68                 f = new File(datas.get(current).getUrl());
 69                 if (f.exists() && f.length() > 0) {
 70                     play();
 71                 } else {
 72                     Toast.makeText(getApplicationContext(), "该文件不是音乐", 0)
 73                             .show();
 74                 }
 75             }
 76         });
 77         // 注册广播
 78         broadcast();
 79         // 服务
 80         service();
 81     }
 82     
 83     private void broadcast() {
 84         // TODO Auto-generated method stub
 85         MusicBroadcast mb = new MusicBroadcast();
 86         IntentFilter ifilter = new IntentFilter();
 87         ifilter.addAction("com.firefly.music");
 88         ifilter.addAction("android.intent.action.PHONE_STATE");
 89         registerReceiver(mb, ifilter);
 90         Log.e("TAG", "动态注册成功");
 91     }
 92 
 93     private void service() {
 94         // TODO Auto-generated method stub
 95         conn = new ServiceConnection() {
 96 
 97             @Override
 98             public void onServiceDisconnected(ComponentName name) {
 99                 // TODO Auto-generated method stub
100 
101             }
102 
103             @Override
104             public void onServiceConnected(ComponentName name, IBinder service) {
105                 // TODO Auto-generated method stub
106                 MyBinder binder = (MyBinder) service;
107                 ms = binder.getService();
108                 ms.setSeekBar(sb);
109             }
110         };
111 
112         // 绑定服务
113         Intent i = new Intent(this, MusicService.class);
114         bindService(i, conn, BIND_AUTO_CREATE);
115     }
116 
117     public void playMusic(View v) {
118         switch (v.getId()) {
119         case R.id.btnPlay:
120             if (isPlay == false) {
121                 play();
122             } else {
123                 pause();
124             }
125             break;
126         // 上一首
127         case R.id.btnUp:
128             if (isPlay) {
129                 if ((current - 1) >= 0) {
130                     --current;
131                     f = new File(datas.get(current).getUrl());
132                     play();
133                 } else {
134                     Toast.makeText(getApplicationContext(), "已经是第一首了", 0).show();
135                 }
136             }
137             break;
138         // 下一首
139         case R.id.btnDown:
140             if ((current + 1) < datas.size()) {
141                 ++current;
142                 f = new File(datas.get(current).getUrl());
143                 play();
144             } else {
145                 Toast.makeText(getApplicationContext(), "已经是最后一首了", 0).show();
146             }
147             break;
148 
149         default:
150             break;
151         }
152     }
153     
154     //播放
155     public void play() {
156         //调用服务中的播放方法
157         ms.PlayMusic(f.getAbsolutePath());
158         btn.setBackgroundResource(R.drawable.play);
159         isPlay = true;
160         // 如果在播放中,就改变它的样式
161         adapter.setIndex(current);
162         adapter.notifyDataSetChanged();
163     }
164     
165     //暂停
166     public void pause() {
167         //调用服务中的暂停方法
168         ms.PauseMusic();
169         btn.setBackgroundResource(R.drawable.pause);
170         isPlay = false;
171     }
172 
173     // 新建广播
174     public class MusicBroadcast extends BroadcastReceiver {
175 
176         @Override
177         public void onReceive(Context context, Intent intent) {
178             // 接收广播
179             if (intent.getAction() == "com.firefly.music") {
180                 if (intent.getStringExtra("end").equals("播放完毕")) {
181                     if ((current + 1) < datas.size()) {
182                         //如果播放完毕了,就播放下一首
183                         f = new File(datas.get(++current).getUrl());
184                         play();
185                     } else {
186                         Toast.makeText(getApplicationContext(), "已经是最后一首了", 0)
187                                 .show();
188                     }
189                 }
190             } else if (intent.getAction().equals(Intent.ACTION_NEW_OUTGOING_CALL)) {
191                 //电话监听事件,如果要拨打电话就暂停音乐
192                 String rs = getResultData();
193                 Toast.makeText(context, "你在拨打" + rs + "的电话了,现在我已经暂停音乐了。", 0).show();
194                 pause();
195             } else {
196                 //来电也要暂停音乐
197                 TelephonyManager telMan = (TelephonyManager) context
198                         .getSystemService(Context.TELEPHONY_SERVICE);
199                 // 判断电话的状态
200                 switch (telMan.getCallState()) {
201                 case TelephonyManager.CALL_STATE_RINGING:
202                     Log.e("TAG", "响铃");
203                     Toast.makeText(getApplicationContext(), "来电话了", 0).show();
204                     pause();
205                     break;
206 
207                 case TelephonyManager.CALL_STATE_IDLE:
208                     Log.e("TAG", "挂断");
209                     Toast.makeText(getApplicationContext(), "您已经挂断,音乐将继续播放", 0).show();
210                     play();
211                     break;
212 
213                 case TelephonyManager.CALL_STATE_OFFHOOK:
214                     Log.e("TAG", "接听");
215                     Toast.makeText(getApplicationContext(), "您正在接收电话,音乐已经暂停", 0).show();
216                     pause();
217                     break;
218 
219                 default:
220                     break;
221                 }
222             }
223         }
224 
225     }
226 }
View Code

  2-3:写一个接口类,统一管理服务(也可不写)

 1 package com.firefly.service;
 2 
 3 public interface IMusic {
 4     // 新建一个接口,能用服务来实现它,在这里可以统一管理
 5     public void PlayMusic(String music_name);
 6 
 7     public void PauseMusic();
 8 
 9     public void PreviousMusic();
10 
11     public void NextMusic();
12 }
IMusic

  2-4:服务类

  1 package com.firefly.service;
  2 
  3 import android.app.Service;
  4 import android.content.Intent;
  5 import android.media.AudioManager;
  6 import android.media.MediaPlayer;
  7 import android.media.MediaPlayer.OnCompletionListener;
  8 import android.media.MediaPlayer.OnPreparedListener;
  9 import android.os.Binder;
 10 import android.os.Handler;
 11 import android.os.IBinder;
 12 import android.os.Message;
 13 import android.util.Log;
 14 import android.widget.SeekBar;
 15 import android.widget.SeekBar.OnSeekBarChangeListener;
 16 import android.widget.Toast;
 17 
 18 public class MusicService extends Service implements IMusic {
 19     MediaPlayer music;
 20     String music_name;
 21     SeekBar sb;
 22     Handler handler;
 23     boolean flag = false;// 控制线程的播放
 24 
 25     @Override
 26     public void PlayMusic(String music_name) {
 27         this.music_name = this.music_name;
 28         if (music != null && music.isPlaying()) {
 29             // 如果在播放的状态下切换到下一首,先把原来的停止、释放、置空,再新建
 30             music.stop();
 31             music.release();
 32             music = null;
 33             try {
 34                 music = new MediaPlayer();
 35                 music.setAudioStreamType(AudioManager.STREAM_MUSIC);
 36                 music.setDataSource(music_name);
 37                 music.prepare();
 38                 music.start();
 39             } catch (Exception e) {
 40                 // TODO Auto-generated catch block
 41                 e.printStackTrace();
 42             }
 43         } else {
 44 
 45             if (music != null) {// 这个状态可能是暂停可能是停止
 46                 // 如果只是把它暂停,重新开始就可以了
 47                 music.start();
 48             } else {
 49                 // 第一次播放这首音乐
 50                 try {
 51                     music = new MediaPlayer();
 52                     music.setAudioStreamType(AudioManager.STREAM_MUSIC);
 53                     music.setDataSource(music_name);
 54                     music.prepareAsync();
 55                     // 准备监听事件
 56                     music.setOnPreparedListener(new OnPreparedListener() {
 57                         @Override
 58                         public void onPrepared(MediaPlayer mp) {
 59                             music.start();
 60                             // 设置SeekBar
 61                             int max = music.getDuration();
 62                             int cur = music.getCurrentPosition();
 63                             Log.e("TAG", max + "==" + cur);
 64                             sb.setMax(max);
 65                             sb.setProgress(cur);
 66                             sb.setEnabled(true);
 67                         }
 68                     });
 69                     // 音乐播放完毕的监听器
 70                     music.setOnCompletionListener(new OnCompletionListener() {
 71 
 72                         @Override
 73                         public void onCompletion(MediaPlayer mp) {
 74                             music.stop();
 75                             music.release();
 76                             music = null;
 77                             // 发送一个广播,已经播放完毕了
 78                             Intent i = new Intent();
 79                             i.setAction("com.firefly.music");
 80                             i.putExtra("end", "播放完毕");
 81                             sendBroadcast(i);
 82 
 83                         }
 84                     });
 85                 } catch (Exception e) {
 86                     e.printStackTrace();
 87                 }
 88             }
 89             flag = true;
 90         }
 91         // 这个线程用来控制进度条的更新
 92         Thread th = new Thread(new Runnable() {
 93 
 94             @Override
 95             public void run() {
 96                 while (flag) {
 97                     try {
 98                         Thread.sleep(200);
 99                     } catch (InterruptedException e) {
100                         // TODO Auto-generated catch block
101                         e.printStackTrace();
102                     }
103                     Message msg = new Message();
104                     msg.what = 0;
105                     handler.sendMessage(msg);
106                 }
107             }
108         });
109         th.start();
110 
111     }
112 
113     @Override
114     public void PauseMusic() {
115         // TODO Auto-generated method stub
116         if (music != null && music.isPlaying()) {
117             music.pause();
118             flag = false;
119         }
120     }
121 
122     @Override
123     public void PreviousMusic() {
124         // TODO Auto-generated method stub
125 
126     }
127 
128     @Override
129     public void NextMusic() {
130         // TODO Auto-generated method stub
131 
132     }
133 
134     public void setSeekBar(final SeekBar sb) {
135         this.sb = sb;
136         sb.setEnabled(false);// 没开始播放的时候进度条是不能拖动的
137         sb.setOnSeekBarChangeListener(new OnSeekBarChangeListener() {
138 
139             @Override
140             public void onStopTrackingTouch(SeekBar seekBar) {
141                 // TODO Auto-generated method stub
142                 if (music != null) {
143                     music.seekTo(seekBar.getProgress());
144                 }
145             }
146 
147             @Override
148             public void onStartTrackingTouch(SeekBar seekBar) {
149                 // TODO Auto-generated method stub
150 
151             }
152 
153             @Override
154             public void onProgressChanged(SeekBar seekBar, int progress,
155                     boolean fromUser) {
156                 // TODO Auto-generated method stub
157 
158             }
159         });
160 
161         handler = new Handler() {
162             @Override
163             public void handleMessage(Message msg) {
164                 switch (msg.what) {
165                 case 0:
166                     if (music != null) {
167                         // 播放和SeekBar同步
168                         sb.setProgress(music.getCurrentPosition());
169                     }
170                     break;
171 
172                 default:
173                     break;
174                 }
175             }
176 
177         };
178 
179     }
180 
181     @Override
182     public IBinder onBind(Intent intent) {
183         // TODO Auto-generated method stub
184         return new MyBinder();
185     }
186 
187     public class MyBinder extends Binder {
188         public MusicService getService() {
189             return MusicService.this;
190         }
191     }
192 
193 }
MusicService

3、清单文件

 1 <?xml version="1.0" encoding="utf-8"?>
 2 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
 3     package="com.firefly.beautifulmusic"
 4     android:versionCode="1"
 5     android:versionName="1.0" >
 6 
 7     <uses-sdk
 8         android:minSdkVersion="14"
 9         android:targetSdkVersion="19" />
10     <!-- 读写SD卡的权限、读取电话状态的权限 -->
11     <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
12     <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
13     <uses-permission android:name="android.permission.READ_PHONE_STATE" />
14 
15     <application
16         android:allowBackup="true"
17         android:icon="@drawable/ic_launcher"
18         android:label="@string/app_name"
19         android:theme="@style/AppTheme" >
20         <activity
21             android:name=".MainActivity"
22             android:label="@string/app_name" >
23             <intent-filter>
24                 <action android:name="android.intent.action.MAIN" />
25 
26                 <category android:name="android.intent.category.LAUNCHER" />
27             </intent-filter>
28         </activity>
29 
30         <service android:name="com.firefly.service.MusicService" >
31         </service>
32     </application>
33 
34 </manifest>
Manifest

4、效果图

种一棵树最早的时间是十年前,其次是现在。
原文地址:https://www.cnblogs.com/firefly-pengdan/p/5506259.html