第 12 页的文章列表

c++11实现的跨平台定时器

一、引言

在一个比较大的系统当中,很多地方都需要定时功能,比如某些存在内存中的数据,需要定时的做持久化(实时持久化可能带来很大的性能问题),如果这个系统要求跨平台。那么实现一个跨平台的定时器就很有必要了。 c++11的标准中,实现了多线程,头文件为<thread>,也有了系统时间相关的库,头文件为<chrono>,这使得使用c++11实现跨平台的定时器有了可能。在这之前,多线程一般都是使用操作系统相关的库。

二、 定时器的基本功能

在定时器的实现之前,需要事先计划好一个定时器的基本功能。大概的功能如下: 1.添加一个定时事件 2.删除一个指定的定时事件 3.定时事件支持只执行一次或者重复执行 4.定时器启动指令 这个部分的内容在Linux 下定时器的实现方式分析中有很详细的介绍

三、 数据结构知识预备

在实现定时器中,需要有一个容器去存储所有的定时事件,比如说最容易想到的数组,如果我们用数组保存定时事件,那么在找出当前需要执行的定时事件的伪代码就是

    for(i = 0 ; i < events_array_size; ++i){
        if(events[i].time <= current_time){
            exec_in_child_thread(events);
        }
    }

这里就是遍历所有的事件,将已经到了执行时间的事件放到子线程中去执行。 在这里有一个可以优化效率问题:注意到每一次查询是否有事件到了执行时间,都会遍历整个数组,也就是O(n)的复杂度,并且查询周期也是非常短的,所以会有非常大的性能问题。因此,我们可以将这里的数组变成有序的,每一次只需查询第一个事件,如果还没到执行时间,那么之后的事件就不用遍历了。这样平均下来就是O(1)的时间复杂度。考虑到我们在插入,删除时始终要保证数组的有序,可以采用最小堆。也就是堆顶始终是最先到执行时间的一项。

四、c++11多线程的知识预备

4.1 join()还是detach()

c++11中创建一个线程很容易

#include <iostream>
#include <thread>

void function_1(){
    std::cout << "hello world" << std::endl;
    while(true){}
}

int main(){
    std::thread t1(function_1);
    t1.join();      //加入运行

    std::cout << "hahah" << std::endl;

    return 0;
}

这段代码就是创建了一个t1的线程,来执行function_1函数。注意到我们这里使用了t1.join(),字面上的意思是将子线程加入的主线程来执行,这样导致的结果是主线程会在t1.join()这里停止,直到t1执行结束后再往下执行。在这里function_1中存在死循环,所以hahah永远不会被输出。而如果使用t1.detach()的话,即将子线程从主线程分离出去,那么主线程就会一直向下执行,在这里就会执行到return 0,然后主线程销毁,detach出去的子线程可能就没有任何显示的机会了(查过资料说detach出去子线程的行为的undefined的)。 在这个定时器的实现中,当然不能使用join(),因为会阻塞程序的正常运行。

4.2 如何使得定时器的使用是线程安全的。

在一个程序中,可能很多地方会使用到定时器去触发某个动作,但是为了效率,肯定不会创建多个定时器实例,因此定时器的实现应该是单例的。因为需要单例,因此定时器的创建,定时器中定时事件的添加和删除都是线程安全的(因为同一时间可能会有多个线程操作定时器)。 4.2.1 线程安全的单例模式 单例模式的实现方法有很多种,因为能力问题,不对这一点多做叙述。可以看这一篇单例模式(Singleton)及其C++实现。 我这里使用的是使用static来创建局部可见的全局变量

static Timer* getInstance(std::chrono::milliseconds tick){
    static Timer timer(tick);
    return &timer;
}

4.2.2 使用锁保证定时事件的添加和删除是线程安全的。 在往定时器中添加和删除定时事件时,就是在做最小堆的添加和删除。这个中间线程不安全的现象为:以添加为例,往最小堆数组末尾加入一个数据,然后再逐层向上调整。如果在这个调整的过程中,另外一个线程执行了删除操作,两个调整过程就可能同时发生,造成不可预知的错误,使得最小堆并不有序。 所以添加和删除操作应该是互斥的,只有其中一个操作执行结束,另一个操作才可以开始。c++中可以使用信号量mutex和锁locker轻松的完成互斥的需求。示例代码如下:

#include <iostream>
#include <thread>
#include <string>
#include <mutex>
#include <fstream>

