Android开源demo---芒果TV-VIP电影播放器

抓取的是芒果tv官方的api,理由永久可用

补上一张播放图

界面随便写了一下,一共分为三次解析,利用芒果tv的影片ID,最终解析出来的是m3u8的地址,播放器我是用的Vitamio播放器,

贴一下MainActivity的代码

  1 package com.macernow.djstava.djmediaplayer;
  2 
  3 import android.content.Intent;
  4 import android.os.Bundle;
  5 import android.os.Environment;
  6 import android.support.v7.app.AppCompatActivity;
  7 import android.support.v7.widget.GridLayoutManager;
  8 import android.util.Log;
  9 import android.view.Menu;
 10 import android.view.MenuItem;
 11 import android.view.View;
 12 import android.view.WindowManager;
 13 import android.widget.AdapterView;
 14 import android.widget.ImageView;
 15 import android.widget.ListView;
 16 import android.widget.TextView;
 17 import android.widget.Toast;
 18 
 19 import com.android.volley.Request;
 20 import com.android.volley.RequestQueue;
 21 import com.android.volley.Response;
 22 import com.android.volley.VolleyError;
 23 import com.android.volley.toolbox.StringRequest;
 24 import com.android.volley.toolbox.Volley;
 25 
 26 import org.json.JSONArray;
 27 import org.json.JSONObject;
 28 
 29 import java.io.File;
 30 import java.util.ArrayList;
 31 import java.util.List;
 32 
 33 public class MainActivity extends AppCompatActivity {
 34 
 35     private SwipeRecyclerView listView;
 36     private int path= 0;
 37     private int a= 0;
 38     private RequestQueue requestQueue;
 39     private ArrayList<NeBss> mDatas = new ArrayList<>();
 40     private ArrayList<NeBss> newslist = new ArrayList<>();
 41     private YingShiAdapter yinshiadapter;
 42     private String titles;
 43 
 44 
 45     @Override
 46     protected void onCreate(Bundle savedInstanceState) {
 47         super.onCreate(savedInstanceState);
 48         setContentView(R.layout.activity_main);
 49         requestQueue = Volley.newRequestQueue(this);
 50         Toast.makeText(this, "列表滑到底部,请手动点击切换下一页", Toast.LENGTH_LONG).show();
 51         //初始化组件
 52         listView = (SwipeRecyclerView)findViewById(R.id.lv_id);
 53         //下一页
 54         TextView textView = (TextView) findViewById(R.id.tv_xia);
 55         textView.setOnClickListener(new View.OnClickListener() {
 56             @Override
 57             public void onClick(View v) {
 58                 path = path+1;
 59                 a++;
 60                 gengduo(0,a);
 61             }
 62         });
 63         //排列方式
 64         listView.getRecyclerView().setLayoutManager(new GridLayoutManager(MainActivity.this, 3));
 65         listView.getSwipeRefreshLayout()
 66                 .setColorSchemeColors(getResources().getColor(R.color.yellow));
 67         //禁止下拉刷新
 68         listView.setRefreshEnable(true);
 69         //禁止加载更多
 70         listView.setLoadMoreEnable(true);
 71         //触发刚发
 72         listView.setOnLoadListener(new SwipeRecyclerView.OnLoadListener(){
 73             //刷新数据
 74             @Override
 75             public void onRefresh() {
 76             }
 77             //加载更多
 78             @Override
 79             public void onLoadMore() {
 80                // path = path+1;
 81                 a++;
 82                 gengduo(0,a);
 83             }
 84         });
 85         //开始加载
 86         kaishi();
 87     }
 88 
 89     /**
 90      * 第一次加载网络
 91      */
 92     private void kaishi() {
 93         StringRequest strq = new StringRequest(Request.Method.GET, getUrls(path), new Response.Listener<String>() {
 94             @Override
 95             public void onResponse(String response) {
 96                 try {
 97                     //解析json
 98                     newslist = getList(response);
 99                     yinshiadapter = new YingShiAdapter(MainActivity.this,newslist);
100                     listView.setAdapter(yinshiadapter);
101                     //点击事件
102                     yinshiadapter.setIjieKOu(new IjieKOu() {
103                         @Override
104                         public void setDianJi(int i) {
105                             //拿到视频ID进行第二次解析
106                             getLissUrl(newslist.get(i).videoId);
107                         }
108                     });
109                 } catch (Exception e) {
110                     e.printStackTrace();
111                 }
112             }
113         }, new Response.ErrorListener() {
114             @Override
115             public void onErrorResponse(VolleyError error) {
116 
117             }
118         });
119         requestQueue.add(strq);
120     }
121     /**
122      * 更多加载网络
123      */
124     private void gengduo(int i,int a) {
125         String url;
126         if(i == 1){
127             url = getUrls(path);
128         }else{
129             url = getYUrls(path,a);
130         }
131         StringRequest strq = new StringRequest(Request.Method.GET, url, new Response.Listener<String>() {
132             @Override
133             public void onResponse(String response) {
134                 try {
135                     newslist = getList(response);
136                     yinshiadapter.setStringList(newslist);
137                 } catch (Exception e) {
138                     e.printStackTrace();
139                 }
140             }
141         }, new Response.ErrorListener() {
142             @Override
143             public void onErrorResponse(VolleyError error) {
144 
145             }
146         });
147         requestQueue.add(strq);
148     }
149 
150     /**
151      * 第二次请求网络
152      * @param videoId
153      */
154     private void getLissUrl(String videoId) {
155         StringRequest strq = new StringRequest(Request.Method.GET, getUrlsss(videoId), new Response.Listener<String>() {
156             @Override
157             public void onResponse(String response) {
158                 try {
159                     String url = getUrl(response);
160                     //拼接出来地址,进行第三次网络请求
161                     getZhenUrl(url);
162                 } catch (Exception e) {
163                     e.printStackTrace();
164                 }
165             }
166         }, new Response.ErrorListener() {
167             @Override
168             public void onErrorResponse(VolleyError error) {
169 
170             }
171         });
172         requestQueue.add(strq);
173     }
174 
175     /**
176      * 第三次请求网络数据
177      * @param url
178      */
179     private void getZhenUrl(String url) {
180         StringRequest strq = new StringRequest(Request.Method.GET, getUrlssss(url), new Response.Listener<String>() {
181             @Override
182             public void onResponse(String response) {
183                 try {
184                     //得到最终结果,解析出来url,传递给播放页面
185                     JSONObject js = new JSONObject(response);
186                     String url = js.getString("info");
187                     Intent intent = new Intent(MainActivity.this,VitamioVideoViewActivity.class);
188                     intent.putExtra("movieUrl",url);
189                     startActivity(intent);
190                 } catch (Exception e) {
191                     e.printStackTrace();
192                 }
193             }
194         }, new Response.ErrorListener() {
195             @Override
196             public void onErrorResponse(VolleyError error) {
197 
198             }
199         });
200         requestQueue.add(strq);
201     }
202 
203 
204 
205 
206 
207     @Override
208     public boolean onCreateOptionsMenu(Menu menu) {
209         // Inflate the menu; this adds items to the action bar if it is present.
210         getMenuInflater().inflate(R.menu.menu_main, menu);
211         return true;
212     }
213 
214     @Override
215     public boolean onOptionsItemSelected(MenuItem item) {
216         // Handle action bar item clicks here. The action bar will
217         // automatically handle clicks on the Home/Up button, so long
218         // as you specify a parent activity in AndroidManifest.xml.
219         int id = item.getItemId();
220 
221         //noinspection SimplifiableIfStatement
222         if (id == R.id.action_settings) {
223             return true;
224         }
225 
226         return super.onOptionsItemSelected(item);
227     }
228 
229     /**
230      * 列表api
231      * @param path
232      * @return
233      */
234     public static String getUrls(int path){
235         String url = "http://mobile.api.hunantv.com/list?userId=&order="+path+"&types=all&pageCount=1.0&pageSize=500&typeId=3";
236         return url;
237     }
238 
239     public static String getYUrls(int path,int a){
240         String url = "http://mobile.api.hunantv.com/list?userId=&order="+path+"&types=all&pageCount="+a+".0&pageSize=800&typeId=3";
241         return url;
242     }
243 
244     /**
245      * 解析信息
246      * @param path
247      * @return
248      */
249     public static String getUrlsss(String path){
250         String url = "http://pcweb.api.mgtv.com/player/video?video_id="+path;
251         return url;
252     }
253 
254     /**
255      * 得到真实地址
256      * @param path
257      * @return
258      */
259     public static String getUrlssss(String path){
260         String url = "http://disp.titan.mgtv.com/"+path;
261         return url;
262     }
263 
264 
265     /**
266      * 解析第一次网络请求的数据
267      * @param response
268      * @return
269      * @throws Exception
270      */
271     public ArrayList<NeBss> getList(String response) throws Exception {
272         try {
273             JSONObject json = new JSONObject(response);
274             JSONArray jsonArray = json.getJSONArray("data");
275             for (int i = 0; i < jsonArray.length(); i++) {
276                 JSONObject jsonObject = jsonArray.getJSONObject(i);
277                 String title = jsonObject.getString("name");
278                 String str = jsonObject.getString("image");
279                 String videoId = jsonObject.getString("videoId");
280                 NeBss info = new NeBss(title,str,videoId);
281                 mDatas.add(info);
282             }
283         } catch (Exception e) {
284 
285         }
286         return mDatas;
287     }
288 
289     /**
290      * 第二次网络请求数据
291      * @param response
292      * @return
293      * @throws Exception
294      */
295     public String getUrl(String response) throws Exception {
296         try {
297             JSONObject json = new JSONObject(response);
298             JSONObject jsonArray = json.getJSONObject("data");
299             JSONArray jsonArrays = jsonArray.getJSONArray("stream");
300             for (int i = 0; i < jsonArray.length(); i++) {
301                 JSONObject jsonArray2 = jsonArrays.getJSONObject(i);
302                 titles = jsonArray2.getString("url");
303             }
304         } catch (Exception e) {
305 
306         }
307         return titles;
308     }
309 }

