Android-域名查询总结

这一段时间在给项目新增一个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
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,558评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,002评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,036评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,024评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,144评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,255评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,295评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,068评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,478评论 1 305
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,789评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,965评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,649评论 4 336
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,267评论 3 318
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,982评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,223评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,800评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,847评论 2 351