java 同一棵树

Recursive
public boolean isSameTree(TreeNode root1,TreeNode root2){
  if(root1==root2==null) return true;
  if(root1!=root2) return false;
  else return isSameTree(root1.left,root2.left)&&isSameTree(root1.right,root2.right);
}

java 拓扑排序

tuopu
public class Topu{
    private Map<Integer,List<Integer>> map = new HashMap<>();
    private List<List<Integer,Integer>> side = new ArrayList<>(){{}};
    private int[] in_degree = new int[n];
    private List<Integer> queue = new ArrayList<>();

    public void creat_linkedlist(){
        for(int i = 0 ;  i < side.length; i++){
            List<Integer,Integer> cur = side.get(i);
            if(map.get(cur.get(0))==null){
                map.put(cur.get(0),new ArrayList<Integer>());
            }
            map.get(cur.get(0)).add(cur.get(1));
            in_degree[cur.get(1)]++;
        }

        for(int i = 0 ; i < in_degree.length; i++){
            if(in_degree[i]==0){
                queue.add(i);
            }
        }
    }

    public List<Integer> tuopu(){
        List<Integer> list = new ArrayList<Integer>();
        while(!queue.isEmpty()){
            int cur = queue.remove(0);
            list.add(cur);
            List<Integer> list = map.get(cur);
            for(int a : list){
                in_degree[a]--;
                if(in_degree[a]==0){
                    queue.add(a);
                }
            }
        }
        return list;
    }
}

java JPA注释

Common annotations.java
// Identity based column, e.g.
// [my_id]  int  identity (1,1) not null
@Id
@GeneratedValue(strategy = IDENTITY)
@Column(name = "MY_ID")
long id;

// Many to one
// Transaction.java
@ManyToOne
@JoinColumn(name = "TRN_PARENT_ID", insertable = false, updatable = false)
private Parent parent;

// Parent.java
@OneToMany(cascade = ALL, orphanRemoval = true)
@JoinColumn(name = "TRN_PARENT_ID", nullable = false)
private List<Transaction> transactions = new ArrayList<>();

public void addTransaction(Transaction trn) {
   transactions.add(trn);
   trn.setParent(this);
}

@DateTimeFormat(iso = ISO.DATE)
@Column(name = "EMS_COMMENCEMENT_DATE")
@NotNull(message = "The start date must be specified")
private LocalDate commencesOnDate;

@Column(name = "EMS_MESSAGE_TYPE")
@Enumerated(EnumType.STRING)
private MessageTypetype;

public enum MessageType {
  ITEM_ONE, ITEM_TWO
}
Data Access Object.java
@Slf4j
@Repository
@Transactional
public class JpaAutoAllocationDao {
  
  @PersistenceContext(unitName = "azure")
  private EntityManager entityManager;
  
  private final DataSource paDatasource;

  @SuppressWarnings("unchecked")
  public List<Transaction> getTransactions(Account account) {
    return entityManager
            .createQuery(
                    "SELECT t FROM Transaction t WHERE t.account = ?1 AND t.inserted >= ?2 AND t.amount <> 0")
            .setParameter(1, account.getAccountNumber())
            .setParameter(2, account.getStartDate())
            .getResultList();
  }
  
  
  public void create(Domain domain) {
    entityManager.persist(domain);
  }
  
  public Domain getDomain(long id) {
    return entityManager.find(Domain.class, id);
  }
  
  public Domain update(Domain domain) {
    return entityManager.merge(domain);
  }
}

java 的DataHandler

DataHandler.java
package me.kodysimpson.quartermaster.utils;

import me.kodysimpson.quartermaster.QuarterMaster;
import org.bukkit.Bukkit;
import org.bukkit.block.Block;
import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.entity.Player;
import org.bukkit.metadata.FixedMetadataValue;
import org.bukkit.metadata.MetadataValue;

import java.io.File;
import java.util.UUID;

public class DataHandler {

    //An easy way to access and manage player config files

    QuarterMaster plugin;
    Player p;
    UUID uuid;
    File userFile;
    FileConfiguration userConfig;

    public DataHandler(Player p){
        this.p = p;
        this.plugin = QuarterMaster.getPlugin();
        this.uuid = p.getUniqueId();
        userFile = new File(plugin.getDataFolder(), uuid.toString() + ".yml");
        userConfig = YamlConfiguration.loadConfiguration(userFile);
    }