activity_main.xml

 1 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
 2     android:layout_width="match_parent"
 3     android:layout_height="match_parent"
 4     android:background="@drawable/music_background"
 5     android:orientation="vertical" >
 6 
 7     <TextView
 8         android:id="@+id/textview_title"
 9         android:layout_width="match_parent"
10         android:layout_height="wrap_content"
11         android:layout_gravity="center"
12         android:gravity="center"
13         android:singleLine="true"
14         android:text="@string/movieList"
15         android:textColor="@color/white"
16         android:textSize="30sp" />
17 
18 
19 
20     <TextView android:text="下一页"
21         android:id="@+id/tv_xia"
22         android:layout_width="wrap_content"
23         android:textColor="@color/white"
24         android:textSize="30sp" android:layout_alignParentRight="true"
25         android:layout_height="wrap_content" />
26 
27 
28     <TextView android:text="上一页"
29         android:layout_width="wrap_content"
30         android:textColor="@color/white"
31         android:textSize="30sp"
32         android:layout_height="wrap_content" />
33     <View android:layout_below="@+id/textview_title"
34         android:layout_width="wrap_content"
35         android:layout_height="1dp"
36         android:id="@+id/ssss"
37         android:background="@drawable/single_line" />
38 
39 
40     <com.macernow.djstava.djmediaplayer.SwipeRecyclerView
41         android:id="@+id/lv_id"
42         android:layout_below="@+id/ssss"
43         android:layout_width="match_parent"
44         android:layout_height="wrap_content"
45         android:visibility="visible"
46         />
47     <Button android:id="@+id/buttid"
48         android:text="下一页" android:layout_below="@+id/lv_id"
49         android:layout_width="match_parent"
50         android:layout_height="wrap_content" />
51 </RelativeLayout>