class LofFile{
    public:
        LofFile(){
            f.open("log.txt");
        }

        void shared_print(std::string id,int value){
            std::lock(m_mutex,m_mutex2);
            std::lock_guard<std::mutex> locker(m_mutex,std::adopt_lock);
            std::lock_guard<std::mutex> locker2(m_mutex2,std::adopt_lock);
            std::cout << "from" << id << ":" << value << std::endl;
        }

        void shared_print2(std::string id,int value){
            std::lock(m_mutex,m_mutex2);
            std::lock_guard<std::mutex> locker2(m_mutex2,std::adopt_lock);      //lock_guard是为了防止语句执行中出现异常,锁不被释放。这样做可以保证在退出代码块后解锁
            std::lock_guard<std::mutex> locker(m_mutex,std::adopt_lock);
            std::cout << "from" << id << ":" << value << std::endl;
        }

    protected:
    private:
        std::mutex m_mutex;     //使用信号量来解决资源竞争
        std::mutex m_mutex2;
        std::ofstream f;

};

void function_1(LofFile& log){
    for(int i = 0; i > -100; i--){
        log.shared_print("From t1:",i);
    }
};

int main(){
    LofFile log;
    std::thread t1(function_1,std::ref(log));//线程开始运行
    for(int i = 0; i < 100; i++){
        log.shared_print2("From main",i);
    }

    t1.join();
    return 0;
}

五、总结与实现

分析到这里,一个定时器的基本要素已经具备了。 1.用来存储定时事件,并且添加和删除操作都是线程安全的最小堆 2.可以多线程实现定时事件的执行从而不阻塞主线程。 下面贴出所有的实现代码:

5.1 最小堆的实现。

SortedHeap.hpp

/**
 * 排序堆的实现
 * 堆使用的是完全二叉树的结构,使用数组(数组从零开始)保存,那么最后一个非叶子节点为(n - 1) / 2
 * 堆的构建一直都是在有序的基础上的,那么每次调整只需比较i和(i - 1) / 2的元素,依次上推
 * 支持任意类的排序
 * 当前还不支持多线程环境下的使用
 * author:jiangpengfie
 * date:2017-05-09
 */
#ifndef SORTEDHEAP_H
#define SORTEDHEAP_H
#include <iostream>
#include <vector>
#include <functional>
#include <memory>
#include <mutex>
#include <condition_variable>
#include "src/core/util/util.h"


template<class T>
class SortedHeap{
    private:
        struct HeapNode{
            unsigned int id;
            T obj;
            HeapNode(unsigned int id,T t):obj(t){
                this->id = id;
            }
        };
        std::vector<HeapNode> heap;
        unsigned int autoIncrementId;
        std::function<bool(T& ,T&)> cmp;    //比较函数,实现选择构造最大堆还是最小堆
        std::mutex mu1;                
        std::mutex mu2;                

        /**
         * 插入节点后调整堆中不符合的节点
         */
        void adjustAfterInsert();

        /**
         * pop出堆顶元素后调整堆中不符合的节点
         */
        void adjustAfterPopTop();

        /**
         * 删除节点后调整堆中不符合的节点
         * @param i 删除的节点id
         */
        void adjustAfterDelete(int id);         

        void swap(HeapNode& t1,HeapNode& t2);

        void deleteNodeByPos(const unsigned int pos);
    public:
        /**
         * 构造函数
         * @param cmp 用来比较
         */
        SortedHeap(std::function<bool(T&,T&)> cmp);
        /**
         * 插入节点
         * @param node 插入的节点
         */
        unsigned int insertNode(T& node);
        /**
         * 删除节点,时间复杂度为O(n)
         * @param id  要删除的节点id
         */
        void deleteNode(unsigned int id);

        /**
         * pop最小的节点
         * @return T* 返回的最顶部的节点指针
         */
        std::unique_ptr<T> popTopNode();

        /**
         * 获取最顶部的节点
         * @return T 最顶部的节点指针
         */
        std::unique_ptr<T> getTopNode();

        /**
         * 删除顶部的节点
         *
         */
        void deleteTopNode();
};

template<typename T>
SortedHeap<T>::SortedHeap(std::function<bool(T&,T&)> cmp){
    this->cmp = cmp;
    this->autoIncrementId = 0;
}

template<typename T>
void SortedHeap<T>::swap(HeapNode& t1,HeapNode& t2){
    HeapNode tmp = t1;
    t1 = t2;
    t2 = tmp;
}

