上次使用xstream解析完百度的免费音乐接口之后才发现那个接口局限性太大,无法满足需求,故又将“魔爪”伸向了虾米的音乐接口。
百度了很久,发现虾米音乐接口地址五花八门,都是能用的,也不太清楚怎么回事哈。。我这里就挑一个感觉比较好用的。
首先还是先上地址:
http://kuang.xiami.com/app/nineteen/search/key/customKey/diandian/1/page/customPage?_=0&callback=;
参数一共有四个,但其实需要管的就是关键字和页数就完了~然后用关键字替换customKey,用页码替换customPage就好。
例如
http://kuang.xiami.com/app/nineteen/search/key/周杰伦/diandian/1/page/1?_=0&callback=;
callback可以等于一个返回的方法名,具体怎样自己打一个名字上去就知道了,例如callback=mycallback
但一般也不需要,所以地址也理所当然可以直接简化为
http://kuang.xiami.com/app/nineteen/search/key/周杰伦/diandian/1/page/1?_=0
甚至于可以简化为
http://kuang.xiami.com/app/nineteen/search/key/周杰伦
大家自己试试就行了,不过给多点参数也不是什么麻烦事对吧。。跑题了~
然后我们来看看返回的内容
然后是对gson的使用,要将这个json转化为bean实在太太太太简单了!首先还是两个bean
package com.jlmusicplayer.domain; import java.util.List; public class ReturnJson { private int total; private List<Results> results; public List<Results> getResults() { return results; } public void setResults(List<Results> results) { this.results = results; } public int getTotal() { return total; } public void setTotal(int total) { this.total = total; } }package com.jlmusicplayer.domain; public class Results { private int song_id; private String song_name; private int artist_id; private String artist_name; private int album_id; private String album_name; private String album_logo; private String song_location; //getter setter接着是连接虾米网的SendPostRequest
public static byte[] SendPostRequest(String path, Map<String, String> params, String enc) throws Exception { // title=dsfdsf&timelength=23&method=save StringBuilder sb = new StringBuilder(); if (params != null && !params.isEmpty()) { for (Map.Entry<String, String> entry : params.entrySet()) { sb.append(entry.getKey()).append('=') .append(URLEncoder.encode(entry.getValue(), enc)) .append('&'); } sb.deleteCharAt(sb.length() - 1); } byte[] entitydata = sb.toString().getBytes();// 得到实体的二进制数据 URL url = new URL(path); HttpURLConnection conn = (HttpURLConnection) url.openConnection(); conn.setRequestMethod("POST"); conn.setConnectTimeout(5 * 1000); conn.setDoOutput(true);// 如果通过post提交数据,必须设置允许对外输出数据 // Content-Type: application/x-www-form-urlencoded // Content-Length: 38 conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded"); conn.setRequestProperty("Content-Length", String.valueOf(entitydata.length)); OutputStream outStream = conn.getOutputStream(); outStream.write(entitydata); outStream.flush(); outStream.close(); if (conn.getResponseCode() == 200) { return readStream(conn.getInputStream()); } return null; }接下来就是怎么使用gson了,首先要一个gson-2.2.4.jar的包~
然后就是连接虾米接口,并且将返回内容保存到bean里面
Thread t=new Thread(new Runnable(){ @Override public void run() { try { byte[] resultByte = HttpUtil.SendPostRequest(tourl, null, "UTF-8"); result = new String(resultByte, "UTF-8"); Gson gson = new Gson(); rjson = gson.fromJson(result, new TypeToken<ReturnJson>() { }.getType()); musicinfo= rjson.getResults(); for(int i=0;i<musicinfo.size();i++){ musicinfo.get(i).setSong_name(URLDecoder.decode(musicinfo.get(i).getSong_name(), "UTF-8")); musicinfo.get(i).setAlbum_name(URLDecoder.decode(musicinfo.get(i).getAlbum_name(), "UTF-8")); musicinfo.get(i).setArtist_name(URLDecoder.decode(musicinfo.get(i).getArtist_name(), "UTF-8")); musicinfo.get(i).setAlbum_logo(musicinfo.get(i).getAlbum_logo().replaceAll("\\\\", null)); tourl=HttpUtil.XIAMI_HQ_LOCATION+musicinfo.get(i).getSong_id(); resultByte=HttpUtil.SendPostRequest(tourl, null, "UTF-8"); System.out.println(tourl); result = new String(resultByte, "UTF-8"); hqlocation = gson.fromJson(result, new TypeToken<HQLocation>() { }.getType()); System.out.println("看看会死吗"+hqlocation.getLocation()); musicinfo.get(i).setSong_location(decodeXiaMiLocation(hqlocation.getLocation())); } handler.post(runnableUi); }catch(Exception e){ System.out.println("啊哈哈哈哈哈有错吗!!"+e); } } }); t.start();貌似在android 稍微高级一点的版本连接网络的动作都必须新开线程处理了,不过开个线程确实有好处~其实仔细看这里使用gson的代码就那么两句
Gson gson = new Gson(); rjson = gson.fromJson(result, new TypeToken<ReturnJson>() { }.getType()); musicinfo= rjson.getResults();这样就完了~将接口内容全部保存在了rjson这个bean里,然后将里面的歌曲信息保存到了musicinfo这个list<results>里面~
不过从接口返回内容可以看出,中文部分都是经过编码的,所以在这里要进行反编码再保存~但是再仔细看虾米返回的内容,居然是没有下载链接的。。。囧,这个时候我们找到了另一个接口地址
http://www.xiami.com/song/gethqsong/sid/
在这个地址后面添加上面那个接口获得的song_id属性,就可以得到歌曲的下载地址,而且据说这个地址的歌曲质量还是比较高的~
例如http://www.xiami.com/song/gethqsong/sid/1771969837
然后看看返回内容
{"status":1,"location":"4h%2Ff.moF5F7E1%5%76759_pFhy3b2Eeb316393Elt3Fmixim7E12%558279_E3l3a_%bc938b68587--ltA%5li.%2%1%27E8F18139.%uk3f7%57cb2-88%np%2.eac2%295F263193%18m3teDc95253e61135u"}location完全就是乱七八糟的东西。。。但是百度是万能的,我们找到一篇教大家如何处理该地址的文章,虽然有那么点点细节不一样,但总体还是一致的,但我忘了地址了,下面简单来说说。。
首先是将第一个数字给剥离出来,这里是4。然后将剩余的字符串写作每列4行的方阵(如果第一个数字为5则每列5行以此类推)我们就可以看到形如
H%2Ff.moF5省略
T省略省略省略省略
T省略省略省略省略
P省略省略省略省略
这样的一个方阵,然后从上往下从左到右进行读取,就变成一个http开头的下载地址啦~
完了之后会得到一个中文进行编码过的地址,所以要将其反编码,然后还要将里面的"^"符号替换为0,这样就大功告成了~
那么接下来就是贴代码的阶段了!
上面那段代码我们可以看到已经获取了这个接口里的location放在hqlocation.getLocation()里面了,然后就对这个奇怪的地址进行处理。
protected String decodeXiaMiLocation(String originalUrl){ String decodedUrl = ""; int row=Integer.parseInt(originalUrl.substring(0,1)); originalUrl=originalUrl.substring(1); int lastRowNum=originalUrl.length()%row; int col=(originalUrl.length()/row); if(lastRowNum!=0){ //col为实际列数 col+=1; } System.out.println("row="+row+"col="+col+"lastrowNum="+lastRowNum); CharSequence[] url=new CharSequence[row]; int location=0; System.out.println("先看看长度多少"+originalUrl.length()); for(int i=0;i<row;i++){ if(lastRowNum!=0){ if(i>=lastRowNum){ System.out.println("从"+location+"到"+(location+col-1)); url[i]=originalUrl.subSequence(location, location+col-1); location=location+col-1; } else{ System.out.println("从"+location+"到"+(location+col)); url[i]=originalUrl.subSequence(location, location+col); location=location+col; } } else { url[i]=originalUrl.subSequence(location, location+col); location=location+col; } System.out.println("让我好好看清楚第"+i+"行是什么"+url[i]); } for(int i=0;i<col;i++){ for(int j=0;j<row;j++) if(url[j].length()>i) decodedUrl+=url[j].charAt(i); } decodedUrl=URLDecoder.decode(decodedUrl); decodedUrl=decodedUrl.replaceAll("\\^", "0"); System.out.println("转换后的初步样子是"+decodedUrl); return decodedUrl; }这里使用了CharSequence[] url来分别存储每一行的字符,然后用两个for循环嵌套按从上往下从左到右的方法读取出来
for(int i=0;i<col;i++){ for(int j=0;j<row;j++) if(url[j].length()>i) decodedUrl+=url[j].charAt(i); }很显然地址总长度是不一定能均分为等长的几列的,所以还要对这种情况进行分类处理(上面代码中已经进行了处理了)——也就是lastRowNum是否为0,为0代表刚好是一个四四方方的方阵,否则~~大家懂的啦
然后就是很简单的反编码和替换"^"符号替换为0
decodedUrl=URLDecoder.decode(decodedUrl); decodedUrl=decodedUrl.replaceAll("\\^", "0");
这些也都包含在方法里了,值得注意的是replaceAll这个方法在替换一些奇怪的符号的时候要在前面添加"\\"。如果要替换的是"\"的话,就要在前面添加"\\\"了~原因嘛百度就知道啦。。。我这里纯粹作为一个记录日后可以翻一下看看怎么用,原理什么的就粗略点见谅~
一切准备就绪,大家可以自己去看看最后的地址咯