android下使用gson解析虾米免费音乐接口,并获取真实下载地址

上次使用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/周杰伦

大家自己试试就行了,不过给多点参数也不是什么麻烦事对吧。。跑题了~

然后我们来看看返回的内容

QQ截图20131228021554

然后是对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这个方法在替换一些奇怪的符号的时候要在前面添加"\\"。如果要替换的是"\"的话,就要在前面添加"\\\"了~原因嘛百度就知道啦。。。我这里纯粹作为一个记录日后可以翻一下看看怎么用,原理什么的就粗略点见谅~

一切准备就绪,大家可以自己去看看最后的地址咯

原文地址:https://www.cnblogs.com/blairsProgrammer/p/3494999.html