template<typename T>
void SortedHeap<T>::adjustAfterInsert(){
    int last = this->heap.size() - 1;
    int flag = true;
    //从插入的节点位置开始向上调整
    while(last > 0 && flag){
        if(this->cmp(this->heap[last].obj,this->heap[(last - 1) / 2].obj)){
            this->swap(this->heap[(last - 1) / 2],this->heap[last]);
        }else{
            //不需要调整了
            flag = false;
        }
        last = (last - 1) / 2;
    }
}

template<typename T>
void SortedHeap<T>::adjustAfterDelete(int pos){
    //从pos位置开始向下调整
    int last = this->heap.size() - 1;
    if(last == 0)
        return;     //最后一个不需要调整
    bool flag = true;   //标记是否需要调整
    while(pos <= (last - 1) / 2 && flag){
        //一直调整到最后一个非叶子结点
        int topNum = 0;     //记录最小的结点编号

          //(pos + 1) * 2 - 1是左孩子,pos是父
        if(this->cmp(this->heap[(pos + 1) * 2 - 1].obj,this->heap[pos].obj)){
            topNum = (pos + 1) * 2 - 1;
        }else{
            topNum = pos;
        }

        if((pos + 1) * 2 <= last){
            //如果存在右结点
            if(this->cmp(this->heap[(pos + 1) * 2].obj,this->heap[topNum].obj)){
                topNum = (pos + 1) * 2;
            }
        }

        //看看topNum是不是自己
        if(pos == topNum){
            //是自己就不用调整了
            flag = false;
        }else{
            //交换
            this->swap(this->heap[pos],this->heap[topNum]);
        }
        pos = topNum;
    }
}


template<typename T>
void SortedHeap<T>::deleteNodeByPos(const unsigned int pos){
    unsigned int last = this->heap.size() - 1;
    if(pos > last){
        return;
    }
    std::lock(mu1,mu2);             //上锁
    std::lock_guard<std::mutex> locker1(mu1,std::adopt_lock);
    std::lock_guard<std::mutex> locker2(mu2,std::adopt_lock);
    //与最后一个交换
    swap(this->heap[pos],this->heap[last]);
    //删除最后一个
    this->heap.pop_back();      

    this->adjustAfterDelete(pos);
}



template<typename T>
unsigned int SortedHeap<T>::insertNode(T& node){
    HeapNode hNode(this->autoIncrementId++,node);
    std::lock(mu1,mu2);             //上锁
    std::lock_guard<std::mutex> locker1(mu1,std::adopt_lock);
    std::lock_guard<std::mutex> locker2(mu2,std::adopt_lock);
    this->heap.push_back(hNode);     //先将node放在最后一位
    if(this->heap.size() != 1){
        //如果大小不等于1,则在新增节点后调整
        this->adjustAfterInsert();
    }
    return this->autoIncrementId - 1;
}


template<typename T>
void SortedHeap<T>::deleteNode(unsigned int id){
    for(unsigned int i = 0; i < this->heap.size(); i++){
        if(heap[i].id == id){
            //找到了id
            this->deleteNodeByPos(i);
            break;
        }
    }


}

template<typename T>
std::unique_ptr<T> SortedHeap<T>::popTopNode(){
    if(this->heap.size() != 0){
        std::unique_ptr<T> top(new T(this->heap[0].obj));
        this->deleteNodeByPos(0);
        return top;
    }else{
        std::unique_ptr<T> p = nullptr;
        return p;
    }
}

template<typename T>
std::unique_ptr<T> SortedHeap<T>::getTopNode(){
    if(this->heap.size() != 0){
        std::unique_ptr<T> top(new T(this->heap[0].obj));
        return top;
    }else{
        std::unique_ptr<T> p = nullptr;
        return p;
    }
}

template<typename T>
void SortedHeap<T>::deleteTopNode(){
   if(this->heap.size() != 0){
        this->deleteNodeByPos(0);
    }
}

#endif

5.2 定时器的实现

Timer.h

/**
 * 定时器的实现
 * 支持int setTimer(T interval,function action):设置一个定时器,指定间隔interval和回调函数action,返回定时器id
 * 支持void deleteTimer(int timerId):删除一个定时器
 * 数据结构:最小堆模型,按照定时器触发的时间排序
 * author:jiangpengfei
 * date:2017-05-09
 */
#ifndef TIMER_H
#define TIMER_H
#include <iostream>
#include <chrono>
#include <functional>
#include <thread>
#include <memory>
#include "SortedHeap.hpp"

