说三道四技术文摘-感悟人生的经典句子
说三道四 > 文档快照

Android开发进阶之NIO非阻塞包(八)

HTML文档下载 WORD文档下载 PDF文档下载
Android开发进阶之NIO非阻塞包(八)

作者:Android开发网


   在整个DDMS中体现Android NIO主要框架的要数MonitorThread.java这个文件了,有关PC和Android手机同步以及NIO非阻塞编程的精髓可以在下面的文件中充分体现出来。

  final class MonitorThread extends Thread {

    private static final int CLIENT_READY = 2;

    private static final int CLIENT_DISCONNECTED = 3;

    private volatile boolean mQuit = false;

    private ArrayList<Client> mClientList; //用一个数组保存客户端信息

    private Selector mSelector;

    private HashMap<Integer, ChunkHandler> mHandlerMap; //这里Android123提示大家,由于在多线程中concurrentHashMap效率比HashMap更安全高效,推荐使用并发库的这个替代版本。

    private ServerSocketChannel mDebugSelectedChan; //一个用于调试的服务器通道

    private int mNewDebugSelectedPort;

    private int mDebugSelectedPort = -1;

    private Client mSelectedClient = null;

    private static MonitorThread mInstance;

    private MonitorThread() {
        super("Monitor");
        mClientList = new ArrayList<Client>();
        mHandlerMap = new HashMap<Integer, ChunkHandler>();

        mNewDebugSelectedPort = DdmPreferences.getSelectedDebugPort();
    }

    static MonitorThread createInstance() {  //创建实例
        return mInstance = new MonitorThread();
    }

    static MonitorThread getInstance() { //获取实例
        return mInstance;
    }

    synchronized void setDebugSelectedPort(int port) throws IllegalStateException { //设置调试端口号
        if (mInstance == null) {
            return;
        }

        if (AndroidDebugBridge.getClientSupport() == false) {
            return;
        }

        if (mDebugSelectedChan != null) {
            Log.d("ddms", "Changing debug-selected port to " + port);
            mNewDebugSelectedPort = port;
            wakeup(); //这里用来唤醒所有的Selector
        } else {
            // we set mNewDebugSelectedPort instead of mDebugSelectedPort so that it's automatically
            mNewDebugSelectedPort = port;
        }
    }

    synchronized void setSelectedClient(Client selectedClient) {
        if (mInstance == null) {
            return;
        }

        if (mSelectedClient != selectedClient) {
            Client oldClient = mSelectedClient;
            mSelectedClient = selectedClient;

            if (oldClient != null) {
                oldClient.update(Client.CHANGE_PORT);
            }

            if (mSelectedClient != null) {
                mSelectedClient.update(Client.CHANGE_PORT);
            }
        }
    }

    Client getSelectedClient() {
        return mSelectedClient;
    }

    boolean getRetryOnBadHandshake() {
        return true; // TODO? make configurable
    }

    Client[] getClients() {
        synchronized (mClientList) {
            return mClientList.toArray(new Client[0]);
        }
    }

    synchronized void registerChunkHandler(int type, ChunkHandler handler) {
        if (mInstance == null) {
            return;
        }

        synchronized (mHandlerMap) {
            if (mHandlerMap.get(type) == null) {
                mHandlerMap.put(type, handler);
            }
        }
    }

    @Override
    public void run() { //本类的主要线程
        Log.d("ddms", "Monitor is up");

        try {
            mSelector = Selector.open();
        } catch (IOException ioe) {
            Log.logAndDisplay(LogLevel.ERROR, "ddms",
                    "Failed to initialize Monitor Thread: " + ioe.getMessage());
            return;
        }

        while (!mQuit) {

            try {
                synchronized (mClientList) {
                }

                try {
                    if (AndroidDebugBridge.getClientSupport()) {
                        if ((mDebugSelectedChan == null ||
                                mNewDebugSelectedPort != mDebugSelectedPort) &&
                                mNewDebugSelectedPort != -1) {
                            if (reopenDebugSelectedPort()) {
                                mDebugSelectedPort = mNewDebugSelectedPort;
                            }
                        }
                    }
                } catch (IOException ioe) {
                    Log.e("ddms",
                            "Failed to reopen debug port for Selected Client to: " + mNewDebugSelectedPort);
                    Log.e("ddms", ioe);
                    mNewDebugSelectedPort = mDebugSelectedPort; // no retry
                }

                int count;
                try {
                    count = mSelector.select();
                } catch (IOException ioe) {
                    ioe.printStackTrace();
                    continue;
                } catch (CancelledKeyException cke) {
                    continue;
                }

                if (count == 0) {
                    continue;
                } //这里代码写的不是很好,Android开发网提示大家因为这个NIO是DDMS工作在PC端的还不明显,这样轮训的在一个while中,效率不是很高,CPU很容易占用率很高。

                Set<SelectionKey> keys = mSelector.selectedKeys();
                Iterator<SelectionKey> iter = keys.iterator(); //使用迭代器获取这个选择键

                while (iter.hasNext()) {
                    SelectionKey key = iter.next();
                    iter.remove();

                    try {
                        if (key.attachment() instanceof Client) { //判断收到的key的附件是否是Client的实例
                            processClientActivity(key);
                        }
                        else if (key.attachment() instanceof Debugger) { //如果是Debug实例
                            processDebuggerActivity(key);
                        }
                        else if (key.attachment() instanceof MonitorThread) {
                            processDebugSelectedActivity(key);
                        }
                        else {
                            Log.e("ddms", "unknown activity key");
                        }
                    } catch (Exception e) {
                        Log.e("ddms", "Exception during activity from Selector.");
                        Log.e("ddms", e);
                    }
                }
            } catch (Exception e) {
                Log.e("ddms", "Exception MonitorThread.run()");
                Log.e("ddms", e);
            }
        }
    }

    int getDebugSelectedPort() {
        return mDebugSelectedPort;
    }

    private void processClientActivity(SelectionKey key) {
        Client client = (Client)key.attachment();

        try {
            if (key.isReadable() == false || key.isValid() == false) {
                Log.d("ddms", "Invalid key from " + client + ". Dropping client.");
                dropClient(client, true /* notify */);
                return;
            }

            client.read();

            JdwpPacket packet = client.getJdwpPacket();
            while (packet != null) {
                if (packet.isDdmPacket()) {
                    // unsolicited DDM request - hand it off
                    assert !packet.isReply();
                    callHandler(client, packet, null);
                    packet.consume();
                } else if (packet.isReply()
                        && client.isResponseToUs(packet.getId()) != null) {
                    // reply to earlier DDM request
                    ChunkHandler handler = client
                            .isResponseToUs(packet.getId());
                    if (packet.isError())
                        client.packetFailed(packet);
                    else if (packet.isEmpty())
                        Log.d("ddms", "Got empty reply for 0x"
                                + Integer.toHexString(packet.getId())
                                + " from " + client);
                    else
                        callHandler(client, packet, handler);
                    packet.consume();
                    client.removeRequestId(packet.getId());
                } else {
                    Log.v("ddms", "Forwarding client "
                            + (packet.isReply() ? "reply" : "event") + " 0x"
                            + Integer.toHexString(packet.getId()) + " to "
                            + client.getDebugger());
                    client.forwardPacketToDebugger(packet);
                }

                packet = client.getJdwpPacket();
            }
        } catch (CancelledKeyException e) { //注意正确处理这个异常
            dropClient(client, true /* notify */);
        } catch (IOException ex) {
            dropClient(client, true /* notify */);
        } catch (Exception ex) {
            Log.e("ddms", ex);

            dropClient(client, true /* notify */);

            if (ex instanceof BufferOverflowException) { //可能存在缓冲区异常
                Log.w("ddms",
                        "Client data packet exceeded maximum buffer size "
                                + client);
            } else {
                // don't know what this is, display it
                Log.e("ddms", ex);
            }
        }
    }

    private void callHandler(Client client, JdwpPacket packet,
            ChunkHandler handler) {

        // on first DDM packet received, broadcast a "ready" message
        if (!client.ddmSeen())
            broadcast(CLIENT_READY, client);

        ByteBuffer buf = packet.getPayload();
        int type, length;
        boolean reply = true;

        type = buf.getInt();
        length = buf.getInt();

        if (handler == null) {
            // not a reply, figure out who wants it
            synchronized (mHandlerMap) {
                handler = mHandlerMap.get(type);
                reply = false;
            }
        }

        if (handler == null) {
            Log.w("ddms", "Received unsupported chunk type "
                    + ChunkHandler.name(type) + " (len=" + length + ")");
        } else {
            Log.d("ddms", "Calling handler for " + ChunkHandler.name(type)
                    + " [" + handler + "] (len=" + length + ")");
            ByteBuffer ibuf = buf.slice();
            ByteBuffer roBuf = ibuf.asReadOnlyBuffer(); // enforce R/O
            roBuf.order(ChunkHandler.CHUNK_ORDER);
            synchronized (mClientList) {
                handler.handleChunk(client, type, roBuf, reply, packet.getId());
            }
        }
    }

    synchronized void dropClient(Client client, boolean notify) {
        if (mInstance == null) {
            return;
        }

        synchronized (mClientList) {
            if (mClientList.remove(client) == false) {
                return;
            }
        }
        client.close(notify);
        broadcast(CLIENT_DISCONNECTED, client);

        /*
         * http://forum.java.sun.com/thread.jspa?threadID=726715&start=0
         * http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=5073504
         */
        wakeup();
    }

    /*
     * Process activity from one of the debugger sockets. This could be a new
     * connection or a data packet.
     */
    private void processDebuggerActivity(SelectionKey key) {
        Debugger dbg = (Debugger)key.attachment();

        try {
            if (key.isAcceptable()) { //处理Server响应这个事件
                try {
                    acceptNewDebugger(dbg, null);
                } catch (IOException ioe) {
                    Log.w("ddms", "debugger accept() failed");
                    ioe.printStackTrace();
                }
            } else if (key.isReadable()) { //如果是收到的数据,则可读取
                processDebuggerData(key);
            } else {
                Log.d("ddm-debugger", "key in unknown state");
            }
        } catch (CancelledKeyException cke) { //记住,NIO处理这个异常,很多入门的开发者很容易忘记
            // key has been cancelled we can ignore that.
        }
    }

     private void acceptNewDebugger(Debugger dbg, ServerSocketChannel acceptChan) //这里用到了阻塞方式
            throws IOException {

        synchronized (mClientList) {
            SocketChannel chan;

            if (acceptChan == null)
                chan = dbg.accept();
            else
                chan = dbg.accept(acceptChan);

            if (chan != null) {
                chan.socket().setTcpNoDelay(true);

                wakeup();

                try {
                    chan.register(mSelector, SelectionKey.OP_READ, dbg);
                } catch (IOException ioe) {
                    // failed, drop the connection
                    dbg.closeData();
                    throw ioe;
                } catch (RuntimeException re) {
                    // failed, drop the connection
                    dbg.closeData();
                    throw re;
                }
            } else {
                Log.w("ddms", "ignoring duplicate debugger");
            }
        }
    }

    private void processDebuggerData(SelectionKey key) {
        Debugger dbg = (Debugger)key.attachment();

        try {
            dbg.read();

            JdwpPacket packet = dbg.getJdwpPacket();
            while (packet != null) {
                Log.v("ddms", "Forwarding dbg req 0x"
                        + Integer.toHexString(packet.getId()) + " to "
                        + dbg.getClient());

                dbg.forwardPacketToClient(packet);

                packet = dbg.getJdwpPacket();
            }
        } catch (IOException ioe) {
            Log.d("ddms", "Closing connection to debugger " + dbg);
            dbg.closeData();
            Client client = dbg.getClient();
            if (client.isDdmAware()) {
                   Log.d("ddms", " (recycling client connection as well)");

                    client.getDeviceImpl().getMonitor().addClientToDropAndReopen(client,
                        IDebugPortProvider.NO_STATIC_PORT);
            } else {
                Log.d("ddms", " (recycling client connection as well)");
                // we should drop the client, but also attempt to reopen it.
                // This is done by the DeviceMonitor.
                client.getDeviceImpl().getMonitor().addClientToDropAndReopen(client,
                        IDebugPortProvider.NO_STATIC_PORT);
            }
        }
    }

    private void wakeup() {
        mSelector.wakeup();
    }

    synchronized void quit() {
        mQuit = true;
        wakeup();
        Log.d("ddms", "Waiting for Monitor thread");
        try {
            this.join();
            // since we're quitting, lets drop all the client and disconnect
            // the DebugSelectedPort
            synchronized (mClientList) {
                for (Client c : mClientList) {
                    c.close(false /* notify */);
                    broadcast(CLIENT_DISCONNECTED, c);
                }
                mClientList.clear();
            }

            if (mDebugSelectedChan != null) {
                mDebugSelectedChan.close();
                mDebugSelectedChan.socket().close();
                mDebugSelectedChan = null;
            }
            mSelector.close();
        } catch (InterruptedException ie) {
            ie.printStackTrace();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

        mInstance = null;
    }

    synchronized void addClient(Client client) {
        if (mInstance == null) {
            return;
        }

        Log.d("ddms", "Adding new client " + client);

        synchronized (mClientList) {
            mClientList.add(client);

            try {
                wakeup();

                client.register(mSelector);

                Debugger dbg = client.getDebugger();
                if (dbg != null) {
                    dbg.registerListener(mSelector);
                }
            } catch (IOException ioe) {
                // not really expecting this to happen
                ioe.printStackTrace();
            }
        }
    }

    /*
     * Broadcast an event to all message handlers.
     */
    private void broadcast(int event, Client client) {
        Log.d("ddms", "broadcast " + event + ": " + client);

        /*
         * The handler objects appear once in mHandlerMap for each message they
         * handle. We want to notify them once each, so we convert the HashMap
         * to a HashSet before we iterate.
         */
        HashSet<ChunkHandler> set;
        synchronized (mHandlerMap) {
            Collection<ChunkHandler> values = mHandlerMap.values();
            set = new HashSet<ChunkHandler>(values);
        }

        Iterator<ChunkHandler> iter = set.iterator();
        while (iter.hasNext()) {
            ChunkHandler handler = iter.next();
            switch (event) {
                case CLIENT_READY:
                    try {
                        handler.clientReady(client);
                    } catch (IOException ioe) {
                        // Something failed with the client. It should
                        // fall out of the list the next time we try to
                        // do something with it, so we discard the
                        // exception here and assume cleanup will happen
                        // later. May need to propagate farther. The
                        // trouble is that not all values for "event" may
                        // actually throw an exception.
                        Log.w("ddms",
                                "Got exception while broadcasting 'ready'");
                        return;
                    }
                    break;
                case CLIENT_DISCONNECTED:
                    handler.clientDisconnected(client);
                    break;
                default:
                    throw new UnsupportedOperationException();
            }
        }

    }

    /**
     * Opens (or reopens) the "debug selected" port and listen for connections.
     * @return true if the port was opened successfully.
     * @throws IOException
     */
    private boolean reopenDebugSelectedPort() throws IOException {

        Log.d("ddms", "reopen debug-selected port: " + mNewDebugSelectedPort);
        if (mDebugSelectedChan != null) {
            mDebugSelectedChan.close();
        }

        mDebugSelectedChan = ServerSocketChannel.open();
        mDebugSelectedChan.configureBlocking(false); // required for Selector

        InetSocketAddress addr = new InetSocketAddress(
                InetAddress.getByName("localhost"), //$NON-NLS-1$
                mNewDebugSelectedPort);
        mDebugSelectedChan.socket().setReuseAddress(true); // enable SO_REUSEADDR

        try {
            mDebugSelectedChan.socket().bind(addr);
            if (mSelectedClient != null) {
                mSelectedClient.update(Client.CHANGE_PORT);
            }

            mDebugSelectedChan.register(mSelector, SelectionKey.OP_ACCEPT, this);

            return true;
        } catch (java.net.BindException e) {
            displayDebugSelectedBindError(mNewDebugSelectedPort);

            // do not attempt to reopen it.
            mDebugSelectedChan = null;
            mNewDebugSelectedPort = -1;

            return false;
        }
    }

    /*
     * We have some activity on the "debug selected" port. Handle it.
     */
    private void processDebugSelectedActivity(SelectionKey key) {
        assert key.isAcceptable();

        ServerSocketChannel acceptChan = (ServerSocketChannel)key.channel();

        /*
         * Find the debugger associated with the currently-selected client.
         */
        if (mSelectedClient != null) {
            Debugger dbg = mSelectedClient.getDebugger();

            if (dbg != null) {
                Log.d("ddms", "Accepting connection on 'debug selected' port");
                try {
                    acceptNewDebugger(dbg, acceptChan);
                } catch (IOException ioe) {
                    // client should be gone, keep going
                }

                return;
            }
        }

        Log.w("ddms",
                "Connection on 'debug selected' port, but none selected");
        try {
            SocketChannel chan = acceptChan.accept();
            chan.close();
        } catch (IOException ioe) {
            // not expected; client should be gone, keep going
        } catch (NotYetBoundException e) {
            displayDebugSelectedBindError(mDebugSelectedPort);
        }
    }

    private void displayDebugSelectedBindError(int port) {
        String message = String.format(
                "Could not open Selected VM debug port (%1$d). Make sure you do not have another instance of DDMS or of the eclipse plugin running. If it's being used by something else, choose a new port number in the preferences.",
                port);

        Log.logAndDisplay(LogLevel.ERROR, "ddms", message);
    }
}

  从上面来看Android的开源代码有关PC上的写的不是很好,很多实现的地方都是用了严重的缝缝补补方式解决,有些习惯不是很到位,有关本NIO例子由于涉及的项目对象多,理解需要网友深入分析DDMS源码中的每个对象。细节写的不是很理想,Android123推荐大家,画出UML后再分析更清晰。

开始3D编程前需注意的十件事 [CTO俱乐部第100期]软件平台的新思维及2345技术团队管理经验分享 失败怪圈:今天的移动App在重蹈1999年的覆辙 极客编程必备的五大PHP开发应用 Oracle或将宣布与Salesforce.com,NetSuite以及微软结盟 【专访间】神州数码谢耘:智慧城市需要以“融合平台”为特征的沃尔玛模式 对话OW2 CEO:开源≠安全隐患,免费≠无利可图 不断抄袭的Facebook正逐步沦为一家普通的公司 Windows 8.1会不会把Windows 8的“Modern”开发者踢到路边 任志鹏:FusionCloud是华为二十年技术积累的精华 机器学习典范?iOS7中的Siri能主动学习如何发音 三星宣布关闭PC业务 主推便携设备和一体机 Web设计师必须掌握的六大设计策略 Facebook为什么要推Instagram视频分享功能? 天气预报(id后面的9位数字为城市代码)年月日,星期,当前时间(仅用于javascript) PHP 5.5.0发布 不再支持Windows XP和2003 专访ThinkPHP创始人刘晨:用最简单最快速的方式开发PHP应用 重构:仔细查看,改进代码 加速编码的17款最棒的CSS工具 生于微信:专访疯狂猜图CEO曹晓刚 谷歌:受感染的合法网站远比恶意网站危险性大 Mozilla正式发布Firefox 22应用程序开发者受益 支持3D游戏、视频通话和文件分享 直接拿来用!最火的前端开发项目(一) Salesforce.com和Oracle在云合作上达成长期协议 即将到来的数据中心僵尸末日,谁能成为最后的幸存者?! Andorid APK反逆向解决方案:梆梆加固原理探寻 科技拥抱设计:体验设计如何创新? Rails 4.0正式发布 命途多舛的webOS,两度易主,生生死死多少回 Citrix全面开源XenServer 为了三十亿人的网络幸福:Google投资O3b发射网络卫星 我想作一个属性页,但如何控制各页的大小?谢谢帮助 播放完flash后,窗体卸载出现异常。除非用end来结束程序。 关于java规范 关于按钮、工具条等问题 怎么控制不能使以一个用户身份多次登录系统??? .NET 2003都直接在CLR层面支持GP了,哪位知道JDK1.5的消息? 为什么不能写服务器 请问那款手机支持wtk2.0 JBuilder8还有Weblogic版的吗?用企业版能和Jbuilder8结合好吗? 各位高手,为什么我装好VS.NET后,有帮助,而我过一段时候后,再去用,怎么会没有帮助了呢 水晶报表问题:当鼠标放在显示的字段上时,会显示“字段名称“的ToolTip提示,怎么样不让他显示提示? 2000下开发的程序在98或95上使用的问题 谁能告诉我关于文件传输的思路 怎样向数据库中添加记录? 如何自動截取上傳文件的文件名保存在數據庫里? TCHAR[32]和CString, char *之间应该如何转化? 怎样在程序里显示jpg,gif图片? 关于按钮、工具条等问题,请进来看看 有两个CTime变量,t1,t2,我想把t1的日期部分和t2的时间部分合并成一个CTime变量t3,怎么实现? 有谁知道珠海的做J2EE薪水有多少(平均水平),谢谢大家! 我编写的CORBA为何总是提示"File not found 'OrbPas.dcu'" 准备今天打辞职报告! 请教高人!!!!!!!!!!!!!!!!!!!!!!!!!!!! LINUX支持多處理器 ActiveReports P2 套打票据打几页后出现套不准,请教? 关于delphi中Label控件的字体问题?? 请问哪里有ASP+ACCESS 的查询源程序下载? 小弟需要一份详细的数据库设计说明,不知有没有人可以帮忙? 請問銀行系統一般用什麼數據庫! 请大家推荐一个方便好用的网格控件,可以编辑、保存单元格的内容,bug又少的 急死我了,这行SQL语句错在那里呢? ORACLE8I安装问题,急。。。。。。 还是这两本书110分 急救:主板CMOS故障!! 有谁知道在JBuilder中插入一副图片应该用哪个Compoent 加急问题??关于.properties!!在线等, Win2K下控制台程序处于选定状态,如何恢复? 窗口自动关闭 救命啊!严重问题!怎么老是显示E2316:TempSongList::PlayFirstSong()is not a member of 'TempSongList'? 关一个小笨蛋生存的困惑,还请各位GG、JJ一定要帮帮忙呀~~~~ Interface中添加方法的问题,请高手指教!再线讨论!! c#访问FOXPOR数据库问题 WORD求源代码加分之四 怎么样获得一个 EDIT 的 句柄 ?? 请问嵌入网页中的Activex如何调整自身的大小?请各位高手帮忙!谢谢! Oracle和SQLSERVER中5用户10用户到底是什么意思? 我身边的一个PLMM说,她想找一个成熟、幽默、聪明的程序员。 一个局域网内访问odbc数据源的问题 对女孩子不能太热情了! 一个easy 的question 请精通C#与VB.net的大虾帮忙翻译一下 饱和碳酸钠溶液的浓度是多少g/l?常温时,碳酸钠在饱和盐水溶液中的溶解度呢? 二氧化锰和稀盐酸为什么不可以反应? 海里的水……盐阅读答案 数控车床加工铝,不锈钢,铜,紫铜,铁,45钢,铸成钢,塑料,PVC,铜 的进给量请问数控车床加工铝,不锈钢,铜,紫铜,铁,45钢,铸成钢,塑料,PVC,铜,锌 的进给量和转速还有单边吃刀量,分别是多少?就用G99 就 扩张性的财政政策如何引起赤字增加?谁能给个分析? 盐酸挥发的气体吸入呼吸道有害吗 视频监控可以用网线(双绞线)作监控线传输视频信号吗?1、实际安装视频监控中,有用网线(双绞线)作电源线的,那么可否用网线作视频线呢?2、两个摄像头“视频-”信号可以用一根线传输 那个带铜丝的插头插哪里忘了说了,这是电视机顶盒 盐酸和铁铜镍合金粉反应,释放出臭味气体,这是什么气体,吸入对人体有害吗?合金粉内含少量石墨,反应容器是软塑料橡胶桶,这个臭味和这些有关吗 用双绞线其中2组当监控视频信号线,剩下的当电力传输线,应该怎么弄,可以直接接入220V交流传输吗?电力传输的意思就是.一头接上插头 直接插到220V电源插座上,另一头接到摄像机上的电源上然 2*4的铜线 配16A插头 插头太大,可以换成10A插头吗?最近入了两卷2*4的铜线和一批16A插头,插座,打算自己做一批排插,接上后发现16A插头太大插不进家用的,能换成10A的吗? 电子为什么绕核运动 这和牛顿定力矛盾吗 25对大对数电缆端接几对双绞线 Fe+(SCN)2、(CN)2+NaOH、HSCN+MnO2(加热)的反应方程式分别是什么? 硫酸与二氧化硅是否反应 蓝色配红色得出什么颜色 云南省为什么可以种植葡萄气候、土壤关系 请问盐酸和水泥反应会生成什么气体,对人体有害吗? 蓝色和红色能搭配出颜色吗? 有关纯硫酸和纯硝酸混合液的若干问题1 纯硫酸的自身电离方程式?2 纯硫酸和纯硝酸混合液,各自存在电离平衡,混合酸中必然发生的反应是? 细胞减数第一次分裂前期中,核膜和核仁都是在终变期消失的吗? 动物的伪装对生存有什么意义 葡萄对环境的要求想种植红提 ,但当地铅厂比较多.空气和土壤铅污染比较严重.不知道是否对葡萄的生长,产量以及品质有多大的影响. 水泥和盐酸反应为什么有臭鸡蛋味的气体生成呢?生成物是什么呢?生成的是什么胶啊? HCl先与Ag、Mg、Fe、CuO中的哪个反应将Ag、Mg、Fe、CuO组成的混合物,放入足量的盐酸中,反应后溶液呈篮绿色,过滤,滤纸上可能含有 硫酸和硝酸加在一起会怎样? 氢氧化钠醇溶液的配制:是先将NaOH溶于水,再溶于醇,还是直接将NaOH溶于醇中? 那些动物会伪装 用二氧化锰与稀盐酸反应制取氯气为什么不可以? 经济学:社会总剩余的小疑问大热天,小B口干舌燥,他对第一瓶水的评价是7元,对第二瓶水的评价是5元,对第三瓶水的评价是3元,对第四瓶水的评价是1元.小E有一台抽水机,随着抽水越来越困难, 能伪装的动物有哪些 fe(scn)3+fe反应么?若反应求方程式 计算氢原子中电子绕核运动的周期和线速度的大小 化学中关于光与光合作用这一节中光合作用制造有机物的量与光合作用积累有机物的量的区别,越详细越好. 紧缩性财政政策和扩张性财政政策是针对什么问题实施的,具体内容是什么? 椭圆运动周期公式是什么 盐酸 硝酸是混合物吗? HCO3-与Ca2+反应生成CaCO3↓和H+RT,有没有这样的反应,如果没有,那应该是什么? 我想做个手工蜡烛,不愿意出门买石蜡,请问,可以直接把家里的蜡烛溶解吗?我很外行的,嘻嘻. 过去,由于环境污染和生态平衡破坏,美国多次发生()灾难 H+ HCO3-结合生成什么 英语翻译 为什么说扩张性财政政策是以财政赤字为前提的 Hcl、H2SO4溶液均能使紫色石蕊变什么色? 铅笔成分 光合作用积累的有机物量判断光合作用和呼吸作用中,积累的有机物量是看瞬时净光合作用速率最大还是长期积累,但光合作用速率已降低? 有线电视线接法我刚搬到新的地方住,想从房东家里接有线电视看.我早上弄了一节线,结果接上后,房东的信号受到了很大的干扰.我家的电视也没有信号.我想问一下,我直接从房东的有线电视线 “西气东输”几次跨过黄河? 有机物的大量积累会使光合作用减弱,为什么呢? 有线电视线断了要怎么接衰减才小?人家说的双通对接什么意思 西气东输跨越黄河干流区段分别经过哪几个地形区 人体卵细胞与精子细胞中DNA分子的化学成分与物理结构有何分别,我有一设想,假如通过人工手段进行以下操作可否得出结果,首先用一吸管将一枚卵细胞的细胞核取出,再另取一枚精子,吸出细 Fe离子+3SCN离子=Fe(SCN)3 (红)怎么反应的?又没有沉淀,为什么还是离子的他们突然就变红了呢?谁是红的? 请问:纳米二氧化硅在水中PH=6左右时为什么会突然发生凝胶现象? 精细胞的化学成分会更新吗是不是新陈代谢贯穿所有细胞的始终 Fe(SCN)3溶液中加入NaOH为何会产生白色沉淀? 细胞有丝分裂前期结束时,核膜核仁为什么会消失 水+蓝矾+甲醇+洗洁精=什么?这几样东西参一起后会发生化学反应吗?如果会的话,产物是什么?不用甲醇了,改用乙醇.水+蓝矾+已醇+洗洁精=什么? 配位化合物是不是沉淀 如果不是 那Fe(SCN)3与NaOH会不会反应产生沉淀FeCL3+3KSCN=Fe (SCN)3+3KCL是可逆反应 平衡后,加入适量NaOH固体,平衡是否会移动 向什么方向移动? 处于有丝分裂前期的细胞均无核仁,这句话是对是错?原因.答案上说是错误的,完整的题目是这样的:观察植物细胞有丝分裂实验现象中, 中国代表在联大全会阐述对国际刑事法院救人干部王青意、蔡福想抢救画面曝光 新加坡制造业和服务业下调未来半年前景全球首辆太阳能小轿车在新加坡亮相德国海关发现1500余幅毕加索等大师加涉毒市长为犯错表歉意 但未明确承认日台风灾区一男子称受灾系因禁止大麻而孙燕姿鲁豫户田惠梨香 女星骨瘦如柴太澳男子头部遭枪击送医后仅数小时自行离泰游船沉没已致6人罹难 包括一名中国美国务卿访问埃及 美将与埃过渡政府合农夫山泉派员举报《京华时报》 称实行印度将发射环火星探测器 否认与华“太调查显示常戴有毒银饰或影响孩子智力叙提前完成销毁化武初期任务 美被指进局势动荡民众对政治改革灰心 埃及再现土耳其总统称叙利亚正成为“地中海的阿国家旅游局:明年节假日方案将参考网络学生网上留言校方32个月后回复 网友美国伯明翰机场收到威胁后紧急关闭 已欧盟欲规范产地标签 德国或成“中国制屈原故里:诗是一种信仰、一种生活方式台“刑事诉讼法”修法 保证金发还应加赵天麟主张民进党进行两岸政策大辩论“立院”挑灯清仓法案王金平:案子不是台“美胸女神”出炉:林采缇打败林志玲桃园副县长用公款雇工扫厝内引争议台一项最新民调显示:民众乐见张志军访云南省盈江县6.1级地震致20万人受蔡英文回锅再任党主席民进党分裂的开始被疑有辱校誉 台一女大学生创建“约炮台“立院”选制改革 学者吁理性讨论台“立院”通过126亿元治水预算台媒:“平时不会临时才会”“立院”沦台82岁老校长读取硕士 求学三年从未雾都富豪吴旭的“贵人们”重庆富豪吴旭被查 与落马高官谭栖伟交汉语促进中以交流(高清组图)台湾GDP成长率、人均水准敬陪亚洲“陪他一辈子!河北三河老人照顾瘫痪老伴广州历史建筑冠英书院起火 烧穿半个屋端午插艾习俗与张献忠有关 命令军队保
备案号:鲁ICP备13029499号-2 说三道四 www.s3d4.cn 说三道四技术文摘