Android实战开发小米主题下载工具
本站已经对小米主题真实地址进行解析了,https://lovestu.com/3.html,那么现在使用Android Studio开发一款解析APP
准备工作
okhttp是一款非常优秀的互联网访问类库,可以减少很多的代码编写,所以这儿使用okhttp方式进行数据访问,首先创建一个项目,添加OKhttp依赖
- compile 'com.squareup.okhttp:okhttp:2.4.0'
- compile 'com.squareup.okio:okio:1.7.0'
然后点击同步,进行下载依赖库
还有最重要的联网权限和写读写文件权限
- <uses-permission android:name="android.permission.INTERNET"/>
- <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
- <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
有这三个权限才能正常的读写文件和联网
开始代码编写
创建一个mithemedown类,解析代码都在这儿编写了,创建一个获取网页源码的函数,本函数的功能是通过传入的主题唯一ID获取网页源码,调用的是OKhttp的同步访问方法,在外部使用的时候需要用到线程
- import com.squareup.okhttp.OkHttpClient;
- import com.squareup.okhttp.Request;
- import com.squareup.okhttp.Response;
- public class mithemedown {
- public static String getdownurl(String value) {
- final String url = "http://thm.market.xiaomi.com/thm/download/v2/" + value;
- String Strjson = "";
- try {
- OkHttpClient client = new OkHttpClient();//创建OkHttpClient对象
- Request request = new Request.Builder()
- .url(url)//请求接口。如果需要传参拼接到接口后面。
- .build();//创建Request 对象
- Response response = null;
- response = client.newCall(request).execute();//得到Response 对象
- if (response.isSuccessful()) {
- Strjson = response.body().string();
- }
- } catch (Exception e) {
- e.printStackTrace();
- }
- return Strjson;
- }
- }
接着我们开始制作界面,界面很简单,两个编辑框,一个是正常的EditText,另一个是多行文本框,所以要给这个EditText添加android:inputType=”textMultiLine”属性,然后设置行数android:minLines=”8″ ,两个按钮,还需要一个进度条ProgressBar。点击解析按钮后会进行解析,把解析到的地址给显示到第二个编辑框,然后点击立即下载后调用下载系统下载功能进行下载主题
完整xml代码如下,基于ConstraintLayout布局创建
- <?xml version="1.0" encoding="utf-8"?>
- <android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:app="http://schemas.android.com/apk/res-auto"
- xmlns:tools="http://schemas.android.com/tools"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- tools:context="com.ghboke.mithemedown.MainActivity">
- <ProgressBar
- android:id="@+id/progressBar"
- style="?android:attr/progressBarStyle"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:visibility="invisible"
- app:layout_constraintBottom_toBottomOf="parent"
- app:layout_constraintLeft_toLeftOf="parent"
- app:layout_constraintRight_toRightOf="parent"
- app:layout_constraintTop_toTopOf="parent" />
- <LinearLayout
- android:id="@+id/linearLayout"
- android:layout_width="0dp"
- android:layout_height="0dp"
- android:layout_marginBottom="8dp"
- android:layout_marginEnd="8dp"
- android:layout_marginStart="8dp"
- android:layout_marginTop="8dp"
- android:orientation="vertical"
- app:layout_constraintBottom_toBottomOf="parent"
- app:layout_constraintEnd_toEndOf="parent"
- app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintTop_toTopOf="parent">
- <EditText
- android:id="@+id/editText_url"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:ems="10"
- android:hint="输入主题地址"
- android:inputType="textPersonName" />
- <Button
- android:id="@+id/button_jx"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:text="解析" />
- <EditText
- android:id="@+id/editText_downurl"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:ems="10"
- android:inputType="textMultiLine"
- android:minLines="8" />
- <Button
- android:id="@+id/Button_down"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:text="立即下载" />
- </LinearLayout>
- </android.support.constraint.ConstraintLayout>
制作好界面以后,去MainActivity中绑定组件
- private ProgressBar mProgressBar;
- private EditText mEditTextUrl;
- private Button mButtonJx;
- private EditText mEditTextDownurl;
- private Button mButtonDown;
- private String downurl;
- private boolean candown=false;
- private String filename;
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_main);
- initView();
- }
- private void initView() {
- mProgressBar = (ProgressBar) findViewById(R.id.progressBar);
- mEditTextUrl = (EditText) findViewById(R.id.editText_url);
- mButtonJx = (Button) findViewById(R.id.button_jx);
- mButtonJx.setOnClickListener(this);
- mEditTextDownurl = (EditText) findViewById(R.id.editText_downurl);
- mButtonDown = (Button) findViewById(R.id.Button_down);
- mButtonDown.setOnClickListener(this);
- }
绑定好以后,给按钮设置点击事件,重写布局的onClick函数
- @Override
- public void onClick(View v) {
- switch (v.getId()) {
- default:
- break;
- case R.id.button_jx:
- jx();
- break;
- case R.id.Button_down:
- down();
- break;
- }
- }
点击按钮就会执行相应的函数,首先编写点击解析按钮的事件 jx(),点击解析按钮后,首先要判断用户是否输入了内容,如果没有输入内容会返回。再接着,获取到输入的网址以后,从31位开始截取网址,因为我们只需要后面的唯一标识。
比如小米主题的地址是http://zhuti.xiaomi.com/detail/e9f4d15d-1a4c-408a-a80c-73c046ff46d8,其中网址的e9f4d15d-1a4c-408a-a80c-73c046ff46d8这一段数据才是我们需要的,所以我们就从这段网址第31位开始截取到全部,即可获取到唯一标识。
再一个是调用最开始的获取网页源码的函数的时候,由于我们给类设置了statc,所以可以直接调用,启动一个线程,在启动线程的时候,给ProgressBar设置为可视状态,因为他默认是圆圈的进度条,所以刚好符合我们需要。解析成功后,需要到主线程里面更新UI,runOnUiThread函数会自动跳到主线程里面,所以在线程中调用这个方法即可更新UI,无需Handler。
下面是完整的代码
- public void jx() {
- downurl = mEditTextUrl.getText().toString();
- if (downurl.length() == 0) {
- Toast.makeText(getApplicationContext(), "请输入网址", Toast.LENGTH_LONG).show();
- return;
- }
- downurl = downurl.substring(31);
- mProgressBar.setVisibility(View.VISIBLE);
- new Thread(new Runnable() {
- @Override
- public void run() {
- downurl = mithemedown.getdownurl(downurl);
- try {
- JSONObject json = new JSONObject(downurl);
- JSONObject downobj = json.getJSONObject("apiData");
- downurl = downobj.getString("downloadUrl");
- } catch (JSONException e) {
- e.printStackTrace();
- }
- runOnUiThread(new Runnable() {
- @Override
- public void run() {
- if (downurl.length()==0){
- Toast.makeText(getApplicationContext(), "解析失败", Toast.LENGTH_LONG).show();
- candown=false;
- return;
- }else {
- Toast.makeText(getApplicationContext(), "解析成功", Toast.LENGTH_LONG).show();
- }
- candown=true;
- mEditTextDownurl.setText(downurl);
- mProgressBar.setVisibility(View.INVISIBLE
- );
- }
- });
- }
- }).start();
- }
接着是下载按钮的功能.down()函数。点击函数,我们先判断用户是否解析成功,再调用下载功能。其中程序集变量candown用来记录是否解析成功,在jx()函数中,如果解析成功了,允许调用下载
- public void down() {
- if (candown==false){
- Toast.makeText(getApplicationContext(), "请解析成功后再使用", Toast.LENGTH_LONG).show();
- return;
- }
- filename=downurl.substring(91);
- try {
- filename=decode(filename, "utf-8");
- } catch (UnsupportedEncodingException e) {
- e.printStackTrace();
- }
- File file=new File(Environment.getExternalStorageDirectory() + "/MIThemeDown/"+filename);
- if (file.exists()){
- Toast.makeText(getApplicationContext(), "文件已经下载过啦", Toast.LENGTH_LONG).show();
- return;
- }
- DownloadManager.Request request = new DownloadManager.Request(Uri.parse(downurl));
- request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
- request.setTitle(filename);
- request.setAllowedOverRoaming(false);
- request.setDescription("小米主题下载");
- request.setVisibleInDownloadsUi(true);
- request.setDestinationInExternalPublicDir("/MIThemeDown/",filename);
- DownloadManager downloadManager = (DownloadManager)getSystemService(DOWNLOAD_SERVICE);
- long downloadId = downloadManager.enqueue(request);
- Toast.makeText(getApplicationContext(), "已经开始下载", Toast.LENGTH_LONG).show();
- }
好了,这个软件大致的功能就写完了,是不是很简单
最终效果
完善代码
程序到最后,还有很多需要完善的地方,比如说判断用户是否乱输入了地址,如果长度没有31位长度,那么截取就可以出问题。
还有部分系统需要手动声明读写文件的权限才能调用系统下载功能DownloadManager,并且上面的代码并没有创建文件夹,还需要自动创建一个主题文件保存的目录。
下载的权限请求函数请查看:https://lovestu.com/5.html