class Timer{
    private:
        std::chrono::milliseconds tick;
        double timeline;     //当前时间线,long double的字节数为12
        bool isStart;        //标志当前定时器的启动状态
        struct SchedulerEvent{
          unsigned int id;                   //定时事件的唯一标示id
          double interval;                   //事件的触发间隔,在重复事件中会用到这个属性
          double deadline;                   //定时事件的触发时间
          std::function<void()> action;      //触发的事件
          bool isRepeat;                     //是否是重复执行事件
          SchedulerEvent( double interval, double timeline,std::function<void()> action,bool isRepeat){
              this->interval = interval;
              this->deadline = interval + timeline;
              this->action = action;
              this->isRepeat = isRepeat;
          }
        };

        SortedHeap<SchedulerEvent> eventQueue;

        /**
         * 执行到达期限的定时器
         */
        void loopForExecute();

        //私有的构造函数
        Timer(std::chrono::milliseconds tick):eventQueue(
            [](SchedulerEvent& a,SchedulerEvent& b){
                return a.deadline < b.deadline;
            }
        ){
            this->timeline = 0;
            this->tick = tick;
            this->isStart = false;
        }

    public:

        //单例模式
        static Timer* getInstance(std::chrono::milliseconds tick){
            static Timer timer(tick);
            return &timer;
        }

        /**
         * 设置定时器
         * @param interval 定时间隔
         * @param action 定时执行的动作
         * @param isRepeat 是否重复执行,默认不重复执行
         * @return unsigned int 定时器的id,可以根据这个id执行删除操作
         */
        unsigned int addEvent(double interval,std::function<void()> action,bool isRepeat = false);

        /**
         * 删除定时器
         * @param timerId 定时器id
         *
         */
        void deleteEvent(unsigned int timerId);

        /**
         * 同步执行启动定时器
         */
         void syncStart();

         /**
         * 异步执行启动定时器
         */
         void asyncStart();

};


#endif

Timer.cpp

#include "src/core/util/Timer.h"

unsigned int Timer::addEvent(double interval,std::function<void()> action,bool isRepeat){
    SchedulerEvent event(interval,this->timeline,action,isRepeat);
    return this->eventQueue.insertNode(event);
}

void Timer::deleteEvent(unsigned int timerId){
    this->eventQueue.deleteNode(timerId);
}

void Timer::loopForExecute(){
    std::unique_ptr<SchedulerEvent> top = this->eventQueue.getTopNode();
    while(top != nullptr && top->deadline <= this->timeline){
        //如果已经到了执行的时间,新开一个子线程执行任务
        std::thread t(top->action);
        t.detach();    //子线程分离

        if(top->isRepeat){
            //如果是重复事件,则重新添加
            this->addEvent(top->interval,top->action,top->isRepeat);
        }

        //从堆中删除
        this->eventQueue.deleteTopNode();
        top = this->eventQueue.getTopNode();
    }
    //执行一次后等待一个周期
    std::this_thread::sleep_for(this->tick);
    //周期增1
    this->timeline++;
}

void Timer::asyncStart(){
    if(!this->isStart){
        std::thread daemon_thread(&Timer::syncStart,this);
        daemon_thread.detach();     //从当前主线程分离
    }
}

void Timer::syncStart(){
    if(!this->isStart){
        while(1)
            this->loopForExecute();
    }
}

测试执行的代码

#include <iostream>
#include <chrono>
#include <ctime>
#include <iomanip>
#include <string>
#include <functional>
#include <thread>
#include <memory>
#include <fstream>
#include "src/core/util/Timer.h"

void myprint(std::string msg){
    std::ofstream of("timer.txt", std::ios::app);
    std::thread::id this_id = std::this_thread::get_id();
    auto t = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
    of << "From Thread " << this_id << "at time " << std::put_time(std::localtime(&t), "%Y-%m-%d %H.%M.%S") << ":" << msg << std::endl;
}

