实现功能:
实现网络音乐搜索功能
实现网络音乐下载功能
下载好的音乐目前不在播放器内,可以通过文件浏览器查看。
后续将博文,将实现歌词和下载音乐扫描功能。
实现效果如图:
实现代码如下:
NetMusicListFragment如下:
package com.iwanghang.drmplayer;
import android.content.Context;
import android.os.AsyncTask;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.EditText;
import android.widget.ImageButton;
import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.Toast;
import com.iwanghang.drmplayer.adapter.NetMusicListAdapter;
import com.iwanghang.drmplayer.utils.AppUtils;
import com.iwanghang.drmplayer.utils.Constant;
import com.iwanghang.drmplayer.utils.SearchMusicUtils;
import com.iwanghang.drmplayer.vo.SearchResult;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.select.Elements;
import java.io.IOException;
import java.util.ArrayList;
public class NetMusicListFragment extends Fragment implements OnItemClickListener,OnClickListener {
private ListView listView_net_music;
private LinearLayout load_layout;
private LinearLayout ll_search_btn_container;//查询按钮的容器
private LinearLayout ll_search_container;//查询按钮的容器
private ImageButton ib_search_btn;
private EditText et_search_content;
private NetMusicListAdapter netMusicListAdapter;
//存放 网络音乐 的集合
private ArrayList searchResults = new ArrayList();
private int page = 1;//搜索音乐的页码
private MainActivity mainActivity;
//private boolean isPause = false;//歌曲播放中的暂停状态
private int position = 0;//当前播放的位置,提供给PlayActivity
//onAttach(),当fragment被绑定到activity时被调用(Activity会被传入.).
@Override
public void onAttach(Context context) {
super.onAttach(context);
//mainActivity = (MainActivity) context;
mainActivity = (MainActivity) getActivity();
}
public static NetMusicListFragment newInstance() {
NetMusicListFragment net = new NetMusicListFragment();
return net;
}
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
//UI组件初始化
View view = inflater.inflate(R.layout.net_music_list_layout,null);
//item
listView_net_music = (ListView) view.findViewById(R.id.listView_net_music);
//findViewById
load_layout = (LinearLayout) view.findViewById(R.id.load_layout);
ll_search_btn_container = (LinearLayout) view.findViewById(R.id.ll_search_btn_container);
ll_search_container = (LinearLayout) view.findViewById(R.id.ll_search_container);
ib_search_btn = (ImageButton) view.findViewById(R.id.ib_search_btn);
et_search_content = (EditText) view.findViewById(R.id.et_search_content);
//Item点击事件监听
listView_net_music.setOnItemClickListener(this);
//按钮点击事件监听
ll_search_btn_container.setOnClickListener(this);
ib_search_btn.setOnClickListener(this);
loadNetData();//加载网络音乐
return view;
}
private void loadNetData() {
load_layout.setVisibility(View.VISIBLE);
//加载网络音乐的异步任务
new LoadNetDataTask().execute(Constant.MIGU_CHINA);
}
@Override
public void onClick(View v) {
switch (v.getId()){
case R.id.ll_search_btn_container:
ll_search_btn_container.setVisibility(View.GONE);
ll_search_container.setVisibility(View.VISIBLE);
break;
case R.id.ib_search_btn:
//搜索事件
searchMusic();
break;
}
}
//搜索音乐
private void searchMusic() {
//隐藏键盘
AppUtils.hideInputMethod(et_search_content);
ll_search_btn_container.setVisibility(View.VISIBLE);
ll_search_container.setVisibility(View.GONE);
//获取输入的文字
String key = et_search_content.getText().toString();
if (TextUtils.isEmpty(key)){//如果为空,则,Toast提示
Toast.makeText(mainActivity,"请输入歌手或歌词",Toast.LENGTH_SHORT).show();
return;
}
load_layout.setVisibility(View.VISIBLE);//加载layout效果.显示
//填充item 使用SearchMusicUtils搜索音乐工具类,并,使用观察者设计模式,自己回调,自己监听
SearchMusicUtils.getInstance().setListener(new SearchMusicUtils.OnSearchResultListener(){
@Override
public void onSearchResult(ArrayList results) {
ArrayList sr = netMusicListAdapter.getSearchResults();
sr.clear();
sr.addAll(results);
netMusicListAdapter.notifyDataSetChanged();//更新网络音乐列表
load_layout.setVisibility(View.GONE);
}
}).search(key,page);
}
//列表项的单击事件
@Override
public void onItemClick(AdapterView parent, View view, int position, long id) {
if (position >= netMusicListAdapter.getSearchResults().size() || position < 0) return;
showDownloadDialog(position);
}
//下载弹窗
private void showDownloadDialog(final int position){
DownloadDialogFragment downloadDialogFragment = DownloadDialogFragment.newInstance(searchResults.get(position));
downloadDialogFragment.show(getFragmentManager(),"download");
}
//加载网络音乐的异步任务
//Android1.5提供了 工具类 android.os.AsyncTask,它使创建异步任务变得更加简单,不再需要编写任务线程和Handler实例即可完成相同的任务。
class LoadNetDataTask extends AsyncTask{
//onPreExecute方法用于在执行后台任务前做一些UI操作
@Override
protected void onPreExecute() {
super.onPreExecute();
load_layout.setVisibility(View.VISIBLE);//加载layout.显示
listView_net_music.setVisibility(View.GONE);//item.隐藏
searchResults.clear();//搜索结果.清理
}
//doInBackground方法内部执行后台任务,不可在此方法内修改UI
@Override
protected Integer doInBackground(String... params) {
String url = params[0];
try {
//使用Jsoup组件请求网络,并解析音乐数据
Document doc = Jsoup.connect(url).userAgent(Constant.USER_AGENT).timeout(6000).get();
System.out.println("start**********doc**********doc**********doc**********");
System.out.println(doc);
System.out.println(" end **********doc**********doc**********doc**********");
//从doc分析以上html代码;把所有span标签下fl song_name查找出来,存在songTitles集合中;即,歌名集合;
//从doc分析以上html代码;把所有span标签下fl singer_name.mr5t查找出来,存在artists集合中;即,歌手集合;
Elements songTitles = doc.select("span.fl.song_name");
System.out.println(songTitles);
Elements artists = doc.select("span.fl.singer_name.mr5");
System.out.println(artists);
for (int i=0;i半壶纱<,半壶纱就是a链接的内容);即,最终的歌名;
Elements urls = songTitles.get(i).getElementsByTag("a");
//System.out.println("@urls : " + urls);
searchResult.setUrl(urls.get(0).attr("href"));//设置最终的url
searchResult.setMusicName(urls.get(0).text());//设置最终的歌名
//a链接,存在urls集合中;即,歌曲url集合;
Elements artistElements = artists.get(i).getElementsByTag("a");
//System.out.println("@artistElements : " + artistElements);
searchResult.setArtist(artistElements.get(0).text());//设置最终的歌手
searchResult.setAlbum("华语榜");//设置最终的专辑
System.out.println("@searchResult : " + searchResult);
searchResults.add(searchResult);//把最终的所有信息,添加到集合
}
System.out.println("@searchResults : " + searchResults);
//System.out.println("@songTitles.size() : " + searchResults.size());
} catch (IOException e) {
e.printStackTrace();
return -1;
}
return 1;
}
//onPostExecute方法用于在执行完后台任务后更新UI,显示结果
@Override
protected void onPostExecute(Integer result) {
super.onPostExecute(result);
if (result==1){
netMusicListAdapter = new NetMusicListAdapter(mainActivity,searchResults);
//System.out.println(searchResults);
listView_net_music.setAdapter(netMusicListAdapter);
listView_net_music.addFooterView(LayoutInflater.from(mainActivity).inflate(R.layout.footviwe_layout,null));
}
load_layout.setVisibility(View.GONE);
listView_net_music.setVisibility(View.VISIBLE);
}
}
}
DownloadDialogFragment如下:
package com.iwanghang.drmplayer;
import android.app.AlertDialog;
import android.app.Dialog;
import android.support.v4.app.DialogFragment;
import android.content.Context;
import android.content.DialogInterface;
import android.os.Bundle;
import android.widget.Toast;
import com.iwanghang.drmplayer.utils.DownloadUtils;
import com.iwanghang.drmplayer.vo.SearchResult;
/**
* Created by iwanghang on 16/5/3.
* DownloadDialogFargment
*/
public class DownloadDialogFragment extends DialogFragment {
private SearchResult searchResult;//当前要下载的歌曲对象
private MainActivity mainActivity;
public static DownloadDialogFragment newInstance(SearchResult searchResult){
DownloadDialogFragment downloadDialogFragment = new DownloadDialogFragment();
downloadDialogFragment.searchResult = searchResult;
return downloadDialogFragment;
}
private String[] items;
@Override
public void onAttach(Context context) {
super.onAttach(context);
mainActivity = (MainActivity) getActivity();
//items = new String[]{"下载","取消"};
items = new String[]{getString(R.string.download),getString(R.string.cancel)};
}
//创建对话框的事件方法
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
final AlertDialog.Builder builder = new AlertDialog.Builder(mainActivity);
builder.setCancelable(true);//返回键可以取消
builder.setItems(items,new DialogInterface.OnClickListener(){
@Override
public void onClick(DialogInterface dialog, int which) {
switch (which) {
case 0:
//执行下载
downloadMusic();
break;
case 1://取消
dialog.dismiss();
break;
}
}
});
return builder.show();
}
//下载音乐
private void downloadMusic() {
Toast.makeText(mainActivity, "正在下载:" + searchResult.getMusicName(), Toast.LENGTH_SHORT).show();
DownloadUtils.getsInstance().setListener(new DownloadUtils.OnDownloadListener() {
@Override
public void onDowload(String mp3Url) { //下载成功
Toast.makeText(mainActivity, "下载成功", Toast.LENGTH_SHORT).show();
}
@Override
public void onFailed(String error) { //下载失败
Toast.makeText(mainActivity, error, Toast.LENGTH_SHORT).show();
}
}).download(searchResult);
}
}
DownloadUtils如下:
package com.iwanghang.drmplayer.utils;
import android.os.Environment;
import android.os.Handler;
import android.os.Message;
import com.iwanghang.drmplayer.vo.SearchResult;
import com.squareup.okhttp.OkHttpClient;
import com.squareup.okhttp.Request;
import com.squareup.okhttp.Response;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.select.Elements;
import java.io.File;
import java.io.IOException;
import java.io.PrintStream;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import javax.xml.parsers.ParserConfigurationException;
/**
* Created by iwanghang on 16/5/3.
* 网络音乐下载工具类
*/
public class DownloadUtils {
private static final String DOWNLOAD_URL = "/download?_o=%2Fsearch%2Fsong";
private static final int SUCCESS_LRC = 1;//下载歌词成功
private static final int FAILED_LRC = 2;//下载歌词失败
private static final int SUCCESS_MP3 = 3;//下载歌曲成功
private static final int FAILED_MP3 = 4;//下载歌曲失败
private static final int GET_MP3_URL = 5;//获取音乐下载地址成功
private static final int GET_FAILED_MP3_URL = 6;//获取音乐下载地址失败
private static final int MUSIC_EXISTS = 7;//下载时,音乐已存在
private static DownloadUtils sInstance;
private OnDownloadListener mListener;
private ExecutorService mThreadPool;
/**
*设置回调监听器对象
* @param mListener
* @return
*/
public DownloadUtils setListener(OnDownloadListener mListener){
this.mListener = mListener;
return this;
}
//获取下载工具的实例
public synchronized static DownloadUtils getsInstance(){
if (sInstance == null){
try {
sInstance = new DownloadUtils();
} catch (ParserConfigurationException e) {
e.printStackTrace();
}
}
return sInstance;
}
/**
* 下载的具体业务方法
* @throws ParserConfigurationException
*/
private DownloadUtils() throws ParserConfigurationException{
mThreadPool = Executors.newSingleThreadExecutor();
}
public void download(final SearchResult searchResult){
final Handler handler = new Handler(){
@Override
public void handleMessage(Message msg) {
switch (msg.what){
case SUCCESS_LRC:
if (mListener != null) mListener.onDowload("歌词下载成功");
break;
case FAILED_LRC:
if (mListener != null) mListener.onFailed("歌词下载失败");
break;
case GET_MP3_URL:
System.out.println("GET_MP3_URL:"+msg.obj);
downloadMusic(searchResult,(String)msg.obj,this);
break;
case GET_FAILED_MP3_URL:
if (mListener != null) mListener.onFailed("下载失败,该歌曲为收费或VIP类型");
break;
case SUCCESS_MP3:
if (mListener != null) mListener.onDowload(searchResult.getMusicName()+"已经下载");
String url = Constant.MIGU_URL + searchResult.getUrl();
System.out.println("download lrc:"+url);
downloadLRC(url,searchResult.getMusicName(),this);
break;
case FAILED_MP3:
if (mListener != null) mListener.onFailed(searchResult.getMusicName()+"下载失败");
break;
case MUSIC_EXISTS:
if (mListener != null) mListener.onFailed("音乐已存在");
break;
}
}
};
getDownloadMusicURL(searchResult,handler);
}
//获取下载音乐的URL
private void getDownloadMusicURL(final SearchResult searchResult, final Handler handler) {
mThreadPool.execute(new Runnable() {
@Override
public void run() {
//https://music.xxxx.cn/#/album/1003215276
//1003215279
//https://music.xxxx.cn/order/1003215279/down/self/P2Z3Y12L1N2/3/001002A/1003215279
System.out.println("searchResult.getUrl() = " + searchResult.getUrl());
String[] aa = searchResult.getUrl().split("/");
String sn = aa[5];
System.out.println("歌曲编号 = " + sn);
String url = Constant.MIGU_DOWN_HEAD + sn + Constant.MIGU_DOWN_FOOT;
System.out.println("下载页面url = " + url);
try {
Document doc = Jsoup.connect(url).userAgent(Constant.USER_AGENT).timeout(6000).get();
//System.out.println(doc);
System.out.println("doc.toString() = " + doc.toString());
String[] bb = doc.toString().split("song");//把 下载页面源码 按照"song"分割
for (int i=0;i0){
System.out.println("mp3?msisdn = " + bb[i]);
String initMp3Url = bb[i];//initMp3Url 初始Mp3下载链接,如下
//mp3?msisdn = ":"https://tyst.xxxx.cn/public/ringmaker01/10月31日中文延期/文件/全套格式/9000首/全曲试听/Mp3_128_44_16/一起走过的日子-刘德华.mp3?msisdn\u003d7b609763f0ff","
String[] arrayHttp = initMp3Url.split("http");//把 初始Mp3下载链接 按照"http"分割
String[] arrayMp3 = arrayHttp[1].split(".mp3");//把 arrayHttp 按照".mp3"分割
String result = "http" + arrayMp3[0] + ".mp3";//把分割去掉的"http"和".mp3",组合回来
System.out.println("result");
//String result = "https://tyst.xxxx.cn/public/ringmaker01/10月31日中文延期/文件/全套格式/9000首/全曲试听/Mp3_128_44_16/一起走过的日子-刘德华.mp3";
Message msg = handler.obtainMessage(GET_MP3_URL,result);
msg.sendToTarget();
}
}
} catch (IOException e) {
e.printStackTrace();
handler.obtainMessage(GET_FAILED_MP3_URL).sendToTarget();
}
}
});
}
//下载歌词
private void downloadLRC(final String url, final String musicName,final Handler handler){
mThreadPool.execute(new Runnable() {
@Override
public void run() {
try {
Document doc = Jsoup.connect(url).userAgent(Constant.USER_AGENT).timeout(6000).get();
Elements lrcTag = doc.select("p.lyric-content");
String lrcURL = lrcTag.attr("data-lrclink");
//System.out.println(lrcURL);
File lrcDirFile = new File(Environment.getExternalStorageDirectory() + Constant.DIR_LRC);
if (!lrcDirFile.exists()){
lrcDirFile.mkdirs();
}
lrcURL = Constant.MIGU_URL + lrcURL;
String target = lrcDirFile + "/" + musicName + ".lrc";
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder().url(lrcURL).build();
try {
Response response = client.newCall(request).execute();
if (response.isSuccessful()){
PrintStream ps = new PrintStream(new File(target));
byte[] bytes = response.body().bytes();
ps.write(bytes,0,bytes.length);
ps.close();
handler.obtainMessage(SUCCESS_LRC,target).sendToTarget();
}
}catch (IOException e){
e.printStackTrace();
handler.obtainMessage(FAILED_LRC).sendToTarget();
}
} catch (IOException e) {
e.printStackTrace();
}
}
});
}
//下载MP3
private void downloadMusic(final SearchResult searchResult,final String url,final Handler handler){
mThreadPool.execute(new Runnable() {
@Override
public void run() {
File musicDirFile = new File(Environment.getExternalStorageDirectory()+Constant.DIR_MUSIC);
if (!musicDirFile.exists()){
musicDirFile.mkdirs();
}
String mp3url = url;
String target = musicDirFile + "/" + searchResult.getMusicName() + ".mp3";
System.out.println(mp3url);
System.out.println(target);
File fileTarget = new File(target);
if (fileTarget.exists()){
handler.obtainMessage(MUSIC_EXISTS).sendToTarget();
return;
}else {
//使用OkHttpClient组件
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder().url(mp3url).build();
System.out.println(request);
try {
Response response = client.newCall(request).execute();
if (response.isSuccessful()){
PrintStream ps = new PrintStream(fileTarget);
byte[] bytes = response.body().bytes();
ps.write(bytes,0,bytes.length);
ps.close();
handler.obtainMessage(SUCCESS_MP3).sendToTarget();
}
} catch (IOException e) {
e.printStackTrace();
handler.obtainMessage(FAILED_MP3).sendToTarget();
}
}
}
});
}
//自定义下载事件监听器
public interface OnDownloadListener {
public void onDowload(String mp3Url);
public void onFailed(String error);
}
}
SearchMusicUtils如下:
package com.iwanghang.drmplayer.utils;
import android.os.Handler;
import android.os.Message;
import com.iwanghang.drmplayer.vo.SearchResult;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.select.Elements;
import java.io.IOException;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import javax.xml.parsers.ParserConfigurationException;
/**
* Created by iwanghang on 16/4/30.
* 搜索音乐工具类
*/
public class SearchMusicUtils {
private static final int SIZE = 20;//查询歌曲数量
//private static final String URL = Constant.BAIDU_URL + Constant.BAIDU_SEARCH;
//private static String URL = Constant.BAIDU_URL + Constant.BAIDU_SEARCH;
private static SearchMusicUtils sInstance;
private OnSearchResultListener mListener;
private ExecutorService mThreadPool;//线程池
public synchronized static SearchMusicUtils getInstance(){
if (sInstance == null){
try {
sInstance = new SearchMusicUtils();
} catch (ParserConfigurationException e) {
e.printStackTrace();
}
}
return sInstance;
}
//设置搜索事件
private SearchMusicUtils() throws ParserConfigurationException{
mThreadPool = Executors.newSingleThreadExecutor();//单任务线程池
}
public SearchMusicUtils setListener(OnSearchResultListener l){
mListener = l;
return this;
}
public void search(final String key,final int page){
final Handler handler = new Handler(){//handler 用于返回数据
@Override
public void handleMessage(Message msg) {
switch (msg.what){
case Constant.SUCCESS:
if (mListener != null) mListener.onSearchResult((ArrayList)msg.obj);
break;
case Constant.FAILED:
if (mListener != null) mListener.onSearchResult(null);
break;
}
}
};
//执行线程
mThreadPool.execute(new Runnable() {
@Override
public void run() {//run方法
ArrayList results = getMusicList(key,page);
if(results == null){
handler.sendEmptyMessage(Constant.FAILED);
return;
}
handler.obtainMessage(Constant.SUCCESS,results).sendToTarget();
}
});
}
//使用Jsoup组件请求网络,并解析音乐数据
private ArrayList getMusicList(final String key,final int page){
final String start = String.valueOf((page - 1) * SIZE);
ArrayList searchResults = new ArrayList();
Document doc = null;
String URL = null;
try {
//经过测试 获取页面 如果key不转码 无法打开正确连接
//使用URLEncoder.encode转码,转为utf8
//冰雨 转换为 %E6%B2%A1%E6%9C%89";
String keyUrlEnCode = URLEncoder.encode(key, "utf8");
URL = Constant.MIGU_SEARCH_HEAD + keyUrlEnCode + Constant.MIGU_SEARCH_FOOT;
System.out.println(URL);
doc = Jsoup.connect(URL)
.data("query", "Java")
.userAgent(Constant.USER_AGENT)
.timeout(60 * 1000).get();
//System.out.println("~~doc = " + doc);
Elements songTitles = doc.select("span.fl.song_name");
System.out.println(songTitles);
Elements artists = doc.select("span.fl.singer_name.mr5");
System.out.println(artists);
for (int i=0;i半壶纱<,半壶纱就是a链接的内容);即,最终的歌名;
Elements urls = songTitles.get(i).getElementsByTag("a");
//System.out.println("@urls : " + urls);
searchResult.setUrl(urls.get(0).attr("href"));//设置最终的url
searchResult.setMusicName(urls.get(0).text());//设置最终的歌名
//a链接,存在urls集合中;即,歌曲url集合;
Elements artistElements = artists.get(i).getElementsByTag("a");
//System.out.println("@artistElements : " + artistElements);
searchResult.setArtist(artistElements.get(0).text());//设置最终的歌手
searchResult.setAlbum("华语榜");//设置最终的专辑
System.out.println("@searchResult : " + searchResult);
searchResults.add(searchResult);//把最终的所有信息,添加到集合
}
System.out.println("@searchResults : " + searchResults);
return searchResults;
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
public interface OnSearchResultListener {
public void onSearchResult(ArrayList results);
}
}