播放页面代码

  1 package com.macernow.djstava.djmediaplayer;
  2 
  3 import android.app.AlertDialog;
  4 import android.content.Context;
  5 import android.content.DialogInterface;
  6 import android.content.Intent;
  7 import android.media.AudioManager;
  8 import android.net.Uri;
  9 import android.os.Bundle;
 10 import android.os.Handler;
 11 import android.os.Message;
 12 import android.support.v7.app.AppCompatActivity;
 13 import android.view.Display;
 14 import android.view.GestureDetector;
 15 import android.view.KeyEvent;
 16 import android.view.Menu;
 17 import android.view.MenuItem;
 18 import android.view.MotionEvent;
 19 import android.view.View;
 20 import android.view.ViewGroup;
 21 import android.view.WindowManager;
 22 import android.widget.ImageView;
 23 import android.widget.Toast;
 24 
 25 import io.vov.vitamio.LibsChecker;
 26 import io.vov.vitamio.MediaPlayer;
 27 import io.vov.vitamio.widget.MediaController;
 28 import io.vov.vitamio.widget.VideoView;
 29 
 30 public class VitamioVideoViewActivity extends AppCompatActivity {
 31 
 32     private VideoView videoView;
 33     private Intent intent;
 34     private String movieUrl;
 35     private MediaController mediaController;
 36     private AudioManager audioManager;
 37     private GestureDetector gestureDetector;
 38     private ImageView mOperationBg, mOperationPercent;
 39     private long currentPosition, duration;
 40     private View mVolumeBrightnessLayout;
 41 
 42     /*最大声音*/
 43     private int maxVolume;
 44 
 45     /*当前声音*/
 46     private int currentVolume = -1;
 47 
 48     /**/
 49 
 50     @Override
 51     protected void onCreate(Bundle savedInstanceState) {
 52         super.onCreate(savedInstanceState);
 53         if (!LibsChecker.checkVitamioLibs(this))
 54             return;
 55         setContentView(R.layout.activity_vitamio_video_view);
 56         Toast.makeText(this, "视频正在加载,请手动点击两下屏幕", Toast.LENGTH_SHORT).show();
 57         /*
 58         * 去屏保
 59         * */
 60         getWindow().setFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON, WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
 61         //初始化组件
 62         videoView = (VideoView) this.findViewById(R.id.surface_view);
 63         mVolumeBrightnessLayout = findViewById(R.id.operation_volume);
 64         mOperationBg = (ImageView) findViewById(R.id.operation_bg);
 65         mOperationPercent = (ImageView) findViewById(R.id.operation_percent);
 66         //接收传递过来的值
 67         intent = getIntent();
 68         movieUrl = intent.getStringExtra("movieUrl");
 69         mediaController = new MediaController(this);
 70         //设置参数
 71         audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
 72         maxVolume = audioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
 73         //设置参数
 74         if (!movieUrl.isEmpty()) {
 75             if (movieUrl.startsWith("http:")) {
 76                 videoView.setVideoURI(Uri.parse(movieUrl));
 77             } else {
 78                 videoView.setVideoPath(movieUrl);
 79             }
 80             //设置参数
 81             videoView.setVideoLayout(VideoView.VIDEO_LAYOUT_STRETCH, 0);
 82             videoView.setMediaController(mediaController);
 83             videoView.requestFocus();
 84 
 85             videoView.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
 86                 @Override
 87                 public void onPrepared(MediaPlayer mp) {
 88                     mp.setPlaybackSpeed(1.0f);
 89                     duration = mp.getDuration();
 90                 }
 91             });
 92 
 93             videoView.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
 94                 @Override
 95                 public void onCompletion(MediaPlayer mp) {
 96                     Toast.makeText(VitamioVideoViewActivity.this, R.string.video_finish, Toast.LENGTH_SHORT).show();
 97                     finish();
 98                 }
 99             });