int main(){
    std::chrono::milliseconds tick(2000);       //1000毫秒作为一个周期
    Timer* timer = Timer::getInstance(tick);
    std::function<void()> f1 = std::bind(myprint,"第一个加入,10tick后执行");
    std::function<void()> f2 = std::bind(myprint,"第二个加入,被删除不执行");
    std::function<void()> f3 = std::bind(myprint,"第三个加入,每5tick重复执行");
    std::function<void()> f4 = std::bind(myprint,"第四个加入,5tick后执行");
    std::function<void()> f5 = std::bind(myprint,"第五个加入,5tick后执行");
    std::function<void()> f6 = std::bind(myprint,"第六个加入,5tick后执行");
    std::function<void()> f7 = std::bind(myprint,"第七个加入,5tick后执行");
    std::function<void()> f8 = std::bind(myprint,"第八个加入,5tick后执行");
    std::function<void()> f9 = std::bind(myprint,"第九个加入,5tick后执行");
    std::function<void()> f10 = std::bind(myprint,"第十个加入,5tick后执行");
    std::function<void()> f11 = std::bind(myprint,"第十一个加入,15tick后执行");
    std::function<void()> f12 = std::bind(myprint,"第十二个在执行后加入,20tick+5s后执行");

    timer->addEvent(10,f1);
    int id = timer->addEvent(11,f2);
    timer->addEvent(5,f3,true);
    timer->addEvent(5,f4);
    timer->addEvent(5,f5);
    timer->addEvent(5,f6);   
    timer->addEvent(5,f7);
    timer->addEvent(5,f8);
    timer->addEvent(5,f9);
    timer->addEvent(5,f10);
    timer->addEvent(15,f11);

    timer->deleteEvent(id);

    myprint("线程开始启动,每tick是2秒");

    //异步执行,程序退出后计时器也会终止,因此在下面使用while循环保证程序不会退出
    timer->asyncStart();
    //timer->syncStart();


    //休眠5秒钟
    std::this_thread::sleep_for(std::chrono::seconds(5));   
    //应该在大概20*tick+5秒后执行,
    //TODO 执行后加入的定时器不对
    timer->addEvent(20,f12);

    getchar();

    return 0;
}
分类:c++标签:#c++11 #多线程 #定时器

stream和buffer的概念解析

1.概念解析

buffer:内存中一块确定的临时存储区域。 stream:一段不确定长度的数据序列,可以认为stream就是I/O中input部分的FIFO实现,为了方便理解,可以用键盘输入作为输入来举例,从键盘中敲入的字符组成一段stream,先敲入的字符先被读取,也就是FIFO,但是读取的过程中,你并不知道总共要读取多少,也就是长度的不确定性。

2.具体使用时的区别与联系

在使用 stream 的过程中,我们遵循这样一个基本的过程,以从文件中读取为例: 1.程序向 stream 发起请求,要读取一个字节。 2.stream 在磁盘上定为到请求的字节,并发送给程序。 3.程序得到这个字节后,在进行请求,重复到第一步 这个样子就有一个很严重的效率问题,读取一个字节需要在程序和stream之间来回一次,那么1000个字节就是1000次。怎么解决这个问题呢? 这时候就到 buffer 出场了,buffer 根据上面的概念,他是内存中一块确定的临时存储区域。如果使用一个 1000 字节的 buffer ,那么程序和 stream 的关系就变成: 1.程序向stream发起请求,要求stream将buffer填满。 2.stream向buffer中填充字节,要么填满1000个字节为止,要么stream到达结尾为止。 3.程序从buffer中一次性获取1000字节的数据 很明显,使用buffer的好处在于减少的请求IO的次数,也大大提升了效率

分类:编程相关

LINUX下修改php.ini配置报错输出

首先是找到php.ini文件 输入 find / -name php.ini 总共有两个结果 /etc/php5/cli/php.ini /etc/php5/apache2/php.ini cli/php.ini指的是在控制台环境下运行php脚本使用的配置文件 apache2/php.ini是apache2环境下运行php脚本使用的配置文件 如果你还不确定是用哪一个,可以新建一个php代码 使用 echo phpinfo(); 来输出php信息,其中有一项是加载的php.ini路径。我的就是apache2/php.ini 然后编辑apache2/php.ini,我这里是要开启他的报错,不然如果代码中有错,浏览器访问就会直接报500错误 找到display_errors这一行,去除前面的; display_errors=On 配置错误级别 error_reporting=E_ALL & ~E_NOTICE 修改完成后要使php.ini配置生效,网上普遍的说法是重启apache服务生效 service apache2 restart 代码出错的地方仍然报500错误 准备去看apache的日志文件,同样使用find / -name error.log找到日志,由于日志太多,就把之前的error.log删掉,重启apache,浏览器打开出错页面,然后查看error.log。发现日志中是记录了代码出错的信息的。确定就是没有开启报错,导致500错误。 修改apache2.conf,在/etc/apache2下面。 添加 php_flag display_errors on php_value error_reporting 2039 重启apache service apache2 restart 浏览器打开错误页面,成功报错!