    public void createPlayerFile(){
        if ( !(userFile.exists()) ) { //If file does not exist already, create one
            try {
                YamlConfiguration UserConfig = YamlConfiguration.loadConfiguration(userFile);

                UserConfig.set("Player.Info.name", p.getName());
                UserConfig.set("Player.Info.uuid", p.getUniqueId().toString());
                UserConfig.set("Player.Info.ip", p.getAddress().getAddress().getHostAddress());

                UserConfig.set("Player.LockData.locks-count", 0);

                UserConfig.save(userFile);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    public FileConfiguration getUserFile(){
        return userConfig;
    }


    public void saveUserFile() {
        try {
            getUserFile().save(userFile);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}

java 意图

用于切换活动

intent.java
// Call this method on Button Click

private void startquiz()
    {
        Intent intent = new Intent(MainActivity.this,QuizActivity.class);
        startActivity(intent);
    }

java MyBatis的批量插入

untitled
SqlSession session = sqlSessionFactory.openSession(ExecutorType.BATCH);
try {
    SimpleTableMapper mapper = session.getMapper(SimpleTableMapper.class);
    List<SimpleTableRecord> records = getRecordsToInsert(); // not shown
 
    BatchInsert<SimpleTableRecord> batchInsert = insert(records)
            .into(simpleTable)
            .map(id).toProperty("id")
            .map(firstName).toProperty("firstName")
            .map(lastName).toProperty("lastName")
            .map(birthDate).toProperty("birthDate")
            .map(employed).toProperty("employed")
            .map(occupation).toProperty("occupation")
            .build()
            .render(RenderingStrategy.MYBATIS3);
 
    batchInsert.insertStatements().stream().forEach(mapper::insert);
 
    session.commit();
} finally {
    session.close();
}

java 服务总线与春天

pom.xml
<!-- Snippets only -->

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.1.4.RELEASE</version>
    <relativePath/>
</parent>

<properties>
    <servicebus.version>2.1.6</servicebus.version>
</properties>

<!--
Use later version of azure-servicebus than that provided by
azure-servicebus-spring-boot-starter (optional)
-->
<dependency>
  <groupId>com.microsoft.azure</groupId>
  <artifactId>azure-servicebus</artifactId>
  <version>1.2.11</version>
</dependency>

<!--
Azure Boot Starter for Service Bus
https://mvnrepository.com/artifact/com.microsoft.azure/azure-servicebus-spring-boot-starter
-->
<dependency>
  <groupId>com.microsoft.azure</groupId>
  <artifactId>azure-servicebus-spring-boot-starter</artifactId>
  <version>${servicebus.version}</version>
</dependency>
application.properties
# Topic
azure.servicebus.topic-name=x-events

# Queue
azure.servicebus.queue-name=x-mailer
azure.servicebus.queue-receive-mode=peeklock

# Subscribe to Topic
azure.servicebus.subscription-name=appian
azure.servicebus.subscription-receive-mode=peeklock

# Service Bus Connection String (without Key Vault)
azure.servicebus.connection-string=Endpoint=sb://sb-doubledecker-ric-eun.servicebus.windows.net/;SharedAccessKeyName=x-events-send-listen;SharedAccessKey=KmsawwhktZgY8gfb7g1rH6iXNJv9Kr3r1pLWol3o2Ik=;EntityPath=x-events
# & with Key Vault (making sure the key vault key name is unique)
azure.servicebus.connection-string=${events-servicebus-connectionstring}
TopicPublisher.java
@Component
@Slf4j
public class EventTopicPublisher {

    private TopicClient topicClient;

    private TelemetryClient telemetry;

    public EventTopicPublisher(TopicClient topicClient, TelemetryClient telemetry) {
        this.topicClient = topicClient;
        this.telemetry = telemetry;
    }

    boolean publishTopicMessage(String code, String messageBody) {
        log.debug("Sending event: {} of type '{}'", messageBody, code);
        final Message message = new Message(messageBody, MediaType.APPLICATION_JSON_VALUE);
        Map<String, String> props = new HashMap<>();
        props.put("X-Event-Code", code);
        message.setProperties(props);

        boolean success = false;
        long startTime = System.currentTimeMillis();

        try {
            topicClient.send(message);
            success = true;
        } catch (InterruptedException | ServiceBusException e) {
            telemetry.trackException(e);
        } finally {
            Duration delta = new Duration(System.currentTimeMillis() - startTime);
            RemoteDependencyTelemetry dependency = new RemoteDependencyTelemetry("x-events", "send", delta, success);
            dependency.setType("Azure Service Bus");
            telemetry.trackDependency(dependency);
        }
        return success;
    }

    @PreDestroy
    public void shutdown() throws ServiceBusException {
        log.debug("Shutting down, closing topic client {}", topicClient.getTopicName());
        topicClient.close();
    }

}
TopicSubscriber.java
@Component
@Slf4j
public class AppianTopicSubscriber implements IMessageHandler {

    private TopicClient topicClient;

    private SubscriptionClient subscriptionClient;

    private TelemetryClient telemetry;

    public AppianTopicSubscriber(TopicClient topicClient, SubscriptionClient subscriptionClient, TelemetryClient telemetry) {
        this.topicClient = topicClient;
        this.subscriptionClient = subscriptionClient;
        this.telemetry = telemetry;
    }

    @PostConstruct
    public void startup() throws ServiceBusException, InterruptedException {
        // start receiving messages
        ExecutorService executorService = Executors.newCachedThreadPool();
        subscriptionClient.registerMessageHandler(this, new MessageHandlerOptions(1, false, ofMinutes(1)), executorService);
    }

    /**
     * The callback that the message pump uses to pass received Messages to the app.
     *
     * @param message received message
     */
    @Override
    public CompletableFuture<Void> onMessageAsync(IMessage message) {
        final String messageBody = new String(message.getBody(), UTF_8);
        boolean success = false;
        long startTime = System.currentTimeMillis();
        try {
            log.debug("Received message from topic: '{}'", messageBody);
            success = processMessageInSomeWay(messageBody);
            if (success) {
                subscriptionClient.complete(message.getLockToken());
            }
            else {
                subscriptionClient.abandon(message.getLockToken());
            }
        } catch (InterruptedException | ServiceBusException e) {
            telemetry.trackException(e);
        } finally {
            trackServiceBusTelemetry(success, startTime);
        }

        return completedFuture(null);
    }

    private void trackServiceBusTelemetry(boolean success, long startTime) {
        Duration delta = new Duration(System.currentTimeMillis() - startTime);
        RemoteDependencyTelemetry dependency = new RemoteDependencyTelemetry("x-events", "receive", delta, success);
        dependency.setType("Azure Service Bus");
        telemetry.trackDependency(dependency);
    }

    @Override
    public void notifyException(Throwable exception, ExceptionPhase phase) {
        ExceptionTelemetry et = new ExceptionTelemetry(exception);
        et.setSeverityLevel(SeverityLevel.Critical);
        telemetry.trackException(et);
    }

    @PreDestroy
    public void shutdown() throws ServiceBusException {
        topicClient.close();
    }
}

java 带有Spring Boot 1.5的JPA

基于:<br/> - Spring Boot 1.5 <br/> - Spring 4.3 <br/> - Spring Data JPA 1.11(Ingalls)<br/> - Hibernate 5.0

Entity.java
@Entity
@EntityListeners(AuditingEntityListener.class)
@Table(name = "EFE_MESSAGES")
@Where(clause = "EMS_DATE_TIME > '01-JAN-2015'")
@GenericGenerator(name = "increment",strategy = "increment")

// Lombok method generators
@Builder
@Data
@NoArgsConstructor
@AllArgsConstructor

// Ensures commencesOnDate is earlier than expiresAfterDate
// @MessageDateRange
@ScriptAssert(lang ="javascript", script ="_this.validateStartBeforeEnd()", message = "The start date must be at least one day before the expiry date")
public class Message {

    /**
     * The <code>EMS_NUMBER</code>column is used as the PK, and maintained using MAX+1 strategy -
     * see the Hibernate-specific {@link GenericGenerator}<code>increment</code>annotation.
     */
    @Id
    @Builder.Default
    @Column(name = "EMS_NUMBER")
    @Setter(AccessLevel.PROTECTED)
    @GeneratedValue(generator = "increment")
    private Long        id = null;

    @CreatedDate
    @Column(name = "EMS_DATE_TIME")
    @DateTimeFormat(iso = ISO.DATE)
    private LocalDate  createdDate;

    @CreatedBy
    @Column(name ="EMS_ADDED_BY")
    private String     addedBy;

    @Column(name ="EMS_TARGET_USER")
    private String     targetUser;

    @DateTimeFormat(iso = ISO.DATE)
    @Column(name = "EMS_COMMENCEMENT_DATE")
    @NotNull(message = "The start date must be specified")
    private LocalDate  commencesOnDate;

    @NotNull
    @NonNull
    @Column(name = "EMS_MESSAGE_TYPE")
    @Enumerated(EnumType.STRING)
    private MessageTypetype;

    public boolean isNew() {
        return getId() == null;
    }

    /**
     * Used by the Hibernate-specific {@link ScriptAssert} annotation on this class.
     *
     * @return true if the commencement date is before the end date, or if either of the dates are
     *         null (null values preclude proper validation).
     */
    public boolean validateStartBeforeEnd() {
        // Ignore if either of the dates are null, as the validation can't be made
        if (getCommencesOnDate() == null || getExpiresAfterDate() == null ) return true;
        return getCommencesOnDate().isBefore(getExpiresAfterDate());
    }

    public boolean isCurrent() {
        return getNullSafeExpiresAfterDate().isAfter(LocalDate.now()) && getNullSafeCommencesOnDate().isBefore(LocalDate.now().plusDays(1));
    }
}
JpaConfig.java
@Configuration
@PropertySource("classpath:jpa-${spring.profiles.active}.properties")

// Trigger Spring Data JPA goodness
@EnableJpaRepositories

// Enable auditing of who creates/updates an Entity and when
@EnableJpaAuditing

// For jsr310 java 8 java.time.*
// Configures the base packages used by auto-configuration when scanning for entity classes.
@EntityScan(basePackageClasses = { Message.class, Jsr310JpaConverters.class })
public class JpaConfig {

    /**
     * Bean for allowing the Jpa Auditing feature to access the authenticated user's username.
     */
    @Bean
    public AuditorAware<String> auditorProvider() {
        return new SpringSecurityAuditorAware();
    }

    /**
     * @return configuration for the {@link #appDataSource()} for the {@link Message} domain model.
     *         The properties come from <code>application.properties</code>.
     */
    @Bean
    @Primary
    @ConfigurationProperties("datasource.app")
    public DataSourceProperties appDataSourceProperties() {
        return new DataSourceProperties();
    }

    @Primary
    @Bean("dataSource")
    @ConfigurationProperties("datasource.app")
    public DataSource appDataSource() {
        return appDataSourceProperties().initializeDataSourceBuilder().build();
    }

    /**
     * @return configuration for the {@link #securityDataSource()} for the security infrastructure.
     *         The properties come from <code>application.properties</code>.
     */
    @Bean
    @ConfigurationProperties("datasource.security")
    public DataSourceProperties securityDataSourceProperties() {
        return new DataSourceProperties();
    }

    @Bean
    @ConfigurationProperties("datasource.security")
    public DataSource securityDataSource() {
        return securityDataSourceProperties().initializeDataSourceBuilder().build();
    }

}
Repo.java
import java.time.LocalDate;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.querydsl.QueryDslPredicateExecutor;

import com.hansard.messaging.domain.Message;
import com.hansard.messaging.domain.QMessage;
import com.querydsl.core.types.dsl.BooleanExpression;

/**
 * Spring Data JPA repository interface (no implementation required) for {@link Message}.
 * 
 * <p>
 * The {@link QueryDslPredicateExecutor} is used in conjunction with Maven artifact
 * <code>com.querydsl.querydsl-jpa</code>. The corresponding {@link QMessage} class is generated to
 * <code>target/generated-sources/java</code>.
 * 
 * <p>
 * A few DSL Expressions are implemented here for convenience. See {@link #isCurrent()} etc.
 * 
 * @author Richard Cowin
 */
public interface MessageRepository extends JpaRepository<Message, Long>, QueryDslPredicateExecutor<Message> {

    // Effectively "where UPPER(messageHtml) like '%UPPER(textToSearch)%'"
    static BooleanExpression messageContains(String textToSearch) {
        return QMessage.message.messageHtml.containsIgnoreCase(textToSearch);
    }

    static BooleanExpression isExpired() {
        return QMessage.message.expiresAfterDate.loe(LocalDate.now());
    }

    static BooleanExpression isPending() {
        return QMessage.message.commencesOnDate.after(LocalDate.now());
    }

    static BooleanExpression isCurrent() {
        BooleanExpression beforeExpiry = QMessage.message.expiresAfterDate.after(LocalDate.now());
        BooleanExpression afterCommencement = QMessage.message.commencesOnDate.loe(LocalDate.now());
        return beforeExpiry.and(afterCommencement);
    }

}
application.properties
spring.jpa.database-platform=org.hibernate.dialect.Oracle10gDialect
spring.jpa.show-sql=true
 
datasource.app.type=org.apache.tomcat.jdbc.pool.DataSource
datasource.app.maximum-pool-size=8
datasource.app.url=jdbc:oracle:thin:@server:port:SID
datasource.app.username=username
datasource.app.password=passw0rd
pom.xml
<!-- Snippets only -->
<parent>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-parent</artifactId>
   <version>1.5.3.RELEASE</version>
   <relativePath />
</parent>
 
<properties>
   <hibernate-validator.version>5.4.1.Final</hibernate-validator.version>
</properties>
 
<dependency>
   <groupId>org.projectlombok</groupId>
   <artifactId>lombok</artifactId>
   <scope>provided</scope>
</dependency>
 
<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
 
<dependency>
   <groupId>com.querydsl</groupId>
   <artifactId>querydsl-jpa</artifactId>
</dependency>

<dependency>
   <groupId>com.querydsl</groupId>
   <artifactId>querydsl-apt</artifactId>
</dependency>
 
<build>

      <plugin>
         <!-- Remember to run mvn eclipse:eclipse, and mvn install -->
         <!-- Generated sources need to be put on Eclipse's Build Path. -->
         <!-- Also put querydsl-codegen jar file onto project's Java Compiler 
            | Annotation Processing | Factory Path. -->
         <groupId>com.mysema.maven</groupId>
         <artifactId>apt-maven-plugin</artifactId>
         <version>1.1.3</version>
         <executions>
            <execution>
               <goals>
                  <goal>process</goal>
               </goals>
               <configuration>
                  <outputDirectory>target/generated-sources/java</outputDirectory>
                  <processor>com.querydsl.apt.jpa.JPAAnnotationProcessor</processor>
               </configuration>
            </execution>
         </executions>
      </plugin>

</build>
Use in Controller.java
@Controller
@RequestMapping("/messages")

// query and status are stored to help with searching
// message is stored for form handling
@SessionAttributes({ "query", "status", "message" })
public class MessageController {

    @Autowired
    private MessageService messageService;

    @GetMapping("/")
    public String searchMessages(@PageableDefault(sort = "createdDate", direction = Direction.DESC, size = 5) Pageable pageable,
            @RequestParam(required = false) String query, @RequestParam(required = false) String status, ModelMap model) {

        BooleanExpression searchExpr;

        String statusSearch = getFromRequestOrModel(status, model, "status", "current");
        switch (statusSearch) {
            case "current":
                searchExpr = MessageRepository.isCurrent();
                break;

            case "expired":
                searchExpr = MessageRepository.isExpired();
                break;

            // Pending
            default:
                searchExpr = MessageRepository.isPending();
                break;
        }

        String textSearch = getFromRequestOrModel(query, model, "query", "");
        if (StringUtils.hasText(textSearch)) {
            searchExpr = searchExpr.and(MessageRepository.messageContains(textSearch));
        }

        Page<Message> messages = messageService.findAll(searchExpr, pageable);

        Order order = messages.getSort().iterator().next();
        model.addAttribute("sort", order.getProperty() + "," + order.getDirection());

        model.addAttribute("messages", messages);
        return "messages";
    }
}

java SquareFrameLayout.java

SquareFrameLayout.java
package net.twisterrob.android.view;

import android.content.Context;
import android.util.AttributeSet;
import android.widget.FrameLayout;

public class SquareFrameLayout extends FrameLayout {
	public SquareFrameLayout(Context context) {
		super(context);
	}

	public SquareFrameLayout(Context context, AttributeSet attrs) {
		super(context, attrs);
	}

	public SquareFrameLayout(Context context, AttributeSet attrs, int defStyle) {
		super(context, attrs, defStyle);
	}

	@Override
	public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
		//noinspection SuspiciousNameCombination
		super.onMeasure(widthMeasureSpec, widthMeasureSpec);
	}
}

java CountDownTimer.java

CountDownTimer.java
/*
 * Copyright (C) 2008 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package alt.android.os;

import android.util.Log;
import android.os.Handler;
import android.os.SystemClock;
import android.os.Message;

/**
 * Schedule a countdown until a time in the future, with
 * regular notifications on intervals along the way.
 *
 * Example of showing a 30 second countdown in a text field:
 *
 * <pre class="prettyprint">
 * new CountdownTimer(30000, 1000) {
 *
 *     public void onTick(long millisUntilFinished) {
 *         mTextField.setText("seconds remaining: " + millisUntilFinished / 1000);
 *     }
 *
 *     public void onFinish() {
 *         mTextField.setText("done!");
 *     }
 *  }.start();
 * </pre>
 *
 * The calls to {@link #onTick(long)} are synchronized to this object so that
 * one call to {@link #onTick(long)} won't ever occur before the previous
 * callback is complete.  This is only relevant when the implementation of
 * {@link #onTick(long)} takes an amount of time to execute that is significant
 * compared to the countdown interval.
 */
public abstract class CountDownTimer {

    /**
     * Millis since epoch when alarm should stop.
     */
    private final long mMillisInFuture;

    /**
     * The interval in millis that the user receives callbacks
     */
    private final long mCountdownInterval;

    private long mStopTimeInFuture;

    private boolean mCancelled = false;

    /**
     * @param millisInFuture The number of millis in the future from the call
     *   to {@link #start()} until the countdown is done and {@link #onFinish()}
     *   is called.
     * @param countDownInterval The interval along the way to receive
     *   {@link #onTick(long)} callbacks.
     */
    public CountDownTimer(long millisInFuture, long countDownInterval) {
        mMillisInFuture = millisInFuture;
        mCountdownInterval = countDownInterval;
    }

    /**
     * Cancel the countdown.
     *
     * Do not call it from inside CountDownTimer threads
     */
    public final void cancel() {
        mHandler.removeMessages(MSG);
        mCancelled = true;
    }

    /**
     * Start the countdown.
     */
    public synchronized final CountDownTimer start() {
        if (mMillisInFuture <= 0) {
            onFinish();
            return this;
        }
        mStopTimeInFuture = SystemClock.elapsedRealtime() + mMillisInFuture;
        mHandler.sendMessage(mHandler.obtainMessage(MSG));
        mCancelled = false;
        return this;
    }


    /**
     * Callback fired on regular interval.
     * @param millisUntilFinished The amount of time until finished.
     */
    public abstract void onTick(long millisUntilFinished);

    /**
     * Callback fired when the time is up.
     */
    public abstract void onFinish();


    private static final int MSG = 1;


    // handles counting down
    private Handler mHandler = new Handler() {

        @Override
        public void handleMessage(Message msg) {

            synchronized (CountDownTimer.this) {
                final long millisLeft = mStopTimeInFuture - SystemClock.elapsedRealtime();

                if (millisLeft <= 0) {
                    onFinish();
                } else if (millisLeft < mCountdownInterval) {
                    // no tick, just delay until done
                    sendMessageDelayed(obtainMessage(MSG), millisLeft);
                } else {
                    long lastTickStart = SystemClock.elapsedRealtime();
                    onTick(millisLeft);

                    // take into account user's onTick taking time to execute
                    long delay = lastTickStart + mCountdownInterval - SystemClock.elapsedRealtime();

                    // special case: user's onTick took more than interval to
                    // complete, skip to next interval
                    while (delay < 0) delay += mCountdownInterval;

                    if (!mCancelled) {
                        sendMessageDelayed(obtainMessage(MSG), delay);
                    }
                }
            }
        }
    };
}