100 
101             videoView.setOnErrorListener(new MediaPlayer.OnErrorListener() {
102                 @Override
103                 public boolean onError(MediaPlayer mp, int what, int extra) {
104                     new AlertDialog.Builder(VitamioVideoViewActivity.this).setTitle(R.string.video_error)
105                             .setPositiveButton(R.string.video_error_yes, new DialogInterface.OnClickListener() {
106                                 @Override
107                                 public void onClick(DialogInterface dialog, int which) {
108                                     finish();
109                                 }
110                             })
111                             .show();
112 
113                     return true;
114                 }
115             });
116         } else {
117             Toast.makeText(VitamioVideoViewActivity.this, R.string.no_such_movie, Toast.LENGTH_SHORT).show();
118         }
119 
120         gestureDetector = new GestureDetector(this, new CustomGestureListener());
121     }
122 
123     @Override
124     public boolean onTouchEvent(MotionEvent event) {
125         if (gestureDetector.onTouchEvent(event)) {
126             return true;
127         }
128 
129         switch (event.getAction() & MotionEvent.ACTION_MASK) {
130             case MotionEvent.ACTION_UP:
131                 endGesture();
132                 break;
133         }
134 
135         return super.onTouchEvent(event);
136     }
137 
138     private void endGesture() {
139         currentVolume = -1;
140 
141         mDismissHandler.removeMessages(0);
142         mDismissHandler.sendEmptyMessageDelayed(0, 500);
143     }
144 
145     private class CustomGestureListener extends GestureDetector.SimpleOnGestureListener {
146 
147         /*
148         * 双击操作
149         * */
150         @Override
151         public boolean onDoubleTap(MotionEvent e) {
152             if (videoView.isPlaying()) {
153                 videoView.pause();
154             } else {
155                 videoView.start();
156             }
157 
158             return super.onDoubleTap(e);
159         }
160 
161         @Override
162         public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
163 
164             //上下滑动
165             if (Math.abs(distanceY) / Math.abs(distanceX) > 3) {
166                 float mOldY = e1.getY();
167                 int y = (int) e2.getRawY();
168                 Display disp = getWindowManager().getDefaultDisplay();
169                 int windowHeight = disp.getHeight();
170 
171                 onVolumeSlide((mOldY - y) / windowHeight);
172 
173             }
174 
175             return super.onScroll(e1, e2, distanceX, distanceY);
176         }
177 
178         /*
179          * 滑动操作
180          * */
181         @Override
182         public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
183 
184             currentPosition = videoView.getCurrentPosition();
185 
186             /*向左滑*/
187             if (e1.getX() - e2.getX() > 120) {
188 
189                 if (currentPosition < 10000) {
190                     currentPosition = 0;
191                     videoView.seekTo(currentPosition);
192                 } else {
193                     videoView.seekTo(currentPosition - 10000);
194                 }
195 
196             } else if (e2.getX() - e1.getX() > 120) {
197             /*向右滑*/
198                 if (currentPosition + 10000 > duration) {
199                     currentPosition = duration;
200                     videoView.seekTo(currentPosition);
201                 } else {
202                     videoView.seekTo(currentPosition + 10000);
203                 }
204 
205             }
206 
207             return super.onFling(e1, e2, velocityX, velocityY);
208         }
209     }
210 
211     /*
212     * 定时隐藏
213     * */
214     private Handler mDismissHandler = new Handler() {
215         @Override
216         public void handleMessage(Message msg) {
217             mVolumeBrightnessLayout.setVisibility(View.GONE);
218         }
219     };
220 
221     /**
222      * 滑动改变声音大小
223      */
224     private void onVolumeSlide(float percent) {
225 
226         if (currentVolume == -1) {
227             currentVolume = audioManager.getStreamVolume(AudioManager.STREAM_MUSIC);
228             if (currentVolume < 0)
229                 currentVolume = 0;
230 
231             // 显示
232             mOperationBg.setImageResource(R.drawable.video_volumn_bg);
233             mVolumeBrightnessLayout.setVisibility(View.VISIBLE);
234         }
235 
236         int value = currentVolume + (int) (percent * maxVolume);
237         if (value > maxVolume) {
238             value = maxVolume;
239         } else if (value < 0) {
240             value = 0;
241         }
242 
243         // 变更声音
244         audioManager.setStreamVolume(AudioManager.STREAM_MUSIC, value, 0);
245 
246         // 变更进度条
247         ViewGroup.LayoutParams lp = mOperationPercent.getLayoutParams();
248         lp.width = findViewById(R.id.operation_full).getLayoutParams().width * value / maxVolume;
249         mOperationPercent.setLayoutParams(lp);
250     }
251 
252     @Override
253     public boolean onKeyDown(int keyCode, KeyEvent event) {
254         switch (keyCode) {
255             case KeyEvent.KEYCODE_BACK:
256                 new AlertDialog.Builder(VitamioVideoViewActivity.this).setMessage(R.string.alertdialog_message)
257                         .setTitle(R.string.alertdialog_title)
258                         .setCancelable(false)
259                         .setPositiveButton(R.string.alertdialog_yes, new DialogInterface.OnClickListener() {
260                             @Override
261                             public void onClick(DialogInterface dialog, int which) {
262                                 finish();
263                             }
264                         })
265                         .setNegativeButton(R.string.alertdialog_no, new DialogInterface.OnClickListener() {
266                             @Override
267                             public void onClick(DialogInterface dialog, int which) {
268                                 dialog.dismiss();
269                             }
270                         }).show();
271                 break;
272 
273             default:
274                 break;
275         }
276 
277         return super.onKeyDown(keyCode, event);
278     }
279 
280     @Override
281     protected void onPause() {
282         super.onPause();
283 
284         if (videoView != null) {
285             videoView.pause();
286         }
287     }
288 
289     @Override
290     protected void onResume() {
291         super.onResume();
292 
293         if (videoView != null) {
294             videoView.resume();
295         }
296     }
297 
298     @Override
299     protected void onDestroy() {
300         super.onDestroy();
301 
302         if (videoView != null) {
303             videoView.stopPlayback();
304         }
305     }
306 
307     @Override
308     public boolean onCreateOptionsMenu(Menu menu) {
309         // Inflate the menu; this adds items to the action bar if it is present.
310         getMenuInflater().inflate(R.menu.menu_vitamio_video_view, menu);
311         return true;
312     }
313 
314     @Override
315     public boolean onOptionsItemSelected(MenuItem item) {
316         // Handle action bar item clicks here. The action bar will
317         // automatically handle clicks on the Home/Up button, so long
318         // as you specify a parent activity in AndroidManifest.xml.
319         int id = item.getItemId();
320 
321         //noinspection SimplifiableIfStatement
322         if (id == R.id.action_settings) {
323             return true;
324         }
325 
326         return super.onOptionsItemSelected(item);
327     }
328 }