分类:linux

linux下一些好用的指令记录

查找当前目录下包含指定字符串的文件 grep -rn “hello,world!” *

  • : 表示当前目录所有文件,也可以是某个文件名 -r 是递归查找 -n 是显示行号 -R 查找所有文件包含子目录 -i 忽略大小写

mysql数据库相关操作 创建用户 mysql> insert into mysql.user(Host,User,Password) values(“localhost”,“phplamp”,password(“1234”)); 授权

grant all privileges on phplampDB.* to phplamp@localhost identified by ‘1234’; 删除用户 mysql>DELETE FROM user WHERE User=“phplamp” and Host=“localhost”; mysql>flush privileges; 修改密码 mysql>update mysql.user set password=password(‘新密码’) where User=“phplamp” and Host=“localhost”; mysql>flush privileges; 刷新系统权限表 mysql>flush privileges;

cinnamon桌面图标重复 gsettings set org.gnome.desktop.background show-desktop-icons false

分类:linux

java反射机制以及在android开发中的应用

java中的反射机制:只要给定类的名字就可以得到所有类的信息。因为这个类的名字是可以在代码运行时动态指定的,所以利用java的反射机制比通过new的方式要灵活的多 在java中,通过new的方式创建的对象称为静态加载(编译时加载类),通过反射机制创建对象称为动态加载(运行时加载类) java反射机制的使用方式 这里有个Human类如下 package com.myway5; public class Human { private String name; private int age; public String word;

public Human() {
    // TODO Auto-generated constructor stub
}

public Human(String name) {
    this.name = name;
}

public Human(String name, int age) {
    this.name = name;
    this.age = age;
}

public String getName() {
    return name;
}

public void setName(String name) {
    this.name = name;
}

public int getAge() {
    return age;
}

public void setAge(int age) {
    this.age = age;
}
public void speak(String word) {
     System.out.println(word);
 }

} 我们使用反射机制获取Human类所有的信息 package com.myway5; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Method; public class Client { public static void main(String[] args) { // 获取类名等信息 Class class1 = Human.class; System.out.println(class1.getName()); System.out.println(class1.getSimpleName());

    System.out.println("***********构造方法**********");

    Constructor[] cs = class1.getDeclaredConstructors();
    for (Constructor constructor : cs) {
        System.out.print(constructor.getName() + "(");
        Class[] paramsType = constructor.getParameterTypes();
        for (Class class2 : paramsType) {
            System.out.print(class2.getName() + ",");
        }
        System.out.println(")");
    }

    System.out.println("**********公共成员变量************");
    Field[] fields = class1.getFields();
    for (Field field : fields) {
        System.out.println(field.getType().getName() + ":" + field.getName());
    }

    System.out.println("**********公共方法**************");
    Method[] methods = class1.getMethods();
    for (Method method : methods) {
        System.out.print(method.getName() + "(");
        Class[] params = method.getParameterTypes();
        for (Class class3 : params) {
            System.out.print(class3.getName() + ",");
        }
        System.out.println(method.getReturnType().getName() + ")");
    }

}

} 输出结果如下: com.myway5.Human Human ***********构造方法********** com.myway5.Human(java.lang.String,int,) com.myway5.Human(java.lang.String,) com.myway5.Human() **********公共成员变量************ java.lang.String:word **********公共方法************** getName(java.lang.String) setName(java.lang.String,void) getAge(int) setAge(int,void) wait(long,int,void) wait(long,void) wait(void) equals(java.lang.Object,boolean) toString(java.lang.String) hashCode(int) getClass(java.lang.Class) notify(void) notifyAll(void)
可以看到,所有公共的成员变量,方法都可以通过这个方式获取到,那么怎么调用其中的方法呢 public static void useMethod() throws IllegalAccessException, IllegalArgumentException, InvocationTargetException, InstantiationException, NoSuchMethodException, SecurityException { Class class1 = Human.class; Human man = (Human) class1.newInstance();//通过newInstance创建实例 Method method = class1.getMethod(“speak”, String.class); Object object = method.invoke(man, new Object[] { “hello reflect” }); } android开发中的使用后续更新 继上文更新: android开发中常常会使用java的反射机制来更改一些系统底层无法更改的代码逻辑。比如说在AlertDialog的使用中,通过自带的setPositiveButton或者setNegativeButton时,一旦点击按钮都会退出dialog,有时候我们不希望他退出,比如用户登录时登录失败再次登录。那么一种解决办法 就是通过java的反射机制。 package com.myway5.java_reflect; import android.app.AlertDialog; import android.content.DialogInterface; import android.os.Handler; import android.os.Message; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import java.lang.ref.WeakReference; import java.lang.reflect.Field; public class MainActivity extends AppCompatActivity{

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    AlertDialog.Builder builder=new AlertDialog.Builder(this);

    builder.setMessage("Hello Dialog")
             .setTitle("对话框")
                .setView(getLayoutInflater().inflate(R.layout.dialog_signin,null));
    builder.setPositiveButton("确定", new DialogInterface.OnClickListener() {
        @Override
        public void onClick(DialogInterface dialog, int which) {

        }
    });
    builder.setNegativeButton("取消", new DialogInterface.OnClickListener() {
        @Override
        public void onClick(DialogInterface dialog, int which) {

        }
    });
    AlertDialog dialog=builder.create();
    try {
        Field field = dialog.getClass().getDeclaredField("mAlert");
        field.setAccessible(true);
        Object obj=field.get(dialog);
        field=obj.getClass().getDeclaredField("mHandler");
        field.setAccessible(true);
        field.set(obj,new ButtonHandler(dialog));
    }catch (Exception e){
        e.printStackTrace();
    }
    dialog.show();
}
private static final class ButtonHandler extends Handler {
    // Button clicks have Message.what as the BUTTON{1,2,3} constant
    private static final int MSG_DISMISS_DIALOG = 1;

