Android-域名查询总结

作者: Jeremy_Ji | 来源:发表于2018-08-11 10:31 被阅读5次

    这一段时间在给项目新增一个NsLookup功能并且学习了一些东西,已经有两周没有写博客了,正好有时间来做个总结。
    先说一下这个功能:该功能是可以让用户输入一个域名然后显示它 的ip地址,并且在右上角菜单有可以查看历史记录的menu菜单,其中主要用到了一些知识例如网络请求,线程,以及dns等。实现效果和windows控制台输入nslookup+domain name效果差不多。

    域名解析(功能主函数)

    其中用到了该java类import org.xbill.DNS.*

     public String showDnsIp(String s)
                throws UnknownHostException, TextParseException {
            String ip=null;
    //            String address = InetAddress.getByName(s).getHostAddress();
    //            System.out.println(address);
    
            //查询类型为A类查询
            Lookup mlookup = new Lookup(s, Type.A);
            mlookup.run();
            //查询失败则返回错误信息
            if (mlookup.getResult() != Lookup.SUCCESSFUL) {
                System.out.println("ERROR: " + mlookup.getErrorString());
                ip = "Error:" + mlookup.getErrorString();
                return ip;
            }
            //获取结果
            Record[] answers = mlookup.getAnswers();
            for (Record rec : answers) {
                System.out.println(rec.toString());
                ip = rec.toString();
    
            }
            return ip;
        }
    

    该java类对网络的请求以及实现过程可以看源码,而且可以注意到lookup函数中有一个type类型,那就是dns的查询类型。

    nslookup有很多种查询种类如下:
    A 地址记录
    AAAA 地址记录
    AFSDB Andrew文件系统数据库服务器记录
    ATMA ATM地址记录
    CNAME 别名记录
    HINFO 硬件配置记录,包括CPU、操作系统信息
    ISDN 域名对应的ISDN号码
    MB 存放指定邮箱的服务器
    MG 邮件组记录
    MINFO 邮件组和邮箱的信息记录
    MR 改名的邮箱记录
    MX 邮件服务器记录
    NS 名字服务器记录
    PTR 反向记录
    RP 负责人记录
    RT 路由穿透记录
    SRV TCP服务器信息记录
    TXT 域名对应的文本信息
    X25 域名对应的X.25地址记录
    

    种类非常的多,但是最常用的还是A类查询

    AsyncTask的使用

    Android中对网络的请求需要把功能函数放到线程中去实现,从而不会导致整个主线程受到影响,可以选择用Android封装的线程类AsyncTask。AsyncTask是Android中的一个抽象类,需要去继承它然后使用,该类可以设置三个泛型参数
    当我们子类继承一个AsyncTask时,至少要重写一个方法那就是(doInBackground(Params...)需要在线程中完成的事情,同类型参数而且可以传入多个)。通常也会实现这个方法(onPostExecute(Result)将结果返回给主线程,参数可以是doInBackground的返回值)。
    这是官方给出的源码

    private class DownloadFilesTask extends AsyncTask<URL, Integer, Long> {
         protected Long doInBackground(URL... urls) {
             int count = urls.length;
             long totalSize = 0;
             for (int i = 0; i < count; i++) {
                 totalSize += Downloader.downloadFile(urls[i]);
                 publishProgress((int) ((i / (float) count) * 100));
                 // Escape early if cancel() is called
                 if (isCancelled()) break;
             }
             return totalSize;
         }
    
         protected void onProgressUpdate(Integer... progress) {
             setProgressPercent(progress[0]);
         }
    
         protected void onPostExecute(Long result) {
             showDialog("Downloaded " + result + " bytes");
         }
     }
     
    

    一旦创建了任务需要在主线程中去调用

    new DownloadFilesTask().execute(url1, url2, url3);
    

    AsyncTask<URL, Integer, Long>这三个分别对应
    1.Params----在主线程调用时传入的参数类型
    2.Progress---由于在后台执行,该参数可以提供一个进度条或者等候界面(类似loading...)执行进度单元的类型,如果不需要则设为Void
    3.Result---如果需要返回一个值给主线程可以设置参数形式

    最终在界面实现方式可以是这样

       class MyNsTask extends AsyncTask<String, Void, String> {
    
            @Override
            protected String doInBackground(String... strings) {
                try {
                    address = showDnsIp(mEdiText.getText().toString());
                } catch (UnknownHostException e) {
                    e.printStackTrace();
                } catch (TextParseException e) {
                    e.printStackTrace();
                }
                return address;
            }
    
            @Override
            protected void onPostExecute(String address) {
                super.onPostExecute(address);
                mTextView.setText(address);
            }
    
    
            /**
             * 查询时等待界面(可添加loading..)
             */
    //    @Override
    //    protected void onPreExecute() {
    ////            mTextView.setText("Wait while loading...");
    //        super.onPreExecute();
    //    }
    
    }
    

    调用函数并将结果显示在Textview中

       /**
             * 搜素按钮的点击事件
             * 点击时执行线程
             */
            mButton.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    if (mEdiText.getText().toString().trim().equals("") ) {
                        mTextView.setText("Please enter domain name");
                    } else {
    //调用线程1
                        MyNsTask nsTask=new MyNsTask();
                        Log.d("--------"+mEdiText.getText().toString());
                        nsTask.execute(mEdiText.getText().toString());
    
                    }
    
                }
            });
    

    界面

    ns--page.png

    这个界面很简单,可以自己设计。

    <LinearLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:id="@+id/nslookup_layout"
        android:orientation="vertical"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
        <LinearLayout
            android:layout_marginTop="20dp"
            android:layout_width="match_parent"
            android:layout_height="wrap_content">
    
            <EditText
                android:id="@+id/edit_nslookup"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:hint="Enter domain name"
                android:lines="1"
                android:singleLine="true"
                android:layout_width="match_parent" />
            <Button
                android:id="@+id/btn_ns_clear"
                android:background="@drawable/ic_btn_clear"
                android:layout_width="25dp"
                android:layout_height="25dp"
                android:visibility="gone"/>
    
        </LinearLayout>
        <LinearLayout
    
            android:layout_marginTop="10dp"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:orientation="vertical">
    
            <Button
                android:layout_marginBottom="10dp"
                android:layout_gravity="center"
                android:gravity="center"
                android:id="@+id/btn_nslookup"
                android:layout_width="100dp"
                android:layout_height="wrap_content"
                android:text="lookup" />
    
            <TextView
                android:layout_width="match_parent"
                android:layout_height="60dp"
                android:gravity="center_vertical"
                android:text="Search result:"
                android:textSize="20sp" />
    
            <TextView
                android:id="@+id/text_showip"
                android:layout_width="match_parent"
                android:layout_height="122dp"
                android:background="#5e2929"
                android:textIsSelectable="true"
                android:textSize="24sp" />
    
    
        </LinearLayout>
    
    </LinearLayout>
    

    设置可以查看搜索历史记录的menu

    大致情况就是 查询时如果访问成功,则将域名用sharedpreferences存起来,利用一个符号比如逗号将每个域名连接起来拼成长字符串,之后在以逗号切割成一个个item放入dialog中。
    实现的一个效果大概这样


    ns--item1.png

    点击右上角可以弹出菜单


    ns--item2.png
    点击按钮会弹出一个dialog显示搜索的记录(搜索失败,或者域名不对不会出现在历史记录)
    nx--item3.png

    活动中设置menu菜单

    设置菜单比较简单就不多说了主要这三个方法

     /**
          *  创建菜单
          **/
        @Override
        public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
            inflater.inflate(R.menu.menu_nslookup_history,menu);
            super.onCreateOptionsMenu(menu, inflater);
    
        }
    
        @Override
        public void onPrepareOptionsMenu(Menu menu) {
            super.onPrepareOptionsMenu(menu);
        }
    
        /**
         * 设置菜单中的history按钮
         * @param item
         * @return
         */
        @Override
        public boolean onOptionsItemSelected(MenuItem item) {
            Log.i("NsLookupFragment onOptionsItemSelected");
            switch (item.getItemId()){
                case R.id.item_ns_history:
                    //显示对话框
                    showHistoryDialog();
                    break;
                    default:break;
            }
            return super.onOptionsItemSelected(item);
        }
    

    设置item布局文件

    <?xml version="1.0" encoding="utf-8"?>
    <menu xmlns:android="http://schemas.android.com/apk/res/android">
        <item
            android:id="@+id/item_ns_history"
            android:title="history"
            />
    </menu>
    

    点击菜单弹出对话框,点击clear可以清除历史记录

    下面是dialog生成方式和实现history的方法放在了一起,主要用到的方法是setHistory()首先是生成dialog 的方法放在了上面onOptionsItemSelected(MenuItem item)的方法中
    下面贴上代码

    /**
         * 显示域名历史记录的dialog
         */
        public void showHistoryDialog(){
            mAllActivity=getActivity();
            AlertDialog.Builder mDialog=new AlertDialog.Builder(getActivity());
            /**
             * 设置列表内容(将从sp中取出的字符串以","分割成字符串数组)
             * 点击item时在输入框填入对应值
             * 点击clear时清空dialog
             */
            final String[] arrayOfString = this.getHistory().split(",");
            AlertDialog.Builder setItemsBuilder =
                    mDialog.setItems(arrayOfString, new DialogInterface.OnClickListener() {
                @Override
                public void onClick(DialogInterface dialog, int which) {
                    setEditText(arrayOfString[which]);
                }
            });
            mDialog.setTitle("History").setPositiveButton
                    ("Clear", new DialogInterface.OnClickListener() {
                @Override
                public void onClick(DialogInterface dialog, int which) {
                    setHistoryNull();
                }
            });
            mDialog.create();
            mDialog.show();
        }
    

    保存历史到sharedpreferences中,因为要保存的数据也不会太多,所以没有使用数据库而是采用了轻量级的存储方案那就是sp,键值对,让数据对应history
    首先是将搜到的域名以长字符串连接起来

    private SharedPreferences mSharedPreferences;
    
     /**
         * 判断并将域名存入sp(以","为间隔将字符串拼接成长字符串)
         * @param domainName
         * @return
         */
        public void setHistory(String domainName) {
            SharedPreferences.Editor localEditor;
            String nsString = this.mSharedPreferences.getString("history", "");
            //判断字符串是否为空
            if (nsString==""){
                nsString = domainName;
                localEditor = this.mSharedPreferences.edit();
                localEditor.putString("history", nsString);
                localEditor.commit();
            }
            //判断字符串是否含有搜索过的的域名
            if (!nsString.contains(domainName)) {
                nsString = nsString + "," + domainName;
                localEditor = this.mSharedPreferences.edit();
                localEditor.putString("history", nsString);
                localEditor.commit();
            }
        }
    

    该函数放在查询功能中加一个判断
    放在之前的主函数中即可

      //查询成功则将域名传入sp
            if (mlookup.getResult()==Lookup.SUCCESSFUL){
                setHistory(s);
                Log.d("--------调试sp-------");
                System.out.print(mSharedPreferences.getString("history",""));
            }
    

    之后是关于clear清空按钮的函数以及sp获取方法等

    /**
         * 从sp中获取数据
         */
        public String getHistory()
        {
            return this.mSharedPreferences.getString("history", "");
        }
    
    
        /**
         * 清空dialog
         */
        public void setHistoryNull()
        {
            SharedPreferences.Editor localEditor = this.mSharedPreferences.edit();
            localEditor.putString("history", null);
            localEditor.commit();
        }
    
    
        /**
         * 设置输入框数据
         * @param paramString
         */
        public void setEditText(String paramString)
        {
            this.mEdiText.setText(paramString);
        }
    

    做一个文本框清除按钮

    ns--clear.png

    思路也很清晰将按钮放在文本框末端并且设置成隐藏,与文本框的焦点获取绑定到一起即可

     android:visibility="gone"
    

    那么之后在文件夹中添加一个图片类似于叉号的都可以
    在drawable文件夹右键→new→image asset


    imageassert.png imageassert1.png

    google提供了很多Android原生按钮,样式背景以及颜色都可以自己实设置之后点击next然后添加到文件夹即可
    然后实现代码

    mClearButton=(Button) mNslookupView.findViewById(R.id.btn_ns_clear);
            mClearButton.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    //点击按钮清空输入框
                    mEdiText.getText().clear();
                }
            });
            mTextView = (TextView) mNslookupView.findViewById(R.id.text_showip);
            //输入框
            mEdiText = (EditText) mNslookupView.findViewById(R.id.edit_nslookup);
            //用户触摸EditText得到焦点
            mEdiText.setOnFocusChangeListener(new View.OnFocusChangeListener() {
                @Override
                public void onFocusChange(View v, boolean hasFocus) {
                    if (hasFocus){
                        //得到焦点时显示清除按钮
                        mClearButton.setVisibility(View.VISIBLE);
                    }else {
                        //失去焦点时隐藏清除按钮
                        mClearButton.setVisibility(View.GONE);
                    }
                }
    
            });
    

    大概就完成了这么多,还可以添加一些东西例如回车键绑定搜索按键,对获取到的信息进行格式化处理,点击按钮直接copy显示出的ip点击并且直接访问该网站,最近在学习material design设计模式等等。
    Android目前还在学习中在这里做点小总结,希望以后坚持下去,对工作或学习做一些总结,每天收获一点东西,厚积薄发。
    最后贴上一张偶像照片。


    王力宏《无问西东》饰演热血飞行员沈光耀.jpg

    相关文章

      网友评论

      本文标题:Android-域名查询总结

      本文链接:https://www.haomeiwen.com/subject/dpagvftx.html