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

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

  • 相关阅读:
    [LeetCode] Lowest Common Ancestor of a Binary Search Tree
    [LeetCode] Palindrome Linked List
    Android控件开发之Chronometer(转)
    andriod 动态设置TextView 和 RelativeLayou 高度
    android RelativeLayout 动态设置高度
    android 判断字符串是否为空与比对["=="与equals()的区别]
    android 实现ImageView按压效果和解决背景图片拉申问题
    android XML布局 属性与运用
    android 解决.XML提示ava.lang.NullPointerException at错误后XML没显示
    Android设置AlertDialog点击按钮对话框不关闭(转)
  • 原文地址:https://www.cnblogs.com/blairsProgrammer/p/3494999.html
Copyright © 2020-2023  润新知