    private WeakReference<DialogInterface> mDialog;

    public ButtonHandler(DialogInterface dialog) {
        mDialog = new WeakReference<DialogInterface>(dialog);
    }

    @Override
    public void handleMessage(Message msg) {
        switch (msg.what) {

            case DialogInterface.BUTTON_POSITIVE:
            case DialogInterface.BUTTON_NEGATIVE:
            case DialogInterface.BUTTON_NEUTRAL:
                ((DialogInterface.OnClickListener) msg.obj).onClick(mDialog.get(), msg.what);
                break;

            case MSG_DISMISS_DIALOG:
                //这里是点击后的逻辑,因为注释掉了dismiss(),dialog不会退出了
                //((DialogInterface) msg.obj).dismiss();
        }
    }
}

} 其中 try { Field field = dialog.getClass().getDeclaredField(“mAlert”); field.setAccessible(true); Object obj=field.get(dialog); field=obj.getClass().getDeclaredField(“mHandler”); field.setAccessible(true); field.set(obj,new ButtonHandler(dialog)); }catch (Exception e){ e.printStackTrace(); } 这个部分就是通过反射获取AlertDialog的私有变量mAlert,然后获取mAlert的成员变量mHandler,将这个Handler设置成我们自己的ButtonHandler,这样就解决了点击后退出的问题 代码地址:https://github.com/joyme123/java\_reflect 参考文章:http://www.oschina.net/question/163910\_27112

分类:java

android studio导入项目出现Gradle version 1.10 is required. Current version is 2.0的解决办法

1.首先在项目下找到gradlew.bat这个文件,单击运行。它会下载Gradle 1.10 。等待任务完成 2.在项目的报错的控制台中选择setting这个选项。然后设置本地的Gradle。我的是默认放在 C:\Users\joyme.gradle\wrapper\dists\gradle-1.10-all\bif7dlrky2i400uf9zsz2c0my\gradle-1.10

分类:问题

vitamio使用出现找不到文件的解决方法

首先我遇到这个问题肯定不是一般的找不到文件的错误。使用vitamio的VideoView的时候,出现一部分视频会找不到文件。 分析很久问题出现的原因,最后得出是因为这部分视频名都是URLEncode过的,可能是这个原因导致字符串在解析的过程中出现问题。然后跟踪代码到VideoView的setVideoPath处,发现他的实现如下 public void setVideoPath(String path) { setVideoURI(Uri.parse(path)); } 很明显这里就是导致错误所在,所以解决办法就是将string 类型的path先编码一下 videoView.setVideoPath(URLEncoder.encode(path, “UTF-8”));

分类:问题

java常量池