注:播放页面,没有什么注释,因为用的是第三方库,

xml

 1 <?xml version="1.0" encoding="utf-8"?>
 2 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
 3     android:layout_width="fill_parent"
 4     android:layout_height="fill_parent"
 5     android:orientation="vertical">
 6 
 7     <io.vov.vitamio.widget.VideoView
 8         android:id="@+id/surface_view"
 9         android:layout_width="fill_parent"
10         android:layout_height="fill_parent"
11         android:layout_centerHorizontal="true"
12         android:layout_centerVertical="true" />
13 
14     <FrameLayout
15         android:id="@+id/operation_volume"
16         android:layout_width="wrap_content"
17         android:layout_height="wrap_content"
18         android:layout_centerInParent="true"
19         android:background="#00000000"
20         android:orientation="horizontal"
21         android:padding="0dip"
22         android:visibility="invisible">
23 
24         <ImageView
25             android:id="@+id/operation_bg"
26             android:layout_width="wrap_content"
27             android:layout_height="wrap_content"
28             android:layout_gravity="center"
29             android:src="@drawable/video_volumn_bg" />
30 
31         <FrameLayout
32             android:layout_width="wrap_content"
33             android:layout_height="wrap_content"
34             android:layout_gravity="bottom|center_horizontal"
35             android:paddingBottom="25dip">
36 
37             <ImageView
38                 android:id="@+id/operation_full"
39                 android:layout_width="94dip"
40                 android:layout_height="wrap_content"
41                 android:layout_gravity="left"
42                 android:src="@drawable/video_num_bg" />
43 
44             <ImageView
45                 android:id="@+id/operation_percent"
46                 android:layout_width="0dip"
47                 android:layout_height="wrap_content"
48                 android:layout_gravity="left"
49                 android:scaleType="matrix"
50                 android:src="@drawable/video_num_front" />
51         </FrameLayout>
52     </FrameLayout>
53 
54 </RelativeLayout>

这样就写完了,

附上代码下载地址链接:http://pan.baidu.com/s/1pKGK25l 密码:pa63

 apk下载地址 链接:http://pan.baidu.com/s/1hrCUX1M 密码:xlnc

依赖的是vitamio,去官方下一个就好,我就不上传了,

原文地址:https://www.cnblogs.com/langfei8818/p/6368507.html