package 测试常量; public class Test { public static void main(String[] args){ String s1 = “hello world”; String s2 = “hello world”; System.out.println(s1==s2); String s4 = new String(“你好”); String s3 = “你好”; System.out.println(s3==s4); Integer i1 = 10; Integer i2 = 10; System.out.println(i1 == i2); Integer i3 = 2000; Integer i4 = 2000; System.out.println(i3 == i4); Integer i5 = new Integer(10); Integer i6 = new Integer(10); System.out.println(i5 == i6); int i7 = 2000; int i8 = 2000; System.out.println(i7 == i8); Double f1 = new Double(1.01); Double f2 = new Double(1.01); System.out.println(f1 == f2); Double f3 = 1000.01; Double f4 = 1000.01; System.out.println(f3 == f4); double f5 = 1000.01; double f6 = 1000.01; System.out.println(f5 == f6); } }


输出结果是 true false true false false true false false true

分类:java

ubuntu下linux创建桌面启动器以及菜单栏点击失效的解决方法

创建桌面启动器 进入/usr/share/applications vi eclipse.desktop 输入一下内容 [Desktop Entry] Encoding=UTF-8 Name=eclipse Comment=Eclipse IDE Exec=/home/jiang/eclipse/eclipse Icon=/home/jiang/eclipse/icon.xpm Terminal=false StartupNotify=true Type=Application Categories=Application;Development; Exec=env UBUNTU_MENUPROXY=0 /home/jiang/eclipse/eclipse 保存后,将这个文件复制到桌面文件夹 cp /usr/share/applications/eclipse.desktop /home/jiang/桌面 对于eclipse打不开菜单的情况,可以修改eclipase文件夹下的eclipse.ini为 -startup plugins/org.eclipse.equinox.launcher_1.3.100.v20150511-1540.jar –launcher.GTK_version 2 –launcher.library plugins/org.eclipse.equinox.launcher.gtk.linux.x86_64_1.1.300.v20150602-1417 -product org.eclipse.epp.package.jee.product –launcher.defaultAction openFile -showsplash org.eclipse.platform –launcher.XXMaxPermSize 256m –launcher.defaultAction openFile –launcher.appendVmargs -vmargs -Dosgi.requiredJavaVersion=1.7 -XX:MaxPermSize=256m -Xms256m -Xmx2048m ———————————— –launcher.GTK_version 2 这一部分是新增的部分

分类:linux

linux下编译安装mysql-connector-cpp

首先在github上下载mysql-connector-cpp git clone https://github.com/mysql/mysql-connector-cpp.git git上面有两个分支,2.0版本是支持mysql作为文档存储的接口,1.1版本是支持正常的关系数据库存储接口 首先我们编译安装2.0版本,进入代码目录执行 cmake . cmake –build . –config CCC cmake –build . –target install –config CCC 到这里已经安装完毕. 然后到testapp下编译测试样例 cmake . 提示出错,WITH-CONCPP没有设置,我试了编辑/etc/profile文件,设置这个变量为/usr/local/mysql/connector-c++-2.0/,但是仍然提示没有设置,于是我将CMakeLists中的 set(WITH_CONCPP $ENV{WITH_CONCPP} CACHE PATH “MySQL Connector/C++ 2.0 install location”) 改为 set(WITH_CONCPP “/usr/local/mysql/connector-c++-2.0/” CACHE PATH “MySQL Connector/C++ 2.0 install location”) 继续cmake . 提示出错,在/usr/local/mysql/connector-c++-2.0/lib64下找不到依赖库 那么将目录下生成的 libmysqlcppconn2.so libmysqlcppconn2.so.1 libmysqlcppconn2.so.1.0 拷贝到/usr/local/mysql/connector-c++-2.0/lib64中,没有的创建一个即可。 cmake . make 在run文件夹下面生成了两个可执行文件 devapi_test xapi_test 这两个文件的区别在于,xapi_test实现了c的接口,是通过mysql的一个叫做x plugin的插件连接mysql的,而devapi则是使用了c++的接口。所以如果使用xapi接口的话,mysql安装时必须安装x plugin 接下来编译安装1.1版本,先将当前的改动都commit了,然后 git checkout 1.1, cmake . 如果出错。 报缺少boost库错误,可以 sudo apt-get install libboost-all-dev 报缺少mysql.h等错误,可以通过sudo apt-get install libmysql++-dev 然后make sudo make install 然后将生成的动态库拷贝的/usr/local/lib下 sudo cp libmysqlcppconn.so* /usr/local/lib/

分类:linux